volleyballsimtypes 0.0.388 → 0.0.390
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 -1
- package/dist/cjs/src/data/models/tactics.d.ts +14 -2
- package/dist/cjs/src/data/models/tactics.js +10 -0
- package/dist/cjs/src/data/transformers/tactics.js +20 -2
- package/dist/cjs/src/service/competition/standing.js +15 -8
- package/dist/cjs/src/service/competition/standing.test.js +12 -0
- package/dist/cjs/src/service/team/designated-sub.d.ts +11 -0
- package/dist/cjs/src/service/team/designated-sub.js +2 -0
- package/dist/cjs/src/service/team/energy-band.d.ts +8 -0
- package/dist/cjs/src/service/team/energy-band.js +20 -0
- package/dist/cjs/src/service/team/index.d.ts +3 -0
- package/dist/cjs/src/service/team/index.js +3 -0
- package/dist/cjs/src/service/team/pinch-condition.d.ts +56 -0
- package/dist/cjs/src/service/team/pinch-condition.js +27 -0
- package/dist/cjs/src/service/team/schemas/designated-sub.z.d.ts +59 -0
- package/dist/cjs/src/service/team/schemas/designated-sub.z.js +39 -0
- package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +32 -0
- package/dist/cjs/src/service/team/schemas/tactics.z.js +8 -2
- package/dist/cjs/src/service/team/schemas/team.z.d.ts +31 -0
- package/dist/cjs/src/service/team/tactics.d.ts +6 -0
- package/dist/cjs/src/service/team/tactics.js +4 -1
- package/dist/cjs/src/service/team/team-generator.d.ts +2 -0
- package/dist/cjs/src/service/team/team-generator.js +7 -3
- package/dist/esm/src/api/index.d.ts +11 -1
- package/dist/esm/src/data/models/tactics.d.ts +14 -2
- package/dist/esm/src/data/models/tactics.js +10 -0
- package/dist/esm/src/data/transformers/tactics.js +20 -2
- package/dist/esm/src/service/competition/standing.js +15 -8
- package/dist/esm/src/service/competition/standing.test.js +12 -0
- package/dist/esm/src/service/team/designated-sub.d.ts +11 -0
- package/dist/esm/src/service/team/designated-sub.js +1 -0
- package/dist/esm/src/service/team/energy-band.d.ts +8 -0
- package/dist/esm/src/service/team/energy-band.js +17 -0
- package/dist/esm/src/service/team/index.d.ts +3 -0
- package/dist/esm/src/service/team/index.js +3 -0
- package/dist/esm/src/service/team/pinch-condition.d.ts +56 -0
- package/dist/esm/src/service/team/pinch-condition.js +24 -0
- package/dist/esm/src/service/team/schemas/designated-sub.z.d.ts +59 -0
- package/dist/esm/src/service/team/schemas/designated-sub.z.js +36 -0
- package/dist/esm/src/service/team/schemas/tactics.z.d.ts +32 -0
- package/dist/esm/src/service/team/schemas/tactics.z.js +8 -2
- package/dist/esm/src/service/team/schemas/team.z.d.ts +31 -0
- package/dist/esm/src/service/team/tactics.d.ts +6 -0
- package/dist/esm/src/service/team/tactics.js +4 -1
- package/dist/esm/src/service/team/team-generator.d.ts +2 -0
- package/dist/esm/src/service/team/team-generator.js +7 -3
- package/package.json +1 -1
|
@@ -9,6 +9,7 @@ const player_1 = require("../player");
|
|
|
9
9
|
const utils_1 = require("../utils");
|
|
10
10
|
const team_1 = require("./team");
|
|
11
11
|
const tactics_1 = require("./tactics");
|
|
12
|
+
const energy_band_1 = require("./energy-band");
|
|
12
13
|
const formation_1 = require("./formation");
|
|
13
14
|
const match_1 = require("../match");
|
|
14
15
|
const logger_1 = __importDefault(require("../../logger"));
|
|
@@ -66,7 +67,8 @@ class TeamGenerator {
|
|
|
66
67
|
lineup,
|
|
67
68
|
liberoReplacements: [lineup[3], lineup[6]],
|
|
68
69
|
pinchServerSubs: new Map(),
|
|
69
|
-
substitutionTolerance: Math.round((0, utils_1.randomNumber)(1, 50))
|
|
70
|
+
substitutionTolerance: Math.round((0, utils_1.randomNumber)(1, 50)),
|
|
71
|
+
substitutionBand: TeamGenerator.BOT_SUB_BANDS[Math.floor(Math.random() * TeamGenerator.BOT_SUB_BANDS.length)]
|
|
70
72
|
});
|
|
71
73
|
}
|
|
72
74
|
static createLineup(players, formation) {
|
|
@@ -92,9 +94,9 @@ class TeamGenerator {
|
|
|
92
94
|
throw new Error('NOT_ENOUGH_PLAYERS_FOR_TEAM_2');
|
|
93
95
|
out[i] = courtPlayer;
|
|
94
96
|
}
|
|
95
|
-
//
|
|
97
|
+
// FIVB: 6 starters + libero + up to 6 substitutes on the bench.
|
|
96
98
|
out.bench = filtered.sort(player_1.Player.compareStats)
|
|
97
|
-
.slice(0,
|
|
99
|
+
.slice(0, 6);
|
|
98
100
|
out[match_1.CourtPosition.LIBERO_ZONE] = libero;
|
|
99
101
|
return out;
|
|
100
102
|
}
|
|
@@ -107,3 +109,5 @@ TeamGenerator.RARITIES = [
|
|
|
107
109
|
...buildBlock(player_1.RarityEnum.LEGENDARY, 2)
|
|
108
110
|
];
|
|
109
111
|
TeamGenerator.ROLES = [player_1.RoleEnum.LIBERO, player_1.RoleEnum.MIDDLE_BLOCKER, player_1.RoleEnum.OPPOSITE_HITTER, player_1.RoleEnum.OUTSIDE_HITTER, player_1.RoleEnum.SETTER];
|
|
112
|
+
// Bots auto-sub on fatigue via the team-default band; they don't configure designated subs.
|
|
113
|
+
TeamGenerator.BOT_SUB_BANDS = [energy_band_1.EnergyBand.WORN, energy_band_1.EnergyBand.TIRED, energy_band_1.EnergyBand.EXHAUSTED];
|
|
@@ -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 } 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, PinchCondition, SubBand } from '../service';
|
|
2
2
|
export type Rally = DataProps<_Rally> & {
|
|
3
3
|
homePlayerPosition: PlayerPosition[];
|
|
4
4
|
awayPlayerPosition: PlayerPosition[];
|
|
@@ -56,12 +56,22 @@ export interface StartingLineup {
|
|
|
56
56
|
[CourtPosition.LIBERO_ZONE]?: string;
|
|
57
57
|
bench: string[];
|
|
58
58
|
}
|
|
59
|
+
export interface ApiDesignatedSub {
|
|
60
|
+
starterId: string;
|
|
61
|
+
benchId?: string;
|
|
62
|
+
isPinchServer: boolean;
|
|
63
|
+
fatigueBand?: SubBand;
|
|
64
|
+
conditions?: PinchCondition[];
|
|
65
|
+
conditionLogic?: ConditionLogic;
|
|
66
|
+
}
|
|
59
67
|
export interface Tactics {
|
|
60
68
|
lineup: StartingLineup;
|
|
61
69
|
substitutionTolerance: number;
|
|
62
70
|
liberoReplacements: string[];
|
|
63
71
|
pinchServerSubs: Record<string, string>;
|
|
64
72
|
receiveRotationOffset: boolean;
|
|
73
|
+
designatedSubs: ApiDesignatedSub[];
|
|
74
|
+
substitutionBand: SubBand;
|
|
65
75
|
}
|
|
66
76
|
export type Team = Omit<DataProps<_Team>, 'roster' | '_rating' | 'rating' | 'tactics'> & {
|
|
67
77
|
roster: Player[];
|
|
@@ -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 { CourtPosition } from '../../service';
|
|
4
|
+
import { ConditionLogic, CourtPosition, PinchCondition, SubBand } from '../../service';
|
|
5
5
|
export interface TacticsLineupAttributes {
|
|
6
6
|
[CourtPosition.LIBERO_ZONE]?: PlayerId;
|
|
7
7
|
[CourtPosition.LEFT_BACK]: PlayerId;
|
|
@@ -13,6 +13,14 @@ export interface TacticsLineupAttributes {
|
|
|
13
13
|
bench: PlayerId[];
|
|
14
14
|
}
|
|
15
15
|
export type PinchServerSubsAttributes = Record<PlayerId, PlayerId>;
|
|
16
|
+
export interface DesignatedSubAttributes {
|
|
17
|
+
starterId: PlayerId;
|
|
18
|
+
benchId?: PlayerId;
|
|
19
|
+
isPinchServer: boolean;
|
|
20
|
+
fatigueBand?: SubBand;
|
|
21
|
+
conditions?: PinchCondition[];
|
|
22
|
+
conditionLogic?: ConditionLogic;
|
|
23
|
+
}
|
|
16
24
|
export interface TacticsAttributes {
|
|
17
25
|
team_id: string;
|
|
18
26
|
substitution_tolerance: number;
|
|
@@ -20,10 +28,12 @@ export interface TacticsAttributes {
|
|
|
20
28
|
libero_replacements: PlayerId[];
|
|
21
29
|
pinch_server_subs: PinchServerSubsAttributes;
|
|
22
30
|
receive_rotation_offset: boolean;
|
|
31
|
+
designated_subs: DesignatedSubAttributes[];
|
|
32
|
+
substitution_band: SubBand;
|
|
23
33
|
}
|
|
24
34
|
export type TacticsPk = 'team_id';
|
|
25
35
|
export type TacticsId = TacticsModel[TacticsPk];
|
|
26
|
-
export type TacticsOptionalAttributes = 'substitution_tolerance' | 'lineup' | 'libero_replacements' | 'pinch_server_subs' | 'receive_rotation_offset';
|
|
36
|
+
export type TacticsOptionalAttributes = 'substitution_tolerance' | 'lineup' | 'libero_replacements' | 'pinch_server_subs' | 'receive_rotation_offset' | 'designated_subs' | 'substitution_band';
|
|
27
37
|
export type TacticsCreationAttributes = Optional<TacticsAttributes, TacticsOptionalAttributes>;
|
|
28
38
|
export declare class TacticsModel extends Model<TacticsAttributes, TacticsCreationAttributes> implements TacticsAttributes {
|
|
29
39
|
team_id: string;
|
|
@@ -32,6 +42,8 @@ export declare class TacticsModel extends Model<TacticsAttributes, TacticsCreati
|
|
|
32
42
|
libero_replacements: PlayerId[];
|
|
33
43
|
pinch_server_subs: PinchServerSubsAttributes;
|
|
34
44
|
receive_rotation_offset: boolean;
|
|
45
|
+
designated_subs: DesignatedSubAttributes[];
|
|
46
|
+
substitution_band: SubBand;
|
|
35
47
|
team: TeamModel;
|
|
36
48
|
getTeam: Sequelize.BelongsToGetAssociationMixin<TeamModel>;
|
|
37
49
|
setTeam: Sequelize.BelongsToSetAssociationMixin<TeamModel, TeamId>;
|
|
@@ -36,6 +36,16 @@ export class TacticsModel extends Model {
|
|
|
36
36
|
type: DataTypes.BOOLEAN,
|
|
37
37
|
allowNull: false,
|
|
38
38
|
defaultValue: false
|
|
39
|
+
},
|
|
40
|
+
designated_subs: {
|
|
41
|
+
type: DataTypes.JSONB,
|
|
42
|
+
allowNull: false,
|
|
43
|
+
defaultValue: []
|
|
44
|
+
},
|
|
45
|
+
substitution_band: {
|
|
46
|
+
type: DataTypes.STRING,
|
|
47
|
+
allowNull: false,
|
|
48
|
+
defaultValue: 'TIRED'
|
|
39
49
|
}
|
|
40
50
|
}, {
|
|
41
51
|
sequelize,
|
|
@@ -36,7 +36,16 @@ function transformToAttributes(tactics, teamId) {
|
|
|
36
36
|
receive_rotation_offset: tactics.receiveRotationOffset ?? false,
|
|
37
37
|
lineup: transformFromLineup(tactics.lineup),
|
|
38
38
|
libero_replacements: tactics.liberoReplacements.map((lr) => lr.id),
|
|
39
|
-
pinch_server_subs: Object.fromEntries([...tactics.pinchServerSubs].map(([k, v]) => [k.id, v.id]))
|
|
39
|
+
pinch_server_subs: Object.fromEntries([...tactics.pinchServerSubs].map(([k, v]) => [k.id, v.id])),
|
|
40
|
+
substitution_band: tactics.substitutionBand,
|
|
41
|
+
designated_subs: tactics.designatedSubs.map(ds => ({
|
|
42
|
+
starterId: ds.starter.id,
|
|
43
|
+
benchId: ds.bench?.id,
|
|
44
|
+
isPinchServer: ds.isPinchServer,
|
|
45
|
+
fatigueBand: ds.fatigueBand,
|
|
46
|
+
conditions: ds.conditions,
|
|
47
|
+
conditionLogic: ds.conditionLogic
|
|
48
|
+
}))
|
|
40
49
|
};
|
|
41
50
|
}
|
|
42
51
|
function buildPinchServerSubs(pinchServerSubs, roster) {
|
|
@@ -52,7 +61,16 @@ function transformToObject(model, roster) {
|
|
|
52
61
|
receiveRotationOffset: model.receive_rotation_offset,
|
|
53
62
|
lineup: transformToLineup(model.lineup, roster),
|
|
54
63
|
liberoReplacements: model.libero_replacements.map((lr) => findPlayer(lr, roster)),
|
|
55
|
-
pinchServerSubs: buildPinchServerSubs(model.pinch_server_subs, roster)
|
|
64
|
+
pinchServerSubs: buildPinchServerSubs(model.pinch_server_subs, roster),
|
|
65
|
+
substitutionBand: model.substitution_band,
|
|
66
|
+
designatedSubs: (model.designated_subs ?? []).map(d => ({
|
|
67
|
+
starter: findPlayer(d.starterId, roster),
|
|
68
|
+
bench: d.benchId != null ? findPlayer(d.benchId, roster) : undefined,
|
|
69
|
+
isPinchServer: d.isPinchServer,
|
|
70
|
+
fatigueBand: d.fatigueBand,
|
|
71
|
+
conditions: d.conditions,
|
|
72
|
+
conditionLogic: d.conditionLogic
|
|
73
|
+
}))
|
|
56
74
|
});
|
|
57
75
|
}
|
|
58
76
|
export { transformToObject as transformToTactics, transformToAttributes as transformFromTactics };
|
|
@@ -33,20 +33,28 @@ export class Standing {
|
|
|
33
33
|
this.totalMatches = this.matchesWon + this.matchesLost;
|
|
34
34
|
}
|
|
35
35
|
static sortFn(matches) {
|
|
36
|
+
// A zero denominator (no sets/points lost) is the BEST possible ratio, not the worst. The stored
|
|
37
|
+
// setsRatio/pointsRatio fall back to 0 in that case (and the table shows "MAX"), so we compare an
|
|
38
|
+
// effective ratio here that treats "won with nothing lost" as +Infinity, ranking it highest.
|
|
39
|
+
const effectiveRatio = (won, lost) => lost > 0 ? won / lost : (won > 0 ? Infinity : 0);
|
|
36
40
|
return (standingA, standingB) => {
|
|
37
41
|
if (standingB.points > standingA.points)
|
|
38
42
|
return 1;
|
|
39
|
-
|
|
43
|
+
if (standingA.points > standingB.points)
|
|
40
44
|
return -1;
|
|
41
|
-
|
|
45
|
+
const setRatioA = effectiveRatio(standingA.setsWon, standingA.setsLost);
|
|
46
|
+
const setRatioB = effectiveRatio(standingB.setsWon, standingB.setsLost);
|
|
47
|
+
if (setRatioB > setRatioA)
|
|
42
48
|
return 1;
|
|
43
|
-
|
|
49
|
+
if (setRatioA > setRatioB)
|
|
44
50
|
return -1;
|
|
45
|
-
|
|
51
|
+
const pointRatioA = effectiveRatio(standingA.pointsWon, standingA.pointsLost);
|
|
52
|
+
const pointRatioB = effectiveRatio(standingB.pointsWon, standingB.pointsLost);
|
|
53
|
+
if (pointRatioB > pointRatioA)
|
|
46
54
|
return 1;
|
|
47
|
-
|
|
55
|
+
if (pointRatioA > pointRatioB)
|
|
48
56
|
return -1;
|
|
49
|
-
|
|
57
|
+
if (matches.length > 0) {
|
|
50
58
|
const ab = matches.find((match) => match.homeTeam.id === standingA.teamId && match.awayTeam.id === standingB.teamId);
|
|
51
59
|
const ba = matches.find((match) => match.awayTeam.id === standingA.teamId && match.homeTeam.id === standingB.teamId);
|
|
52
60
|
if (ba == null && ab == null)
|
|
@@ -63,8 +71,7 @@ export class Standing {
|
|
|
63
71
|
const b = (ba.homeScore ?? 0) + (ab.awayScore ?? 0);
|
|
64
72
|
return b - a;
|
|
65
73
|
}
|
|
66
|
-
|
|
67
|
-
return 0;
|
|
74
|
+
return 0;
|
|
68
75
|
};
|
|
69
76
|
}
|
|
70
77
|
calculateStanding(matches) {
|
|
@@ -134,6 +134,18 @@ describe('Standing', () => {
|
|
|
134
134
|
const sorted = [b, a].sort(Standing.sortFn([]));
|
|
135
135
|
expect(sorted[0].teamId).toBe(idA);
|
|
136
136
|
});
|
|
137
|
+
it('ranks a team with no sets lost (MAX ratio) above a finite setsRatio when points tie', () => {
|
|
138
|
+
const idA = uuidv4();
|
|
139
|
+
const idB = uuidv4();
|
|
140
|
+
// Both 6 points. A swept its matches (won30=2 → setsWon=6, setsLost=0 → ratio is MAX/infinite);
|
|
141
|
+
// B also has 6 points but conceded sets (won31=2 → setsWon=6, setsLost=2 → setsRatio=3).
|
|
142
|
+
// The undefeated-in-sets team must rank FIRST, not last (the divide-by-zero bug ranked it last).
|
|
143
|
+
const a = makeStanding(idA, { won30: 2 });
|
|
144
|
+
const b = makeStanding(idB, { won31: 2 });
|
|
145
|
+
const sorted = [b, a].sort(Standing.sortFn([]));
|
|
146
|
+
expect(sorted[0].teamId).toBe(idA);
|
|
147
|
+
expect(sorted[1].teamId).toBe(idB);
|
|
148
|
+
});
|
|
137
149
|
it('falls back to pointsRatio when points and setsRatio are equal', () => {
|
|
138
150
|
const idA = uuidv4();
|
|
139
151
|
const idB = uuidv4();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Player } from '../player';
|
|
2
|
+
import { SubBand } from './energy-band';
|
|
3
|
+
import { ConditionLogic, PinchCondition } from './pinch-condition';
|
|
4
|
+
export interface DesignatedSub {
|
|
5
|
+
readonly starter: Player;
|
|
6
|
+
readonly bench?: Player;
|
|
7
|
+
readonly isPinchServer: boolean;
|
|
8
|
+
readonly fatigueBand?: SubBand;
|
|
9
|
+
readonly conditions?: PinchCondition[];
|
|
10
|
+
readonly conditionLogic?: ConditionLogic;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// The named fatigue bands a player's energy falls into. The numeric cutoffs and per-band performance
|
|
2
|
+
// multipliers live in the simulator (VolleyballSim court-player.ts ENERGY_BANDS); this enum is the shared
|
|
3
|
+
// vocabulary used by tactics configuration (substitution thresholds) and, later, the UI.
|
|
4
|
+
export var EnergyBand;
|
|
5
|
+
(function (EnergyBand) {
|
|
6
|
+
EnergyBand["ENERGETIC"] = "ENERGETIC";
|
|
7
|
+
EnergyBand["WORN"] = "WORN";
|
|
8
|
+
EnergyBand["TIRED"] = "TIRED";
|
|
9
|
+
EnergyBand["EXHAUSTED"] = "EXHAUSTED";
|
|
10
|
+
})(EnergyBand || (EnergyBand = {}));
|
|
11
|
+
// Ordered best -> worst. Used to compare "is this player at-or-below the configured sub band".
|
|
12
|
+
export const ENERGY_BAND_ORDER = [
|
|
13
|
+
EnergyBand.ENERGETIC,
|
|
14
|
+
EnergyBand.WORN,
|
|
15
|
+
EnergyBand.TIRED,
|
|
16
|
+
EnergyBand.EXHAUSTED
|
|
17
|
+
];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export declare enum PinchConditionType {
|
|
2
|
+
ASAP = "ASAP",// every time the linked starter wins back the serve
|
|
3
|
+
TEAM_SET_POINT = "TEAM_SET_POINT",// team within `margin` of winning the set
|
|
4
|
+
OPPONENT_SET_POINT = "OPPONENT_SET_POINT",// opponent within `margin` of winning the set
|
|
5
|
+
SCORE_DIFF = "SCORE_DIFF",// team trailing/leading by at least `points`
|
|
6
|
+
AFTER_ROTATIONS = "AFTER_ROTATIONS",// from the team's Nth serve turn in the set onward
|
|
7
|
+
FROM_SET = "FROM_SET",// only set N or later
|
|
8
|
+
MIN_OWN_SCORE = "MIN_OWN_SCORE"
|
|
9
|
+
}
|
|
10
|
+
export declare enum OpponentRelation {
|
|
11
|
+
AHEAD = "AHEAD",
|
|
12
|
+
BEHIND = "BEHIND",
|
|
13
|
+
EITHER = "EITHER"
|
|
14
|
+
}
|
|
15
|
+
export declare enum ScoreDirection {
|
|
16
|
+
TRAILING = "TRAILING",
|
|
17
|
+
LEADING = "LEADING"
|
|
18
|
+
}
|
|
19
|
+
export interface AsapCondition {
|
|
20
|
+
readonly type: PinchConditionType.ASAP;
|
|
21
|
+
}
|
|
22
|
+
export interface TeamSetPointCondition {
|
|
23
|
+
readonly type: PinchConditionType.TEAM_SET_POINT;
|
|
24
|
+
readonly margin: number;
|
|
25
|
+
readonly opponent: OpponentRelation;
|
|
26
|
+
}
|
|
27
|
+
export interface OpponentSetPointCondition {
|
|
28
|
+
readonly type: PinchConditionType.OPPONENT_SET_POINT;
|
|
29
|
+
readonly margin: number;
|
|
30
|
+
}
|
|
31
|
+
export interface ScoreDiffCondition {
|
|
32
|
+
readonly type: PinchConditionType.SCORE_DIFF;
|
|
33
|
+
readonly direction: ScoreDirection;
|
|
34
|
+
readonly points: number;
|
|
35
|
+
}
|
|
36
|
+
export interface AfterRotationsCondition {
|
|
37
|
+
readonly type: PinchConditionType.AFTER_ROTATIONS;
|
|
38
|
+
readonly rotations: number;
|
|
39
|
+
}
|
|
40
|
+
export interface FromSetCondition {
|
|
41
|
+
readonly type: PinchConditionType.FROM_SET;
|
|
42
|
+
readonly set: number;
|
|
43
|
+
}
|
|
44
|
+
export interface MinOwnScoreCondition {
|
|
45
|
+
readonly type: PinchConditionType.MIN_OWN_SCORE;
|
|
46
|
+
readonly score: number;
|
|
47
|
+
}
|
|
48
|
+
export type PinchCondition = AsapCondition | TeamSetPointCondition | OpponentSetPointCondition | ScoreDiffCondition | AfterRotationsCondition | FromSetCondition | MinOwnScoreCondition;
|
|
49
|
+
export type ConditionLogic = 'ALL' | 'ANY';
|
|
50
|
+
export interface PinchSetState {
|
|
51
|
+
readonly ownScore: number;
|
|
52
|
+
readonly opponentScore: number;
|
|
53
|
+
readonly setTarget: number;
|
|
54
|
+
readonly setNumber: number;
|
|
55
|
+
readonly ownServeTurns: number;
|
|
56
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Conditions that gate when a pinch server is forced in to serve (in for its linked starter when the team
|
|
2
|
+
// wins back the serve, out the moment the serve is lost). Each condition is evaluated against the live set
|
|
3
|
+
// state; a pinch entry combines them with ALL (every condition must hold) or ANY (one is enough).
|
|
4
|
+
export var PinchConditionType;
|
|
5
|
+
(function (PinchConditionType) {
|
|
6
|
+
PinchConditionType["ASAP"] = "ASAP";
|
|
7
|
+
PinchConditionType["TEAM_SET_POINT"] = "TEAM_SET_POINT";
|
|
8
|
+
PinchConditionType["OPPONENT_SET_POINT"] = "OPPONENT_SET_POINT";
|
|
9
|
+
PinchConditionType["SCORE_DIFF"] = "SCORE_DIFF";
|
|
10
|
+
PinchConditionType["AFTER_ROTATIONS"] = "AFTER_ROTATIONS";
|
|
11
|
+
PinchConditionType["FROM_SET"] = "FROM_SET";
|
|
12
|
+
PinchConditionType["MIN_OWN_SCORE"] = "MIN_OWN_SCORE"; // only once own score >= N
|
|
13
|
+
})(PinchConditionType || (PinchConditionType = {}));
|
|
14
|
+
export var OpponentRelation;
|
|
15
|
+
(function (OpponentRelation) {
|
|
16
|
+
OpponentRelation["AHEAD"] = "AHEAD";
|
|
17
|
+
OpponentRelation["BEHIND"] = "BEHIND";
|
|
18
|
+
OpponentRelation["EITHER"] = "EITHER";
|
|
19
|
+
})(OpponentRelation || (OpponentRelation = {}));
|
|
20
|
+
export var ScoreDirection;
|
|
21
|
+
(function (ScoreDirection) {
|
|
22
|
+
ScoreDirection["TRAILING"] = "TRAILING";
|
|
23
|
+
ScoreDirection["LEADING"] = "LEADING";
|
|
24
|
+
})(ScoreDirection || (ScoreDirection = {}));
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Player } from '../../player';
|
|
3
|
+
import { EnergyBand } from '../energy-band';
|
|
4
|
+
import { OpponentRelation, PinchConditionType, ScoreDirection } from '../pinch-condition';
|
|
5
|
+
export declare const SubBandSchema: z.ZodUnion<readonly [z.ZodEnum<typeof EnergyBand>, z.ZodLiteral<"NEVER">]>;
|
|
6
|
+
export declare const PinchConditionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
7
|
+
type: z.ZodLiteral<PinchConditionType.ASAP>;
|
|
8
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
9
|
+
type: z.ZodLiteral<PinchConditionType.TEAM_SET_POINT>;
|
|
10
|
+
margin: z.ZodNumber;
|
|
11
|
+
opponent: z.ZodEnum<typeof OpponentRelation>;
|
|
12
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
13
|
+
type: z.ZodLiteral<PinchConditionType.OPPONENT_SET_POINT>;
|
|
14
|
+
margin: z.ZodNumber;
|
|
15
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
16
|
+
type: z.ZodLiteral<PinchConditionType.SCORE_DIFF>;
|
|
17
|
+
direction: z.ZodEnum<typeof ScoreDirection>;
|
|
18
|
+
points: z.ZodNumber;
|
|
19
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
20
|
+
type: z.ZodLiteral<PinchConditionType.AFTER_ROTATIONS>;
|
|
21
|
+
rotations: z.ZodNumber;
|
|
22
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
23
|
+
type: z.ZodLiteral<PinchConditionType.FROM_SET>;
|
|
24
|
+
set: z.ZodNumber;
|
|
25
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
26
|
+
type: z.ZodLiteral<PinchConditionType.MIN_OWN_SCORE>;
|
|
27
|
+
score: z.ZodNumber;
|
|
28
|
+
}, z.core.$strip>], "type">;
|
|
29
|
+
export declare const ConditionLogicSchema: z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>;
|
|
30
|
+
export declare const DesignatedSubSchema: z.ZodObject<{
|
|
31
|
+
starter: z.ZodCustom<Player, Player>;
|
|
32
|
+
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
33
|
+
isPinchServer: z.ZodBoolean;
|
|
34
|
+
fatigueBand: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<typeof EnergyBand>, z.ZodLiteral<"NEVER">]>>;
|
|
35
|
+
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
36
|
+
type: z.ZodLiteral<PinchConditionType.ASAP>;
|
|
37
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
38
|
+
type: z.ZodLiteral<PinchConditionType.TEAM_SET_POINT>;
|
|
39
|
+
margin: z.ZodNumber;
|
|
40
|
+
opponent: z.ZodEnum<typeof OpponentRelation>;
|
|
41
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
42
|
+
type: z.ZodLiteral<PinchConditionType.OPPONENT_SET_POINT>;
|
|
43
|
+
margin: z.ZodNumber;
|
|
44
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
45
|
+
type: z.ZodLiteral<PinchConditionType.SCORE_DIFF>;
|
|
46
|
+
direction: z.ZodEnum<typeof ScoreDirection>;
|
|
47
|
+
points: z.ZodNumber;
|
|
48
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
49
|
+
type: z.ZodLiteral<PinchConditionType.AFTER_ROTATIONS>;
|
|
50
|
+
rotations: z.ZodNumber;
|
|
51
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
52
|
+
type: z.ZodLiteral<PinchConditionType.FROM_SET>;
|
|
53
|
+
set: z.ZodNumber;
|
|
54
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
55
|
+
type: z.ZodLiteral<PinchConditionType.MIN_OWN_SCORE>;
|
|
56
|
+
score: z.ZodNumber;
|
|
57
|
+
}, z.core.$strip>], "type">>>;
|
|
58
|
+
conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
|
|
59
|
+
}, z.core.$strip>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Player } from '../../player';
|
|
3
|
+
import { EnergyBand } from '../energy-band';
|
|
4
|
+
import { OpponentRelation, PinchConditionType, ScoreDirection } from '../pinch-condition';
|
|
5
|
+
const playerInstanceSchema = z.custom((v) => v instanceof Player, {
|
|
6
|
+
message: 'INVALID_PLAYER_INSTANCE'
|
|
7
|
+
});
|
|
8
|
+
export const SubBandSchema = z.union([z.nativeEnum(EnergyBand), z.literal('NEVER')]);
|
|
9
|
+
export const PinchConditionSchema = z.discriminatedUnion('type', [
|
|
10
|
+
z.object({ type: z.literal(PinchConditionType.ASAP) }),
|
|
11
|
+
z.object({ type: z.literal(PinchConditionType.TEAM_SET_POINT), margin: z.number().int().min(1).max(25), opponent: z.nativeEnum(OpponentRelation) }),
|
|
12
|
+
z.object({ type: z.literal(PinchConditionType.OPPONENT_SET_POINT), margin: z.number().int().min(1).max(25) }),
|
|
13
|
+
z.object({ type: z.literal(PinchConditionType.SCORE_DIFF), direction: z.nativeEnum(ScoreDirection), points: z.number().int().min(1).max(25) }),
|
|
14
|
+
z.object({ type: z.literal(PinchConditionType.AFTER_ROTATIONS), rotations: z.number().int().min(1).max(6) }),
|
|
15
|
+
z.object({ type: z.literal(PinchConditionType.FROM_SET), set: z.number().int().min(1).max(5) }),
|
|
16
|
+
z.object({ type: z.literal(PinchConditionType.MIN_OWN_SCORE), score: z.number().int().min(0).max(40) })
|
|
17
|
+
]);
|
|
18
|
+
export const ConditionLogicSchema = z.union([z.literal('ALL'), z.literal('ANY')]);
|
|
19
|
+
export const DesignatedSubSchema = z.object({
|
|
20
|
+
starter: playerInstanceSchema,
|
|
21
|
+
bench: playerInstanceSchema.optional(),
|
|
22
|
+
isPinchServer: z.boolean(),
|
|
23
|
+
fatigueBand: SubBandSchema.optional(),
|
|
24
|
+
conditions: z.array(PinchConditionSchema).optional(),
|
|
25
|
+
conditionLogic: ConditionLogicSchema.optional()
|
|
26
|
+
}).superRefine((data, ctx) => {
|
|
27
|
+
if (data.isPinchServer) {
|
|
28
|
+
// Pinch mode needs a designated bench player (the server) and at least one condition.
|
|
29
|
+
if (data.bench == null) {
|
|
30
|
+
ctx.addIssue({ code: 'custom', message: 'PINCH_SERVER_REQUIRES_BENCH', path: ['bench'] });
|
|
31
|
+
}
|
|
32
|
+
if (data.conditions == null || data.conditions.length === 0) {
|
|
33
|
+
ctx.addIssue({ code: 'custom', message: 'PINCH_SERVER_REQUIRES_CONDITIONS', path: ['conditions'] });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { Player } from '../../player';
|
|
3
|
+
import { EnergyBand } from '../energy-band';
|
|
3
4
|
export declare const TacticsInputSchema: z.ZodObject<{
|
|
4
5
|
lineup: z.ZodObject<{
|
|
5
6
|
4: z.ZodCustom<Player, Player>;
|
|
@@ -15,5 +16,36 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
15
16
|
liberoReplacements: z.ZodArray<z.ZodCustom<Player, Player>>;
|
|
16
17
|
receiveRotationOffset: z.ZodDefault<z.ZodBoolean>;
|
|
17
18
|
pinchServerSubs: z.ZodCustom<Map<Player, Player>, Map<Player, Player>>;
|
|
19
|
+
designatedSubs: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
20
|
+
starter: z.ZodCustom<Player, Player>;
|
|
21
|
+
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
22
|
+
isPinchServer: z.ZodBoolean;
|
|
23
|
+
fatigueBand: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<typeof EnergyBand>, z.ZodLiteral<"NEVER">]>>;
|
|
24
|
+
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
25
|
+
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
26
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
27
|
+
type: z.ZodLiteral<import("..").PinchConditionType.TEAM_SET_POINT>;
|
|
28
|
+
margin: z.ZodNumber;
|
|
29
|
+
opponent: z.ZodEnum<typeof import("..").OpponentRelation>;
|
|
30
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
31
|
+
type: z.ZodLiteral<import("..").PinchConditionType.OPPONENT_SET_POINT>;
|
|
32
|
+
margin: z.ZodNumber;
|
|
33
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
34
|
+
type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
|
|
35
|
+
direction: z.ZodEnum<typeof import("..").ScoreDirection>;
|
|
36
|
+
points: z.ZodNumber;
|
|
37
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
38
|
+
type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
|
|
39
|
+
rotations: z.ZodNumber;
|
|
40
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
41
|
+
type: z.ZodLiteral<import("..").PinchConditionType.FROM_SET>;
|
|
42
|
+
set: z.ZodNumber;
|
|
43
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
44
|
+
type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
|
|
45
|
+
score: z.ZodNumber;
|
|
46
|
+
}, z.core.$strip>], "type">>>;
|
|
47
|
+
conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
|
|
48
|
+
}, z.core.$strip>>>;
|
|
49
|
+
substitutionBand: z.ZodDefault<z.ZodUnion<readonly [z.ZodEnum<typeof EnergyBand>, z.ZodLiteral<"NEVER">]>>;
|
|
18
50
|
}, z.core.$strip>;
|
|
19
51
|
export type TacticsInput = z.infer<typeof TacticsInputSchema>;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { Player } from '../../player';
|
|
3
|
+
import { EnergyBand } from '../energy-band';
|
|
4
|
+
import { DesignatedSubSchema, SubBandSchema } from './designated-sub.z';
|
|
3
5
|
const playerInstanceSchema = z.custom((v) => v instanceof Player, {
|
|
4
6
|
message: 'INVALID_PLAYER_INSTANCE'
|
|
5
7
|
});
|
|
@@ -18,7 +20,8 @@ const lineupSchema = z.object({
|
|
|
18
20
|
[MIDDLE_BACK]: playerInstanceSchema,
|
|
19
21
|
[RIGHT_BACK]: playerInstanceSchema,
|
|
20
22
|
[LIBERO_ZONE]: playerInstanceSchema.optional(),
|
|
21
|
-
|
|
23
|
+
// FIVB: up to 6 substitutes (the libero is tracked separately in LIBERO_ZONE).
|
|
24
|
+
bench: z.array(playerInstanceSchema).max(6)
|
|
22
25
|
}).superRefine((data, ctx) => {
|
|
23
26
|
const players = [
|
|
24
27
|
data[LEFT_FRONT],
|
|
@@ -47,5 +50,8 @@ export const TacticsInputSchema = z.object({
|
|
|
47
50
|
// Default false keeps every existing Tactics.create({...}) call (that omits this) valid.
|
|
48
51
|
receiveRotationOffset: z.boolean().default(false),
|
|
49
52
|
pinchServerSubs: z.custom((v) => v instanceof Map &&
|
|
50
|
-
Array.from(v.entries()).every(([k, val]) => k instanceof Player && val instanceof Player), { message: 'INVALID_PINCH_SERVER_SUBS' })
|
|
53
|
+
Array.from(v.entries()).every(([k, val]) => k instanceof Player && val instanceof Player), { message: 'INVALID_PINCH_SERVER_SUBS' }),
|
|
54
|
+
// New (designated-subs feature). Defaults keep every existing Tactics.create({...}) call valid.
|
|
55
|
+
designatedSubs: z.array(DesignatedSubSchema).default([]),
|
|
56
|
+
substitutionBand: SubBandSchema.default(EnergyBand.TIRED)
|
|
51
57
|
});
|
|
@@ -24,6 +24,37 @@ export declare const TeamInputSchema: z.ZodObject<{
|
|
|
24
24
|
liberoReplacements: z.ZodArray<z.ZodCustom<Player, Player>>;
|
|
25
25
|
receiveRotationOffset: z.ZodDefault<z.ZodBoolean>;
|
|
26
26
|
pinchServerSubs: z.ZodCustom<Map<Player, Player>, Map<Player, Player>>;
|
|
27
|
+
designatedSubs: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
28
|
+
starter: z.ZodCustom<Player, Player>;
|
|
29
|
+
bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
30
|
+
isPinchServer: z.ZodBoolean;
|
|
31
|
+
fatigueBand: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<typeof import("..").EnergyBand>, z.ZodLiteral<"NEVER">]>>;
|
|
32
|
+
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
33
|
+
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
34
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
35
|
+
type: z.ZodLiteral<import("..").PinchConditionType.TEAM_SET_POINT>;
|
|
36
|
+
margin: z.ZodNumber;
|
|
37
|
+
opponent: z.ZodEnum<typeof import("..").OpponentRelation>;
|
|
38
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
39
|
+
type: z.ZodLiteral<import("..").PinchConditionType.OPPONENT_SET_POINT>;
|
|
40
|
+
margin: z.ZodNumber;
|
|
41
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
42
|
+
type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
|
|
43
|
+
direction: z.ZodEnum<typeof import("..").ScoreDirection>;
|
|
44
|
+
points: z.ZodNumber;
|
|
45
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
46
|
+
type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
|
|
47
|
+
rotations: z.ZodNumber;
|
|
48
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
49
|
+
type: z.ZodLiteral<import("..").PinchConditionType.FROM_SET>;
|
|
50
|
+
set: z.ZodNumber;
|
|
51
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
52
|
+
type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
|
|
53
|
+
score: z.ZodNumber;
|
|
54
|
+
}, z.core.$strip>], "type">>>;
|
|
55
|
+
conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
|
|
56
|
+
}, z.core.$strip>>>;
|
|
57
|
+
substitutionBand: z.ZodDefault<z.ZodUnion<readonly [z.ZodEnum<typeof import("..").EnergyBand>, z.ZodLiteral<"NEVER">]>>;
|
|
27
58
|
}, z.core.$strip>>;
|
|
28
59
|
}, z.core.$strip>;
|
|
29
60
|
export type TeamInput = z.infer<typeof TeamInputSchema>;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { CourtPosition } from '../match';
|
|
2
2
|
import { Player } from '../player';
|
|
3
|
+
import { DesignatedSub } from './designated-sub';
|
|
4
|
+
import { SubBand } from './energy-band';
|
|
3
5
|
export interface StartingLineup {
|
|
4
6
|
readonly [CourtPosition.LEFT_FRONT]: Player;
|
|
5
7
|
readonly [CourtPosition.MIDDLE_FRONT]: Player;
|
|
@@ -16,6 +18,8 @@ export interface TacticsOpts {
|
|
|
16
18
|
readonly liberoReplacements: Player[];
|
|
17
19
|
readonly pinchServerSubs: Map<Player, Player>;
|
|
18
20
|
readonly receiveRotationOffset: boolean;
|
|
21
|
+
readonly designatedSubs: DesignatedSub[];
|
|
22
|
+
readonly substitutionBand: SubBand;
|
|
19
23
|
}
|
|
20
24
|
export declare class Tactics {
|
|
21
25
|
readonly lineup: StartingLineup;
|
|
@@ -23,6 +27,8 @@ export declare class Tactics {
|
|
|
23
27
|
readonly liberoReplacements: Player[];
|
|
24
28
|
readonly pinchServerSubs: Map<Player, Player>;
|
|
25
29
|
readonly receiveRotationOffset: boolean;
|
|
30
|
+
readonly designatedSubs: DesignatedSub[];
|
|
31
|
+
readonly substitutionBand: SubBand;
|
|
26
32
|
static create(input: unknown): Tactics;
|
|
27
33
|
private constructor();
|
|
28
34
|
findPlayerInLineup(id: string): CourtPosition | undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CourtPosition } from '../match';
|
|
2
|
+
import { EnergyBand } from './energy-band';
|
|
2
3
|
import { TacticsInputSchema } from './schemas/tactics.z';
|
|
3
4
|
export class Tactics {
|
|
4
5
|
static create(input) {
|
|
@@ -13,12 +14,14 @@ export class Tactics {
|
|
|
13
14
|
}
|
|
14
15
|
return new Tactics(result.data);
|
|
15
16
|
}
|
|
16
|
-
constructor({ lineup, pinchServerSubs, liberoReplacements, substitutionTolerance = 1, receiveRotationOffset = false }) {
|
|
17
|
+
constructor({ lineup, pinchServerSubs, liberoReplacements, substitutionTolerance = 1, receiveRotationOffset = false, designatedSubs = [], substitutionBand = EnergyBand.TIRED }) {
|
|
17
18
|
this.lineup = lineup;
|
|
18
19
|
this.substitutionTolerance = substitutionTolerance;
|
|
19
20
|
this.pinchServerSubs = pinchServerSubs;
|
|
20
21
|
this.liberoReplacements = liberoReplacements;
|
|
21
22
|
this.receiveRotationOffset = receiveRotationOffset;
|
|
23
|
+
this.designatedSubs = designatedSubs;
|
|
24
|
+
this.substitutionBand = substitutionBand;
|
|
22
25
|
}
|
|
23
26
|
findPlayerInLineup(id) {
|
|
24
27
|
if (this.lineup.bench.some(p => p.id === id))
|
|
@@ -2,6 +2,7 @@ import { Player, RarityEnum, Role } from '../player';
|
|
|
2
2
|
import { Team } from './team';
|
|
3
3
|
import { Country } from '../country';
|
|
4
4
|
import { Tactics } from './tactics';
|
|
5
|
+
import { EnergyBand } from './energy-band';
|
|
5
6
|
export declare class TeamGenerator {
|
|
6
7
|
static readonly TEAM_PER_DIVISION = 16;
|
|
7
8
|
static readonly RARITIES: RarityEnum[];
|
|
@@ -10,6 +11,7 @@ export declare class TeamGenerator {
|
|
|
10
11
|
private static comparePlayers;
|
|
11
12
|
static createRoster(country: Country): Player[];
|
|
12
13
|
static createTeams(divisionId: string, country: Country): Team[];
|
|
14
|
+
static readonly BOT_SUB_BANDS: EnergyBand[];
|
|
13
15
|
static createTactics(roster: Player[]): Tactics;
|
|
14
16
|
private static createLineup;
|
|
15
17
|
}
|