@taipeistudio/gamekit-career 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +36 -0
- package/README.md +50 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/lifestyle.d.ts +24 -0
- package/dist/lifestyle.d.ts.map +1 -0
- package/dist/lifestyle.js +38 -0
- package/dist/lifestyle.js.map +1 -0
- package/dist/money.d.ts +25 -0
- package/dist/money.d.ts.map +1 -0
- package/dist/money.js +59 -0
- package/dist/money.js.map +1 -0
- package/dist/portfolio.d.ts +105 -0
- package/dist/portfolio.d.ts.map +1 -0
- package/dist/portfolio.js +167 -0
- package/dist/portfolio.js.map +1 -0
- package/dist/social.d.ts +28 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +29 -0
- package/dist/social.js.map +1 -0
- package/dist/sponsors.d.ts +51 -0
- package/dist/sponsors.d.ts.map +1 -0
- package/dist/sponsors.js +74 -0
- package/dist/sponsors.js.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
MIT License (with Attribution)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rosdyana Kusuma
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
1. Copyright notice. The above copyright notice and this permission notice
|
|
13
|
+
shall be included in all copies or substantial portions of the Software,
|
|
14
|
+
including modified versions. The copyright notice must not be removed or
|
|
15
|
+
altered; you may add your own notice for your modifications alongside it.
|
|
16
|
+
|
|
17
|
+
2. Attribution. Any product, game, application, website or service that
|
|
18
|
+
includes the Software or a work derived from it, in source, compiled,
|
|
19
|
+
bundled or minified form, must give visible credit to the author where end
|
|
20
|
+
users can reasonably find it (for example a credits screen, an "About"
|
|
21
|
+
page, or the product's documentation or store listing). The credit must be
|
|
22
|
+
substantially similar to:
|
|
23
|
+
|
|
24
|
+
gamekit by Rosdyana Kusuma - https://github.com/rosdyana/gamekit
|
|
25
|
+
|
|
26
|
+
3. No endorsement. The name of the copyright holder may not be used to endorse
|
|
27
|
+
or promote products derived from the Software without prior written
|
|
28
|
+
permission.
|
|
29
|
+
|
|
30
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
31
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
32
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
33
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
34
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
35
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
36
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @taipeistudio/gamekit-career
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@taipeistudio/gamekit-career) [](LICENSE)
|
|
4
|
+
|
|
5
|
+
The money and public-life side of an athlete career game, as plain data and pure functions.
|
|
6
|
+
Anything random takes a `rand: () => number` (pass a seeded one, e.g. from
|
|
7
|
+
`@taipeistudio/gamekit-rng`); it uses the same noise as gamekit-rng, draw for draw.
|
|
8
|
+
Actions return an error message (string) when refused and never draw random numbers then.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @taipeistudio/gamekit-career
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## API
|
|
17
|
+
|
|
18
|
+
- **Money**: `roundMoney` (price-like rounding), `shortMoney` ($12.5K, $3.2M), `spend(wallet,
|
|
19
|
+
amount)`, `convert(amount, from, to, perDollar)`, `progressiveTax(income, brackets)`,
|
|
20
|
+
`splitTax(income, places)` (per-state "jock tax").
|
|
21
|
+
- **Portfolio**: `blankPortfolio`, `deposit`/`withdraw` (savings, index, crypto),
|
|
22
|
+
`buyProperty`/`sellProperty`, `openBusiness(wallet, p, kind, cost, make)`/`sellBusiness`,
|
|
23
|
+
`netWorth(cash, p, extra)`, `recordWorth`, and `marketWeek(p, rand, { businesses, fame,
|
|
24
|
+
scale, priceOf, rates })`, which returns the week's income and events (crypto crash or moon,
|
|
25
|
+
business slump, bust or boom). `DEFAULT_RATES` holds the yearly rates.
|
|
26
|
+
- **Sponsors**: `sponsorKind(rand, fame, image)` (local → clean or edgy by image),
|
|
27
|
+
`sponsorPay`, `imageWindow`, `acceptSponsor` (per-kind cap), `declineSponsor`,
|
|
28
|
+
`sponsorExitFee`/`cancelSponsor` (buy-outs), `sponsorUpkeep` (expiry and image clauses),
|
|
29
|
+
`sponsorIncome`, `sponsorBonus`.
|
|
30
|
+
- **Social**: `followersFor(fame)`, `socialWeek(p)` (followers drift to target, fame fades,
|
|
31
|
+
notoriety bonus), `viral(p, rand, size)`, `followerLabel`.
|
|
32
|
+
- **Lifestyle and tiers**: `lifestyleBlocked` (weeks of cost in the bank), `buyTier` (houses,
|
|
33
|
+
cars), `tierOf`/`tierProgress` (bronze/silver/gold style thresholds).
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { asFn } from "@taipeistudio/gamekit-rng";
|
|
37
|
+
import { blankPortfolio, deposit, marketWeek, socialWeek } from "@taipeistudio/gamekit-career";
|
|
38
|
+
|
|
39
|
+
const s = { rngState: 42, money: 50_000, fame: 30, image: 10, followers: 2_000, invest: blankPortfolio() };
|
|
40
|
+
deposit(s, s.invest, "index", 20_000);
|
|
41
|
+
const { income, events } = marketWeek(s.invest, asFn(s), { businesses: {}, fame: s.fame });
|
|
42
|
+
s.money += income;
|
|
43
|
+
socialWeek(s);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
MIT with attribution. Part of [gamekit](https://github.com/rosdyana/gamekit).
|
|
49
|
+
Copyright © 2026 Rosdyana Kusuma. See [LICENSE](LICENSE); products using this package must
|
|
50
|
+
credit the author (e.g. "gamekit by Rosdyana Kusuma - https://github.com/rosdyana/gamekit").
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Wallet } from "./money.js";
|
|
2
|
+
export interface Lifestyle {
|
|
3
|
+
label: string;
|
|
4
|
+
weekly: number;
|
|
5
|
+
/** Weekly morale and fame effects, if the game uses them. */
|
|
6
|
+
morale?: number;
|
|
7
|
+
fame?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface Tier {
|
|
10
|
+
label: string;
|
|
11
|
+
price: number;
|
|
12
|
+
}
|
|
13
|
+
/** Moving up a lifestyle tier needs `weeks` of its cost in the bank. */
|
|
14
|
+
export declare function lifestyleBlocked(money: number, tiers: readonly Lifestyle[], current: number, next: number, weeks?: number): string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Buy rung `tier` of an owned ladder (index 0 is usually "nothing yet"). Only upgrades.
|
|
17
|
+
* Returns an error message, or null after paying.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buyTier(w: Wallet, owned: number, tiers: readonly Tier[], tier: number): string | null;
|
|
20
|
+
/** Which rung of `thresholds` (ascending) a value has reached: 0 = none, 1 = first... */
|
|
21
|
+
export declare function tierOf(value: number, thresholds: readonly number[]): number;
|
|
22
|
+
/** Progress 0..1 from the current rung towards the next (1 once the top is reached). */
|
|
23
|
+
export declare function tierProgress(value: number, thresholds: readonly number[]): number;
|
|
24
|
+
//# sourceMappingURL=lifestyle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifestyle.d.ts","sourceRoot":"","sources":["../src/lifestyle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,GAAG,IAAI,CAIpI;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMrG;AAED,yFAAyF;AACzF,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAI3E;AAED,wFAAwF;AACxF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAKjF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Moving up a lifestyle tier needs `weeks` of its cost in the bank. */
|
|
2
|
+
export function lifestyleBlocked(money, tiers, current, next, weeks = 8) {
|
|
3
|
+
if (next < 0 || next >= tiers.length)
|
|
4
|
+
return "Unknown lifestyle";
|
|
5
|
+
if (next > current && money < tiers[next].weekly * weeks)
|
|
6
|
+
return `You can't afford ${weeks} weeks of that.`;
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Buy rung `tier` of an owned ladder (index 0 is usually "nothing yet"). Only upgrades.
|
|
11
|
+
* Returns an error message, or null after paying.
|
|
12
|
+
*/
|
|
13
|
+
export function buyTier(w, owned, tiers, tier) {
|
|
14
|
+
const t = tiers[tier];
|
|
15
|
+
if (!t || tier <= owned)
|
|
16
|
+
return "Already have something better";
|
|
17
|
+
if (w.money < t.price)
|
|
18
|
+
return "Not enough money";
|
|
19
|
+
w.money -= t.price;
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
/** Which rung of `thresholds` (ascending) a value has reached: 0 = none, 1 = first... */
|
|
23
|
+
export function tierOf(value, thresholds) {
|
|
24
|
+
let t = 0;
|
|
25
|
+
for (const x of thresholds)
|
|
26
|
+
if (value >= x)
|
|
27
|
+
t++;
|
|
28
|
+
return t;
|
|
29
|
+
}
|
|
30
|
+
/** Progress 0..1 from the current rung towards the next (1 once the top is reached). */
|
|
31
|
+
export function tierProgress(value, thresholds) {
|
|
32
|
+
const t = tierOf(value, thresholds);
|
|
33
|
+
if (t >= thresholds.length)
|
|
34
|
+
return 1;
|
|
35
|
+
const lo = t ? thresholds[t - 1] : 0;
|
|
36
|
+
return Math.max(0, Math.min(1, (value - lo) / (thresholds[t] - lo)));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=lifestyle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifestyle.js","sourceRoot":"","sources":["../src/lifestyle.ts"],"names":[],"mappings":"AAgBA,wEAAwE;AACxE,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,KAA2B,EAAE,OAAe,EAAE,IAAY,EAAE,KAAK,GAAG,CAAC;IACnH,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,mBAAmB,CAAC;IACjE,IAAI,IAAI,GAAG,OAAO,IAAI,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,KAAK;QAAE,OAAO,oBAAoB,KAAK,iBAAiB,CAAC;IAC5G,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,CAAS,EAAE,KAAa,EAAE,KAAsB,EAAE,IAAY;IACpF,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,KAAK;QAAE,OAAO,+BAA+B,CAAC;IAChE,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK;QAAE,OAAO,kBAAkB,CAAC;IACjD,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC;IACnB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,UAA6B;IACjE,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,MAAM,CAAC,IAAI,UAAU;QAAE,IAAI,KAAK,IAAI,CAAC;YAAE,CAAC,EAAE,CAAC;IAChD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,UAA6B;IACvE,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;AACvE,CAAC"}
|
package/dist/money.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Any () => number in [0, 1); pass a seeded one for reproducible careers. */
|
|
2
|
+
export type Rand = () => number;
|
|
3
|
+
/** Cash on hand. Career states usually embed this field. */
|
|
4
|
+
export interface Wallet {
|
|
5
|
+
money: number;
|
|
6
|
+
}
|
|
7
|
+
/** Round to a "price-like" value: tens under 1k, hundreds under 100k, thousands above. */
|
|
8
|
+
export declare function roundMoney(v: number): number;
|
|
9
|
+
/** Compact label: $950, $12.5K, $3.2M, $1.05B. */
|
|
10
|
+
export declare function shortMoney(v: number, symbol?: string): string;
|
|
11
|
+
/** Spend if affordable. Returns an error message, or null on success. */
|
|
12
|
+
export declare function spend(w: Wallet, amount: number, why?: string): string | null;
|
|
13
|
+
/** Convert between currencies given units-per-dollar rates (e.g. { USD: 1, IDR: 16000 }). */
|
|
14
|
+
export declare function convert(amount: number, from: string, to: string, perDollar: Readonly<Record<string, number>>): number;
|
|
15
|
+
/** Tiered progressive tax: brackets are [threshold, rate] sorted by threshold. */
|
|
16
|
+
export declare function progressiveTax(income: number, brackets: readonly (readonly [number, number])[]): number;
|
|
17
|
+
/**
|
|
18
|
+
* Split a salary across the places it was earned (athletes' "jock tax"): each entry is
|
|
19
|
+
* a share of working days and that place's flat rate. Shares are normalised.
|
|
20
|
+
*/
|
|
21
|
+
export declare function splitTax(income: number, places: readonly {
|
|
22
|
+
share: number;
|
|
23
|
+
rate: number;
|
|
24
|
+
}[]): number;
|
|
25
|
+
//# sourceMappingURL=money.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC;AAEhC,4DAA4D;AAC5D,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,0FAA0F;AAC1F,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI5C;AAED,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,MAAM,CAQ1D;AAED,yEAAyE;AACzE,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAqB,GAAG,MAAM,GAAG,IAAI,CAIxF;AAED,6FAA6F;AAC7F,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAKrH;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,GAAG,MAAM,CASvG;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,MAAM,CAInG"}
|
package/dist/money.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** Round to a "price-like" value: tens under 1k, hundreds under 100k, thousands above. */
|
|
2
|
+
export function roundMoney(v) {
|
|
3
|
+
if (v < 1000)
|
|
4
|
+
return Math.round(v / 10) * 10;
|
|
5
|
+
if (v < 100_000)
|
|
6
|
+
return Math.round(v / 100) * 100;
|
|
7
|
+
return Math.round(v / 1000) * 1000;
|
|
8
|
+
}
|
|
9
|
+
/** Compact label: $950, $12.5K, $3.2M, $1.05B. */
|
|
10
|
+
export function shortMoney(v, symbol = "$") {
|
|
11
|
+
const sign = v < 0 ? "-" : "";
|
|
12
|
+
const a = Math.abs(v);
|
|
13
|
+
const fmt = (x, unit) => `${sign}${symbol}${Number(x.toPrecision(3))}${unit}`;
|
|
14
|
+
if (a >= 1e9)
|
|
15
|
+
return fmt(a / 1e9, "B");
|
|
16
|
+
if (a >= 1e6)
|
|
17
|
+
return fmt(a / 1e6, "M");
|
|
18
|
+
if (a >= 1e4)
|
|
19
|
+
return fmt(a / 1e3, "K");
|
|
20
|
+
return `${sign}${symbol}${Math.round(a).toLocaleString("en-US")}`;
|
|
21
|
+
}
|
|
22
|
+
/** Spend if affordable. Returns an error message, or null on success. */
|
|
23
|
+
export function spend(w, amount, why = "Not enough money") {
|
|
24
|
+
if (w.money < amount)
|
|
25
|
+
return why;
|
|
26
|
+
w.money -= amount;
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
/** Convert between currencies given units-per-dollar rates (e.g. { USD: 1, IDR: 16000 }). */
|
|
30
|
+
export function convert(amount, from, to, perDollar) {
|
|
31
|
+
const a = perDollar[from];
|
|
32
|
+
const b = perDollar[to];
|
|
33
|
+
if (!a || !b)
|
|
34
|
+
throw new Error(`Unknown currency ${!a ? from : to}`);
|
|
35
|
+
return (amount / a) * b;
|
|
36
|
+
}
|
|
37
|
+
/** Tiered progressive tax: brackets are [threshold, rate] sorted by threshold. */
|
|
38
|
+
export function progressiveTax(income, brackets) {
|
|
39
|
+
let tax = 0;
|
|
40
|
+
for (let i = 0; i < brackets.length; i++) {
|
|
41
|
+
const [lo, rate] = brackets[i];
|
|
42
|
+
const hi = i + 1 < brackets.length ? brackets[i + 1][0] : Infinity;
|
|
43
|
+
if (income <= lo)
|
|
44
|
+
break;
|
|
45
|
+
tax += (Math.min(income, hi) - lo) * rate;
|
|
46
|
+
}
|
|
47
|
+
return tax;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Split a salary across the places it was earned (athletes' "jock tax"): each entry is
|
|
51
|
+
* a share of working days and that place's flat rate. Shares are normalised.
|
|
52
|
+
*/
|
|
53
|
+
export function splitTax(income, places) {
|
|
54
|
+
const total = places.reduce((a, p) => a + Math.max(0, p.share), 0);
|
|
55
|
+
if (!total)
|
|
56
|
+
return 0;
|
|
57
|
+
return places.reduce((a, p) => a + (income * Math.max(0, p.share) * p.rate) / total, 0);
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=money.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.js","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { type Rand, type Wallet } from "./money.js";
|
|
2
|
+
export interface BusinessDef {
|
|
3
|
+
label: string;
|
|
4
|
+
/** Default cost to start one. */
|
|
5
|
+
cost: number;
|
|
6
|
+
/** Yearly yield on the business value. */
|
|
7
|
+
yield: number;
|
|
8
|
+
/** Yearly chance of trouble (half a slump, half going bust); booms at half this rate. */
|
|
9
|
+
risk: number;
|
|
10
|
+
text?: string;
|
|
11
|
+
/** Income scales with the owner's fame (fashion labels, restaurants). */
|
|
12
|
+
fameLinked?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface Business<K extends string = string> {
|
|
15
|
+
id: string;
|
|
16
|
+
kind: K;
|
|
17
|
+
name: string;
|
|
18
|
+
/** What the business is worth now. */
|
|
19
|
+
value: number;
|
|
20
|
+
/** Total money put in. */
|
|
21
|
+
invested: number;
|
|
22
|
+
open: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface Portfolio<K extends string = string> {
|
|
25
|
+
savings: number;
|
|
26
|
+
index: number;
|
|
27
|
+
crypto: number;
|
|
28
|
+
/** Rental property units and the current price of one. */
|
|
29
|
+
property: number;
|
|
30
|
+
propertyPrice: number;
|
|
31
|
+
businesses: Business<K>[];
|
|
32
|
+
/** Net worth snapshots, newest first. */
|
|
33
|
+
history: {
|
|
34
|
+
week: number;
|
|
35
|
+
worth: number;
|
|
36
|
+
}[];
|
|
37
|
+
/** Investment income since the game last reset it (e.g. per season). */
|
|
38
|
+
income: number;
|
|
39
|
+
}
|
|
40
|
+
export declare const blankPortfolio: <K extends string = string>(propertyPrice?: number) => Portfolio<K>;
|
|
41
|
+
export declare const LIQUID_ASSETS: readonly ["savings", "index", "crypto"];
|
|
42
|
+
export type Liquid = (typeof LIQUID_ASSETS)[number];
|
|
43
|
+
export declare const ASSET_LABEL: Record<Liquid, string>;
|
|
44
|
+
/** Yearly rates. Volatilities are yearly standard deviations. */
|
|
45
|
+
export interface MarketRates {
|
|
46
|
+
savings: number;
|
|
47
|
+
indexDrift: number;
|
|
48
|
+
indexVol: number;
|
|
49
|
+
cryptoDrift: number;
|
|
50
|
+
cryptoVol: number;
|
|
51
|
+
/** Weekly chances of a crypto crash (x0.45) or moon (x1.9). */
|
|
52
|
+
crash: number;
|
|
53
|
+
moon: number;
|
|
54
|
+
propertyGrowth: number;
|
|
55
|
+
/** Weekly standard deviation of the property price. */
|
|
56
|
+
propertyVol: number;
|
|
57
|
+
propertyFloor: number;
|
|
58
|
+
rentYield: number;
|
|
59
|
+
}
|
|
60
|
+
export declare const DEFAULT_RATES: MarketRates;
|
|
61
|
+
export declare function deposit(w: Wallet, p: Portfolio, asset: Liquid, amount: number): string | null;
|
|
62
|
+
export declare function withdraw(w: Wallet, p: Portfolio, asset: Liquid, amount: number): string | null;
|
|
63
|
+
/** `price` is the price of one unit as the game shows it (it may scale propertyPrice). */
|
|
64
|
+
export declare function buyProperty(w: Wallet, p: Portfolio, price: number, units?: number): string | null;
|
|
65
|
+
/** Sells at `keep` of the price (agent fees and taxes). */
|
|
66
|
+
export declare function sellProperty(w: Wallet, p: Portfolio, price: number, units?: number, keep?: number): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* Start a business for `cost`. `make` is only called once the checks pass (so a refused
|
|
69
|
+
* purchase draws no random numbers) and names the new business.
|
|
70
|
+
*/
|
|
71
|
+
export declare function openBusiness<K extends string>(w: Wallet, p: Portfolio<K>, kind: K, cost: number, make: () => {
|
|
72
|
+
id: string;
|
|
73
|
+
name: string;
|
|
74
|
+
}, max?: number): Business<K> | string;
|
|
75
|
+
/** Sell at `keep` of its value. Returns the business sold, or an error message. */
|
|
76
|
+
export declare function sellBusiness<K extends string>(w: Wallet, p: Portfolio<K>, id: string, keep?: number): Business<K> | string;
|
|
77
|
+
/** Cash plus everything the portfolio holds; `extra` adds game-specific assets. */
|
|
78
|
+
export declare function netWorth(cash: number, p: Portfolio, extra?: number): number;
|
|
79
|
+
/** Keep a net worth snapshot every `every` weeks (newest first, at most `keep`). */
|
|
80
|
+
export declare function recordWorth(p: Portfolio, week: number, worth: number, every?: number, keep?: number): void;
|
|
81
|
+
export type MarketEvent<K extends string = string> = {
|
|
82
|
+
type: "cryptoCrash" | "cryptoMoon";
|
|
83
|
+
} | {
|
|
84
|
+
type: "slump" | "bust" | "boom";
|
|
85
|
+
business: Business<K>;
|
|
86
|
+
};
|
|
87
|
+
export interface MarketWeekOptions<K extends string> {
|
|
88
|
+
businesses: Readonly<Record<K, BusinessDef>>;
|
|
89
|
+
/** Owner fame 0..100 for fame-linked businesses (default 50). */
|
|
90
|
+
fame?: number;
|
|
91
|
+
/** Scales rent (e.g. a poorer economy). */
|
|
92
|
+
scale?: number;
|
|
93
|
+
/** Rounds the rented unit's price before rent is paid (defaults to none). */
|
|
94
|
+
priceOf?: (propertyPrice: number) => number;
|
|
95
|
+
rates?: Partial<MarketRates>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* One week of returns, rent, business income and the occasional disaster. Mutates the
|
|
99
|
+
* portfolio and returns the income earned (not yet paid into any wallet) plus what happened.
|
|
100
|
+
*/
|
|
101
|
+
export declare function marketWeek<K extends string>(p: Portfolio<K>, r: Rand, o: MarketWeekOptions<K>): {
|
|
102
|
+
income: number;
|
|
103
|
+
events: MarketEvent<K>[];
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=portfolio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portfolio.d.ts","sourceRoot":"","sources":["../src/portfolio.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
export const blankPortfolio = (propertyPrice = 250_000) => ({
|
|
2
|
+
savings: 0,
|
|
3
|
+
index: 0,
|
|
4
|
+
crypto: 0,
|
|
5
|
+
property: 0,
|
|
6
|
+
propertyPrice,
|
|
7
|
+
businesses: [],
|
|
8
|
+
history: [],
|
|
9
|
+
income: 0,
|
|
10
|
+
});
|
|
11
|
+
export const LIQUID_ASSETS = ["savings", "index", "crypto"];
|
|
12
|
+
export const ASSET_LABEL = {
|
|
13
|
+
savings: "Savings account",
|
|
14
|
+
index: "Index fund",
|
|
15
|
+
crypto: "Crypto",
|
|
16
|
+
};
|
|
17
|
+
export const DEFAULT_RATES = {
|
|
18
|
+
savings: 0.025,
|
|
19
|
+
indexDrift: 0.07,
|
|
20
|
+
indexVol: 0.16,
|
|
21
|
+
cryptoDrift: 0.15,
|
|
22
|
+
cryptoVol: 0.85,
|
|
23
|
+
crash: 0.006,
|
|
24
|
+
moon: 0.004,
|
|
25
|
+
propertyGrowth: 0.03,
|
|
26
|
+
propertyVol: 0.01,
|
|
27
|
+
propertyFloor: 80_000,
|
|
28
|
+
rentYield: 0.05,
|
|
29
|
+
};
|
|
30
|
+
// Same noise as gamekit-rng's gauss (sum of three uniforms), so results match it draw for draw.
|
|
31
|
+
const gauss = (r, sd) => (r() + r() + r() - 1.5) * 2 * sd;
|
|
32
|
+
const chance = (r, p) => r() < p;
|
|
33
|
+
// ---------------------------------------------------------------- actions
|
|
34
|
+
export function deposit(w, p, asset, amount) {
|
|
35
|
+
amount = Math.floor(amount);
|
|
36
|
+
if (amount <= 0)
|
|
37
|
+
return "Nothing to invest.";
|
|
38
|
+
if (w.money < amount)
|
|
39
|
+
return "Not enough cash.";
|
|
40
|
+
w.money -= amount;
|
|
41
|
+
p[asset] += amount;
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
export function withdraw(w, p, asset, amount) {
|
|
45
|
+
const have = p[asset];
|
|
46
|
+
if (have < 1)
|
|
47
|
+
return "Nothing to withdraw.";
|
|
48
|
+
// "All" takes every last cent, not just whole dollars.
|
|
49
|
+
const take = amount >= have - 1 ? have : Math.floor(amount);
|
|
50
|
+
if (take <= 0)
|
|
51
|
+
return "Nothing to withdraw.";
|
|
52
|
+
p[asset] -= take;
|
|
53
|
+
w.money += take;
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
/** `price` is the price of one unit as the game shows it (it may scale propertyPrice). */
|
|
57
|
+
export function buyProperty(w, p, price, units = 1) {
|
|
58
|
+
const cost = price * units;
|
|
59
|
+
if (w.money < cost)
|
|
60
|
+
return "Not enough cash.";
|
|
61
|
+
w.money -= cost;
|
|
62
|
+
p.property += units;
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
/** Sells at `keep` of the price (agent fees and taxes). */
|
|
66
|
+
export function sellProperty(w, p, price, units = 1, keep = 0.95) {
|
|
67
|
+
if (p.property < units)
|
|
68
|
+
return "No property to sell.";
|
|
69
|
+
p.property -= units;
|
|
70
|
+
w.money += Math.round(price * units * keep);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Start a business for `cost`. `make` is only called once the checks pass (so a refused
|
|
75
|
+
* purchase draws no random numbers) and names the new business.
|
|
76
|
+
*/
|
|
77
|
+
export function openBusiness(w, p, kind, cost, make, max = 6) {
|
|
78
|
+
if (w.money < cost)
|
|
79
|
+
return "Not enough cash.";
|
|
80
|
+
if (p.businesses.filter((b) => b.open).length >= max)
|
|
81
|
+
return `Your advisers say ${max} businesses is plenty.`;
|
|
82
|
+
w.money -= cost;
|
|
83
|
+
const { id, name } = make();
|
|
84
|
+
const b = { id, kind, name, value: cost, invested: cost, open: true };
|
|
85
|
+
p.businesses.push(b);
|
|
86
|
+
return b;
|
|
87
|
+
}
|
|
88
|
+
/** Sell at `keep` of its value. Returns the business sold, or an error message. */
|
|
89
|
+
export function sellBusiness(w, p, id, keep = 0.9) {
|
|
90
|
+
const b = p.businesses.find((x) => x.id === id && x.open);
|
|
91
|
+
if (!b)
|
|
92
|
+
return "No such business.";
|
|
93
|
+
b.open = false;
|
|
94
|
+
w.money += Math.round(b.value * keep);
|
|
95
|
+
return b;
|
|
96
|
+
}
|
|
97
|
+
/** Cash plus everything the portfolio holds; `extra` adds game-specific assets. */
|
|
98
|
+
export function netWorth(cash, p, extra = 0) {
|
|
99
|
+
return (cash +
|
|
100
|
+
p.savings +
|
|
101
|
+
p.index +
|
|
102
|
+
p.crypto +
|
|
103
|
+
p.property * p.propertyPrice +
|
|
104
|
+
p.businesses.filter((b) => b.open).reduce((a, b) => a + b.value, 0) +
|
|
105
|
+
extra);
|
|
106
|
+
}
|
|
107
|
+
/** Keep a net worth snapshot every `every` weeks (newest first, at most `keep`). */
|
|
108
|
+
export function recordWorth(p, week, worth, every = 4, keep = 150) {
|
|
109
|
+
if (week % every !== 0)
|
|
110
|
+
return;
|
|
111
|
+
p.history.unshift({ week, worth: Math.round(worth) });
|
|
112
|
+
if (p.history.length > keep)
|
|
113
|
+
p.history.length = keep;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* One week of returns, rent, business income and the occasional disaster. Mutates the
|
|
117
|
+
* portfolio and returns the income earned (not yet paid into any wallet) plus what happened.
|
|
118
|
+
*/
|
|
119
|
+
export function marketWeek(p, r, o) {
|
|
120
|
+
const k = { ...DEFAULT_RATES, ...o.rates };
|
|
121
|
+
const events = [];
|
|
122
|
+
const fame = o.fame ?? 50;
|
|
123
|
+
const wk = Math.sqrt(52);
|
|
124
|
+
let income = 0;
|
|
125
|
+
p.savings *= 1 + k.savings / 52;
|
|
126
|
+
p.index = Math.max(0, p.index * Math.exp(k.indexDrift / 52 + gauss(r, k.indexVol / wk)));
|
|
127
|
+
if (p.crypto > 0) {
|
|
128
|
+
p.crypto = Math.max(0, p.crypto * Math.exp(k.cryptoDrift / 52 + gauss(r, k.cryptoVol / wk)));
|
|
129
|
+
if (chance(r, k.crash)) {
|
|
130
|
+
p.crypto *= 0.45;
|
|
131
|
+
events.push({ type: "cryptoCrash" });
|
|
132
|
+
}
|
|
133
|
+
else if (chance(r, k.moon)) {
|
|
134
|
+
p.crypto *= 1.9;
|
|
135
|
+
events.push({ type: "cryptoMoon" });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
p.propertyPrice = Math.max(k.propertyFloor, p.propertyPrice * (1 + k.propertyGrowth / 52 + gauss(r, k.propertyVol)));
|
|
139
|
+
if (p.property) {
|
|
140
|
+
const unit = o.priceOf ? o.priceOf(p.propertyPrice) : p.propertyPrice * (o.scale ?? 1);
|
|
141
|
+
income += p.property * unit * (k.rentYield / 52);
|
|
142
|
+
}
|
|
143
|
+
for (const b of p.businesses) {
|
|
144
|
+
if (!b.open)
|
|
145
|
+
continue;
|
|
146
|
+
const def = o.businesses[b.kind];
|
|
147
|
+
const fameK = def.fameLinked ? 0.6 + fame / 125 : 1;
|
|
148
|
+
income += Math.max(0, b.value * (def.yield / 52) * fameK * (0.6 + r() * 0.8));
|
|
149
|
+
b.value = Math.max(0, b.value * (1 + gauss(r, 0.02)));
|
|
150
|
+
if (chance(r, def.risk / 52)) {
|
|
151
|
+
if (chance(r, 0.5)) {
|
|
152
|
+
b.value *= 0.55;
|
|
153
|
+
events.push({ type: "slump", business: b });
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
b.open = false;
|
|
157
|
+
events.push({ type: "bust", business: b });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (chance(r, def.risk / 104)) {
|
|
161
|
+
b.value *= 1.5;
|
|
162
|
+
events.push({ type: "boom", business: b });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { income, events };
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=portfolio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portfolio.js","sourceRoot":"","sources":["../src/portfolio.ts"],"names":[],"mappings":"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"}
|
package/dist/social.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Rand } from "./money.js";
|
|
2
|
+
export interface Public {
|
|
3
|
+
/** 0..100 */
|
|
4
|
+
fame: number;
|
|
5
|
+
/** -100 (villain) .. 100 (role model) */
|
|
6
|
+
image: number;
|
|
7
|
+
followers: number;
|
|
8
|
+
}
|
|
9
|
+
/** Followers a player of this fame settles towards. */
|
|
10
|
+
export declare const followersFor: (fame: number) => number;
|
|
11
|
+
export interface SocialOptions {
|
|
12
|
+
/** Share of the gap to the target closed per week. */
|
|
13
|
+
pull?: number;
|
|
14
|
+
/** Controversy sells: followers multiplier when image is below -30. */
|
|
15
|
+
notoriety?: number;
|
|
16
|
+
/** Weekly fame fade: a flat amount plus a share of fame. */
|
|
17
|
+
fade?: [number, number];
|
|
18
|
+
}
|
|
19
|
+
/** One week: followers drift towards their target, fame fades unless it is fed. */
|
|
20
|
+
export declare function socialWeek(p: Public, o?: SocialOptions): void;
|
|
21
|
+
/**
|
|
22
|
+
* A moment going viral (poster dunk, buzzer-beater, a mixtape). `size` 0..1 is how
|
|
23
|
+
* spectacular it was; small accounts grow fastest in relative terms. Returns the gain.
|
|
24
|
+
*/
|
|
25
|
+
export declare function viral(p: Public, r: Rand, size: number): number;
|
|
26
|
+
/** Label for follower counts: 950, 12.4K, 3.1M. */
|
|
27
|
+
export declare function followerLabel(n: number): string;
|
|
28
|
+
//# sourceMappingURL=social.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"social.d.ts","sourceRoot":"","sources":["../src/social.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,MAAM;IACrB,aAAa;IACb,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,uDAAuD;AACvD,eAAO,MAAM,YAAY,SAAU,MAAM,WAA4D,CAAC;AAEtG,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzB;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,GAAE,aAAkB,QAK1D;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAM9D;AAED,mDAAmD;AACnD,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI/C"}
|
package/dist/social.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Followers a player of this fame settles towards. */
|
|
2
|
+
export const followersFor = (fame) => Math.round(Math.pow(fame, 2.6) * 60 + fame * 500 + 120);
|
|
3
|
+
/** One week: followers drift towards their target, fame fades unless it is fed. */
|
|
4
|
+
export function socialWeek(p, o = {}) {
|
|
5
|
+
const target = followersFor(p.fame) * (p.image < -30 ? (o.notoriety ?? 1.25) : 1);
|
|
6
|
+
p.followers = Math.round(p.followers + (target - p.followers) * (o.pull ?? 0.08));
|
|
7
|
+
const [flat, share] = o.fade ?? [0.03, 0.004];
|
|
8
|
+
p.fame = Math.max(0, Math.min(100, p.fame - flat - p.fame * share));
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A moment going viral (poster dunk, buzzer-beater, a mixtape). `size` 0..1 is how
|
|
12
|
+
* spectacular it was; small accounts grow fastest in relative terms. Returns the gain.
|
|
13
|
+
*/
|
|
14
|
+
export function viral(p, r, size) {
|
|
15
|
+
const base = 400 + Math.sqrt(p.followers) * 120;
|
|
16
|
+
const gain = Math.round(base * size * (0.5 + r()) * (1 + size * 3));
|
|
17
|
+
p.followers += gain;
|
|
18
|
+
p.fame = Math.min(100, p.fame + size * 2);
|
|
19
|
+
return gain;
|
|
20
|
+
}
|
|
21
|
+
/** Label for follower counts: 950, 12.4K, 3.1M. */
|
|
22
|
+
export function followerLabel(n) {
|
|
23
|
+
if (n >= 1e6)
|
|
24
|
+
return `${Number((n / 1e6).toPrecision(3))}M`;
|
|
25
|
+
if (n >= 1e4)
|
|
26
|
+
return `${Number((n / 1e3).toPrecision(3))}K`;
|
|
27
|
+
return Math.round(n).toLocaleString("en-US");
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=social.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"social.js","sourceRoot":"","sources":["../src/social.ts"],"names":[],"mappings":"AAWA,uDAAuD;AACvD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AAWtG,mFAAmF;AACnF,MAAM,UAAU,UAAU,CAAC,CAAS,EAAE,IAAmB,EAAE;IACzD,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC;IAClF,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC;AACtE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,CAAS,EAAE,CAAO,EAAE,IAAY;IACpD,MAAM,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;IAChD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;IACpE,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC;IACpB,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,aAAa,CAAC,CAAS;IACrC,IAAI,CAAC,IAAI,GAAG;QAAE,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC5D,IAAI,CAAC,IAAI,GAAG;QAAE,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC5D,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type Rand, type Wallet } from "./money.js";
|
|
2
|
+
/** local: hometown businesses; clean: mainstream brands; edgy: brands that like a bad boy. */
|
|
3
|
+
export type SponsorKind = "local" | "clean" | "edgy";
|
|
4
|
+
export interface Sponsor<K extends string = SponsorKind> {
|
|
5
|
+
id: string;
|
|
6
|
+
brand: string;
|
|
7
|
+
kind: K;
|
|
8
|
+
/** Market (e.g. nation code) the brand comes from. */
|
|
9
|
+
country?: string;
|
|
10
|
+
weekly: number;
|
|
11
|
+
/** Paid per match feat (goal, clean sheet, double-double...). */
|
|
12
|
+
bonus: number;
|
|
13
|
+
/** Absolute week the deal ends. */
|
|
14
|
+
until: number;
|
|
15
|
+
/** The sponsor walks if image drops below minImage (clean) or rises above maxImage (edgy). */
|
|
16
|
+
minImage: number;
|
|
17
|
+
maxImage: number;
|
|
18
|
+
/** Offers only: the week the offer lapses. */
|
|
19
|
+
expires?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface SponsorBook<K extends string = SponsorKind> {
|
|
22
|
+
sponsors: Sponsor<K>[];
|
|
23
|
+
sponsorOffers: Sponsor<K>[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The kind of brand that approaches: local until `localBelow` fame, then clean or edgy by
|
|
27
|
+
* image (image -100..100). Draws one number only once past the local stage.
|
|
28
|
+
*/
|
|
29
|
+
export declare function sponsorKind(r: Rand, fame: number, image: number, localBelow?: number): SponsorKind;
|
|
30
|
+
/** Weekly fee by fame (0..100); `scale` shrinks it for smaller markets. Rounded to tens. */
|
|
31
|
+
export declare function sponsorPay(kind: SponsorKind, fame: number, scale?: number): number;
|
|
32
|
+
/** Image window for a kind: clean brands leave below -10, edgy ones above 60. */
|
|
33
|
+
export declare const imageWindow: (kind: SponsorKind) => [number, number];
|
|
34
|
+
export declare const sponsorIncome: (list: readonly Sponsor<string>[]) => number;
|
|
35
|
+
/** Bonus money for `units` feats in one match. */
|
|
36
|
+
export declare const sponsorBonus: (list: readonly Sponsor<string>[], units: number) => number;
|
|
37
|
+
/**
|
|
38
|
+
* Accept an offer. At most `maxPerKind` deals of a kind at once, except the `unlimited` kinds.
|
|
39
|
+
* Returns the signed deal, or an error message.
|
|
40
|
+
*/
|
|
41
|
+
export declare function acceptSponsor<K extends string>(book: SponsorBook<K>, id: string, maxPerKind?: number, unlimited?: readonly string[]): Sponsor<K> | string;
|
|
42
|
+
export declare function declineSponsor<K extends string>(book: SponsorBook<K>, id: string): void;
|
|
43
|
+
/** Buying out a deal: up to `weeks` of the remaining fees plus a `penalty` of weeks on top. */
|
|
44
|
+
export declare function sponsorExitFee(sp: Sponsor<string>, week: number, weeks?: number, penalty?: number): number;
|
|
45
|
+
export declare function cancelSponsor<K extends string>(w: Wallet, book: SponsorBook<K>, id: string, week: number): Sponsor<K> | string;
|
|
46
|
+
/** Weekly upkeep: drop lapsed offers, end finished deals, enforce image clauses (in list order). */
|
|
47
|
+
export declare function sponsorUpkeep<K extends string>(book: SponsorBook<K>, week: number, image: number): {
|
|
48
|
+
sponsor: Sponsor<K>;
|
|
49
|
+
why: "ended" | "image";
|
|
50
|
+
}[];
|
|
51
|
+
//# sourceMappingURL=sponsors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sponsors.d.ts","sourceRoot":"","sources":["../src/sponsors.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAEhE,8FAA8F;AAC9F,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM,GAAG,WAAW;IACrD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,CAAC,CAAC;IACR,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,WAAW;IACzD,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,SAAK,GAAG,WAAW,CAI9F;AAED,4FAA4F;AAC5F,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,CAG7E;AAED,iFAAiF;AACjF,eAAO,MAAM,WAAW,SAAU,WAAW,KAAG,CAAC,MAAM,EAAE,MAAM,CAAgE,CAAC;AAEhI,eAAO,MAAM,aAAa,SAAU,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,WAA6C,CAAC;AAE5G,kDAAkD;AAClD,eAAO,MAAM,YAAY,SAAU,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,WACX,CAAC;AAElE;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC5C,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,EAAE,EAAE,MAAM,EACV,UAAU,SAAI,EACd,SAAS,GAAE,SAAS,MAAM,EAAc,GACvC,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CASrB;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,QAEhF;AAED,+FAA+F;AAC/F,wBAAgB,cAAc,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,SAAK,EAAE,OAAO,SAAI,GAAG,MAAM,CAGjG;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAQ9H;AAED,oGAAoG;AACpG,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC5C,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ;IAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAA;CAAE,EAAE,CAanD"}
|
package/dist/sponsors.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Endorsements: who approaches you (by fame and image), what they pay, image clauses,
|
|
2
|
+
// buy-outs and expiry. Brand names and markets stay in the game.
|
|
3
|
+
import { roundMoney } from "./money.js";
|
|
4
|
+
/**
|
|
5
|
+
* The kind of brand that approaches: local until `localBelow` fame, then clean or edgy by
|
|
6
|
+
* image (image -100..100). Draws one number only once past the local stage.
|
|
7
|
+
*/
|
|
8
|
+
export function sponsorKind(r, fame, image, localBelow = 15) {
|
|
9
|
+
if (fame < localBelow)
|
|
10
|
+
return "local";
|
|
11
|
+
const edgy = image < -20 ? 0.7 : image < 10 ? 0.25 : 0.05;
|
|
12
|
+
return r() < edgy ? "edgy" : "clean";
|
|
13
|
+
}
|
|
14
|
+
/** Weekly fee by fame (0..100); `scale` shrinks it for smaller markets. Rounded to tens. */
|
|
15
|
+
export function sponsorPay(kind, fame, scale = 1) {
|
|
16
|
+
const big = 30 + Math.pow(fame, 2.1) * 3 * scale;
|
|
17
|
+
return Math.round((kind === "local" ? 20 + fame * 4 : big * (kind === "edgy" ? 1.3 : 1)) / 10) * 10;
|
|
18
|
+
}
|
|
19
|
+
/** Image window for a kind: clean brands leave below -10, edgy ones above 60. */
|
|
20
|
+
export const imageWindow = (kind) => [kind === "clean" ? -10 : -100, kind === "edgy" ? 60 : 100];
|
|
21
|
+
export const sponsorIncome = (list) => list.reduce((a, sp) => a + sp.weekly, 0);
|
|
22
|
+
/** Bonus money for `units` feats in one match. */
|
|
23
|
+
export const sponsorBonus = (list, units) => units > 0 ? list.reduce((a, sp) => a + sp.bonus * units, 0) : 0;
|
|
24
|
+
/**
|
|
25
|
+
* Accept an offer. At most `maxPerKind` deals of a kind at once, except the `unlimited` kinds.
|
|
26
|
+
* Returns the signed deal, or an error message.
|
|
27
|
+
*/
|
|
28
|
+
export function acceptSponsor(book, id, maxPerKind = 3, unlimited = ["local"]) {
|
|
29
|
+
const o = book.sponsorOffers.find((x) => x.id === id);
|
|
30
|
+
if (!o)
|
|
31
|
+
return "Offer expired";
|
|
32
|
+
if (!unlimited.includes(o.kind) && book.sponsors.filter((x) => x.kind === o.kind).length >= maxPerKind)
|
|
33
|
+
return `Your agent says ${maxPerKind} big brands at once is the limit.`;
|
|
34
|
+
book.sponsorOffers = book.sponsorOffers.filter((x) => x.id !== id);
|
|
35
|
+
delete o.expires;
|
|
36
|
+
book.sponsors.push(o);
|
|
37
|
+
return o;
|
|
38
|
+
}
|
|
39
|
+
export function declineSponsor(book, id) {
|
|
40
|
+
book.sponsorOffers = book.sponsorOffers.filter((x) => x.id !== id);
|
|
41
|
+
}
|
|
42
|
+
/** Buying out a deal: up to `weeks` of the remaining fees plus a `penalty` of weeks on top. */
|
|
43
|
+
export function sponsorExitFee(sp, week, weeks = 26, penalty = 2) {
|
|
44
|
+
const left = Math.max(0, sp.until - week);
|
|
45
|
+
return roundMoney(sp.weekly * Math.min(weeks, left) + sp.weekly * penalty);
|
|
46
|
+
}
|
|
47
|
+
export function cancelSponsor(w, book, id, week) {
|
|
48
|
+
const sp = book.sponsors.find((x) => x.id === id);
|
|
49
|
+
if (!sp)
|
|
50
|
+
return "No such deal.";
|
|
51
|
+
const fee = sponsorExitFee(sp, week);
|
|
52
|
+
if (w.money < fee)
|
|
53
|
+
return `You need $${fee.toLocaleString("en-US")} to buy out the contract.`;
|
|
54
|
+
w.money -= fee;
|
|
55
|
+
book.sponsors = book.sponsors.filter((x) => x !== sp);
|
|
56
|
+
return sp;
|
|
57
|
+
}
|
|
58
|
+
/** Weekly upkeep: drop lapsed offers, end finished deals, enforce image clauses (in list order). */
|
|
59
|
+
export function sponsorUpkeep(book, week, image) {
|
|
60
|
+
const out = [];
|
|
61
|
+
book.sponsorOffers = book.sponsorOffers.filter((o) => (o.expires ?? 0) > week);
|
|
62
|
+
for (const sp of [...book.sponsors]) {
|
|
63
|
+
if (sp.until <= week) {
|
|
64
|
+
book.sponsors = book.sponsors.filter((x) => x !== sp);
|
|
65
|
+
out.push({ sponsor: sp, why: "ended" });
|
|
66
|
+
}
|
|
67
|
+
else if (image < sp.minImage || image > sp.maxImage) {
|
|
68
|
+
book.sponsors = book.sponsors.filter((x) => x !== sp);
|
|
69
|
+
out.push({ sponsor: sp, why: "image" });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=sponsors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sponsors.js","sourceRoot":"","sources":["../src/sponsors.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,iEAAiE;AACjE,OAAO,EAAE,UAAU,EAA0B,MAAM,YAAY,CAAC;AA4BhE;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,CAAO,EAAE,IAAY,EAAE,KAAa,EAAE,UAAU,GAAG,EAAE;IAC/E,IAAI,IAAI,GAAG,UAAU;QAAE,OAAO,OAAO,CAAC;IACtC,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1D,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;AACvC,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,UAAU,CAAC,IAAiB,EAAE,IAAY,EAAE,KAAK,GAAG,CAAC;IACnE,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;AACtG,CAAC;AAED,iFAAiF;AACjF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAiB,EAAoB,EAAE,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAEhI,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAgC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAE5G,kDAAkD;AAClD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAgC,EAAE,KAAa,EAAE,EAAE,CAC9E,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAoB,EACpB,EAAU,EACV,UAAU,GAAG,CAAC,EACd,YAA+B,CAAC,OAAO,CAAC;IAExC,MAAM,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,IAAI,CAAC,CAAC;QAAE,OAAO,eAAe,CAAC;IAC/B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,UAAU;QACpG,OAAO,mBAAmB,UAAU,mCAAmC,CAAC;IAC1E,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACnE,OAAO,CAAC,CAAC,OAAO,CAAC;IACjB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,cAAc,CAAmB,IAAoB,EAAE,EAAU;IAC/E,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,cAAc,CAAC,EAAmB,EAAE,IAAY,EAAE,KAAK,GAAG,EAAE,EAAE,OAAO,GAAG,CAAC;IACvF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAC1C,OAAO,UAAU,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,aAAa,CAAmB,CAAS,EAAE,IAAoB,EAAE,EAAU,EAAE,IAAY;IACvG,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAClD,IAAI,CAAC,EAAE;QAAE,OAAO,eAAe,CAAC;IAChC,MAAM,GAAG,GAAG,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,CAAC,KAAK,GAAG,GAAG;QAAE,OAAO,aAAa,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,2BAA2B,CAAC;IAC9F,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC;IACf,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACtD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,aAAa,CAC3B,IAAoB,EACpB,IAAY,EACZ,KAAa;IAEb,MAAM,GAAG,GAAsD,EAAE,CAAC;IAClE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC/E,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpC,IAAI,EAAE,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACtD,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QAC1C,CAAC;aAAM,IAAI,KAAK,GAAG,EAAE,CAAC,QAAQ,IAAI,KAAK,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;YACtD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACtD,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@taipeistudio/gamekit-career",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Athlete-career economy: money, investments, businesses, sponsors, fame and followers, lifestyle ladders, taxes.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"gamekit",
|
|
7
|
+
"gamedev",
|
|
8
|
+
"typescript",
|
|
9
|
+
"career",
|
|
10
|
+
"sports",
|
|
11
|
+
"life-sim",
|
|
12
|
+
"economy",
|
|
13
|
+
"sponsors",
|
|
14
|
+
"investments"
|
|
15
|
+
],
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "Rosdyana Kusuma",
|
|
18
|
+
"email": "rosdyana.kusuma@gmail.com",
|
|
19
|
+
"url": "https://github.com/rosdyana"
|
|
20
|
+
},
|
|
21
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
22
|
+
"homepage": "https://github.com/rosdyana/gamekit/tree/main/packages/career#readme",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/rosdyana/gamekit.git",
|
|
26
|
+
"directory": "packages/career"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/rosdyana/gamekit/issues"
|
|
30
|
+
},
|
|
31
|
+
"type": "module",
|
|
32
|
+
"sideEffects": false,
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"import": "./dist/index.js"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc -p tsconfig.build.json"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
}
|
|
50
|
+
}
|