volleyballsimtypes 0.0.443 → 0.0.445
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 +2 -0
- package/dist/cjs/src/data/init-models.js +5 -0
- package/dist/cjs/src/data/models/friendly.d.ts +28 -0
- package/dist/cjs/src/data/models/friendly.js +41 -0
- package/dist/cjs/src/data/models/index.d.ts +1 -0
- package/dist/cjs/src/data/models/index.js +1 -0
- package/dist/cjs/src/data/transformers/tactics.js +18 -2
- package/dist/cjs/src/stat-config/traits.json +5 -4
- package/dist/esm/src/api/index.d.ts +2 -0
- package/dist/esm/src/data/init-models.js +6 -1
- package/dist/esm/src/data/models/friendly.d.ts +28 -0
- package/dist/esm/src/data/models/friendly.js +37 -0
- package/dist/esm/src/data/models/index.d.ts +1 -0
- package/dist/esm/src/data/models/index.js +1 -0
- package/dist/esm/src/data/transformers/tactics.js +18 -2
- package/dist/esm/src/stat-config/traits.json +5 -4
- package/package.json +1 -1
|
@@ -47,6 +47,8 @@ export type Match = Omit<DataProps<_Match>, 'sets' | 'homeTeam' | 'awayTeam' | '
|
|
|
47
47
|
breakdown?: PointBreakdown;
|
|
48
48
|
homeForm?: MatchForm[];
|
|
49
49
|
awayForm?: MatchForm[];
|
|
50
|
+
isFriendly?: boolean;
|
|
51
|
+
detailUnavailable?: boolean;
|
|
50
52
|
};
|
|
51
53
|
export type RallyEvent = DataProps<_RallyEvent> & {
|
|
52
54
|
teamId?: string;
|
|
@@ -45,6 +45,7 @@ function initModels(sequelize) {
|
|
|
45
45
|
const RetiredPlayer = models_1.RetiredPlayerModel.initModel(sequelize);
|
|
46
46
|
const Rally = models_1.RallyModel.initModel(sequelize);
|
|
47
47
|
const PromotionMatch = models_1.PromotionMatchModel.initModel(sequelize);
|
|
48
|
+
const Friendly = models_1.FriendlyModel.initModel(sequelize);
|
|
48
49
|
const RegionQualifier = models_1.RegionQualifierModel.initModel(sequelize);
|
|
49
50
|
const NationalCountry = models_1.NationalCountryModel.initModel(sequelize);
|
|
50
51
|
const Team = models_1.TeamModel.initModel(sequelize);
|
|
@@ -132,6 +133,7 @@ function initModels(sequelize) {
|
|
|
132
133
|
Match.hasMany(MatchSet, { as: 'MatchSets', foreignKey: 'match_id' });
|
|
133
134
|
Match.hasOne(CompetitionMatch, { as: 'CompetitionMatch', foreignKey: 'match_id' });
|
|
134
135
|
Match.hasOne(MatchResult, { as: 'MatchResult', foreignKey: 'match_id' });
|
|
136
|
+
Match.hasOne(Friendly, { as: 'Friendly', foreignKey: 'match_id' });
|
|
135
137
|
MatchResult.belongsTo(Match, { as: 'Match', foreignKey: 'match_id' });
|
|
136
138
|
MatchResult.belongsTo(Team, { as: 'Winner', foreignKey: 'winner_team_id' });
|
|
137
139
|
MatchRating.belongsTo(Match, { as: 'match', foreignKey: 'match_id' });
|
|
@@ -183,6 +185,8 @@ function initModels(sequelize) {
|
|
|
183
185
|
PromotionMatch.belongsTo(Match, { as: 'Match', foreignKey: 'match_id' });
|
|
184
186
|
PromotionMatch.belongsTo(Competition, { as: 'upperCompetition', foreignKey: 'upper_competition' });
|
|
185
187
|
PromotionMatch.belongsTo(Competition, { as: 'lowerCompetition', foreignKey: 'lower_competition' });
|
|
188
|
+
Friendly.belongsTo(Iteration, { as: 'iterationData', foreignKey: 'iteration' });
|
|
189
|
+
Friendly.belongsTo(Match, { as: 'Match', foreignKey: 'match_id' });
|
|
186
190
|
RegionQualifier.belongsTo(Iteration, { as: 'Iteration', foreignKey: 'iteration' });
|
|
187
191
|
RegionQualifier.belongsTo(Country, { as: 'countryRegion', foreignKey: 'region_code', targetKey: 'region_code' });
|
|
188
192
|
RegionQualifier.belongsTo(Competition, { as: 'competition', foreignKey: 'competition_id' });
|
|
@@ -269,6 +273,7 @@ function initModels(sequelize) {
|
|
|
269
273
|
RetiredPlayer,
|
|
270
274
|
PlayerImprovementLog,
|
|
271
275
|
PromotionMatch,
|
|
276
|
+
Friendly,
|
|
272
277
|
RegionQualifier,
|
|
273
278
|
NationalCountry,
|
|
274
279
|
Rally,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as Sequelize from 'sequelize';
|
|
2
|
+
import { Model, Optional } from 'sequelize';
|
|
3
|
+
import { IterationId, IterationModel, MatchAttributes, MatchId, MatchModel } from '.';
|
|
4
|
+
export interface FriendlyAttributes {
|
|
5
|
+
match_id: string;
|
|
6
|
+
iteration: number;
|
|
7
|
+
wave_key: string;
|
|
8
|
+
purged_at: Date | null;
|
|
9
|
+
match?: MatchAttributes;
|
|
10
|
+
}
|
|
11
|
+
export type FriendlyPk = 'match_id';
|
|
12
|
+
export type FriendlyId = FriendlyModel[FriendlyPk];
|
|
13
|
+
export type FriendlyCreationAttributes = Optional<FriendlyAttributes, 'purged_at'>;
|
|
14
|
+
export declare class FriendlyModel extends Model<FriendlyAttributes, FriendlyCreationAttributes> implements FriendlyAttributes {
|
|
15
|
+
match_id: string;
|
|
16
|
+
iteration: number;
|
|
17
|
+
wave_key: string;
|
|
18
|
+
purged_at: Date | null;
|
|
19
|
+
Match: MatchModel;
|
|
20
|
+
getMatch: Sequelize.BelongsToGetAssociationMixin<MatchModel>;
|
|
21
|
+
setMatch: Sequelize.BelongsToSetAssociationMixin<MatchModel, MatchId>;
|
|
22
|
+
createMatch: Sequelize.BelongsToCreateAssociationMixin<MatchModel>;
|
|
23
|
+
iterationData: IterationModel;
|
|
24
|
+
getIterationData: Sequelize.BelongsToGetAssociationMixin<IterationModel>;
|
|
25
|
+
setIterationData: Sequelize.BelongsToSetAssociationMixin<IterationModel, IterationId>;
|
|
26
|
+
createIterationData: Sequelize.BelongsToCreateAssociationMixin<IterationModel>;
|
|
27
|
+
static initModel(sequelize: Sequelize.Sequelize): typeof FriendlyModel;
|
|
28
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FriendlyModel = void 0;
|
|
4
|
+
const sequelize_1 = require("sequelize");
|
|
5
|
+
class FriendlyModel extends sequelize_1.Model {
|
|
6
|
+
static initModel(sequelize) {
|
|
7
|
+
return FriendlyModel.init({
|
|
8
|
+
match_id: {
|
|
9
|
+
type: sequelize_1.DataTypes.UUID,
|
|
10
|
+
allowNull: false,
|
|
11
|
+
primaryKey: true,
|
|
12
|
+
references: {
|
|
13
|
+
model: 'Match',
|
|
14
|
+
key: 'match_id'
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
iteration: {
|
|
18
|
+
type: sequelize_1.DataTypes.INTEGER,
|
|
19
|
+
allowNull: false,
|
|
20
|
+
references: {
|
|
21
|
+
model: 'Iteration',
|
|
22
|
+
key: 'iteration_id'
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
wave_key: {
|
|
26
|
+
type: sequelize_1.DataTypes.TEXT,
|
|
27
|
+
allowNull: false
|
|
28
|
+
},
|
|
29
|
+
purged_at: {
|
|
30
|
+
type: sequelize_1.DataTypes.DATE,
|
|
31
|
+
allowNull: true
|
|
32
|
+
}
|
|
33
|
+
}, {
|
|
34
|
+
sequelize,
|
|
35
|
+
tableName: 'Friendly',
|
|
36
|
+
schema: 'public',
|
|
37
|
+
timestamps: false
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.FriendlyModel = FriendlyModel;
|
|
@@ -26,6 +26,7 @@ export * from './player-team';
|
|
|
26
26
|
export * from './retired-player';
|
|
27
27
|
export * from './rally';
|
|
28
28
|
export * from './promotion-match';
|
|
29
|
+
export * from './friendly';
|
|
29
30
|
export * from './region-qualifier';
|
|
30
31
|
export * from './national-country';
|
|
31
32
|
export * from './coach';
|
|
@@ -42,6 +42,7 @@ __exportStar(require("./player-team"), exports);
|
|
|
42
42
|
__exportStar(require("./retired-player"), exports);
|
|
43
43
|
__exportStar(require("./rally"), exports);
|
|
44
44
|
__exportStar(require("./promotion-match"), exports);
|
|
45
|
+
__exportStar(require("./friendly"), exports);
|
|
45
46
|
__exportStar(require("./region-qualifier"), exports);
|
|
46
47
|
__exportStar(require("./national-country"), exports);
|
|
47
48
|
__exportStar(require("./coach"), exports);
|
|
@@ -105,6 +105,16 @@ function transformToObject(model, roster) {
|
|
|
105
105
|
const secondLibero = (hasStartingLibero && model.second_libero != null && !inLineupIds.has(model.second_libero))
|
|
106
106
|
? roster.find((p) => p.id === model.second_libero)
|
|
107
107
|
: undefined;
|
|
108
|
+
// A persisted `libero_replacement_sets` whose entries are ALL empty (a stale [[],[],[],[],[]] left by an
|
|
109
|
+
// older "configure each set independently" session that a later "apply to all sets" save could not clear --
|
|
110
|
+
// the pre-fix write omitted the column instead of nulling it) silently benches the libero: the sim picks
|
|
111
|
+
// `sets?.[i] ?? liberoReplacements`, and an empty array is not nullish, so every set gets an empty target
|
|
112
|
+
// list and the libero never enters the court. Treat an all-empty per-set list as absent so the sim falls
|
|
113
|
+
// back to the flat `liberoReplacements`; the next tactics save rewrites the column cleanly.
|
|
114
|
+
const mappedLiberoReplacementSets = model.libero_replacement_sets?.map(set => set.map((id) => findPlayer(id, roster)));
|
|
115
|
+
const liberoReplacementSets = mappedLiberoReplacementSets?.some(set => set.length > 0) === true
|
|
116
|
+
? mappedLiberoReplacementSets
|
|
117
|
+
: undefined;
|
|
108
118
|
return service_1.Tactics.create({
|
|
109
119
|
receiveRotationOffset: model.receive_rotation_offset,
|
|
110
120
|
lineup,
|
|
@@ -112,7 +122,7 @@ function transformToObject(model, roster) {
|
|
|
112
122
|
substitutionBand: model.substitution_band,
|
|
113
123
|
secondLibero,
|
|
114
124
|
liberoSub: secondLibero != null ? (model.libero_sub ?? undefined) : undefined,
|
|
115
|
-
liberoReplacementSets
|
|
125
|
+
liberoReplacementSets,
|
|
116
126
|
designatedSubs: (model.designated_subs ?? []).map(d => ({
|
|
117
127
|
starter: findPlayer(d.starterId, roster),
|
|
118
128
|
bench: d.benchId != null ? findPlayer(d.benchId, roster) : undefined,
|
|
@@ -160,6 +170,12 @@ function tacticsColumnsToApiDto(model) {
|
|
|
160
170
|
const validSecondLibero = (model.second_libero != null && model.lineup[service_1.CourtPosition.LIBERO_ZONE] != null && !lineupIds.has(model.second_libero))
|
|
161
171
|
? model.second_libero
|
|
162
172
|
: undefined;
|
|
173
|
+
// Same all-empty heal as transformToObject: an all-empty per-set `libero_replacement_sets` is a stale
|
|
174
|
+
// artifact that shadows the flat `liberoReplacements` in the sim, so don't surface it to the API/preset
|
|
175
|
+
// shape -- otherwise the UI would reload it as "configure each set independently" (all blank) and re-emit it.
|
|
176
|
+
const validLiberoReplacementSets = model.libero_replacement_sets?.some(set => set.length > 0) === true
|
|
177
|
+
? model.libero_replacement_sets
|
|
178
|
+
: undefined;
|
|
163
179
|
return {
|
|
164
180
|
lineup: model.lineup,
|
|
165
181
|
liberoReplacements: model.libero_replacements,
|
|
@@ -167,7 +183,7 @@ function tacticsColumnsToApiDto(model) {
|
|
|
167
183
|
substitutionBand: model.substitution_band,
|
|
168
184
|
secondLibero: validSecondLibero,
|
|
169
185
|
liberoSub: validSecondLibero != null ? model.libero_sub : undefined,
|
|
170
|
-
liberoReplacementSets:
|
|
186
|
+
liberoReplacementSets: validLiberoReplacementSets,
|
|
171
187
|
designatedSubs: (model.designated_subs ?? []).map(d => ({
|
|
172
188
|
starterId: d.starterId,
|
|
173
189
|
benchId: d.benchId,
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
"chance": 0.25,
|
|
6
6
|
"roles": [
|
|
7
7
|
"SETTER",
|
|
8
|
-
"LIBERO"
|
|
8
|
+
"LIBERO",
|
|
9
|
+
"OUTSIDE_HITTER"
|
|
9
10
|
]
|
|
10
11
|
},
|
|
11
12
|
{
|
|
@@ -25,7 +26,8 @@
|
|
|
25
26
|
"roles": [
|
|
26
27
|
"MIDDLE_BLOCKER",
|
|
27
28
|
"OPPOSITE_HITTER",
|
|
28
|
-
"SETTER"
|
|
29
|
+
"SETTER",
|
|
30
|
+
"OUTSIDE_HITTER"
|
|
29
31
|
]
|
|
30
32
|
},
|
|
31
33
|
{
|
|
@@ -35,8 +37,7 @@
|
|
|
35
37
|
"roles": [
|
|
36
38
|
"SETTER",
|
|
37
39
|
"OPPOSITE_HITTER",
|
|
38
|
-
"OUTSIDE_HITTER"
|
|
39
|
-
"MIDDLE_BLOCKER"
|
|
40
|
+
"OUTSIDE_HITTER"
|
|
40
41
|
]
|
|
41
42
|
},
|
|
42
43
|
{
|
|
@@ -47,6 +47,8 @@ export type Match = Omit<DataProps<_Match>, 'sets' | 'homeTeam' | 'awayTeam' | '
|
|
|
47
47
|
breakdown?: PointBreakdown;
|
|
48
48
|
homeForm?: MatchForm[];
|
|
49
49
|
awayForm?: MatchForm[];
|
|
50
|
+
isFriendly?: boolean;
|
|
51
|
+
detailUnavailable?: boolean;
|
|
50
52
|
};
|
|
51
53
|
export type RallyEvent = DataProps<_RallyEvent> & {
|
|
52
54
|
teamId?: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthIdentityModel, AuthSessionModel, AuthUserModel, BoxScoreModel, CoachModel, ScoutModel, BoxScoreTotalsModel, CompetitionChampionModel, CurrencyTransactionModel, GachaPityModel, GachaPullHistoryModel, WishlistModel, UserSettingsModel, CompetitionMatchModel, CompetitionMVPModel, CompetitionModel, CompetitionStandingsMatchModel, CompetitionStandingsModel, CompetitionTeamsModel, CountryModel, DivisionModel, DivisionSeasonModel, IterationModel, LeagueModel, MatchModel, MatchRatingModel, MatchResultModel, MatchSetModel, PerformanceStatsModel, PlayerModel, PlayerImprovementLogModel, PlayerTeamModel, RetiredPlayerModel, PromotionMatchModel, RegionQualifierModel, NationalCountryModel, RallyModel, TacticsModel, LineupPresetModel, TeamModel, TeamReplacementModel, UserCurrencyModel, UserPlayerProgressModel, UserPullGrantModel, UserTeamModel, VPERModel, NotificationModel, AppConfigModel, LegacyTeamFlagModel } from './models';
|
|
1
|
+
import { AuthIdentityModel, AuthSessionModel, AuthUserModel, BoxScoreModel, CoachModel, ScoutModel, BoxScoreTotalsModel, CompetitionChampionModel, CurrencyTransactionModel, GachaPityModel, GachaPullHistoryModel, WishlistModel, UserSettingsModel, CompetitionMatchModel, CompetitionMVPModel, CompetitionModel, CompetitionStandingsMatchModel, CompetitionStandingsModel, CompetitionTeamsModel, CountryModel, DivisionModel, DivisionSeasonModel, IterationModel, LeagueModel, MatchModel, MatchRatingModel, MatchResultModel, MatchSetModel, PerformanceStatsModel, PlayerModel, PlayerImprovementLogModel, PlayerTeamModel, RetiredPlayerModel, PromotionMatchModel, FriendlyModel, RegionQualifierModel, NationalCountryModel, RallyModel, TacticsModel, LineupPresetModel, TeamModel, TeamReplacementModel, UserCurrencyModel, UserPlayerProgressModel, UserPullGrantModel, UserTeamModel, VPERModel, NotificationModel, AppConfigModel, LegacyTeamFlagModel } from './models';
|
|
2
2
|
export function initModels(sequelize) {
|
|
3
3
|
const Coach = CoachModel.initModel(sequelize);
|
|
4
4
|
const Scout = ScoutModel.initModel(sequelize);
|
|
@@ -42,6 +42,7 @@ export function initModels(sequelize) {
|
|
|
42
42
|
const RetiredPlayer = RetiredPlayerModel.initModel(sequelize);
|
|
43
43
|
const Rally = RallyModel.initModel(sequelize);
|
|
44
44
|
const PromotionMatch = PromotionMatchModel.initModel(sequelize);
|
|
45
|
+
const Friendly = FriendlyModel.initModel(sequelize);
|
|
45
46
|
const RegionQualifier = RegionQualifierModel.initModel(sequelize);
|
|
46
47
|
const NationalCountry = NationalCountryModel.initModel(sequelize);
|
|
47
48
|
const Team = TeamModel.initModel(sequelize);
|
|
@@ -129,6 +130,7 @@ export function initModels(sequelize) {
|
|
|
129
130
|
Match.hasMany(MatchSet, { as: 'MatchSets', foreignKey: 'match_id' });
|
|
130
131
|
Match.hasOne(CompetitionMatch, { as: 'CompetitionMatch', foreignKey: 'match_id' });
|
|
131
132
|
Match.hasOne(MatchResult, { as: 'MatchResult', foreignKey: 'match_id' });
|
|
133
|
+
Match.hasOne(Friendly, { as: 'Friendly', foreignKey: 'match_id' });
|
|
132
134
|
MatchResult.belongsTo(Match, { as: 'Match', foreignKey: 'match_id' });
|
|
133
135
|
MatchResult.belongsTo(Team, { as: 'Winner', foreignKey: 'winner_team_id' });
|
|
134
136
|
MatchRating.belongsTo(Match, { as: 'match', foreignKey: 'match_id' });
|
|
@@ -180,6 +182,8 @@ export function initModels(sequelize) {
|
|
|
180
182
|
PromotionMatch.belongsTo(Match, { as: 'Match', foreignKey: 'match_id' });
|
|
181
183
|
PromotionMatch.belongsTo(Competition, { as: 'upperCompetition', foreignKey: 'upper_competition' });
|
|
182
184
|
PromotionMatch.belongsTo(Competition, { as: 'lowerCompetition', foreignKey: 'lower_competition' });
|
|
185
|
+
Friendly.belongsTo(Iteration, { as: 'iterationData', foreignKey: 'iteration' });
|
|
186
|
+
Friendly.belongsTo(Match, { as: 'Match', foreignKey: 'match_id' });
|
|
183
187
|
RegionQualifier.belongsTo(Iteration, { as: 'Iteration', foreignKey: 'iteration' });
|
|
184
188
|
RegionQualifier.belongsTo(Country, { as: 'countryRegion', foreignKey: 'region_code', targetKey: 'region_code' });
|
|
185
189
|
RegionQualifier.belongsTo(Competition, { as: 'competition', foreignKey: 'competition_id' });
|
|
@@ -266,6 +270,7 @@ export function initModels(sequelize) {
|
|
|
266
270
|
RetiredPlayer,
|
|
267
271
|
PlayerImprovementLog,
|
|
268
272
|
PromotionMatch,
|
|
273
|
+
Friendly,
|
|
269
274
|
RegionQualifier,
|
|
270
275
|
NationalCountry,
|
|
271
276
|
Rally,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as Sequelize from 'sequelize';
|
|
2
|
+
import { Model, Optional } from 'sequelize';
|
|
3
|
+
import { IterationId, IterationModel, MatchAttributes, MatchId, MatchModel } from '.';
|
|
4
|
+
export interface FriendlyAttributes {
|
|
5
|
+
match_id: string;
|
|
6
|
+
iteration: number;
|
|
7
|
+
wave_key: string;
|
|
8
|
+
purged_at: Date | null;
|
|
9
|
+
match?: MatchAttributes;
|
|
10
|
+
}
|
|
11
|
+
export type FriendlyPk = 'match_id';
|
|
12
|
+
export type FriendlyId = FriendlyModel[FriendlyPk];
|
|
13
|
+
export type FriendlyCreationAttributes = Optional<FriendlyAttributes, 'purged_at'>;
|
|
14
|
+
export declare class FriendlyModel extends Model<FriendlyAttributes, FriendlyCreationAttributes> implements FriendlyAttributes {
|
|
15
|
+
match_id: string;
|
|
16
|
+
iteration: number;
|
|
17
|
+
wave_key: string;
|
|
18
|
+
purged_at: Date | null;
|
|
19
|
+
Match: MatchModel;
|
|
20
|
+
getMatch: Sequelize.BelongsToGetAssociationMixin<MatchModel>;
|
|
21
|
+
setMatch: Sequelize.BelongsToSetAssociationMixin<MatchModel, MatchId>;
|
|
22
|
+
createMatch: Sequelize.BelongsToCreateAssociationMixin<MatchModel>;
|
|
23
|
+
iterationData: IterationModel;
|
|
24
|
+
getIterationData: Sequelize.BelongsToGetAssociationMixin<IterationModel>;
|
|
25
|
+
setIterationData: Sequelize.BelongsToSetAssociationMixin<IterationModel, IterationId>;
|
|
26
|
+
createIterationData: Sequelize.BelongsToCreateAssociationMixin<IterationModel>;
|
|
27
|
+
static initModel(sequelize: Sequelize.Sequelize): typeof FriendlyModel;
|
|
28
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { DataTypes, Model } from 'sequelize';
|
|
2
|
+
export class FriendlyModel extends Model {
|
|
3
|
+
static initModel(sequelize) {
|
|
4
|
+
return FriendlyModel.init({
|
|
5
|
+
match_id: {
|
|
6
|
+
type: DataTypes.UUID,
|
|
7
|
+
allowNull: false,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
references: {
|
|
10
|
+
model: 'Match',
|
|
11
|
+
key: 'match_id'
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
iteration: {
|
|
15
|
+
type: DataTypes.INTEGER,
|
|
16
|
+
allowNull: false,
|
|
17
|
+
references: {
|
|
18
|
+
model: 'Iteration',
|
|
19
|
+
key: 'iteration_id'
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
wave_key: {
|
|
23
|
+
type: DataTypes.TEXT,
|
|
24
|
+
allowNull: false
|
|
25
|
+
},
|
|
26
|
+
purged_at: {
|
|
27
|
+
type: DataTypes.DATE,
|
|
28
|
+
allowNull: true
|
|
29
|
+
}
|
|
30
|
+
}, {
|
|
31
|
+
sequelize,
|
|
32
|
+
tableName: 'Friendly',
|
|
33
|
+
schema: 'public',
|
|
34
|
+
timestamps: false
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -26,6 +26,7 @@ export * from './player-team';
|
|
|
26
26
|
export * from './retired-player';
|
|
27
27
|
export * from './rally';
|
|
28
28
|
export * from './promotion-match';
|
|
29
|
+
export * from './friendly';
|
|
29
30
|
export * from './region-qualifier';
|
|
30
31
|
export * from './national-country';
|
|
31
32
|
export * from './coach';
|
|
@@ -26,6 +26,7 @@ export * from './player-team';
|
|
|
26
26
|
export * from './retired-player';
|
|
27
27
|
export * from './rally';
|
|
28
28
|
export * from './promotion-match';
|
|
29
|
+
export * from './friendly';
|
|
29
30
|
export * from './region-qualifier';
|
|
30
31
|
export * from './national-country';
|
|
31
32
|
export * from './coach';
|
|
@@ -100,6 +100,16 @@ function transformToObject(model, roster) {
|
|
|
100
100
|
const secondLibero = (hasStartingLibero && model.second_libero != null && !inLineupIds.has(model.second_libero))
|
|
101
101
|
? roster.find((p) => p.id === model.second_libero)
|
|
102
102
|
: undefined;
|
|
103
|
+
// A persisted `libero_replacement_sets` whose entries are ALL empty (a stale [[],[],[],[],[]] left by an
|
|
104
|
+
// older "configure each set independently" session that a later "apply to all sets" save could not clear --
|
|
105
|
+
// the pre-fix write omitted the column instead of nulling it) silently benches the libero: the sim picks
|
|
106
|
+
// `sets?.[i] ?? liberoReplacements`, and an empty array is not nullish, so every set gets an empty target
|
|
107
|
+
// list and the libero never enters the court. Treat an all-empty per-set list as absent so the sim falls
|
|
108
|
+
// back to the flat `liberoReplacements`; the next tactics save rewrites the column cleanly.
|
|
109
|
+
const mappedLiberoReplacementSets = model.libero_replacement_sets?.map(set => set.map((id) => findPlayer(id, roster)));
|
|
110
|
+
const liberoReplacementSets = mappedLiberoReplacementSets?.some(set => set.length > 0) === true
|
|
111
|
+
? mappedLiberoReplacementSets
|
|
112
|
+
: undefined;
|
|
103
113
|
return Tactics.create({
|
|
104
114
|
receiveRotationOffset: model.receive_rotation_offset,
|
|
105
115
|
lineup,
|
|
@@ -107,7 +117,7 @@ function transformToObject(model, roster) {
|
|
|
107
117
|
substitutionBand: model.substitution_band,
|
|
108
118
|
secondLibero,
|
|
109
119
|
liberoSub: secondLibero != null ? (model.libero_sub ?? undefined) : undefined,
|
|
110
|
-
liberoReplacementSets
|
|
120
|
+
liberoReplacementSets,
|
|
111
121
|
designatedSubs: (model.designated_subs ?? []).map(d => ({
|
|
112
122
|
starter: findPlayer(d.starterId, roster),
|
|
113
123
|
bench: d.benchId != null ? findPlayer(d.benchId, roster) : undefined,
|
|
@@ -155,6 +165,12 @@ function tacticsColumnsToApiDto(model) {
|
|
|
155
165
|
const validSecondLibero = (model.second_libero != null && model.lineup[CourtPosition.LIBERO_ZONE] != null && !lineupIds.has(model.second_libero))
|
|
156
166
|
? model.second_libero
|
|
157
167
|
: undefined;
|
|
168
|
+
// Same all-empty heal as transformToObject: an all-empty per-set `libero_replacement_sets` is a stale
|
|
169
|
+
// artifact that shadows the flat `liberoReplacements` in the sim, so don't surface it to the API/preset
|
|
170
|
+
// shape -- otherwise the UI would reload it as "configure each set independently" (all blank) and re-emit it.
|
|
171
|
+
const validLiberoReplacementSets = model.libero_replacement_sets?.some(set => set.length > 0) === true
|
|
172
|
+
? model.libero_replacement_sets
|
|
173
|
+
: undefined;
|
|
158
174
|
return {
|
|
159
175
|
lineup: model.lineup,
|
|
160
176
|
liberoReplacements: model.libero_replacements,
|
|
@@ -162,7 +178,7 @@ function tacticsColumnsToApiDto(model) {
|
|
|
162
178
|
substitutionBand: model.substitution_band,
|
|
163
179
|
secondLibero: validSecondLibero,
|
|
164
180
|
liberoSub: validSecondLibero != null ? model.libero_sub : undefined,
|
|
165
|
-
liberoReplacementSets:
|
|
181
|
+
liberoReplacementSets: validLiberoReplacementSets,
|
|
166
182
|
designatedSubs: (model.designated_subs ?? []).map(d => ({
|
|
167
183
|
starterId: d.starterId,
|
|
168
184
|
benchId: d.benchId,
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
"chance": 0.25,
|
|
6
6
|
"roles": [
|
|
7
7
|
"SETTER",
|
|
8
|
-
"LIBERO"
|
|
8
|
+
"LIBERO",
|
|
9
|
+
"OUTSIDE_HITTER"
|
|
9
10
|
]
|
|
10
11
|
},
|
|
11
12
|
{
|
|
@@ -25,7 +26,8 @@
|
|
|
25
26
|
"roles": [
|
|
26
27
|
"MIDDLE_BLOCKER",
|
|
27
28
|
"OPPOSITE_HITTER",
|
|
28
|
-
"SETTER"
|
|
29
|
+
"SETTER",
|
|
30
|
+
"OUTSIDE_HITTER"
|
|
29
31
|
]
|
|
30
32
|
},
|
|
31
33
|
{
|
|
@@ -35,8 +37,7 @@
|
|
|
35
37
|
"roles": [
|
|
36
38
|
"SETTER",
|
|
37
39
|
"OPPOSITE_HITTER",
|
|
38
|
-
"OUTSIDE_HITTER"
|
|
39
|
-
"MIDDLE_BLOCKER"
|
|
40
|
+
"OUTSIDE_HITTER"
|
|
40
41
|
]
|
|
41
42
|
},
|
|
42
43
|
{
|