volleyballsimtypes 0.0.396 → 0.0.397
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 +3 -3
- package/dist/cjs/src/data/models/tactics.d.ts +3 -2
- package/dist/cjs/src/data/transformers/tactics.js +15 -3
- package/dist/cjs/src/service/team/designated-sub.d.ts +3 -3
- package/dist/cjs/src/service/team/energy-band.d.ts +2 -0
- package/dist/cjs/src/service/team/energy-band.js +2 -1
- package/dist/cjs/src/service/team/schemas/designated-sub.z.d.ts +11 -2
- package/dist/cjs/src/service/team/schemas/designated-sub.z.js +5 -4
- package/dist/cjs/src/service/team/schemas/designated-sub.z.test.js +20 -6
- package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +6 -2
- package/dist/cjs/src/service/team/schemas/team.z.d.ts +6 -2
- package/dist/esm/src/api/index.d.ts +3 -3
- package/dist/esm/src/data/models/tactics.d.ts +3 -2
- package/dist/esm/src/data/transformers/tactics.js +15 -3
- package/dist/esm/src/service/team/designated-sub.d.ts +3 -3
- package/dist/esm/src/service/team/energy-band.d.ts +2 -0
- package/dist/esm/src/service/team/energy-band.js +1 -0
- package/dist/esm/src/service/team/schemas/designated-sub.z.d.ts +11 -2
- package/dist/esm/src/service/team/schemas/designated-sub.z.js +4 -3
- package/dist/esm/src/service/team/schemas/designated-sub.z.test.js +20 -6
- package/dist/esm/src/service/team/schemas/tactics.z.d.ts +6 -2
- package/dist/esm/src/service/team/schemas/team.z.d.ts +6 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BoxScore, DataProps, DeclineStatus, Division as _Division, League as _League, Match as _Match, MatchSet as _MatchSet, Player as _Player, PlayerPosition, Qualifier as _Qualifier, QualifierMatch as _QualifierMatch, Rally as _Rally, RallyEvent as _RallyEvent, Season as _Season, Standing as _Standing, Team as _Team, Tournament as _Tournament, TournamentMatch as _TournamentMatch, National as _National, NationalMatch as _NationalMatch, Country as _Country, CourtPosition, ConditionLogic, PinchCondition, RotationSystemEnum, SubBand } from '../service';
|
|
1
|
+
import { BoxScore, DataProps, DeclineStatus, Division as _Division, League as _League, Match as _Match, MatchSet as _MatchSet, Player as _Player, PlayerPosition, Qualifier as _Qualifier, QualifierMatch as _QualifierMatch, Rally as _Rally, RallyEvent as _RallyEvent, Season as _Season, Standing as _Standing, Team as _Team, Tournament as _Tournament, TournamentMatch as _TournamentMatch, National as _National, NationalMatch as _NationalMatch, Country as _Country, CourtPosition, ConditionLogic, EnergyBand, PinchCondition, RotationSystemEnum, SubBand, SubMode } from '../service';
|
|
2
2
|
export type Rally = DataProps<_Rally> & {
|
|
3
3
|
homePlayerPosition: PlayerPosition[];
|
|
4
4
|
awayPlayerPosition: PlayerPosition[];
|
|
@@ -59,8 +59,8 @@ export interface StartingLineup {
|
|
|
59
59
|
export interface ApiDesignatedSub {
|
|
60
60
|
starterId: string;
|
|
61
61
|
benchId?: string;
|
|
62
|
-
|
|
63
|
-
fatigueBand?:
|
|
62
|
+
mode: SubMode;
|
|
63
|
+
fatigueBand?: EnergyBand;
|
|
64
64
|
conditions?: PinchCondition[];
|
|
65
65
|
conditionLogic?: ConditionLogic;
|
|
66
66
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as Sequelize from 'sequelize';
|
|
2
2
|
import { Model, Optional } from 'sequelize';
|
|
3
3
|
import { PlayerId, TeamId, TeamModel } from '.';
|
|
4
|
-
import { ConditionLogic, CourtPosition, PinchCondition, RotationSystemEnum, SubBand } from '../../service';
|
|
4
|
+
import { ConditionLogic, CourtPosition, PinchCondition, RotationSystemEnum, SubBand, SubMode } from '../../service';
|
|
5
5
|
export interface TacticsLineupAttributes {
|
|
6
6
|
[CourtPosition.LIBERO_ZONE]?: PlayerId;
|
|
7
7
|
[CourtPosition.LEFT_BACK]: PlayerId;
|
|
@@ -16,7 +16,8 @@ export type PinchServerSubsAttributes = Record<PlayerId, PlayerId>;
|
|
|
16
16
|
export interface DesignatedSubAttributes {
|
|
17
17
|
starterId: PlayerId;
|
|
18
18
|
benchId?: PlayerId;
|
|
19
|
-
|
|
19
|
+
mode?: SubMode;
|
|
20
|
+
isPinchServer?: boolean;
|
|
20
21
|
fatigueBand?: SubBand;
|
|
21
22
|
conditions?: PinchCondition[];
|
|
22
23
|
conditionLogic?: ConditionLogic;
|
|
@@ -3,6 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.transformToTactics = transformToObject;
|
|
4
4
|
exports.transformFromTactics = transformToAttributes;
|
|
5
5
|
const service_1 = require("../../service");
|
|
6
|
+
// Back-compat: new rows persist `mode`; rows written before the mode change persist `isPinchServer` (+ a
|
|
7
|
+
// 'NEVER' fatigueBand for the old "never fatigue-sub" state). Derive the mode so existing data keeps working
|
|
8
|
+
// until the next tactics save rewrites it in the new shape.
|
|
9
|
+
function subModeFromAttributes(d) {
|
|
10
|
+
if (d.mode != null)
|
|
11
|
+
return d.mode;
|
|
12
|
+
if (d.isPinchServer === true)
|
|
13
|
+
return 'PINCH';
|
|
14
|
+
if (d.fatigueBand === 'NEVER')
|
|
15
|
+
return 'NEVER';
|
|
16
|
+
return 'FATIGUE';
|
|
17
|
+
}
|
|
6
18
|
function findPlayer(id, roster) {
|
|
7
19
|
const player = roster.find((p) => p.id === id);
|
|
8
20
|
if (player == null)
|
|
@@ -45,7 +57,7 @@ function transformToAttributes(tactics, teamId) {
|
|
|
45
57
|
designated_subs: tactics.designatedSubs.map(ds => ({
|
|
46
58
|
starterId: ds.starter.id,
|
|
47
59
|
benchId: ds.bench?.id,
|
|
48
|
-
|
|
60
|
+
mode: ds.mode,
|
|
49
61
|
fatigueBand: ds.fatigueBand,
|
|
50
62
|
conditions: ds.conditions,
|
|
51
63
|
conditionLogic: ds.conditionLogic
|
|
@@ -76,8 +88,8 @@ function transformToObject(model, roster) {
|
|
|
76
88
|
designatedSubs: (model.designated_subs ?? []).map(d => ({
|
|
77
89
|
starter: findPlayer(d.starterId, roster),
|
|
78
90
|
bench: d.benchId != null ? findPlayer(d.benchId, roster) : undefined,
|
|
79
|
-
|
|
80
|
-
fatigueBand: d.fatigueBand,
|
|
91
|
+
mode: subModeFromAttributes(d),
|
|
92
|
+
fatigueBand: d.fatigueBand === 'NEVER' ? undefined : d.fatigueBand,
|
|
81
93
|
conditions: d.conditions,
|
|
82
94
|
conditionLogic: d.conditionLogic
|
|
83
95
|
})),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Player } from '../player';
|
|
2
|
-
import {
|
|
2
|
+
import { EnergyBand, SubMode } from './energy-band';
|
|
3
3
|
import { ConditionLogic, PinchCondition } from './pinch-condition';
|
|
4
4
|
export interface DesignatedSub {
|
|
5
5
|
readonly starter: Player;
|
|
6
6
|
readonly bench?: Player;
|
|
7
|
-
readonly
|
|
8
|
-
readonly fatigueBand?:
|
|
7
|
+
readonly mode: SubMode;
|
|
8
|
+
readonly fatigueBand?: EnergyBand;
|
|
9
9
|
readonly conditions?: PinchCondition[];
|
|
10
10
|
readonly conditionLogic?: ConditionLogic;
|
|
11
11
|
}
|
|
@@ -5,4 +5,6 @@ export declare enum EnergyBand {
|
|
|
5
5
|
EXHAUSTED = "EXHAUSTED"
|
|
6
6
|
}
|
|
7
7
|
export type SubBand = EnergyBand | 'NEVER';
|
|
8
|
+
export type SubMode = 'FATIGUE' | 'PINCH' | 'NEVER';
|
|
9
|
+
export declare const SUB_MODES: readonly SubMode[];
|
|
8
10
|
export declare const ENERGY_BAND_ORDER: readonly EnergyBand[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ENERGY_BAND_ORDER = exports.EnergyBand = void 0;
|
|
3
|
+
exports.ENERGY_BAND_ORDER = exports.SUB_MODES = 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,6 +11,7 @@ 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'];
|
|
14
15
|
// Ordered best -> worst. Used to compare "is this player at-or-below the configured sub band".
|
|
15
16
|
exports.ENERGY_BAND_ORDER = [
|
|
16
17
|
EnergyBand.ENERGETIC,
|
|
@@ -27,11 +27,20 @@ export declare const PinchConditionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
|
|
|
27
27
|
score: z.ZodNumber;
|
|
28
28
|
}, z.core.$strip>], "type">;
|
|
29
29
|
export declare const ConditionLogicSchema: z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>;
|
|
30
|
+
export declare const SubModeSchema: z.ZodEnum<{
|
|
31
|
+
NEVER: "NEVER";
|
|
32
|
+
FATIGUE: "FATIGUE";
|
|
33
|
+
PINCH: "PINCH";
|
|
34
|
+
}>;
|
|
30
35
|
export declare const DesignatedSubSchema: z.ZodObject<{
|
|
31
36
|
starter: z.ZodCustom<Player, Player>;
|
|
32
37
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
mode: z.ZodEnum<{
|
|
39
|
+
NEVER: "NEVER";
|
|
40
|
+
FATIGUE: "FATIGUE";
|
|
41
|
+
PINCH: "PINCH";
|
|
42
|
+
}>;
|
|
43
|
+
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
35
44
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
36
45
|
type: z.ZodLiteral<PinchConditionType.ASAP>;
|
|
37
46
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DesignatedSubSchema = exports.ConditionLogicSchema = exports.PinchConditionSchema = exports.SubBandSchema = void 0;
|
|
3
|
+
exports.DesignatedSubSchema = exports.SubModeSchema = exports.ConditionLogicSchema = exports.PinchConditionSchema = exports.SubBandSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const player_1 = require("../../player");
|
|
6
6
|
const energy_band_1 = require("../energy-band");
|
|
@@ -19,15 +19,16 @@ exports.PinchConditionSchema = zod_1.z.discriminatedUnion('type', [
|
|
|
19
19
|
zod_1.z.object({ type: zod_1.z.literal(pinch_condition_1.PinchConditionType.MIN_OWN_SCORE), score: zod_1.z.number().int().min(0).max(40) })
|
|
20
20
|
]);
|
|
21
21
|
exports.ConditionLogicSchema = zod_1.z.union([zod_1.z.literal('ALL'), zod_1.z.literal('ANY')]);
|
|
22
|
+
exports.SubModeSchema = zod_1.z.enum(['FATIGUE', 'PINCH', 'NEVER']);
|
|
22
23
|
exports.DesignatedSubSchema = zod_1.z.object({
|
|
23
24
|
starter: playerInstanceSchema,
|
|
24
25
|
bench: playerInstanceSchema.optional(),
|
|
25
|
-
|
|
26
|
-
fatigueBand:
|
|
26
|
+
mode: exports.SubModeSchema,
|
|
27
|
+
fatigueBand: zod_1.z.nativeEnum(energy_band_1.EnergyBand).optional(),
|
|
27
28
|
conditions: zod_1.z.array(exports.PinchConditionSchema).optional(),
|
|
28
29
|
conditionLogic: exports.ConditionLogicSchema.optional()
|
|
29
30
|
}).superRefine((data, ctx) => {
|
|
30
|
-
if (data.
|
|
31
|
+
if (data.mode === 'PINCH') {
|
|
31
32
|
// Pinch mode needs a designated bench player (the server) and at least one condition.
|
|
32
33
|
if (data.bench == null) {
|
|
33
34
|
ctx.addIssue({ code: 'custom', message: 'PINCH_SERVER_REQUIRES_BENCH', path: ['bench'] });
|
|
@@ -67,7 +67,7 @@ const test_helpers_1 = require("../../test-helpers");
|
|
|
67
67
|
const res = designated_sub_z_1.DesignatedSubSchema.safeParse({
|
|
68
68
|
starter,
|
|
69
69
|
bench,
|
|
70
|
-
|
|
70
|
+
mode: 'FATIGUE',
|
|
71
71
|
fatigueBand: energy_band_1.EnergyBand.TIRED
|
|
72
72
|
});
|
|
73
73
|
(0, globals_1.expect)(res.success).toBe(true);
|
|
@@ -75,15 +75,29 @@ const test_helpers_1 = require("../../test-helpers");
|
|
|
75
75
|
(0, globals_1.it)('accepts a fatigue-mode entry with no bench (falls back to closest sub)', () => {
|
|
76
76
|
const res = designated_sub_z_1.DesignatedSubSchema.safeParse({
|
|
77
77
|
starter,
|
|
78
|
-
|
|
78
|
+
mode: 'FATIGUE'
|
|
79
79
|
});
|
|
80
80
|
(0, globals_1.expect)(res.success).toBe(true);
|
|
81
81
|
});
|
|
82
|
+
(0, globals_1.it)('accepts a never-mode entry with no bench, band, or conditions', () => {
|
|
83
|
+
const res = designated_sub_z_1.DesignatedSubSchema.safeParse({
|
|
84
|
+
starter,
|
|
85
|
+
mode: 'NEVER'
|
|
86
|
+
});
|
|
87
|
+
(0, globals_1.expect)(res.success).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
(0, globals_1.it)('rejects an unknown mode', () => {
|
|
90
|
+
const res = designated_sub_z_1.DesignatedSubSchema.safeParse({
|
|
91
|
+
starter,
|
|
92
|
+
mode: 'SOMETIMES'
|
|
93
|
+
});
|
|
94
|
+
(0, globals_1.expect)(res.success).toBe(false);
|
|
95
|
+
});
|
|
82
96
|
(0, globals_1.it)('accepts a pinch-mode entry with a bench server and conditions', () => {
|
|
83
97
|
const res = designated_sub_z_1.DesignatedSubSchema.safeParse({
|
|
84
98
|
starter,
|
|
85
99
|
bench,
|
|
86
|
-
|
|
100
|
+
mode: 'PINCH',
|
|
87
101
|
conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }],
|
|
88
102
|
conditionLogic: 'ALL'
|
|
89
103
|
});
|
|
@@ -92,7 +106,7 @@ const test_helpers_1 = require("../../test-helpers");
|
|
|
92
106
|
(0, globals_1.it)('rejects a pinch-mode entry without a bench server', () => {
|
|
93
107
|
const res = designated_sub_z_1.DesignatedSubSchema.safeParse({
|
|
94
108
|
starter,
|
|
95
|
-
|
|
109
|
+
mode: 'PINCH',
|
|
96
110
|
conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }]
|
|
97
111
|
});
|
|
98
112
|
(0, globals_1.expect)(res.success).toBe(false);
|
|
@@ -104,7 +118,7 @@ const test_helpers_1 = require("../../test-helpers");
|
|
|
104
118
|
const res = designated_sub_z_1.DesignatedSubSchema.safeParse({
|
|
105
119
|
starter,
|
|
106
120
|
bench,
|
|
107
|
-
|
|
121
|
+
mode: 'PINCH',
|
|
108
122
|
conditions: []
|
|
109
123
|
});
|
|
110
124
|
(0, globals_1.expect)(res.success).toBe(false);
|
|
@@ -115,7 +129,7 @@ const test_helpers_1 = require("../../test-helpers");
|
|
|
115
129
|
(0, globals_1.it)('rejects a starter that is not a Player instance', () => {
|
|
116
130
|
const res = designated_sub_z_1.DesignatedSubSchema.safeParse({
|
|
117
131
|
starter: { id: 'not-a-player' },
|
|
118
|
-
|
|
132
|
+
mode: 'FATIGUE'
|
|
119
133
|
});
|
|
120
134
|
(0, globals_1.expect)(res.success).toBe(false);
|
|
121
135
|
});
|
|
@@ -20,8 +20,12 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
20
20
|
designatedSubs: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
21
21
|
starter: z.ZodCustom<Player, Player>;
|
|
22
22
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
mode: z.ZodEnum<{
|
|
24
|
+
NEVER: "NEVER";
|
|
25
|
+
FATIGUE: "FATIGUE";
|
|
26
|
+
PINCH: "PINCH";
|
|
27
|
+
}>;
|
|
28
|
+
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
25
29
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
26
30
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
27
31
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -28,8 +28,12 @@ export declare const TeamInputSchema: z.ZodObject<{
|
|
|
28
28
|
designatedSubs: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
29
29
|
starter: z.ZodCustom<Player, Player>;
|
|
30
30
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
mode: z.ZodEnum<{
|
|
32
|
+
NEVER: "NEVER";
|
|
33
|
+
FATIGUE: "FATIGUE";
|
|
34
|
+
PINCH: "PINCH";
|
|
35
|
+
}>;
|
|
36
|
+
fatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
|
|
33
37
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
34
38
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
35
39
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BoxScore, DataProps, DeclineStatus, Division as _Division, League as _League, Match as _Match, MatchSet as _MatchSet, Player as _Player, PlayerPosition, Qualifier as _Qualifier, QualifierMatch as _QualifierMatch, Rally as _Rally, RallyEvent as _RallyEvent, Season as _Season, Standing as _Standing, Team as _Team, Tournament as _Tournament, TournamentMatch as _TournamentMatch, National as _National, NationalMatch as _NationalMatch, Country as _Country, CourtPosition, ConditionLogic, PinchCondition, RotationSystemEnum, SubBand } from '../service';
|
|
1
|
+
import { BoxScore, DataProps, DeclineStatus, Division as _Division, League as _League, Match as _Match, MatchSet as _MatchSet, Player as _Player, PlayerPosition, Qualifier as _Qualifier, QualifierMatch as _QualifierMatch, Rally as _Rally, RallyEvent as _RallyEvent, Season as _Season, Standing as _Standing, Team as _Team, Tournament as _Tournament, TournamentMatch as _TournamentMatch, National as _National, NationalMatch as _NationalMatch, Country as _Country, CourtPosition, ConditionLogic, EnergyBand, PinchCondition, RotationSystemEnum, SubBand, SubMode } from '../service';
|
|
2
2
|
export type Rally = DataProps<_Rally> & {
|
|
3
3
|
homePlayerPosition: PlayerPosition[];
|
|
4
4
|
awayPlayerPosition: PlayerPosition[];
|
|
@@ -59,8 +59,8 @@ export interface StartingLineup {
|
|
|
59
59
|
export interface ApiDesignatedSub {
|
|
60
60
|
starterId: string;
|
|
61
61
|
benchId?: string;
|
|
62
|
-
|
|
63
|
-
fatigueBand?:
|
|
62
|
+
mode: SubMode;
|
|
63
|
+
fatigueBand?: EnergyBand;
|
|
64
64
|
conditions?: PinchCondition[];
|
|
65
65
|
conditionLogic?: ConditionLogic;
|
|
66
66
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as Sequelize from 'sequelize';
|
|
2
2
|
import { Model, Optional } from 'sequelize';
|
|
3
3
|
import { PlayerId, TeamId, TeamModel } from '.';
|
|
4
|
-
import { ConditionLogic, CourtPosition, PinchCondition, RotationSystemEnum, SubBand } from '../../service';
|
|
4
|
+
import { ConditionLogic, CourtPosition, PinchCondition, RotationSystemEnum, SubBand, SubMode } from '../../service';
|
|
5
5
|
export interface TacticsLineupAttributes {
|
|
6
6
|
[CourtPosition.LIBERO_ZONE]?: PlayerId;
|
|
7
7
|
[CourtPosition.LEFT_BACK]: PlayerId;
|
|
@@ -16,7 +16,8 @@ export type PinchServerSubsAttributes = Record<PlayerId, PlayerId>;
|
|
|
16
16
|
export interface DesignatedSubAttributes {
|
|
17
17
|
starterId: PlayerId;
|
|
18
18
|
benchId?: PlayerId;
|
|
19
|
-
|
|
19
|
+
mode?: SubMode;
|
|
20
|
+
isPinchServer?: boolean;
|
|
20
21
|
fatigueBand?: SubBand;
|
|
21
22
|
conditions?: PinchCondition[];
|
|
22
23
|
conditionLogic?: ConditionLogic;
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { CourtPosition, Tactics } from '../../service';
|
|
2
|
+
// Back-compat: new rows persist `mode`; rows written before the mode change persist `isPinchServer` (+ a
|
|
3
|
+
// 'NEVER' fatigueBand for the old "never fatigue-sub" state). Derive the mode so existing data keeps working
|
|
4
|
+
// until the next tactics save rewrites it in the new shape.
|
|
5
|
+
function subModeFromAttributes(d) {
|
|
6
|
+
if (d.mode != null)
|
|
7
|
+
return d.mode;
|
|
8
|
+
if (d.isPinchServer === true)
|
|
9
|
+
return 'PINCH';
|
|
10
|
+
if (d.fatigueBand === 'NEVER')
|
|
11
|
+
return 'NEVER';
|
|
12
|
+
return 'FATIGUE';
|
|
13
|
+
}
|
|
2
14
|
function findPlayer(id, roster) {
|
|
3
15
|
const player = roster.find((p) => p.id === id);
|
|
4
16
|
if (player == null)
|
|
@@ -41,7 +53,7 @@ function transformToAttributes(tactics, teamId) {
|
|
|
41
53
|
designated_subs: tactics.designatedSubs.map(ds => ({
|
|
42
54
|
starterId: ds.starter.id,
|
|
43
55
|
benchId: ds.bench?.id,
|
|
44
|
-
|
|
56
|
+
mode: ds.mode,
|
|
45
57
|
fatigueBand: ds.fatigueBand,
|
|
46
58
|
conditions: ds.conditions,
|
|
47
59
|
conditionLogic: ds.conditionLogic
|
|
@@ -72,8 +84,8 @@ function transformToObject(model, roster) {
|
|
|
72
84
|
designatedSubs: (model.designated_subs ?? []).map(d => ({
|
|
73
85
|
starter: findPlayer(d.starterId, roster),
|
|
74
86
|
bench: d.benchId != null ? findPlayer(d.benchId, roster) : undefined,
|
|
75
|
-
|
|
76
|
-
fatigueBand: d.fatigueBand,
|
|
87
|
+
mode: subModeFromAttributes(d),
|
|
88
|
+
fatigueBand: d.fatigueBand === 'NEVER' ? undefined : d.fatigueBand,
|
|
77
89
|
conditions: d.conditions,
|
|
78
90
|
conditionLogic: d.conditionLogic
|
|
79
91
|
})),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Player } from '../player';
|
|
2
|
-
import {
|
|
2
|
+
import { EnergyBand, SubMode } from './energy-band';
|
|
3
3
|
import { ConditionLogic, PinchCondition } from './pinch-condition';
|
|
4
4
|
export interface DesignatedSub {
|
|
5
5
|
readonly starter: Player;
|
|
6
6
|
readonly bench?: Player;
|
|
7
|
-
readonly
|
|
8
|
-
readonly fatigueBand?:
|
|
7
|
+
readonly mode: SubMode;
|
|
8
|
+
readonly fatigueBand?: EnergyBand;
|
|
9
9
|
readonly conditions?: PinchCondition[];
|
|
10
10
|
readonly conditionLogic?: ConditionLogic;
|
|
11
11
|
}
|
|
@@ -5,4 +5,6 @@ export declare enum EnergyBand {
|
|
|
5
5
|
EXHAUSTED = "EXHAUSTED"
|
|
6
6
|
}
|
|
7
7
|
export type SubBand = EnergyBand | 'NEVER';
|
|
8
|
+
export type SubMode = 'FATIGUE' | 'PINCH' | 'NEVER';
|
|
9
|
+
export declare const SUB_MODES: readonly SubMode[];
|
|
8
10
|
export declare const ENERGY_BAND_ORDER: readonly EnergyBand[];
|
|
@@ -8,6 +8,7 @@ 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'];
|
|
11
12
|
// Ordered best -> worst. Used to compare "is this player at-or-below the configured sub band".
|
|
12
13
|
export const ENERGY_BAND_ORDER = [
|
|
13
14
|
EnergyBand.ENERGETIC,
|
|
@@ -27,11 +27,20 @@ export declare const PinchConditionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
|
|
|
27
27
|
score: z.ZodNumber;
|
|
28
28
|
}, z.core.$strip>], "type">;
|
|
29
29
|
export declare const ConditionLogicSchema: z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>;
|
|
30
|
+
export declare const SubModeSchema: z.ZodEnum<{
|
|
31
|
+
NEVER: "NEVER";
|
|
32
|
+
FATIGUE: "FATIGUE";
|
|
33
|
+
PINCH: "PINCH";
|
|
34
|
+
}>;
|
|
30
35
|
export declare const DesignatedSubSchema: z.ZodObject<{
|
|
31
36
|
starter: z.ZodCustom<Player, Player>;
|
|
32
37
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
mode: z.ZodEnum<{
|
|
39
|
+
NEVER: "NEVER";
|
|
40
|
+
FATIGUE: "FATIGUE";
|
|
41
|
+
PINCH: "PINCH";
|
|
42
|
+
}>;
|
|
43
|
+
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
35
44
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
36
45
|
type: z.ZodLiteral<PinchConditionType.ASAP>;
|
|
37
46
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -16,15 +16,16 @@ export const PinchConditionSchema = z.discriminatedUnion('type', [
|
|
|
16
16
|
z.object({ type: z.literal(PinchConditionType.MIN_OWN_SCORE), score: z.number().int().min(0).max(40) })
|
|
17
17
|
]);
|
|
18
18
|
export const ConditionLogicSchema = z.union([z.literal('ALL'), z.literal('ANY')]);
|
|
19
|
+
export const SubModeSchema = z.enum(['FATIGUE', 'PINCH', 'NEVER']);
|
|
19
20
|
export const DesignatedSubSchema = z.object({
|
|
20
21
|
starter: playerInstanceSchema,
|
|
21
22
|
bench: playerInstanceSchema.optional(),
|
|
22
|
-
|
|
23
|
-
fatigueBand:
|
|
23
|
+
mode: SubModeSchema,
|
|
24
|
+
fatigueBand: z.nativeEnum(EnergyBand).optional(),
|
|
24
25
|
conditions: z.array(PinchConditionSchema).optional(),
|
|
25
26
|
conditionLogic: ConditionLogicSchema.optional()
|
|
26
27
|
}).superRefine((data, ctx) => {
|
|
27
|
-
if (data.
|
|
28
|
+
if (data.mode === 'PINCH') {
|
|
28
29
|
// Pinch mode needs a designated bench player (the server) and at least one condition.
|
|
29
30
|
if (data.bench == null) {
|
|
30
31
|
ctx.addIssue({ code: 'custom', message: 'PINCH_SERVER_REQUIRES_BENCH', path: ['bench'] });
|
|
@@ -65,7 +65,7 @@ describe('DesignatedSubSchema', () => {
|
|
|
65
65
|
const res = DesignatedSubSchema.safeParse({
|
|
66
66
|
starter,
|
|
67
67
|
bench,
|
|
68
|
-
|
|
68
|
+
mode: 'FATIGUE',
|
|
69
69
|
fatigueBand: EnergyBand.TIRED
|
|
70
70
|
});
|
|
71
71
|
expect(res.success).toBe(true);
|
|
@@ -73,15 +73,29 @@ describe('DesignatedSubSchema', () => {
|
|
|
73
73
|
it('accepts a fatigue-mode entry with no bench (falls back to closest sub)', () => {
|
|
74
74
|
const res = DesignatedSubSchema.safeParse({
|
|
75
75
|
starter,
|
|
76
|
-
|
|
76
|
+
mode: 'FATIGUE'
|
|
77
77
|
});
|
|
78
78
|
expect(res.success).toBe(true);
|
|
79
79
|
});
|
|
80
|
+
it('accepts a never-mode entry with no bench, band, or conditions', () => {
|
|
81
|
+
const res = DesignatedSubSchema.safeParse({
|
|
82
|
+
starter,
|
|
83
|
+
mode: 'NEVER'
|
|
84
|
+
});
|
|
85
|
+
expect(res.success).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
it('rejects an unknown mode', () => {
|
|
88
|
+
const res = DesignatedSubSchema.safeParse({
|
|
89
|
+
starter,
|
|
90
|
+
mode: 'SOMETIMES'
|
|
91
|
+
});
|
|
92
|
+
expect(res.success).toBe(false);
|
|
93
|
+
});
|
|
80
94
|
it('accepts a pinch-mode entry with a bench server and conditions', () => {
|
|
81
95
|
const res = DesignatedSubSchema.safeParse({
|
|
82
96
|
starter,
|
|
83
97
|
bench,
|
|
84
|
-
|
|
98
|
+
mode: 'PINCH',
|
|
85
99
|
conditions: [{ type: PinchConditionType.ASAP }],
|
|
86
100
|
conditionLogic: 'ALL'
|
|
87
101
|
});
|
|
@@ -90,7 +104,7 @@ describe('DesignatedSubSchema', () => {
|
|
|
90
104
|
it('rejects a pinch-mode entry without a bench server', () => {
|
|
91
105
|
const res = DesignatedSubSchema.safeParse({
|
|
92
106
|
starter,
|
|
93
|
-
|
|
107
|
+
mode: 'PINCH',
|
|
94
108
|
conditions: [{ type: PinchConditionType.ASAP }]
|
|
95
109
|
});
|
|
96
110
|
expect(res.success).toBe(false);
|
|
@@ -102,7 +116,7 @@ describe('DesignatedSubSchema', () => {
|
|
|
102
116
|
const res = DesignatedSubSchema.safeParse({
|
|
103
117
|
starter,
|
|
104
118
|
bench,
|
|
105
|
-
|
|
119
|
+
mode: 'PINCH',
|
|
106
120
|
conditions: []
|
|
107
121
|
});
|
|
108
122
|
expect(res.success).toBe(false);
|
|
@@ -113,7 +127,7 @@ describe('DesignatedSubSchema', () => {
|
|
|
113
127
|
it('rejects a starter that is not a Player instance', () => {
|
|
114
128
|
const res = DesignatedSubSchema.safeParse({
|
|
115
129
|
starter: { id: 'not-a-player' },
|
|
116
|
-
|
|
130
|
+
mode: 'FATIGUE'
|
|
117
131
|
});
|
|
118
132
|
expect(res.success).toBe(false);
|
|
119
133
|
});
|
|
@@ -20,8 +20,12 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
20
20
|
designatedSubs: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
21
21
|
starter: z.ZodCustom<Player, Player>;
|
|
22
22
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
mode: z.ZodEnum<{
|
|
24
|
+
NEVER: "NEVER";
|
|
25
|
+
FATIGUE: "FATIGUE";
|
|
26
|
+
PINCH: "PINCH";
|
|
27
|
+
}>;
|
|
28
|
+
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
25
29
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
26
30
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
27
31
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -28,8 +28,12 @@ export declare const TeamInputSchema: z.ZodObject<{
|
|
|
28
28
|
designatedSubs: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
29
29
|
starter: z.ZodCustom<Player, Player>;
|
|
30
30
|
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
mode: z.ZodEnum<{
|
|
32
|
+
NEVER: "NEVER";
|
|
33
|
+
FATIGUE: "FATIGUE";
|
|
34
|
+
PINCH: "PINCH";
|
|
35
|
+
}>;
|
|
36
|
+
fatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
|
|
33
37
|
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
34
38
|
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
35
39
|
}, z.core.$strip>, z.ZodObject<{
|