volleyballsimtypes 0.0.425 → 0.0.427
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/dist/cjs/src/api/index.d.ts +11 -0
- package/dist/cjs/src/data/models/tactics.d.ts +2 -0
- package/dist/cjs/src/data/transformers/tactics.js +4 -0
- package/dist/cjs/src/service/player/decline.d.ts +2 -0
- package/dist/cjs/src/service/player/decline.js +7 -0
- package/dist/cjs/src/service/player/decline.test.js +17 -0
- package/dist/cjs/src/service/player/improvement.d.ts +3 -0
- package/dist/cjs/src/service/player/improvement.js +23 -0
- package/dist/cjs/src/service/player/improvement.test.js +36 -0
- package/dist/cjs/src/service/team/designated-sub.d.ts +1 -0
- package/dist/cjs/src/service/team/schemas/designated-sub.z.d.ts +3 -0
- package/dist/cjs/src/service/team/schemas/designated-sub.z.js +1 -0
- package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +2 -0
- package/dist/cjs/src/service/team/schemas/team.z.d.ts +2 -0
- package/dist/cjs/src/stat-config/decline-profiles.json +4 -4
- package/dist/esm/src/api/index.d.ts +11 -0
- package/dist/esm/src/data/models/tactics.d.ts +2 -0
- package/dist/esm/src/data/transformers/tactics.js +4 -0
- package/dist/esm/src/service/player/decline.d.ts +2 -0
- package/dist/esm/src/service/player/decline.js +6 -0
- package/dist/esm/src/service/player/decline.test.js +18 -1
- package/dist/esm/src/service/player/improvement.d.ts +3 -0
- package/dist/esm/src/service/player/improvement.js +21 -0
- package/dist/esm/src/service/player/improvement.test.js +37 -1
- package/dist/esm/src/service/team/designated-sub.d.ts +1 -0
- package/dist/esm/src/service/team/schemas/designated-sub.z.d.ts +3 -0
- package/dist/esm/src/service/team/schemas/designated-sub.z.js +1 -0
- package/dist/esm/src/service/team/schemas/tactics.z.d.ts +2 -0
- package/dist/esm/src/service/team/schemas/team.z.d.ts +2 -0
- package/dist/esm/src/stat-config/decline-profiles.json +4 -4
- package/package.json +1 -1
|
@@ -24,6 +24,13 @@ export interface MatchCompetition {
|
|
|
24
24
|
tier?: number;
|
|
25
25
|
subdivision?: number;
|
|
26
26
|
}
|
|
27
|
+
export interface MatchForm {
|
|
28
|
+
matchId: string;
|
|
29
|
+
result: 'WIN' | 'LOSE';
|
|
30
|
+
side: 'HOME' | 'AWAY';
|
|
31
|
+
opponent: string;
|
|
32
|
+
dR: number;
|
|
33
|
+
}
|
|
27
34
|
export type Match = Omit<DataProps<_Match>, 'sets' | 'homeTeam' | 'awayTeam' | 'VPERs'> & {
|
|
28
35
|
sets: MatchSet[];
|
|
29
36
|
boxScores: BoxScore[];
|
|
@@ -37,6 +44,8 @@ export type Match = Omit<DataProps<_Match>, 'sets' | 'homeTeam' | 'awayTeam' | '
|
|
|
37
44
|
results?: Results;
|
|
38
45
|
competition?: MatchCompetition;
|
|
39
46
|
rallyMetrics?: RallyMetrics;
|
|
47
|
+
homeForm?: MatchForm[];
|
|
48
|
+
awayForm?: MatchForm[];
|
|
40
49
|
};
|
|
41
50
|
export type RallyEvent = DataProps<_RallyEvent> & {
|
|
42
51
|
team?: Team;
|
|
@@ -65,6 +74,7 @@ export interface ApiSetSubConfig {
|
|
|
65
74
|
mode: SubMode;
|
|
66
75
|
benchId?: string;
|
|
67
76
|
fatigueBand?: EnergyBand;
|
|
77
|
+
benchFatigueBand?: EnergyBand;
|
|
68
78
|
conditions?: PinchCondition[];
|
|
69
79
|
conditionLogic?: ConditionLogic;
|
|
70
80
|
}
|
|
@@ -73,6 +83,7 @@ export interface ApiDesignatedSub {
|
|
|
73
83
|
benchId?: string;
|
|
74
84
|
mode: SubMode;
|
|
75
85
|
fatigueBand?: EnergyBand;
|
|
86
|
+
benchFatigueBand?: EnergyBand;
|
|
76
87
|
conditions?: PinchCondition[];
|
|
77
88
|
conditionLogic?: ConditionLogic;
|
|
78
89
|
sets?: ApiSetSubConfig[];
|
|
@@ -17,6 +17,7 @@ export interface SetSubConfigAttributes {
|
|
|
17
17
|
mode: SubMode;
|
|
18
18
|
benchId?: PlayerId;
|
|
19
19
|
fatigueBand?: EnergyBand;
|
|
20
|
+
benchFatigueBand?: EnergyBand;
|
|
20
21
|
conditions?: PinchCondition[];
|
|
21
22
|
conditionLogic?: ConditionLogic;
|
|
22
23
|
}
|
|
@@ -26,6 +27,7 @@ export interface DesignatedSubAttributes {
|
|
|
26
27
|
mode?: SubMode;
|
|
27
28
|
isPinchServer?: boolean;
|
|
28
29
|
fatigueBand?: SubBand;
|
|
30
|
+
benchFatigueBand?: EnergyBand;
|
|
29
31
|
conditions?: PinchCondition[];
|
|
30
32
|
conditionLogic?: ConditionLogic;
|
|
31
33
|
sets?: SetSubConfigAttributes[];
|
|
@@ -62,12 +62,14 @@ function transformToAttributes(tactics, teamId) {
|
|
|
62
62
|
benchId: ds.bench?.id,
|
|
63
63
|
mode: ds.mode,
|
|
64
64
|
fatigueBand: ds.fatigueBand,
|
|
65
|
+
benchFatigueBand: ds.benchFatigueBand,
|
|
65
66
|
conditions: ds.conditions,
|
|
66
67
|
conditionLogic: ds.conditionLogic,
|
|
67
68
|
sets: ds.sets?.map(s => ({
|
|
68
69
|
mode: s.mode,
|
|
69
70
|
benchId: s.bench?.id,
|
|
70
71
|
fatigueBand: s.fatigueBand,
|
|
72
|
+
benchFatigueBand: s.benchFatigueBand,
|
|
71
73
|
conditions: s.conditions,
|
|
72
74
|
conditionLogic: s.conditionLogic
|
|
73
75
|
}))
|
|
@@ -115,12 +117,14 @@ function transformToObject(model, roster) {
|
|
|
115
117
|
bench: d.benchId != null ? findPlayer(d.benchId, roster) : undefined,
|
|
116
118
|
mode: subModeFromAttributes(d),
|
|
117
119
|
fatigueBand: d.fatigueBand === 'NEVER' ? undefined : d.fatigueBand,
|
|
120
|
+
benchFatigueBand: d.benchFatigueBand,
|
|
118
121
|
conditions: d.conditions,
|
|
119
122
|
conditionLogic: d.conditionLogic,
|
|
120
123
|
sets: d.sets?.map(s => ({
|
|
121
124
|
mode: s.mode,
|
|
122
125
|
bench: s.benchId != null ? findPlayer(s.benchId, roster) : undefined,
|
|
123
126
|
fatigueBand: s.fatigueBand,
|
|
127
|
+
benchFatigueBand: s.benchFatigueBand,
|
|
124
128
|
conditions: s.conditions,
|
|
125
129
|
conditionLogic: s.conditionLogic
|
|
126
130
|
}))
|
|
@@ -10,6 +10,7 @@ export declare const declineProfiles: DeclineProfileEnum[];
|
|
|
10
10
|
interface DeclineProfileEntry {
|
|
11
11
|
profile: string;
|
|
12
12
|
startAge: number;
|
|
13
|
+
youthAge: number;
|
|
13
14
|
initTicks: number;
|
|
14
15
|
accel: number;
|
|
15
16
|
peakTicks: number;
|
|
@@ -25,6 +26,7 @@ export declare const SKILL_POOL: PerformanceStatsKey[];
|
|
|
25
26
|
*/
|
|
26
27
|
export declare const ENERGY_POOL: PerformanceStatsKey[];
|
|
27
28
|
export declare function getDeclineProfile(profile: DeclineProfile): DeclineProfileEntry;
|
|
29
|
+
export declare function youthMultiplier(age: number, profile: DeclineProfile): number;
|
|
28
30
|
export type DeclineStatus = 'ACTIVE' | 'DECLINING' | 'PEAK';
|
|
29
31
|
export interface DeclineTick {
|
|
30
32
|
/** Number of decline ticks to apply this iteration. */
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ENERGY_POOL = exports.SKILL_POOL = exports.declineProfiles = exports.DeclineProfileEnum = void 0;
|
|
4
4
|
exports.getDeclineProfile = getDeclineProfile;
|
|
5
|
+
exports.youthMultiplier = youthMultiplier;
|
|
5
6
|
exports.computeDeclineTick = computeDeclineTick;
|
|
6
7
|
const stat_config_1 = require("../../stat-config");
|
|
7
8
|
var DeclineProfileEnum;
|
|
@@ -34,6 +35,12 @@ function getDeclineProfile(profile) {
|
|
|
34
35
|
throw new Error(`UNKNOWN_DECLINE_PROFILE: ${profile}`);
|
|
35
36
|
return entry;
|
|
36
37
|
}
|
|
38
|
+
// Prospects below their profile's youthAge develop faster: a multiplier applied to the per-match improvement
|
|
39
|
+
// experience. At or above youthAge (and pre-decline) growth is at the normal rate.
|
|
40
|
+
const YOUTH_EXP_MULTIPLIER = 1.25;
|
|
41
|
+
function youthMultiplier(age, profile) {
|
|
42
|
+
return age < getDeclineProfile(profile).youthAge ? YOUTH_EXP_MULTIPLIER : 1;
|
|
43
|
+
}
|
|
37
44
|
/**
|
|
38
45
|
* Decline is fully derivable from age + profile — no running state is persisted.
|
|
39
46
|
* tickRate = min(peakTicks, initTicks + (age - startAge) * accel)
|
|
@@ -100,3 +100,20 @@ const performance_stats_keys_1 = require("./performance-stats-keys");
|
|
|
100
100
|
(0, globals_1.expect)((0, decline_1.computeDeclineTick)(33, decline_1.DeclineProfileEnum.STANDARD).atPeak).toBe(true);
|
|
101
101
|
});
|
|
102
102
|
});
|
|
103
|
+
// ─── youthMultiplier ───────────────────────────────────────────────────────────
|
|
104
|
+
(0, globals_1.describe)('youthMultiplier()', () => {
|
|
105
|
+
globals_1.it.each([
|
|
106
|
+
[decline_1.DeclineProfileEnum.PRODIGY, 20],
|
|
107
|
+
[decline_1.DeclineProfileEnum.STANDARD, 21],
|
|
108
|
+
[decline_1.DeclineProfileEnum.VETERAN, 22],
|
|
109
|
+
[decline_1.DeclineProfileEnum.IRONMAN, 23]
|
|
110
|
+
])('%s youthAge is %i', (profile, youthAge) => {
|
|
111
|
+
(0, globals_1.expect)((0, decline_1.getDeclineProfile)(profile).youthAge).toBe(youthAge);
|
|
112
|
+
});
|
|
113
|
+
(0, globals_1.it)('boosts players below youthAge by 1.25 and leaves the rest at 1', () => {
|
|
114
|
+
(0, globals_1.expect)((0, decline_1.youthMultiplier)(20, decline_1.DeclineProfileEnum.STANDARD)).toBe(1.25); // youthAge 21
|
|
115
|
+
(0, globals_1.expect)((0, decline_1.youthMultiplier)(21, decline_1.DeclineProfileEnum.STANDARD)).toBe(1);
|
|
116
|
+
(0, globals_1.expect)((0, decline_1.youthMultiplier)(19, decline_1.DeclineProfileEnum.PRODIGY)).toBe(1.25); // youthAge 20
|
|
117
|
+
(0, globals_1.expect)((0, decline_1.youthMultiplier)(20, decline_1.DeclineProfileEnum.PRODIGY)).toBe(1);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Rarity } from './rarity';
|
|
2
2
|
import { Stats } from './stats';
|
|
3
|
+
import { DeclineProfile } from './decline';
|
|
3
4
|
export type TrainingFocus = Stats;
|
|
4
5
|
export declare function getStatCap(rarity: Rarity): number;
|
|
5
6
|
export declare function getRarityMultiplier(rarity: Rarity): number;
|
|
6
7
|
export declare function getImprovementThreshold(statTotal: number, rarity: Rarity): number;
|
|
7
8
|
export declare function getCoachMultiplier(rarity: Rarity): number;
|
|
9
|
+
export declare function matchExperience(pointsPlayed: number): number;
|
|
10
|
+
export declare function experienceGained(pointsPlayed: number, age: number, profile: DeclineProfile): number;
|
|
8
11
|
export declare function selectImprovableStat(rarity: Rarity, trainingFocus: TrainingFocus, currentStats: Record<string, number>): string | null;
|
|
@@ -4,11 +4,14 @@ exports.getStatCap = getStatCap;
|
|
|
4
4
|
exports.getRarityMultiplier = getRarityMultiplier;
|
|
5
5
|
exports.getImprovementThreshold = getImprovementThreshold;
|
|
6
6
|
exports.getCoachMultiplier = getCoachMultiplier;
|
|
7
|
+
exports.matchExperience = matchExperience;
|
|
8
|
+
exports.experienceGained = experienceGained;
|
|
7
9
|
exports.selectImprovableStat = selectImprovableStat;
|
|
8
10
|
const stat_config_1 = require("../../stat-config");
|
|
9
11
|
const rarity_1 = require("./rarity");
|
|
10
12
|
const performance_stats_1 = require("./performance-stats");
|
|
11
13
|
const stats_1 = require("./stats");
|
|
14
|
+
const decline_1 = require("./decline");
|
|
12
15
|
const CURVE_BASE = 100;
|
|
13
16
|
const CURVE_SCALAR = 0.098;
|
|
14
17
|
const CURVE_EXPONENT = 1.5;
|
|
@@ -40,6 +43,26 @@ const COACH_MULTIPLIERS = {
|
|
|
40
43
|
function getCoachMultiplier(rarity) {
|
|
41
44
|
return COACH_MULTIPLIERS[rarity] ?? 1.0;
|
|
42
45
|
}
|
|
46
|
+
// ── Per-match experience curve ──────────────────────────────────────────────
|
|
47
|
+
// Experience is no longer 1:1 with points played: each point is worth progressively less (a saturating
|
|
48
|
+
// curve), so high-volume matches (5-setters, never-subbed players) don't improve disproportionately fast.
|
|
49
|
+
// MATCH_EXP_K sets the long-match premium - a ~206-point 5-setter yields only ~13% more exp than a ~147-point
|
|
50
|
+
// 3-setter. MATCH_EXP_SCALE is the asymptote, calibrated so exp == points at ~60 points played (the league-
|
|
51
|
+
// average match): a normal match keeps roughly its old value and only longer matches are throttled. Tuning
|
|
52
|
+
// MATCH_EXP_SCALE shifts overall pace without changing the premium; MATCH_EXP_K changes the premium.
|
|
53
|
+
const MATCH_EXP_SCALE = 160;
|
|
54
|
+
const MATCH_EXP_K = 100;
|
|
55
|
+
function matchExperience(pointsPlayed) {
|
|
56
|
+
if (pointsPlayed <= 0)
|
|
57
|
+
return 0;
|
|
58
|
+
return (MATCH_EXP_SCALE * pointsPlayed) / (pointsPlayed + MATCH_EXP_K);
|
|
59
|
+
}
|
|
60
|
+
// Total improvement experience a player earns from a match: the saturating per-match curve, boosted for
|
|
61
|
+
// players still below their decline profile's youthAge (prospects develop faster). Rounded - the progress
|
|
62
|
+
// counter holds whole units.
|
|
63
|
+
function experienceGained(pointsPlayed, age, profile) {
|
|
64
|
+
return Math.round(matchExperience(pointsPlayed) * (0, decline_1.youthMultiplier)(age, profile));
|
|
65
|
+
}
|
|
43
66
|
const KEY_TO_COLUMN = {
|
|
44
67
|
setting: 'setting',
|
|
45
68
|
serve: 'serve',
|
|
@@ -4,6 +4,7 @@ const globals_1 = require("@jest/globals");
|
|
|
4
4
|
const rarity_1 = require("./rarity");
|
|
5
5
|
const improvement_1 = require("./improvement");
|
|
6
6
|
const stats_1 = require("./stats");
|
|
7
|
+
const decline_1 = require("./decline");
|
|
7
8
|
// ─── getStatCap ───────────────────────────────────────────────────────────────
|
|
8
9
|
(0, globals_1.describe)('getStatCap()', () => {
|
|
9
10
|
globals_1.it.each([
|
|
@@ -142,3 +143,38 @@ function allStats(value) {
|
|
|
142
143
|
}
|
|
143
144
|
});
|
|
144
145
|
});
|
|
146
|
+
// ─── matchExperience / experienceGained ───────────────────────────────────────
|
|
147
|
+
(0, globals_1.describe)('matchExperience()', () => {
|
|
148
|
+
(0, globals_1.it)('breaks even (~1:1) at the league-average match (~60 points)', () => {
|
|
149
|
+
(0, globals_1.expect)((0, improvement_1.matchExperience)(60)).toBeCloseTo(60, 0);
|
|
150
|
+
});
|
|
151
|
+
(0, globals_1.it)('throttles high-volume matches well below their raw points', () => {
|
|
152
|
+
(0, globals_1.expect)((0, improvement_1.matchExperience)(206)).toBeLessThan(206);
|
|
153
|
+
(0, globals_1.expect)((0, improvement_1.matchExperience)(206)).toBeCloseTo(108, 0);
|
|
154
|
+
});
|
|
155
|
+
(0, globals_1.it)('gives a 5-set match (~206 pts) only ~13% more than a 3-set match (~147 pts)', () => {
|
|
156
|
+
(0, globals_1.expect)((0, improvement_1.matchExperience)(206) / (0, improvement_1.matchExperience)(147)).toBeCloseTo(1.13, 2);
|
|
157
|
+
});
|
|
158
|
+
(0, globals_1.it)('is monotonic non-decreasing in points played', () => {
|
|
159
|
+
let prev = -1;
|
|
160
|
+
for (let p = 0; p <= 250; p += 10) {
|
|
161
|
+
const v = (0, improvement_1.matchExperience)(p);
|
|
162
|
+
(0, globals_1.expect)(v).toBeGreaterThanOrEqual(prev);
|
|
163
|
+
prev = v;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
(0, globals_1.it)('zero or negative points → zero exp', () => {
|
|
167
|
+
(0, globals_1.expect)((0, improvement_1.matchExperience)(0)).toBe(0);
|
|
168
|
+
(0, globals_1.expect)((0, improvement_1.matchExperience)(-5)).toBe(0);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
(0, globals_1.describe)('experienceGained()', () => {
|
|
172
|
+
(0, globals_1.it)('young players (below their profile youthAge) gain 25% more than the base', () => {
|
|
173
|
+
const base = Math.round((0, improvement_1.matchExperience)(100));
|
|
174
|
+
(0, globals_1.expect)((0, improvement_1.experienceGained)(100, 18, decline_1.DeclineProfileEnum.STANDARD)).toBe(Math.round((0, improvement_1.matchExperience)(100) * 1.25));
|
|
175
|
+
(0, globals_1.expect)((0, improvement_1.experienceGained)(100, 25, decline_1.DeclineProfileEnum.STANDARD)).toBe(base);
|
|
176
|
+
});
|
|
177
|
+
(0, globals_1.it)('zero points → zero exp regardless of age', () => {
|
|
178
|
+
(0, globals_1.expect)((0, improvement_1.experienceGained)(0, 18, decline_1.DeclineProfileEnum.STANDARD)).toBe(0);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -43,6 +43,7 @@ export declare const SetSubConfigSchema: z.ZodObject<{
|
|
|
43
43
|
}>;
|
|
44
44
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
45
45
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
46
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
46
47
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
47
48
|
type: z.ZodLiteral<PinchConditionType.ASAP>;
|
|
48
49
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -80,6 +81,7 @@ export declare const DesignatedSubSchema: z.ZodObject<{
|
|
|
80
81
|
}>;
|
|
81
82
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
82
83
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
84
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
83
85
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
84
86
|
type: z.ZodLiteral<PinchConditionType.ASAP>;
|
|
85
87
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -115,6 +117,7 @@ export declare const DesignatedSubSchema: z.ZodObject<{
|
|
|
115
117
|
}>;
|
|
116
118
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
117
119
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
120
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
118
121
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
119
122
|
type: z.ZodLiteral<PinchConditionType.ASAP>;
|
|
120
123
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -26,6 +26,7 @@ const setSubConfigShape = {
|
|
|
26
26
|
mode: exports.SubModeSchema,
|
|
27
27
|
bench: playerInstanceSchema.optional(),
|
|
28
28
|
fatigueBand: zod_1.z.nativeEnum(energy_band_1.EnergyBand).optional(),
|
|
29
|
+
benchFatigueBand: zod_1.z.nativeEnum(energy_band_1.EnergyBand).optional(),
|
|
29
30
|
conditions: zod_1.z.array(exports.PinchConditionSchema).optional(),
|
|
30
31
|
conditionLogic: exports.ConditionLogicSchema.optional()
|
|
31
32
|
};
|
|
@@ -26,6 +26,7 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
26
26
|
}>;
|
|
27
27
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
28
28
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
29
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
29
30
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
30
31
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
31
32
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -61,6 +62,7 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
61
62
|
}>;
|
|
62
63
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
63
64
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
65
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
64
66
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
65
67
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
66
68
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -34,6 +34,7 @@ export declare const TeamInputSchema: z.ZodObject<{
|
|
|
34
34
|
}>;
|
|
35
35
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
36
36
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
|
|
37
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
|
|
37
38
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
38
39
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
39
40
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -69,6 +70,7 @@ export declare const TeamInputSchema: z.ZodObject<{
|
|
|
69
70
|
}>;
|
|
70
71
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
71
72
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
|
|
73
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
|
|
72
74
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
73
75
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
74
76
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[
|
|
2
|
-
{ "profile": "PRODIGY", "startAge": 27, "initTicks": 13.0, "accel": 1.00, "peakTicks": 16.0 },
|
|
3
|
-
{ "profile": "STANDARD", "startAge": 29, "initTicks": 9.0, "accel": 1.75, "peakTicks": 16.0 },
|
|
4
|
-
{ "profile": "VETERAN", "startAge": 32, "initTicks": 10.0, "accel": 1.50, "peakTicks": 16.0 },
|
|
5
|
-
{ "profile": "IRONMAN", "startAge": 34, "initTicks": 10.0, "accel": 1.50, "peakTicks": 16.0 }
|
|
2
|
+
{ "profile": "PRODIGY", "startAge": 27, "youthAge": 20, "initTicks": 13.0, "accel": 1.00, "peakTicks": 16.0 },
|
|
3
|
+
{ "profile": "STANDARD", "startAge": 29, "youthAge": 21, "initTicks": 9.0, "accel": 1.75, "peakTicks": 16.0 },
|
|
4
|
+
{ "profile": "VETERAN", "startAge": 32, "youthAge": 22, "initTicks": 10.0, "accel": 1.50, "peakTicks": 16.0 },
|
|
5
|
+
{ "profile": "IRONMAN", "startAge": 34, "youthAge": 23, "initTicks": 10.0, "accel": 1.50, "peakTicks": 16.0 }
|
|
6
6
|
]
|
|
@@ -24,6 +24,13 @@ export interface MatchCompetition {
|
|
|
24
24
|
tier?: number;
|
|
25
25
|
subdivision?: number;
|
|
26
26
|
}
|
|
27
|
+
export interface MatchForm {
|
|
28
|
+
matchId: string;
|
|
29
|
+
result: 'WIN' | 'LOSE';
|
|
30
|
+
side: 'HOME' | 'AWAY';
|
|
31
|
+
opponent: string;
|
|
32
|
+
dR: number;
|
|
33
|
+
}
|
|
27
34
|
export type Match = Omit<DataProps<_Match>, 'sets' | 'homeTeam' | 'awayTeam' | 'VPERs'> & {
|
|
28
35
|
sets: MatchSet[];
|
|
29
36
|
boxScores: BoxScore[];
|
|
@@ -37,6 +44,8 @@ export type Match = Omit<DataProps<_Match>, 'sets' | 'homeTeam' | 'awayTeam' | '
|
|
|
37
44
|
results?: Results;
|
|
38
45
|
competition?: MatchCompetition;
|
|
39
46
|
rallyMetrics?: RallyMetrics;
|
|
47
|
+
homeForm?: MatchForm[];
|
|
48
|
+
awayForm?: MatchForm[];
|
|
40
49
|
};
|
|
41
50
|
export type RallyEvent = DataProps<_RallyEvent> & {
|
|
42
51
|
team?: Team;
|
|
@@ -65,6 +74,7 @@ export interface ApiSetSubConfig {
|
|
|
65
74
|
mode: SubMode;
|
|
66
75
|
benchId?: string;
|
|
67
76
|
fatigueBand?: EnergyBand;
|
|
77
|
+
benchFatigueBand?: EnergyBand;
|
|
68
78
|
conditions?: PinchCondition[];
|
|
69
79
|
conditionLogic?: ConditionLogic;
|
|
70
80
|
}
|
|
@@ -73,6 +83,7 @@ export interface ApiDesignatedSub {
|
|
|
73
83
|
benchId?: string;
|
|
74
84
|
mode: SubMode;
|
|
75
85
|
fatigueBand?: EnergyBand;
|
|
86
|
+
benchFatigueBand?: EnergyBand;
|
|
76
87
|
conditions?: PinchCondition[];
|
|
77
88
|
conditionLogic?: ConditionLogic;
|
|
78
89
|
sets?: ApiSetSubConfig[];
|
|
@@ -17,6 +17,7 @@ export interface SetSubConfigAttributes {
|
|
|
17
17
|
mode: SubMode;
|
|
18
18
|
benchId?: PlayerId;
|
|
19
19
|
fatigueBand?: EnergyBand;
|
|
20
|
+
benchFatigueBand?: EnergyBand;
|
|
20
21
|
conditions?: PinchCondition[];
|
|
21
22
|
conditionLogic?: ConditionLogic;
|
|
22
23
|
}
|
|
@@ -26,6 +27,7 @@ export interface DesignatedSubAttributes {
|
|
|
26
27
|
mode?: SubMode;
|
|
27
28
|
isPinchServer?: boolean;
|
|
28
29
|
fatigueBand?: SubBand;
|
|
30
|
+
benchFatigueBand?: EnergyBand;
|
|
29
31
|
conditions?: PinchCondition[];
|
|
30
32
|
conditionLogic?: ConditionLogic;
|
|
31
33
|
sets?: SetSubConfigAttributes[];
|
|
@@ -58,12 +58,14 @@ function transformToAttributes(tactics, teamId) {
|
|
|
58
58
|
benchId: ds.bench?.id,
|
|
59
59
|
mode: ds.mode,
|
|
60
60
|
fatigueBand: ds.fatigueBand,
|
|
61
|
+
benchFatigueBand: ds.benchFatigueBand,
|
|
61
62
|
conditions: ds.conditions,
|
|
62
63
|
conditionLogic: ds.conditionLogic,
|
|
63
64
|
sets: ds.sets?.map(s => ({
|
|
64
65
|
mode: s.mode,
|
|
65
66
|
benchId: s.bench?.id,
|
|
66
67
|
fatigueBand: s.fatigueBand,
|
|
68
|
+
benchFatigueBand: s.benchFatigueBand,
|
|
67
69
|
conditions: s.conditions,
|
|
68
70
|
conditionLogic: s.conditionLogic
|
|
69
71
|
}))
|
|
@@ -111,12 +113,14 @@ function transformToObject(model, roster) {
|
|
|
111
113
|
bench: d.benchId != null ? findPlayer(d.benchId, roster) : undefined,
|
|
112
114
|
mode: subModeFromAttributes(d),
|
|
113
115
|
fatigueBand: d.fatigueBand === 'NEVER' ? undefined : d.fatigueBand,
|
|
116
|
+
benchFatigueBand: d.benchFatigueBand,
|
|
114
117
|
conditions: d.conditions,
|
|
115
118
|
conditionLogic: d.conditionLogic,
|
|
116
119
|
sets: d.sets?.map(s => ({
|
|
117
120
|
mode: s.mode,
|
|
118
121
|
bench: s.benchId != null ? findPlayer(s.benchId, roster) : undefined,
|
|
119
122
|
fatigueBand: s.fatigueBand,
|
|
123
|
+
benchFatigueBand: s.benchFatigueBand,
|
|
120
124
|
conditions: s.conditions,
|
|
121
125
|
conditionLogic: s.conditionLogic
|
|
122
126
|
}))
|
|
@@ -10,6 +10,7 @@ export declare const declineProfiles: DeclineProfileEnum[];
|
|
|
10
10
|
interface DeclineProfileEntry {
|
|
11
11
|
profile: string;
|
|
12
12
|
startAge: number;
|
|
13
|
+
youthAge: number;
|
|
13
14
|
initTicks: number;
|
|
14
15
|
accel: number;
|
|
15
16
|
peakTicks: number;
|
|
@@ -25,6 +26,7 @@ export declare const SKILL_POOL: PerformanceStatsKey[];
|
|
|
25
26
|
*/
|
|
26
27
|
export declare const ENERGY_POOL: PerformanceStatsKey[];
|
|
27
28
|
export declare function getDeclineProfile(profile: DeclineProfile): DeclineProfileEntry;
|
|
29
|
+
export declare function youthMultiplier(age: number, profile: DeclineProfile): number;
|
|
28
30
|
export type DeclineStatus = 'ACTIVE' | 'DECLINING' | 'PEAK';
|
|
29
31
|
export interface DeclineTick {
|
|
30
32
|
/** Number of decline ticks to apply this iteration. */
|
|
@@ -29,6 +29,12 @@ export function getDeclineProfile(profile) {
|
|
|
29
29
|
throw new Error(`UNKNOWN_DECLINE_PROFILE: ${profile}`);
|
|
30
30
|
return entry;
|
|
31
31
|
}
|
|
32
|
+
// Prospects below their profile's youthAge develop faster: a multiplier applied to the per-match improvement
|
|
33
|
+
// experience. At or above youthAge (and pre-decline) growth is at the normal rate.
|
|
34
|
+
const YOUTH_EXP_MULTIPLIER = 1.25;
|
|
35
|
+
export function youthMultiplier(age, profile) {
|
|
36
|
+
return age < getDeclineProfile(profile).youthAge ? YOUTH_EXP_MULTIPLIER : 1;
|
|
37
|
+
}
|
|
32
38
|
/**
|
|
33
39
|
* Decline is fully derivable from age + profile — no running state is persisted.
|
|
34
40
|
* tickRate = min(peakTicks, initTicks + (age - startAge) * accel)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from '@jest/globals';
|
|
2
|
-
import { DeclineProfileEnum, declineProfiles, getDeclineProfile, computeDeclineTick, SKILL_POOL, ENERGY_POOL } from './decline';
|
|
2
|
+
import { DeclineProfileEnum, declineProfiles, getDeclineProfile, computeDeclineTick, youthMultiplier, SKILL_POOL, ENERGY_POOL } from './decline';
|
|
3
3
|
import { performanceStatKeys } from './performance-stats-keys';
|
|
4
4
|
// ─── getDeclineProfile ─────────────────────────────────────────────────────────
|
|
5
5
|
describe('getDeclineProfile()', () => {
|
|
@@ -98,3 +98,20 @@ describe('computeDeclineTick()', () => {
|
|
|
98
98
|
expect(computeDeclineTick(33, DeclineProfileEnum.STANDARD).atPeak).toBe(true);
|
|
99
99
|
});
|
|
100
100
|
});
|
|
101
|
+
// ─── youthMultiplier ───────────────────────────────────────────────────────────
|
|
102
|
+
describe('youthMultiplier()', () => {
|
|
103
|
+
it.each([
|
|
104
|
+
[DeclineProfileEnum.PRODIGY, 20],
|
|
105
|
+
[DeclineProfileEnum.STANDARD, 21],
|
|
106
|
+
[DeclineProfileEnum.VETERAN, 22],
|
|
107
|
+
[DeclineProfileEnum.IRONMAN, 23]
|
|
108
|
+
])('%s youthAge is %i', (profile, youthAge) => {
|
|
109
|
+
expect(getDeclineProfile(profile).youthAge).toBe(youthAge);
|
|
110
|
+
});
|
|
111
|
+
it('boosts players below youthAge by 1.25 and leaves the rest at 1', () => {
|
|
112
|
+
expect(youthMultiplier(20, DeclineProfileEnum.STANDARD)).toBe(1.25); // youthAge 21
|
|
113
|
+
expect(youthMultiplier(21, DeclineProfileEnum.STANDARD)).toBe(1);
|
|
114
|
+
expect(youthMultiplier(19, DeclineProfileEnum.PRODIGY)).toBe(1.25); // youthAge 20
|
|
115
|
+
expect(youthMultiplier(20, DeclineProfileEnum.PRODIGY)).toBe(1);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Rarity } from './rarity';
|
|
2
2
|
import { Stats } from './stats';
|
|
3
|
+
import { DeclineProfile } from './decline';
|
|
3
4
|
export type TrainingFocus = Stats;
|
|
4
5
|
export declare function getStatCap(rarity: Rarity): number;
|
|
5
6
|
export declare function getRarityMultiplier(rarity: Rarity): number;
|
|
6
7
|
export declare function getImprovementThreshold(statTotal: number, rarity: Rarity): number;
|
|
7
8
|
export declare function getCoachMultiplier(rarity: Rarity): number;
|
|
9
|
+
export declare function matchExperience(pointsPlayed: number): number;
|
|
10
|
+
export declare function experienceGained(pointsPlayed: number, age: number, profile: DeclineProfile): number;
|
|
8
11
|
export declare function selectImprovableStat(rarity: Rarity, trainingFocus: TrainingFocus, currentStats: Record<string, number>): string | null;
|
|
@@ -2,6 +2,7 @@ import { statCapsJSON } from '../../stat-config';
|
|
|
2
2
|
import { RarityEnum } from './rarity';
|
|
3
3
|
import { performanceStatKeys } from './performance-stats';
|
|
4
4
|
import { getMultipliers } from './stats';
|
|
5
|
+
import { youthMultiplier } from './decline';
|
|
5
6
|
const CURVE_BASE = 100;
|
|
6
7
|
const CURVE_SCALAR = 0.098;
|
|
7
8
|
const CURVE_EXPONENT = 1.5;
|
|
@@ -33,6 +34,26 @@ const COACH_MULTIPLIERS = {
|
|
|
33
34
|
export function getCoachMultiplier(rarity) {
|
|
34
35
|
return COACH_MULTIPLIERS[rarity] ?? 1.0;
|
|
35
36
|
}
|
|
37
|
+
// ── Per-match experience curve ──────────────────────────────────────────────
|
|
38
|
+
// Experience is no longer 1:1 with points played: each point is worth progressively less (a saturating
|
|
39
|
+
// curve), so high-volume matches (5-setters, never-subbed players) don't improve disproportionately fast.
|
|
40
|
+
// MATCH_EXP_K sets the long-match premium - a ~206-point 5-setter yields only ~13% more exp than a ~147-point
|
|
41
|
+
// 3-setter. MATCH_EXP_SCALE is the asymptote, calibrated so exp == points at ~60 points played (the league-
|
|
42
|
+
// average match): a normal match keeps roughly its old value and only longer matches are throttled. Tuning
|
|
43
|
+
// MATCH_EXP_SCALE shifts overall pace without changing the premium; MATCH_EXP_K changes the premium.
|
|
44
|
+
const MATCH_EXP_SCALE = 160;
|
|
45
|
+
const MATCH_EXP_K = 100;
|
|
46
|
+
export function matchExperience(pointsPlayed) {
|
|
47
|
+
if (pointsPlayed <= 0)
|
|
48
|
+
return 0;
|
|
49
|
+
return (MATCH_EXP_SCALE * pointsPlayed) / (pointsPlayed + MATCH_EXP_K);
|
|
50
|
+
}
|
|
51
|
+
// Total improvement experience a player earns from a match: the saturating per-match curve, boosted for
|
|
52
|
+
// players still below their decline profile's youthAge (prospects develop faster). Rounded - the progress
|
|
53
|
+
// counter holds whole units.
|
|
54
|
+
export function experienceGained(pointsPlayed, age, profile) {
|
|
55
|
+
return Math.round(matchExperience(pointsPlayed) * youthMultiplier(age, profile));
|
|
56
|
+
}
|
|
36
57
|
const KEY_TO_COLUMN = {
|
|
37
58
|
setting: 'setting',
|
|
38
59
|
serve: 'serve',
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { describe, it, expect } from '@jest/globals';
|
|
2
2
|
import { RarityEnum } from './rarity';
|
|
3
|
-
import { getStatCap, getRarityMultiplier, getImprovementThreshold, getCoachMultiplier, selectImprovableStat } from './improvement';
|
|
3
|
+
import { getStatCap, getRarityMultiplier, getImprovementThreshold, getCoachMultiplier, selectImprovableStat, matchExperience, experienceGained } from './improvement';
|
|
4
4
|
import { StatsEnum } from './stats';
|
|
5
|
+
import { DeclineProfileEnum } from './decline';
|
|
5
6
|
// ─── getStatCap ───────────────────────────────────────────────────────────────
|
|
6
7
|
describe('getStatCap()', () => {
|
|
7
8
|
it.each([
|
|
@@ -140,3 +141,38 @@ describe('selectImprovableStat()', () => {
|
|
|
140
141
|
}
|
|
141
142
|
});
|
|
142
143
|
});
|
|
144
|
+
// ─── matchExperience / experienceGained ───────────────────────────────────────
|
|
145
|
+
describe('matchExperience()', () => {
|
|
146
|
+
it('breaks even (~1:1) at the league-average match (~60 points)', () => {
|
|
147
|
+
expect(matchExperience(60)).toBeCloseTo(60, 0);
|
|
148
|
+
});
|
|
149
|
+
it('throttles high-volume matches well below their raw points', () => {
|
|
150
|
+
expect(matchExperience(206)).toBeLessThan(206);
|
|
151
|
+
expect(matchExperience(206)).toBeCloseTo(108, 0);
|
|
152
|
+
});
|
|
153
|
+
it('gives a 5-set match (~206 pts) only ~13% more than a 3-set match (~147 pts)', () => {
|
|
154
|
+
expect(matchExperience(206) / matchExperience(147)).toBeCloseTo(1.13, 2);
|
|
155
|
+
});
|
|
156
|
+
it('is monotonic non-decreasing in points played', () => {
|
|
157
|
+
let prev = -1;
|
|
158
|
+
for (let p = 0; p <= 250; p += 10) {
|
|
159
|
+
const v = matchExperience(p);
|
|
160
|
+
expect(v).toBeGreaterThanOrEqual(prev);
|
|
161
|
+
prev = v;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
it('zero or negative points → zero exp', () => {
|
|
165
|
+
expect(matchExperience(0)).toBe(0);
|
|
166
|
+
expect(matchExperience(-5)).toBe(0);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
describe('experienceGained()', () => {
|
|
170
|
+
it('young players (below their profile youthAge) gain 25% more than the base', () => {
|
|
171
|
+
const base = Math.round(matchExperience(100));
|
|
172
|
+
expect(experienceGained(100, 18, DeclineProfileEnum.STANDARD)).toBe(Math.round(matchExperience(100) * 1.25));
|
|
173
|
+
expect(experienceGained(100, 25, DeclineProfileEnum.STANDARD)).toBe(base);
|
|
174
|
+
});
|
|
175
|
+
it('zero points → zero exp regardless of age', () => {
|
|
176
|
+
expect(experienceGained(0, 18, DeclineProfileEnum.STANDARD)).toBe(0);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
@@ -43,6 +43,7 @@ export declare const SetSubConfigSchema: z.ZodObject<{
|
|
|
43
43
|
}>;
|
|
44
44
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
45
45
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
46
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
46
47
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
47
48
|
type: z.ZodLiteral<PinchConditionType.ASAP>;
|
|
48
49
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -80,6 +81,7 @@ export declare const DesignatedSubSchema: z.ZodObject<{
|
|
|
80
81
|
}>;
|
|
81
82
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
82
83
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
84
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
83
85
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
84
86
|
type: z.ZodLiteral<PinchConditionType.ASAP>;
|
|
85
87
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -115,6 +117,7 @@ export declare const DesignatedSubSchema: z.ZodObject<{
|
|
|
115
117
|
}>;
|
|
116
118
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
117
119
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
120
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
118
121
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
119
122
|
type: z.ZodLiteral<PinchConditionType.ASAP>;
|
|
120
123
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -23,6 +23,7 @@ const setSubConfigShape = {
|
|
|
23
23
|
mode: SubModeSchema,
|
|
24
24
|
bench: playerInstanceSchema.optional(),
|
|
25
25
|
fatigueBand: z.nativeEnum(EnergyBand).optional(),
|
|
26
|
+
benchFatigueBand: z.nativeEnum(EnergyBand).optional(),
|
|
26
27
|
conditions: z.array(PinchConditionSchema).optional(),
|
|
27
28
|
conditionLogic: ConditionLogicSchema.optional()
|
|
28
29
|
};
|
|
@@ -26,6 +26,7 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
26
26
|
}>;
|
|
27
27
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
28
28
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
29
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
29
30
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
30
31
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
31
32
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -61,6 +62,7 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
61
62
|
}>;
|
|
62
63
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
63
64
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
65
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
64
66
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
65
67
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
66
68
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -34,6 +34,7 @@ export declare const TeamInputSchema: z.ZodObject<{
|
|
|
34
34
|
}>;
|
|
35
35
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
36
36
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
|
|
37
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
|
|
37
38
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
38
39
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
39
40
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -69,6 +70,7 @@ export declare const TeamInputSchema: z.ZodObject<{
|
|
|
69
70
|
}>;
|
|
70
71
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
71
72
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
|
|
73
|
+
benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
|
|
72
74
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
73
75
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
74
76
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[
|
|
2
|
-
{ "profile": "PRODIGY", "startAge": 27, "initTicks": 13.0, "accel": 1.00, "peakTicks": 16.0 },
|
|
3
|
-
{ "profile": "STANDARD", "startAge": 29, "initTicks": 9.0, "accel": 1.75, "peakTicks": 16.0 },
|
|
4
|
-
{ "profile": "VETERAN", "startAge": 32, "initTicks": 10.0, "accel": 1.50, "peakTicks": 16.0 },
|
|
5
|
-
{ "profile": "IRONMAN", "startAge": 34, "initTicks": 10.0, "accel": 1.50, "peakTicks": 16.0 }
|
|
2
|
+
{ "profile": "PRODIGY", "startAge": 27, "youthAge": 20, "initTicks": 13.0, "accel": 1.00, "peakTicks": 16.0 },
|
|
3
|
+
{ "profile": "STANDARD", "startAge": 29, "youthAge": 21, "initTicks": 9.0, "accel": 1.75, "peakTicks": 16.0 },
|
|
4
|
+
{ "profile": "VETERAN", "startAge": 32, "youthAge": 22, "initTicks": 10.0, "accel": 1.50, "peakTicks": 16.0 },
|
|
5
|
+
{ "profile": "IRONMAN", "startAge": 34, "youthAge": 23, "initTicks": 10.0, "accel": 1.50, "peakTicks": 16.0 }
|
|
6
6
|
]
|