volleyballsimtypes 0.0.483 → 0.0.486
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/service/competition/season.d.ts +2 -3
- package/dist/cjs/src/service/competition/season.js +0 -11
- package/dist/cjs/src/service/event/block.d.ts +0 -1
- package/dist/cjs/src/service/event/block.js +0 -6
- package/dist/cjs/src/service/event/free-ball.d.ts +0 -1
- package/dist/cjs/src/service/event/free-ball.js +0 -5
- package/dist/cjs/src/service/event/in-play-event.d.ts +0 -1
- package/dist/cjs/src/service/event/in-play-event.js +0 -8
- package/dist/cjs/src/service/event/libero-replacement.d.ts +0 -1
- package/dist/cjs/src/service/event/libero-replacement.js +0 -7
- package/dist/cjs/src/service/event/rally-event.d.ts +0 -1
- package/dist/cjs/src/service/event/reception.d.ts +0 -1
- package/dist/cjs/src/service/event/reception.js +0 -5
- package/dist/cjs/src/service/event/serve.d.ts +0 -1
- package/dist/cjs/src/service/event/serve.js +0 -5
- package/dist/cjs/src/service/event/set.d.ts +0 -1
- package/dist/cjs/src/service/event/set.js +0 -6
- package/dist/cjs/src/service/event/spike.d.ts +0 -1
- package/dist/cjs/src/service/event/spike.js +0 -5
- package/dist/cjs/src/service/event/substitution.d.ts +0 -1
- package/dist/cjs/src/service/event/substitution.js +0 -7
- package/dist/cjs/src/service/player/improvement.d.ts +0 -1
- package/dist/cjs/src/service/player/improvement.js +0 -9
- package/dist/cjs/src/service/player/improvement.test.js +0 -15
- package/dist/cjs/src/service/team/energy-band.d.ts +0 -1
- package/dist/cjs/src/service/team/energy-band.js +1 -2
- package/dist/cjs/src/service/team/formation.d.ts +0 -1
- package/dist/cjs/src/service/team/formation.js +0 -3
- package/dist/cjs/src/service/team/sub-config-convert.js +7 -2
- package/dist/cjs/src/service/team/sub-config-convert.test.js +19 -0
- package/dist/cjs/src/service/utils/object-utils.d.ts +0 -1
- package/dist/cjs/src/service/utils/object-utils.js +0 -2
- package/dist/esm/src/service/competition/season.d.ts +2 -3
- package/dist/esm/src/service/competition/season.js +0 -11
- package/dist/esm/src/service/event/block.d.ts +0 -1
- package/dist/esm/src/service/event/block.js +0 -6
- package/dist/esm/src/service/event/free-ball.d.ts +0 -1
- package/dist/esm/src/service/event/free-ball.js +0 -5
- package/dist/esm/src/service/event/in-play-event.d.ts +0 -1
- package/dist/esm/src/service/event/in-play-event.js +0 -8
- package/dist/esm/src/service/event/libero-replacement.d.ts +0 -1
- package/dist/esm/src/service/event/libero-replacement.js +0 -7
- package/dist/esm/src/service/event/rally-event.d.ts +0 -1
- package/dist/esm/src/service/event/reception.d.ts +0 -1
- package/dist/esm/src/service/event/reception.js +0 -5
- package/dist/esm/src/service/event/serve.d.ts +0 -1
- package/dist/esm/src/service/event/serve.js +0 -5
- package/dist/esm/src/service/event/set.d.ts +0 -1
- package/dist/esm/src/service/event/set.js +0 -6
- package/dist/esm/src/service/event/spike.d.ts +0 -1
- package/dist/esm/src/service/event/spike.js +0 -5
- package/dist/esm/src/service/event/substitution.d.ts +0 -1
- package/dist/esm/src/service/event/substitution.js +0 -7
- package/dist/esm/src/service/player/improvement.d.ts +0 -1
- package/dist/esm/src/service/player/improvement.js +0 -8
- package/dist/esm/src/service/player/improvement.test.js +1 -16
- package/dist/esm/src/service/team/energy-band.d.ts +0 -1
- package/dist/esm/src/service/team/energy-band.js +0 -1
- package/dist/esm/src/service/team/formation.d.ts +0 -1
- package/dist/esm/src/service/team/formation.js +0 -3
- package/dist/esm/src/service/team/sub-config-convert.js +7 -2
- package/dist/esm/src/service/team/sub-config-convert.test.js +19 -0
- package/dist/esm/src/service/utils/object-utils.d.ts +0 -1
- package/dist/esm/src/service/utils/object-utils.js +1 -1
- package/package.json +1 -1
|
@@ -10,12 +10,11 @@ export declare class Season {
|
|
|
10
10
|
readonly iteration: Iteration;
|
|
11
11
|
readonly divisionId: string;
|
|
12
12
|
readonly standings: Standing[];
|
|
13
|
-
private _status;
|
|
14
|
-
private _champion?;
|
|
13
|
+
private readonly _status;
|
|
14
|
+
private readonly _champion?;
|
|
15
15
|
static create(input: unknown): Season;
|
|
16
16
|
private constructor();
|
|
17
17
|
calculateStandings(): Standing[];
|
|
18
|
-
updateStandings(): void;
|
|
19
18
|
get champion(): Team | undefined;
|
|
20
19
|
get status(): Status;
|
|
21
20
|
static createSeason(iteration: Iteration, teams: Team[], divisionId: string): Season;
|
|
@@ -40,17 +40,6 @@ class Season {
|
|
|
40
40
|
return standing;
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
|
-
updateStandings() {
|
|
44
|
-
if (this.matches == null || this.matches.length < 1)
|
|
45
|
-
throw new Error('NO_MATCHES');
|
|
46
|
-
const updated = this.calculateStandings();
|
|
47
|
-
this.standings.splice(0, this.standings.length, ...updated);
|
|
48
|
-
const isOver = this.matches.every((match) => match.isOver());
|
|
49
|
-
if (isOver) {
|
|
50
|
-
this._status = data_1.StatusEnum.COMPLETE;
|
|
51
|
-
this._champion = this.teams.find((team) => team.id === this.standings[0].teamId);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
43
|
get champion() {
|
|
55
44
|
if (this._champion != null)
|
|
56
45
|
return this._champion;
|
|
@@ -40,11 +40,5 @@ class Block extends in_play_event_1.InPlayEvent {
|
|
|
40
40
|
this.failure = failure;
|
|
41
41
|
this.blockers = blockers;
|
|
42
42
|
}
|
|
43
|
-
toString() {
|
|
44
|
-
const failure = `"failure":${this.failure}`;
|
|
45
|
-
const type = `"type":${this.type}`;
|
|
46
|
-
const blockers = `"blockers":[${this.blockers.map(b => `"${b}"`).join(',')}]`;
|
|
47
|
-
return `{${this._toString()},${failure},${type},${blockers}}`;
|
|
48
|
-
}
|
|
49
43
|
}
|
|
50
44
|
exports.Block = Block;
|
|
@@ -38,10 +38,5 @@ class FreeBall extends in_play_event_1.InPlayEvent {
|
|
|
38
38
|
this.type = type;
|
|
39
39
|
this.failure = failure;
|
|
40
40
|
}
|
|
41
|
-
toString() {
|
|
42
|
-
const failure = `"failure":${this.failure}`;
|
|
43
|
-
const type = `"type":${this.type}`;
|
|
44
|
-
return `{${this._toString()},${failure},${type}}`;
|
|
45
|
-
}
|
|
46
41
|
}
|
|
47
42
|
exports.FreeBall = FreeBall;
|
|
@@ -20,5 +20,4 @@ export declare abstract class InPlayEvent<F, T> extends RallyEvent {
|
|
|
20
20
|
readonly activeTraits: Trait[];
|
|
21
21
|
readonly incident?: EventIncident;
|
|
22
22
|
protected constructor({ eventType, playerId, target, score, activeTraits, incident }: InPlayEventOpts);
|
|
23
|
-
protected _toString(): string;
|
|
24
23
|
}
|
|
@@ -10,13 +10,5 @@ class InPlayEvent extends rally_event_1.RallyEvent {
|
|
|
10
10
|
this.activeTraits = activeTraits;
|
|
11
11
|
this.incident = incident;
|
|
12
12
|
}
|
|
13
|
-
_toString() {
|
|
14
|
-
const score = `"score":${this.score}`;
|
|
15
|
-
const playerId = `"playerId":"${this.playerId}"`;
|
|
16
|
-
const target = `"target":${this.target}`;
|
|
17
|
-
const eventType = `"eventType":${this.eventType}`;
|
|
18
|
-
const traits = `"activeTraits":[${this.activeTraits.map(t => `"${t}"`).join(',')}]`;
|
|
19
|
-
return `${score},${playerId},${target},${eventType},${traits}`;
|
|
20
|
-
}
|
|
21
13
|
}
|
|
22
14
|
exports.InPlayEvent = InPlayEvent;
|
|
@@ -27,12 +27,5 @@ class LiberoReplacement extends rally_event_1.RallyEvent {
|
|
|
27
27
|
this.type = type;
|
|
28
28
|
this.libero = libero;
|
|
29
29
|
}
|
|
30
|
-
toString() {
|
|
31
|
-
const playerId = `"playerId":"${this.playerId}"`;
|
|
32
|
-
const type = `"type":${this.type}`;
|
|
33
|
-
const eventType = `"eventType":${this.eventType}`;
|
|
34
|
-
const libero = `"libero":"${this.libero}"`;
|
|
35
|
-
return `{${playerId},${type},${eventType},${libero}}`;
|
|
36
|
-
}
|
|
37
30
|
}
|
|
38
31
|
exports.LiberoReplacement = LiberoReplacement;
|
|
@@ -41,10 +41,5 @@ class Reception extends in_play_event_1.InPlayEvent {
|
|
|
41
41
|
this.type = type;
|
|
42
42
|
this.failure = failure;
|
|
43
43
|
}
|
|
44
|
-
toString() {
|
|
45
|
-
const failure = `"failure":${this.failure}`;
|
|
46
|
-
const type = `"type":${this.type}`;
|
|
47
|
-
return `{${this._toString()},${failure},${type}}`;
|
|
48
|
-
}
|
|
49
44
|
}
|
|
50
45
|
exports.Reception = Reception;
|
|
@@ -38,10 +38,5 @@ class Serve extends in_play_event_1.InPlayEvent {
|
|
|
38
38
|
this.type = type;
|
|
39
39
|
this.failure = failure;
|
|
40
40
|
}
|
|
41
|
-
toString() {
|
|
42
|
-
const failure = `"failure":${this.failure}`;
|
|
43
|
-
const type = `"type":${this.type}`;
|
|
44
|
-
return `{${this._toString()},${failure},${type}}`;
|
|
45
|
-
}
|
|
46
41
|
}
|
|
47
42
|
exports.Serve = Serve;
|
|
@@ -47,11 +47,5 @@ class Set extends in_play_event_1.InPlayEvent {
|
|
|
47
47
|
this.failure = failure;
|
|
48
48
|
this.tempo = tempo;
|
|
49
49
|
}
|
|
50
|
-
toString() {
|
|
51
|
-
const failure = `"failure":${this.failure}`;
|
|
52
|
-
const type = `"type":${this.type}`;
|
|
53
|
-
const tempo = this.tempo !== undefined ? `,"tempo":${this.tempo}` : '';
|
|
54
|
-
return `{${this._toString()},${failure},${type}${tempo}}`;
|
|
55
|
-
}
|
|
56
50
|
}
|
|
57
51
|
exports.Set = Set;
|
|
@@ -40,10 +40,5 @@ class Spike extends in_play_event_1.InPlayEvent {
|
|
|
40
40
|
this.type = type;
|
|
41
41
|
this.failure = failure;
|
|
42
42
|
}
|
|
43
|
-
toString() {
|
|
44
|
-
const failure = `"failure":${this.failure}`;
|
|
45
|
-
const type = `"type":${this.type}`;
|
|
46
|
-
return `{${this._toString()},${failure},${type}}`;
|
|
47
|
-
}
|
|
48
43
|
}
|
|
49
44
|
exports.Spike = Spike;
|
|
@@ -22,12 +22,5 @@ class Substitution extends rally_event_1.RallyEvent {
|
|
|
22
22
|
this.playerOut = playerOut;
|
|
23
23
|
this.injury = injury;
|
|
24
24
|
}
|
|
25
|
-
toString() {
|
|
26
|
-
const playerId = `"playerId":"${this.playerId}"`;
|
|
27
|
-
const playerOut = `"playerOut":"${this.playerOut}"`;
|
|
28
|
-
const eventType = `"eventType":${this.eventType}`;
|
|
29
|
-
const injury = this.injury === true ? ',"injury":true' : '';
|
|
30
|
-
return `{${playerId},${playerOut},${eventType}${injury}}`;
|
|
31
|
-
}
|
|
32
25
|
}
|
|
33
26
|
exports.Substitution = Substitution;
|
|
@@ -3,7 +3,6 @@ import { Stats } from './stats';
|
|
|
3
3
|
import { DeclineProfile } from './decline';
|
|
4
4
|
export type TrainingFocus = Stats;
|
|
5
5
|
export declare function getStatCap(rarity: Rarity): number;
|
|
6
|
-
export declare function getRarityMultiplier(rarity: Rarity): number;
|
|
7
6
|
export declare function getImprovementFloor(rarity: Rarity): number;
|
|
8
7
|
export declare function getImprovementThreshold(statTotal: number, rarity: Rarity): number;
|
|
9
8
|
export declare function getCoachMultiplier(rarity: Rarity): number;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getStatCap = getStatCap;
|
|
4
|
-
exports.getRarityMultiplier = getRarityMultiplier;
|
|
5
4
|
exports.getImprovementFloor = getImprovementFloor;
|
|
6
5
|
exports.getImprovementThreshold = getImprovementThreshold;
|
|
7
6
|
exports.getCoachMultiplier = getCoachMultiplier;
|
|
@@ -40,14 +39,6 @@ function getStatCap(rarity) {
|
|
|
40
39
|
throw new Error(`UNKNOWN_RARITY: ${rarity}`);
|
|
41
40
|
return entry.cap;
|
|
42
41
|
}
|
|
43
|
-
// Retained for config/reference. No longer used by the improvement curve -- superseded by IMPROVEMENT_FLOOR,
|
|
44
|
-
// which is the per-rarity adjustment now.
|
|
45
|
-
function getRarityMultiplier(rarity) {
|
|
46
|
-
const entry = statCaps.find(e => e.rarity === rarity);
|
|
47
|
-
if (entry == null)
|
|
48
|
-
throw new Error(`UNKNOWN_RARITY: ${rarity}`);
|
|
49
|
-
return entry.rarityMultiplier;
|
|
50
|
-
}
|
|
51
42
|
function getImprovementFloor(rarity) {
|
|
52
43
|
const floor = IMPROVEMENT_FLOOR[rarity];
|
|
53
44
|
if (floor == null)
|
|
@@ -20,21 +20,6 @@ const decline_1 = require("./decline");
|
|
|
20
20
|
(0, globals_1.expect)(() => (0, improvement_1.getStatCap)('DIVINE')).toThrow('UNKNOWN_RARITY');
|
|
21
21
|
});
|
|
22
22
|
});
|
|
23
|
-
// ─── getRarityMultiplier ──────────────────────────────────────────────────────
|
|
24
|
-
(0, globals_1.describe)('getRarityMultiplier()', () => {
|
|
25
|
-
globals_1.it.each([
|
|
26
|
-
[rarity_1.RarityEnum.COMMON, 1.0],
|
|
27
|
-
[rarity_1.RarityEnum.RARE, 1.15],
|
|
28
|
-
[rarity_1.RarityEnum.LEGENDARY, 1.3],
|
|
29
|
-
[rarity_1.RarityEnum.MYTHIC, 1.5],
|
|
30
|
-
[rarity_1.RarityEnum.SPECIAL, 1.75]
|
|
31
|
-
])('%s → multiplier %f', (rarity, expected) => {
|
|
32
|
-
(0, globals_1.expect)((0, improvement_1.getRarityMultiplier)(rarity)).toBeCloseTo(expected, 5);
|
|
33
|
-
});
|
|
34
|
-
(0, globals_1.it)('throws UNKNOWN_RARITY for an unrecognised rarity string', () => {
|
|
35
|
-
(0, globals_1.expect)(() => (0, improvement_1.getRarityMultiplier)('DIVINE')).toThrow('UNKNOWN_RARITY');
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
23
|
// ─── getImprovementThreshold ──────────────────────────────────────────────────
|
|
39
24
|
(0, globals_1.describe)('getImprovementThreshold()', () => {
|
|
40
25
|
(0, globals_1.it)('at or below the floor the threshold is CURVE_BASE (180) — the fastest pace', () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ENERGY_BAND_ORDER = exports.
|
|
3
|
+
exports.ENERGY_BAND_ORDER = exports.EnergyBand = void 0;
|
|
4
4
|
// The named fatigue bands a player's energy falls into. The numeric cutoffs and per-band performance
|
|
5
5
|
// multipliers live in the simulator (VolleyballSim court-player.ts ENERGY_BANDS); this enum is the shared
|
|
6
6
|
// vocabulary used by tactics configuration (substitution thresholds) and, later, the UI.
|
|
@@ -11,7 +11,6 @@ var EnergyBand;
|
|
|
11
11
|
EnergyBand["TIRED"] = "TIRED";
|
|
12
12
|
EnergyBand["EXHAUSTED"] = "EXHAUSTED";
|
|
13
13
|
})(EnergyBand || (exports.EnergyBand = EnergyBand = {}));
|
|
14
|
-
exports.SUB_MODES = ['FATIGUE', 'PINCH', 'NEVER'];
|
|
15
14
|
// Ordered best -> worst. Used to compare "is this player at-or-below the configured sub band".
|
|
16
15
|
exports.ENERGY_BAND_ORDER = [
|
|
17
16
|
EnergyBand.ENERGETIC,
|
|
@@ -23,12 +23,17 @@ function fatigueCondition(band) {
|
|
|
23
23
|
function convertLegacySetSubConfig(raw) {
|
|
24
24
|
if (!isRecord(raw))
|
|
25
25
|
return raw;
|
|
26
|
-
// Derive the mode for the oldest (pre-mode) generation; a unified shape has none of these keys.
|
|
26
|
+
// Derive the mode for the oldest (pre-mode) generation; a unified shape has none of these keys. Test the
|
|
27
|
+
// isPinchServer VALUE, not key presence: the sim read path (transformToObject) spreads `isPinchServer:
|
|
28
|
+
// d.isPinchServer` onto every row, so a unified row carries an `isPinchServer: undefined` key. `'isPinchServer'
|
|
29
|
+
// in raw` would then misread that unified row as a legacy FATIGUE sub and overwrite its real conditions /
|
|
30
|
+
// pinchServer flag. A genuine legacy isPinchServer-era row always has a boolean flag (true handled above), so
|
|
31
|
+
// the only remaining legacy case is an explicit `=== false`.
|
|
27
32
|
const mode = raw.mode ?? (raw.isPinchServer === true
|
|
28
33
|
? 'PINCH'
|
|
29
34
|
: raw.fatigueBand === 'NEVER'
|
|
30
35
|
? 'NEVER'
|
|
31
|
-
: raw.fatigueBand != null ||
|
|
36
|
+
: raw.fatigueBand != null || raw.isPinchServer === false ? 'FATIGUE' : null);
|
|
32
37
|
if (mode == null)
|
|
33
38
|
return raw;
|
|
34
39
|
const { mode: _m, fatigueBand, isPinchServer: _p, ...rest } = raw;
|
|
@@ -12,6 +12,25 @@ const sub_config_convert_1 = require("./sub-config-convert");
|
|
|
12
12
|
const unified = { bench: 'X', conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }], pinchServer: true };
|
|
13
13
|
(0, globals_1.expect)((0, sub_config_convert_1.convertLegacySetSubConfig)(unified)).toBe(unified);
|
|
14
14
|
});
|
|
15
|
+
(0, globals_1.it)('keeps a unified pinch server intact when a caller injects isPinchServer: undefined (sim read path)', () => {
|
|
16
|
+
// The sim read path (transformToObject) spreads `isPinchServer: d.isPinchServer` onto every row, so a
|
|
17
|
+
// unified row arrives carrying an `isPinchServer: undefined` key. Detecting the legacy shape by key
|
|
18
|
+
// PRESENCE misread it as a FATIGUE sub and wiped its real conditions + pinchServer flag; the value check
|
|
19
|
+
// (=== false) keeps the unified shape. Regression for the "pinch server never serves" prod bug.
|
|
20
|
+
const out = (0, sub_config_convert_1.convertLegacySetSubConfig)({
|
|
21
|
+
bench: 'X',
|
|
22
|
+
conditions: [{ type: pinch_condition_1.PinchConditionType.AFTER_ROTATIONS, rotations: 1 }],
|
|
23
|
+
conditionLogic: 'ANY',
|
|
24
|
+
pinchServer: true,
|
|
25
|
+
subBackMode: 'FATIGUE',
|
|
26
|
+
isPinchServer: undefined,
|
|
27
|
+
mode: undefined,
|
|
28
|
+
fatigueBand: undefined
|
|
29
|
+
});
|
|
30
|
+
(0, globals_1.expect)(out.pinchServer).toBe(true);
|
|
31
|
+
(0, globals_1.expect)(out.conditions).toEqual([{ type: pinch_condition_1.PinchConditionType.AFTER_ROTATIONS, rotations: 1 }]);
|
|
32
|
+
(0, globals_1.expect)(out.conditionLogic).toBe('ANY');
|
|
33
|
+
});
|
|
15
34
|
(0, globals_1.it)('converts mode NEVER to an explicit empty condition list, dropping every other field', () => {
|
|
16
35
|
(0, globals_1.expect)((0, sub_config_convert_1.convertLegacySetSubConfig)({ mode: 'NEVER', bench: 'X' })).toEqual({ conditions: [] });
|
|
17
36
|
});
|
|
@@ -10,12 +10,11 @@ export declare class Season {
|
|
|
10
10
|
readonly iteration: Iteration;
|
|
11
11
|
readonly divisionId: string;
|
|
12
12
|
readonly standings: Standing[];
|
|
13
|
-
private _status;
|
|
14
|
-
private _champion?;
|
|
13
|
+
private readonly _status;
|
|
14
|
+
private readonly _champion?;
|
|
15
15
|
static create(input: unknown): Season;
|
|
16
16
|
private constructor();
|
|
17
17
|
calculateStandings(): Standing[];
|
|
18
|
-
updateStandings(): void;
|
|
19
18
|
get champion(): Team | undefined;
|
|
20
19
|
get status(): Status;
|
|
21
20
|
static createSeason(iteration: Iteration, teams: Team[], divisionId: string): Season;
|
|
@@ -34,17 +34,6 @@ export class Season {
|
|
|
34
34
|
return standing;
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
-
updateStandings() {
|
|
38
|
-
if (this.matches == null || this.matches.length < 1)
|
|
39
|
-
throw new Error('NO_MATCHES');
|
|
40
|
-
const updated = this.calculateStandings();
|
|
41
|
-
this.standings.splice(0, this.standings.length, ...updated);
|
|
42
|
-
const isOver = this.matches.every((match) => match.isOver());
|
|
43
|
-
if (isOver) {
|
|
44
|
-
this._status = StatusEnum.COMPLETE;
|
|
45
|
-
this._champion = this.teams.find((team) => team.id === this.standings[0].teamId);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
37
|
get champion() {
|
|
49
38
|
if (this._champion != null)
|
|
50
39
|
return this._champion;
|
|
@@ -37,10 +37,4 @@ export class Block extends InPlayEvent {
|
|
|
37
37
|
this.failure = failure;
|
|
38
38
|
this.blockers = blockers;
|
|
39
39
|
}
|
|
40
|
-
toString() {
|
|
41
|
-
const failure = `"failure":${this.failure}`;
|
|
42
|
-
const type = `"type":${this.type}`;
|
|
43
|
-
const blockers = `"blockers":[${this.blockers.map(b => `"${b}"`).join(',')}]`;
|
|
44
|
-
return `{${this._toString()},${failure},${type},${blockers}}`;
|
|
45
|
-
}
|
|
46
40
|
}
|
|
@@ -35,9 +35,4 @@ export class FreeBall extends InPlayEvent {
|
|
|
35
35
|
this.type = type;
|
|
36
36
|
this.failure = failure;
|
|
37
37
|
}
|
|
38
|
-
toString() {
|
|
39
|
-
const failure = `"failure":${this.failure}`;
|
|
40
|
-
const type = `"type":${this.type}`;
|
|
41
|
-
return `{${this._toString()},${failure},${type}}`;
|
|
42
|
-
}
|
|
43
38
|
}
|
|
@@ -20,5 +20,4 @@ export declare abstract class InPlayEvent<F, T> extends RallyEvent {
|
|
|
20
20
|
readonly activeTraits: Trait[];
|
|
21
21
|
readonly incident?: EventIncident;
|
|
22
22
|
protected constructor({ eventType, playerId, target, score, activeTraits, incident }: InPlayEventOpts);
|
|
23
|
-
protected _toString(): string;
|
|
24
23
|
}
|
|
@@ -7,12 +7,4 @@ export class InPlayEvent extends RallyEvent {
|
|
|
7
7
|
this.activeTraits = activeTraits;
|
|
8
8
|
this.incident = incident;
|
|
9
9
|
}
|
|
10
|
-
_toString() {
|
|
11
|
-
const score = `"score":${this.score}`;
|
|
12
|
-
const playerId = `"playerId":"${this.playerId}"`;
|
|
13
|
-
const target = `"target":${this.target}`;
|
|
14
|
-
const eventType = `"eventType":${this.eventType}`;
|
|
15
|
-
const traits = `"activeTraits":[${this.activeTraits.map(t => `"${t}"`).join(',')}]`;
|
|
16
|
-
return `${score},${playerId},${target},${eventType},${traits}`;
|
|
17
|
-
}
|
|
18
10
|
}
|
|
@@ -24,11 +24,4 @@ export class LiberoReplacement extends RallyEvent {
|
|
|
24
24
|
this.type = type;
|
|
25
25
|
this.libero = libero;
|
|
26
26
|
}
|
|
27
|
-
toString() {
|
|
28
|
-
const playerId = `"playerId":"${this.playerId}"`;
|
|
29
|
-
const type = `"type":${this.type}`;
|
|
30
|
-
const eventType = `"eventType":${this.eventType}`;
|
|
31
|
-
const libero = `"libero":"${this.libero}"`;
|
|
32
|
-
return `{${playerId},${type},${eventType},${libero}}`;
|
|
33
|
-
}
|
|
34
27
|
}
|
|
@@ -38,9 +38,4 @@ export class Reception extends InPlayEvent {
|
|
|
38
38
|
this.type = type;
|
|
39
39
|
this.failure = failure;
|
|
40
40
|
}
|
|
41
|
-
toString() {
|
|
42
|
-
const failure = `"failure":${this.failure}`;
|
|
43
|
-
const type = `"type":${this.type}`;
|
|
44
|
-
return `{${this._toString()},${failure},${type}}`;
|
|
45
|
-
}
|
|
46
41
|
}
|
|
@@ -35,9 +35,4 @@ export class Serve extends InPlayEvent {
|
|
|
35
35
|
this.type = type;
|
|
36
36
|
this.failure = failure;
|
|
37
37
|
}
|
|
38
|
-
toString() {
|
|
39
|
-
const failure = `"failure":${this.failure}`;
|
|
40
|
-
const type = `"type":${this.type}`;
|
|
41
|
-
return `{${this._toString()},${failure},${type}}`;
|
|
42
|
-
}
|
|
43
38
|
}
|
|
@@ -44,10 +44,4 @@ export class Set extends InPlayEvent {
|
|
|
44
44
|
this.failure = failure;
|
|
45
45
|
this.tempo = tempo;
|
|
46
46
|
}
|
|
47
|
-
toString() {
|
|
48
|
-
const failure = `"failure":${this.failure}`;
|
|
49
|
-
const type = `"type":${this.type}`;
|
|
50
|
-
const tempo = this.tempo !== undefined ? `,"tempo":${this.tempo}` : '';
|
|
51
|
-
return `{${this._toString()},${failure},${type}${tempo}}`;
|
|
52
|
-
}
|
|
53
47
|
}
|
|
@@ -37,9 +37,4 @@ export class Spike extends InPlayEvent {
|
|
|
37
37
|
this.type = type;
|
|
38
38
|
this.failure = failure;
|
|
39
39
|
}
|
|
40
|
-
toString() {
|
|
41
|
-
const failure = `"failure":${this.failure}`;
|
|
42
|
-
const type = `"type":${this.type}`;
|
|
43
|
-
return `{${this._toString()},${failure},${type}}`;
|
|
44
|
-
}
|
|
45
40
|
}
|
|
@@ -19,11 +19,4 @@ export class Substitution extends RallyEvent {
|
|
|
19
19
|
this.playerOut = playerOut;
|
|
20
20
|
this.injury = injury;
|
|
21
21
|
}
|
|
22
|
-
toString() {
|
|
23
|
-
const playerId = `"playerId":"${this.playerId}"`;
|
|
24
|
-
const playerOut = `"playerOut":"${this.playerOut}"`;
|
|
25
|
-
const eventType = `"eventType":${this.eventType}`;
|
|
26
|
-
const injury = this.injury === true ? ',"injury":true' : '';
|
|
27
|
-
return `{${playerId},${playerOut},${eventType}${injury}}`;
|
|
28
|
-
}
|
|
29
22
|
}
|
|
@@ -3,7 +3,6 @@ import { Stats } from './stats';
|
|
|
3
3
|
import { DeclineProfile } from './decline';
|
|
4
4
|
export type TrainingFocus = Stats;
|
|
5
5
|
export declare function getStatCap(rarity: Rarity): number;
|
|
6
|
-
export declare function getRarityMultiplier(rarity: Rarity): number;
|
|
7
6
|
export declare function getImprovementFloor(rarity: Rarity): number;
|
|
8
7
|
export declare function getImprovementThreshold(statTotal: number, rarity: Rarity): number;
|
|
9
8
|
export declare function getCoachMultiplier(rarity: Rarity): number;
|
|
@@ -30,14 +30,6 @@ export function getStatCap(rarity) {
|
|
|
30
30
|
throw new Error(`UNKNOWN_RARITY: ${rarity}`);
|
|
31
31
|
return entry.cap;
|
|
32
32
|
}
|
|
33
|
-
// Retained for config/reference. No longer used by the improvement curve -- superseded by IMPROVEMENT_FLOOR,
|
|
34
|
-
// which is the per-rarity adjustment now.
|
|
35
|
-
export function getRarityMultiplier(rarity) {
|
|
36
|
-
const entry = statCaps.find(e => e.rarity === rarity);
|
|
37
|
-
if (entry == null)
|
|
38
|
-
throw new Error(`UNKNOWN_RARITY: ${rarity}`);
|
|
39
|
-
return entry.rarityMultiplier;
|
|
40
|
-
}
|
|
41
33
|
export function getImprovementFloor(rarity) {
|
|
42
34
|
const floor = IMPROVEMENT_FLOOR[rarity];
|
|
43
35
|
if (floor == null)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from '@jest/globals';
|
|
2
2
|
import { RarityEnum } from './rarity';
|
|
3
|
-
import { getStatCap,
|
|
3
|
+
import { getStatCap, getImprovementFloor, getImprovementThreshold, getCoachMultiplier, selectImprovableStat, matchExperience, experienceGained } from './improvement';
|
|
4
4
|
import { StatsEnum } from './stats';
|
|
5
5
|
import { DeclineProfileEnum } from './decline';
|
|
6
6
|
// ─── getStatCap ───────────────────────────────────────────────────────────────
|
|
@@ -18,21 +18,6 @@ describe('getStatCap()', () => {
|
|
|
18
18
|
expect(() => getStatCap('DIVINE')).toThrow('UNKNOWN_RARITY');
|
|
19
19
|
});
|
|
20
20
|
});
|
|
21
|
-
// ─── getRarityMultiplier ──────────────────────────────────────────────────────
|
|
22
|
-
describe('getRarityMultiplier()', () => {
|
|
23
|
-
it.each([
|
|
24
|
-
[RarityEnum.COMMON, 1.0],
|
|
25
|
-
[RarityEnum.RARE, 1.15],
|
|
26
|
-
[RarityEnum.LEGENDARY, 1.3],
|
|
27
|
-
[RarityEnum.MYTHIC, 1.5],
|
|
28
|
-
[RarityEnum.SPECIAL, 1.75]
|
|
29
|
-
])('%s → multiplier %f', (rarity, expected) => {
|
|
30
|
-
expect(getRarityMultiplier(rarity)).toBeCloseTo(expected, 5);
|
|
31
|
-
});
|
|
32
|
-
it('throws UNKNOWN_RARITY for an unrecognised rarity string', () => {
|
|
33
|
-
expect(() => getRarityMultiplier('DIVINE')).toThrow('UNKNOWN_RARITY');
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
21
|
// ─── getImprovementThreshold ──────────────────────────────────────────────────
|
|
37
22
|
describe('getImprovementThreshold()', () => {
|
|
38
23
|
it('at or below the floor the threshold is CURVE_BASE (180) — the fastest pace', () => {
|
|
@@ -8,7 +8,6 @@ export var EnergyBand;
|
|
|
8
8
|
EnergyBand["TIRED"] = "TIRED";
|
|
9
9
|
EnergyBand["EXHAUSTED"] = "EXHAUSTED";
|
|
10
10
|
})(EnergyBand || (EnergyBand = {}));
|
|
11
|
-
export const SUB_MODES = ['FATIGUE', 'PINCH', 'NEVER'];
|
|
12
11
|
// Ordered best -> worst. Used to compare "is this player at-or-below the configured sub band".
|
|
13
12
|
export const ENERGY_BAND_ORDER = [
|
|
14
13
|
EnergyBand.ENERGETIC,
|
|
@@ -17,12 +17,17 @@ function fatigueCondition(band) {
|
|
|
17
17
|
export function convertLegacySetSubConfig(raw) {
|
|
18
18
|
if (!isRecord(raw))
|
|
19
19
|
return raw;
|
|
20
|
-
// Derive the mode for the oldest (pre-mode) generation; a unified shape has none of these keys.
|
|
20
|
+
// Derive the mode for the oldest (pre-mode) generation; a unified shape has none of these keys. Test the
|
|
21
|
+
// isPinchServer VALUE, not key presence: the sim read path (transformToObject) spreads `isPinchServer:
|
|
22
|
+
// d.isPinchServer` onto every row, so a unified row carries an `isPinchServer: undefined` key. `'isPinchServer'
|
|
23
|
+
// in raw` would then misread that unified row as a legacy FATIGUE sub and overwrite its real conditions /
|
|
24
|
+
// pinchServer flag. A genuine legacy isPinchServer-era row always has a boolean flag (true handled above), so
|
|
25
|
+
// the only remaining legacy case is an explicit `=== false`.
|
|
21
26
|
const mode = raw.mode ?? (raw.isPinchServer === true
|
|
22
27
|
? 'PINCH'
|
|
23
28
|
: raw.fatigueBand === 'NEVER'
|
|
24
29
|
? 'NEVER'
|
|
25
|
-
: raw.fatigueBand != null ||
|
|
30
|
+
: raw.fatigueBand != null || raw.isPinchServer === false ? 'FATIGUE' : null);
|
|
26
31
|
if (mode == null)
|
|
27
32
|
return raw;
|
|
28
33
|
const { mode: _m, fatigueBand, isPinchServer: _p, ...rest } = raw;
|
|
@@ -10,6 +10,25 @@ describe('convertLegacySetSubConfig', () => {
|
|
|
10
10
|
const unified = { bench: 'X', conditions: [{ type: PinchConditionType.ASAP }], pinchServer: true };
|
|
11
11
|
expect(convertLegacySetSubConfig(unified)).toBe(unified);
|
|
12
12
|
});
|
|
13
|
+
it('keeps a unified pinch server intact when a caller injects isPinchServer: undefined (sim read path)', () => {
|
|
14
|
+
// The sim read path (transformToObject) spreads `isPinchServer: d.isPinchServer` onto every row, so a
|
|
15
|
+
// unified row arrives carrying an `isPinchServer: undefined` key. Detecting the legacy shape by key
|
|
16
|
+
// PRESENCE misread it as a FATIGUE sub and wiped its real conditions + pinchServer flag; the value check
|
|
17
|
+
// (=== false) keeps the unified shape. Regression for the "pinch server never serves" prod bug.
|
|
18
|
+
const out = convertLegacySetSubConfig({
|
|
19
|
+
bench: 'X',
|
|
20
|
+
conditions: [{ type: PinchConditionType.AFTER_ROTATIONS, rotations: 1 }],
|
|
21
|
+
conditionLogic: 'ANY',
|
|
22
|
+
pinchServer: true,
|
|
23
|
+
subBackMode: 'FATIGUE',
|
|
24
|
+
isPinchServer: undefined,
|
|
25
|
+
mode: undefined,
|
|
26
|
+
fatigueBand: undefined
|
|
27
|
+
});
|
|
28
|
+
expect(out.pinchServer).toBe(true);
|
|
29
|
+
expect(out.conditions).toEqual([{ type: PinchConditionType.AFTER_ROTATIONS, rotations: 1 }]);
|
|
30
|
+
expect(out.conditionLogic).toBe('ANY');
|
|
31
|
+
});
|
|
13
32
|
it('converts mode NEVER to an explicit empty condition list, dropping every other field', () => {
|
|
14
33
|
expect(convertLegacySetSubConfig({ mode: 'NEVER', bench: 'X' })).toEqual({ conditions: [] });
|
|
15
34
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export {};
|