volleyballsimtypes 0.0.489 → 0.0.492
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 +4 -3
- package/dist/cjs/src/api/index.js +15 -0
- package/dist/cjs/src/api/tactics-player-refs.d.ts +13 -0
- package/dist/cjs/src/api/tactics-player-refs.js +43 -0
- package/dist/cjs/src/data/event-purge.d.ts +14 -0
- package/dist/cjs/src/data/event-purge.js +116 -0
- package/dist/cjs/src/data/index.d.ts +1 -0
- package/dist/cjs/src/data/index.js +1 -0
- package/dist/cjs/src/data/init-models.js +41 -0
- package/dist/cjs/src/data/models/competition.d.ts +1 -1
- package/dist/cjs/src/data/models/competition.js +1 -1
- package/dist/cjs/src/data/models/event-bot-team.d.ts +21 -0
- package/dist/cjs/src/data/models/event-bot-team.js +44 -0
- package/dist/cjs/src/data/models/event-competition.d.ts +29 -0
- package/dist/cjs/src/data/models/event-competition.js +52 -0
- package/dist/cjs/src/data/models/event-pack-player.d.ts +22 -0
- package/dist/cjs/src/data/models/event-pack-player.js +31 -0
- package/dist/cjs/src/data/models/event-pack.d.ts +37 -0
- package/dist/cjs/src/data/models/event-pack.js +55 -0
- package/dist/cjs/src/data/models/event-result.d.ts +34 -0
- package/dist/cjs/src/data/models/event-result.js +48 -0
- package/dist/cjs/src/data/models/event-squad.d.ts +30 -0
- package/dist/cjs/src/data/models/event-squad.js +46 -0
- package/dist/cjs/src/data/models/event.d.ts +51 -0
- package/dist/cjs/src/data/models/event.js +82 -0
- package/dist/cjs/src/data/models/index.d.ts +7 -0
- package/dist/cjs/src/data/models/index.js +7 -0
- package/dist/cjs/src/data/models/lineup-preset.d.ts +3 -1
- package/dist/cjs/src/data/models/lineup-preset.js +53 -0
- package/dist/cjs/src/data/models/tactics.d.ts +2 -2
- package/dist/cjs/src/data/transformers/tactics.test.js +19 -0
- package/dist/cjs/src/index.d.ts +1 -0
- package/dist/cjs/src/index.js +6 -0
- package/dist/cjs/src/service/competition/event-draft.d.ts +46 -0
- package/dist/cjs/src/service/competition/event-draft.js +85 -0
- package/dist/cjs/src/service/competition/event-draft.test.d.ts +1 -0
- package/dist/cjs/src/service/competition/event-draft.test.js +91 -0
- package/dist/cjs/src/service/competition/event-rewards.d.ts +26 -0
- package/dist/cjs/src/service/competition/event-rewards.js +49 -0
- package/dist/cjs/src/service/competition/event-rewards.test.d.ts +1 -0
- package/dist/cjs/src/service/competition/event-rewards.test.js +58 -0
- package/dist/cjs/src/service/competition/event-schedule.d.ts +126 -0
- package/dist/cjs/src/service/competition/event-schedule.js +141 -0
- package/dist/cjs/src/service/competition/event-schedule.test.d.ts +1 -0
- package/dist/cjs/src/service/competition/event-schedule.test.js +138 -0
- package/dist/cjs/src/service/competition/index.d.ts +3 -0
- package/dist/cjs/src/service/competition/index.js +3 -0
- package/dist/cjs/src/service/match/index.d.ts +1 -0
- package/dist/cjs/src/service/match/index.js +1 -0
- package/dist/cjs/src/service/match/match-generator.js +25 -48
- package/dist/cjs/src/service/match/match-generator.test.d.ts +1 -0
- package/dist/cjs/src/service/match/match-generator.test.js +48 -0
- package/dist/cjs/src/service/match/round-robin.d.ts +11 -0
- package/dist/cjs/src/service/match/round-robin.js +44 -0
- package/dist/cjs/src/service/match/round-robin.test.d.ts +1 -0
- package/dist/cjs/src/service/match/round-robin.test.js +71 -0
- package/dist/cjs/src/service/notification/catalog/en.json +21 -0
- package/dist/cjs/src/service/notification/catalog/es.json +21 -0
- package/dist/cjs/src/service/notification/catalog/fr.json +21 -0
- package/dist/cjs/src/service/notification/catalog/it.json +21 -0
- package/dist/cjs/src/service/notification/catalog/pl.json +21 -0
- package/dist/cjs/src/service/notification/catalog/pt-BR.json +21 -0
- package/dist/cjs/src/service/notification/catalog/tr.json +21 -0
- package/dist/cjs/src/service/notification/notification-type.d.ts +1 -1
- package/dist/cjs/src/service/notification/notification-type.js +6 -1
- package/dist/cjs/src/service/notification/notification.test.js +3 -1
- package/dist/cjs/src/service/notification/registry.js +52 -0
- package/dist/cjs/src/service/player/player-generator.d.ts +1 -0
- package/dist/cjs/src/service/player/player-generator.js +16 -1
- package/dist/cjs/src/service/team/blocking-preference.d.ts +3 -1
- package/dist/cjs/src/service/team/blocking-preference.js +7 -0
- package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +2 -2
- package/dist/cjs/src/service/team/schemas/tactics.z.js +7 -2
- package/dist/cjs/src/service/team/schemas/tactics.z.test.js +32 -0
- package/dist/cjs/src/service/team/schemas/team.z.d.ts +2 -2
- package/dist/esm/src/api/index.d.ts +4 -3
- package/dist/esm/src/api/index.js +1 -0
- package/dist/esm/src/api/tactics-player-refs.d.ts +13 -0
- package/dist/esm/src/api/tactics-player-refs.js +40 -0
- package/dist/esm/src/data/event-purge.d.ts +14 -0
- package/dist/esm/src/data/event-purge.js +110 -0
- package/dist/esm/src/data/index.d.ts +1 -0
- package/dist/esm/src/data/index.js +1 -0
- package/dist/esm/src/data/init-models.js +42 -1
- package/dist/esm/src/data/models/competition.d.ts +1 -1
- package/dist/esm/src/data/models/competition.js +1 -1
- package/dist/esm/src/data/models/event-bot-team.d.ts +21 -0
- package/dist/esm/src/data/models/event-bot-team.js +40 -0
- package/dist/esm/src/data/models/event-competition.d.ts +29 -0
- package/dist/esm/src/data/models/event-competition.js +48 -0
- package/dist/esm/src/data/models/event-pack-player.d.ts +22 -0
- package/dist/esm/src/data/models/event-pack-player.js +27 -0
- package/dist/esm/src/data/models/event-pack.d.ts +37 -0
- package/dist/esm/src/data/models/event-pack.js +51 -0
- package/dist/esm/src/data/models/event-result.d.ts +34 -0
- package/dist/esm/src/data/models/event-result.js +44 -0
- package/dist/esm/src/data/models/event-squad.d.ts +30 -0
- package/dist/esm/src/data/models/event-squad.js +42 -0
- package/dist/esm/src/data/models/event.d.ts +51 -0
- package/dist/esm/src/data/models/event.js +78 -0
- package/dist/esm/src/data/models/index.d.ts +7 -0
- package/dist/esm/src/data/models/index.js +7 -0
- package/dist/esm/src/data/models/lineup-preset.d.ts +3 -1
- package/dist/esm/src/data/models/lineup-preset.js +20 -0
- package/dist/esm/src/data/models/tactics.d.ts +2 -2
- package/dist/esm/src/data/transformers/tactics.test.js +20 -1
- package/dist/esm/src/index.d.ts +1 -0
- package/dist/esm/src/index.js +4 -0
- package/dist/esm/src/service/competition/event-draft.d.ts +46 -0
- package/dist/esm/src/service/competition/event-draft.js +78 -0
- package/dist/esm/src/service/competition/event-draft.test.d.ts +1 -0
- package/dist/esm/src/service/competition/event-draft.test.js +89 -0
- package/dist/esm/src/service/competition/event-rewards.d.ts +26 -0
- package/dist/esm/src/service/competition/event-rewards.js +44 -0
- package/dist/esm/src/service/competition/event-rewards.test.d.ts +1 -0
- package/dist/esm/src/service/competition/event-rewards.test.js +56 -0
- package/dist/esm/src/service/competition/event-schedule.d.ts +126 -0
- package/dist/esm/src/service/competition/event-schedule.js +129 -0
- package/dist/esm/src/service/competition/event-schedule.test.d.ts +1 -0
- package/dist/esm/src/service/competition/event-schedule.test.js +136 -0
- package/dist/esm/src/service/competition/index.d.ts +3 -0
- package/dist/esm/src/service/competition/index.js +3 -0
- package/dist/esm/src/service/match/index.d.ts +1 -0
- package/dist/esm/src/service/match/index.js +1 -0
- package/dist/esm/src/service/match/match-generator.js +25 -48
- package/dist/esm/src/service/match/match-generator.test.d.ts +1 -0
- package/dist/esm/src/service/match/match-generator.test.js +43 -0
- package/dist/esm/src/service/match/round-robin.d.ts +11 -0
- package/dist/esm/src/service/match/round-robin.js +41 -0
- package/dist/esm/src/service/match/round-robin.test.d.ts +1 -0
- package/dist/esm/src/service/match/round-robin.test.js +69 -0
- package/dist/esm/src/service/notification/catalog/en.json +21 -0
- package/dist/esm/src/service/notification/catalog/es.json +21 -0
- package/dist/esm/src/service/notification/catalog/fr.json +21 -0
- package/dist/esm/src/service/notification/catalog/it.json +21 -0
- package/dist/esm/src/service/notification/catalog/pl.json +21 -0
- package/dist/esm/src/service/notification/catalog/pt-BR.json +21 -0
- package/dist/esm/src/service/notification/catalog/tr.json +21 -0
- package/dist/esm/src/service/notification/notification-type.d.ts +1 -1
- package/dist/esm/src/service/notification/notification-type.js +6 -1
- package/dist/esm/src/service/notification/notification.test.js +3 -1
- package/dist/esm/src/service/notification/registry.js +52 -0
- package/dist/esm/src/service/player/player-generator.d.ts +1 -0
- package/dist/esm/src/service/player/player-generator.js +15 -0
- package/dist/esm/src/service/team/blocking-preference.d.ts +3 -1
- package/dist/esm/src/service/team/blocking-preference.js +6 -1
- package/dist/esm/src/service/team/schemas/tactics.z.d.ts +2 -2
- package/dist/esm/src/service/team/schemas/tactics.z.js +7 -2
- package/dist/esm/src/service/team/schemas/tactics.z.test.js +32 -0
- package/dist/esm/src/service/team/schemas/team.z.d.ts +2 -2
- package/package.json +6 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as Sequelize from 'sequelize';
|
|
2
|
+
import { Model, Optional } from 'sequelize';
|
|
3
|
+
import { EventId, EventModel, EventPackPlayerModel, PlayerId, PlayerModel } from '.';
|
|
4
|
+
export interface EventPackAttributes {
|
|
5
|
+
pack_id: string;
|
|
6
|
+
event_id: string;
|
|
7
|
+
user_id: string;
|
|
8
|
+
/** Position in DRAFT_PLAN, so the pack's role and the draft's progress are both derivable. */
|
|
9
|
+
index: number;
|
|
10
|
+
/** The role this pack offers. A free pack stores the role it actually rolled, not 'ANY'. */
|
|
11
|
+
role: string;
|
|
12
|
+
opened_at: Date;
|
|
13
|
+
/** Null until the entrant picks; set once, and the three unpicked players are deleted at that moment. */
|
|
14
|
+
picked_player_id: string | null;
|
|
15
|
+
}
|
|
16
|
+
export type EventPackPk = 'pack_id';
|
|
17
|
+
export type EventPackId = EventPackModel[EventPackPk];
|
|
18
|
+
export type EventPackOptionalAttributes = 'pack_id' | 'opened_at' | 'picked_player_id';
|
|
19
|
+
export type EventPackCreationAttributes = Optional<EventPackAttributes, EventPackOptionalAttributes>;
|
|
20
|
+
export declare class EventPackModel extends Model<EventPackAttributes, EventPackCreationAttributes> implements EventPackAttributes {
|
|
21
|
+
pack_id: string;
|
|
22
|
+
event_id: string;
|
|
23
|
+
user_id: string;
|
|
24
|
+
index: number;
|
|
25
|
+
role: string;
|
|
26
|
+
opened_at: Date;
|
|
27
|
+
picked_player_id: string | null;
|
|
28
|
+
Event: EventModel;
|
|
29
|
+
getEvent: Sequelize.BelongsToGetAssociationMixin<EventModel>;
|
|
30
|
+
setEvent: Sequelize.BelongsToSetAssociationMixin<EventModel, EventId>;
|
|
31
|
+
choices: EventPackPlayerModel[];
|
|
32
|
+
getChoices: Sequelize.HasManyGetAssociationsMixin<EventPackPlayerModel>;
|
|
33
|
+
picked: PlayerModel;
|
|
34
|
+
getPicked: Sequelize.BelongsToGetAssociationMixin<PlayerModel>;
|
|
35
|
+
setPicked: Sequelize.BelongsToSetAssociationMixin<PlayerModel, PlayerId>;
|
|
36
|
+
static initModel(sequelize: Sequelize.Sequelize): typeof EventPackModel;
|
|
37
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { DataTypes, Model } from 'sequelize';
|
|
2
|
+
export class EventPackModel extends Model {
|
|
3
|
+
static initModel(sequelize) {
|
|
4
|
+
return EventPackModel.init({
|
|
5
|
+
pack_id: {
|
|
6
|
+
type: DataTypes.UUID,
|
|
7
|
+
allowNull: false,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
defaultValue: DataTypes.UUIDV4
|
|
10
|
+
},
|
|
11
|
+
event_id: {
|
|
12
|
+
type: DataTypes.UUID,
|
|
13
|
+
allowNull: false,
|
|
14
|
+
references: { model: 'Event', key: 'event_id' }
|
|
15
|
+
},
|
|
16
|
+
user_id: {
|
|
17
|
+
type: DataTypes.UUID,
|
|
18
|
+
allowNull: false,
|
|
19
|
+
references: { model: 'AuthUser', key: 'user_id' }
|
|
20
|
+
},
|
|
21
|
+
index: {
|
|
22
|
+
type: DataTypes.INTEGER,
|
|
23
|
+
allowNull: false
|
|
24
|
+
},
|
|
25
|
+
role: {
|
|
26
|
+
type: DataTypes.TEXT,
|
|
27
|
+
allowNull: false
|
|
28
|
+
},
|
|
29
|
+
opened_at: {
|
|
30
|
+
type: DataTypes.DATE,
|
|
31
|
+
allowNull: false,
|
|
32
|
+
defaultValue: DataTypes.NOW
|
|
33
|
+
},
|
|
34
|
+
picked_player_id: {
|
|
35
|
+
type: DataTypes.UUID,
|
|
36
|
+
allowNull: true,
|
|
37
|
+
references: { model: 'Player', key: 'player_id' }
|
|
38
|
+
}
|
|
39
|
+
}, {
|
|
40
|
+
sequelize,
|
|
41
|
+
tableName: 'EventPack',
|
|
42
|
+
schema: 'public',
|
|
43
|
+
timestamps: false,
|
|
44
|
+
indexes: [
|
|
45
|
+
{ name: 'EventPack_pk', unique: true, fields: [{ name: 'pack_id' }] },
|
|
46
|
+
// One pack per position per entrant: reopening returns the stored pack instead of generating a new one.
|
|
47
|
+
{ name: 'EventPack_one_per_index', unique: true, fields: [{ name: 'event_id' }, { name: 'user_id' }, { name: 'index' }] }
|
|
48
|
+
]
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as Sequelize from 'sequelize';
|
|
2
|
+
import { Model } from 'sequelize';
|
|
3
|
+
import type { EventKey } from '../../service/competition/event-schedule';
|
|
4
|
+
import { EventId, EventModel, TeamAttributes, TeamId, TeamModel } from '.';
|
|
5
|
+
export interface EventResultAttributes {
|
|
6
|
+
event_id: string;
|
|
7
|
+
/** The user's permanent team, so their accolades page can find it. */
|
|
8
|
+
team_id: string;
|
|
9
|
+
/** Place in the FINAL table, or null for an entrant who never reached it (group stage). */
|
|
10
|
+
position: number | null;
|
|
11
|
+
entrants: number;
|
|
12
|
+
key: EventKey;
|
|
13
|
+
ended_at: Date;
|
|
14
|
+
Event?: EventModel;
|
|
15
|
+
team?: TeamAttributes;
|
|
16
|
+
}
|
|
17
|
+
export type EventResultPk = 'event_id' | 'team_id';
|
|
18
|
+
export type EventResultId = EventResultModel[EventResultPk];
|
|
19
|
+
export type EventResultCreationAttributes = EventResultAttributes;
|
|
20
|
+
export declare class EventResultModel extends Model<EventResultAttributes, EventResultCreationAttributes> implements EventResultAttributes {
|
|
21
|
+
event_id: string;
|
|
22
|
+
team_id: string;
|
|
23
|
+
position: number | null;
|
|
24
|
+
entrants: number;
|
|
25
|
+
key: EventKey;
|
|
26
|
+
ended_at: Date;
|
|
27
|
+
Event: EventModel;
|
|
28
|
+
getEvent: Sequelize.BelongsToGetAssociationMixin<EventModel>;
|
|
29
|
+
setEvent: Sequelize.BelongsToSetAssociationMixin<EventModel, EventId>;
|
|
30
|
+
team: TeamModel;
|
|
31
|
+
getTeam: Sequelize.BelongsToGetAssociationMixin<TeamModel>;
|
|
32
|
+
setTeam: Sequelize.BelongsToSetAssociationMixin<TeamModel, TeamId>;
|
|
33
|
+
static initModel(sequelize: Sequelize.Sequelize): typeof EventResultModel;
|
|
34
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { DataTypes, Model } from 'sequelize';
|
|
2
|
+
export class EventResultModel extends Model {
|
|
3
|
+
static initModel(sequelize) {
|
|
4
|
+
return EventResultModel.init({
|
|
5
|
+
event_id: {
|
|
6
|
+
type: DataTypes.UUID,
|
|
7
|
+
allowNull: false,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
references: { model: 'Event', key: 'event_id' }
|
|
10
|
+
},
|
|
11
|
+
team_id: {
|
|
12
|
+
type: DataTypes.UUID,
|
|
13
|
+
allowNull: false,
|
|
14
|
+
primaryKey: true,
|
|
15
|
+
references: { model: 'Team', key: 'team_id' }
|
|
16
|
+
},
|
|
17
|
+
position: {
|
|
18
|
+
type: DataTypes.INTEGER,
|
|
19
|
+
allowNull: true
|
|
20
|
+
},
|
|
21
|
+
entrants: {
|
|
22
|
+
type: DataTypes.INTEGER,
|
|
23
|
+
allowNull: false
|
|
24
|
+
},
|
|
25
|
+
key: {
|
|
26
|
+
type: DataTypes.TEXT,
|
|
27
|
+
allowNull: false
|
|
28
|
+
},
|
|
29
|
+
ended_at: {
|
|
30
|
+
type: DataTypes.DATE,
|
|
31
|
+
allowNull: false
|
|
32
|
+
}
|
|
33
|
+
}, {
|
|
34
|
+
sequelize,
|
|
35
|
+
tableName: 'EventResult',
|
|
36
|
+
schema: 'public',
|
|
37
|
+
timestamps: false,
|
|
38
|
+
indexes: [
|
|
39
|
+
{ name: 'EventResult_pk', unique: true, fields: [{ name: 'event_id' }, { name: 'team_id' }] },
|
|
40
|
+
{ name: 'EventResult_team_id_idx', fields: [{ name: 'team_id' }] }
|
|
41
|
+
]
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as Sequelize from 'sequelize';
|
|
2
|
+
import { Model, Optional } from 'sequelize';
|
|
3
|
+
import { EventId, EventModel, TeamAttributes, TeamId, TeamModel } from '.';
|
|
4
|
+
export interface EventSquadAttributes {
|
|
5
|
+
event_id: string;
|
|
6
|
+
user_id: string;
|
|
7
|
+
/** The throwaway Team (active = false) holding the drafted players. Deleted at purge. */
|
|
8
|
+
team_id: string;
|
|
9
|
+
complete: boolean;
|
|
10
|
+
drafted_at: Date | null;
|
|
11
|
+
team?: TeamAttributes;
|
|
12
|
+
}
|
|
13
|
+
export type EventSquadPk = 'event_id' | 'user_id';
|
|
14
|
+
export type EventSquadId = EventSquadModel[EventSquadPk];
|
|
15
|
+
export type EventSquadOptionalAttributes = 'complete' | 'drafted_at';
|
|
16
|
+
export type EventSquadCreationAttributes = Optional<EventSquadAttributes, EventSquadOptionalAttributes>;
|
|
17
|
+
export declare class EventSquadModel extends Model<EventSquadAttributes, EventSquadCreationAttributes> implements EventSquadAttributes {
|
|
18
|
+
event_id: string;
|
|
19
|
+
user_id: string;
|
|
20
|
+
team_id: string;
|
|
21
|
+
complete: boolean;
|
|
22
|
+
drafted_at: Date | null;
|
|
23
|
+
Event: EventModel;
|
|
24
|
+
getEvent: Sequelize.BelongsToGetAssociationMixin<EventModel>;
|
|
25
|
+
setEvent: Sequelize.BelongsToSetAssociationMixin<EventModel, EventId>;
|
|
26
|
+
team: TeamModel;
|
|
27
|
+
getTeam: Sequelize.BelongsToGetAssociationMixin<TeamModel>;
|
|
28
|
+
setTeam: Sequelize.BelongsToSetAssociationMixin<TeamModel, TeamId>;
|
|
29
|
+
static initModel(sequelize: Sequelize.Sequelize): typeof EventSquadModel;
|
|
30
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { DataTypes, Model } from 'sequelize';
|
|
2
|
+
export class EventSquadModel extends Model {
|
|
3
|
+
static initModel(sequelize) {
|
|
4
|
+
return EventSquadModel.init({
|
|
5
|
+
event_id: {
|
|
6
|
+
type: DataTypes.UUID,
|
|
7
|
+
allowNull: false,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
references: { model: 'Event', key: 'event_id' }
|
|
10
|
+
},
|
|
11
|
+
user_id: {
|
|
12
|
+
type: DataTypes.UUID,
|
|
13
|
+
allowNull: false,
|
|
14
|
+
primaryKey: true,
|
|
15
|
+
references: { model: 'AuthUser', key: 'user_id' }
|
|
16
|
+
},
|
|
17
|
+
team_id: {
|
|
18
|
+
type: DataTypes.UUID,
|
|
19
|
+
allowNull: false,
|
|
20
|
+
references: { model: 'Team', key: 'team_id' }
|
|
21
|
+
},
|
|
22
|
+
complete: {
|
|
23
|
+
type: DataTypes.BOOLEAN,
|
|
24
|
+
allowNull: false,
|
|
25
|
+
defaultValue: false
|
|
26
|
+
},
|
|
27
|
+
drafted_at: {
|
|
28
|
+
type: DataTypes.DATE,
|
|
29
|
+
allowNull: true
|
|
30
|
+
}
|
|
31
|
+
}, {
|
|
32
|
+
sequelize,
|
|
33
|
+
tableName: 'EventSquad',
|
|
34
|
+
schema: 'public',
|
|
35
|
+
timestamps: false,
|
|
36
|
+
indexes: [
|
|
37
|
+
{ name: 'EventSquad_pk', unique: true, fields: [{ name: 'event_id' }, { name: 'user_id' }] },
|
|
38
|
+
{ name: 'EventSquad_team_id_idx', fields: [{ name: 'team_id' }] }
|
|
39
|
+
]
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as Sequelize from 'sequelize';
|
|
2
|
+
import { Model, Optional } from 'sequelize';
|
|
3
|
+
import type { EventFormat, EventKey, EventStatus } from '../../service/competition/event-schedule';
|
|
4
|
+
import { EventCompetitionAttributes, EventCompetitionModel, EventSquadAttributes, EventSquadModel } from '.';
|
|
5
|
+
export interface EventAttributes {
|
|
6
|
+
event_id: string;
|
|
7
|
+
key: EventKey;
|
|
8
|
+
format: EventFormat;
|
|
9
|
+
/** The rarity every player in an entrant's lineup must be. */
|
|
10
|
+
restriction: string;
|
|
11
|
+
status: EventStatus;
|
|
12
|
+
registration_opens_at: Date;
|
|
13
|
+
registration_closes_at: Date;
|
|
14
|
+
starts_at: Date;
|
|
15
|
+
final_starts_at: Date;
|
|
16
|
+
ends_at: Date;
|
|
17
|
+
purge_at: Date;
|
|
18
|
+
/** Set when the event's data was deleted; null while it still has competitions and matches. */
|
|
19
|
+
purged_at: Date | null;
|
|
20
|
+
/**
|
|
21
|
+
* Which of this event's notifications have already gone out, keyed by notification type. The tick runs every
|
|
22
|
+
* ~60 seconds, so without a marker every phase transition would re-send on every pass.
|
|
23
|
+
*/
|
|
24
|
+
notified: Record<string, boolean>;
|
|
25
|
+
EventCompetitions?: EventCompetitionAttributes[];
|
|
26
|
+
EventSquads?: EventSquadAttributes[];
|
|
27
|
+
}
|
|
28
|
+
export type EventPk = 'event_id';
|
|
29
|
+
export type EventId = EventModel[EventPk];
|
|
30
|
+
export type EventOptionalAttributes = 'event_id' | 'purged_at' | 'notified';
|
|
31
|
+
export type EventCreationAttributes = Optional<EventAttributes, EventOptionalAttributes>;
|
|
32
|
+
export declare class EventModel extends Model<EventAttributes, EventCreationAttributes> implements EventAttributes {
|
|
33
|
+
event_id: string;
|
|
34
|
+
key: EventKey;
|
|
35
|
+
format: EventFormat;
|
|
36
|
+
restriction: string;
|
|
37
|
+
status: EventStatus;
|
|
38
|
+
registration_opens_at: Date;
|
|
39
|
+
registration_closes_at: Date;
|
|
40
|
+
starts_at: Date;
|
|
41
|
+
final_starts_at: Date;
|
|
42
|
+
ends_at: Date;
|
|
43
|
+
purge_at: Date;
|
|
44
|
+
purged_at: Date | null;
|
|
45
|
+
notified: Record<string, boolean>;
|
|
46
|
+
EventCompetitions: EventCompetitionModel[];
|
|
47
|
+
getEventCompetitions: Sequelize.HasManyGetAssociationsMixin<EventCompetitionModel>;
|
|
48
|
+
EventSquads: EventSquadModel[];
|
|
49
|
+
getEventSquads: Sequelize.HasManyGetAssociationsMixin<EventSquadModel>;
|
|
50
|
+
static initModel(sequelize: Sequelize.Sequelize): typeof EventModel;
|
|
51
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { DataTypes, Model } from 'sequelize';
|
|
2
|
+
export class EventModel extends Model {
|
|
3
|
+
static initModel(sequelize) {
|
|
4
|
+
return EventModel.init({
|
|
5
|
+
event_id: {
|
|
6
|
+
type: DataTypes.UUID,
|
|
7
|
+
allowNull: false,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
defaultValue: DataTypes.UUIDV4
|
|
10
|
+
},
|
|
11
|
+
key: {
|
|
12
|
+
type: DataTypes.TEXT,
|
|
13
|
+
allowNull: false
|
|
14
|
+
},
|
|
15
|
+
format: {
|
|
16
|
+
type: DataTypes.TEXT,
|
|
17
|
+
allowNull: false
|
|
18
|
+
},
|
|
19
|
+
restriction: {
|
|
20
|
+
type: DataTypes.TEXT,
|
|
21
|
+
allowNull: false
|
|
22
|
+
},
|
|
23
|
+
status: {
|
|
24
|
+
type: DataTypes.TEXT,
|
|
25
|
+
allowNull: false
|
|
26
|
+
},
|
|
27
|
+
registration_opens_at: {
|
|
28
|
+
type: DataTypes.DATE,
|
|
29
|
+
allowNull: false
|
|
30
|
+
},
|
|
31
|
+
registration_closes_at: {
|
|
32
|
+
type: DataTypes.DATE,
|
|
33
|
+
allowNull: false
|
|
34
|
+
},
|
|
35
|
+
starts_at: {
|
|
36
|
+
type: DataTypes.DATE,
|
|
37
|
+
allowNull: false
|
|
38
|
+
},
|
|
39
|
+
final_starts_at: {
|
|
40
|
+
type: DataTypes.DATE,
|
|
41
|
+
allowNull: false
|
|
42
|
+
},
|
|
43
|
+
ends_at: {
|
|
44
|
+
type: DataTypes.DATE,
|
|
45
|
+
allowNull: false
|
|
46
|
+
},
|
|
47
|
+
purge_at: {
|
|
48
|
+
type: DataTypes.DATE,
|
|
49
|
+
allowNull: false
|
|
50
|
+
},
|
|
51
|
+
purged_at: {
|
|
52
|
+
type: DataTypes.DATE,
|
|
53
|
+
allowNull: true
|
|
54
|
+
},
|
|
55
|
+
notified: {
|
|
56
|
+
type: DataTypes.JSONB,
|
|
57
|
+
allowNull: false,
|
|
58
|
+
defaultValue: {}
|
|
59
|
+
}
|
|
60
|
+
}, {
|
|
61
|
+
sequelize,
|
|
62
|
+
tableName: 'Event',
|
|
63
|
+
schema: 'public',
|
|
64
|
+
timestamps: false,
|
|
65
|
+
indexes: [
|
|
66
|
+
{
|
|
67
|
+
name: 'Event_pk',
|
|
68
|
+
unique: true,
|
|
69
|
+
fields: [{ name: 'event_id' }]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'Event_starts_at_idx',
|
|
73
|
+
fields: [{ name: 'starts_at' }]
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -38,6 +38,13 @@ export * from './team';
|
|
|
38
38
|
export * from './team-replacement';
|
|
39
39
|
export * from './tactics';
|
|
40
40
|
export * from './lineup-preset';
|
|
41
|
+
export * from './event';
|
|
42
|
+
export * from './event-competition';
|
|
43
|
+
export * from './event-bot-team';
|
|
44
|
+
export * from './event-squad';
|
|
45
|
+
export * from './event-pack';
|
|
46
|
+
export * from './event-pack-player';
|
|
47
|
+
export * from './event-result';
|
|
41
48
|
export * from './match-preset';
|
|
42
49
|
export * from './user-currency';
|
|
43
50
|
export * from './user-player-progress';
|
|
@@ -38,6 +38,13 @@ export * from './team';
|
|
|
38
38
|
export * from './team-replacement';
|
|
39
39
|
export * from './tactics';
|
|
40
40
|
export * from './lineup-preset';
|
|
41
|
+
export * from './event';
|
|
42
|
+
export * from './event-competition';
|
|
43
|
+
export * from './event-bot-team';
|
|
44
|
+
export * from './event-squad';
|
|
45
|
+
export * from './event-pack';
|
|
46
|
+
export * from './event-pack-player';
|
|
47
|
+
export * from './event-result';
|
|
41
48
|
export * from './match-preset';
|
|
42
49
|
export * from './user-currency';
|
|
43
50
|
export * from './user-player-progress';
|
|
@@ -9,10 +9,11 @@ export interface LineupPresetAttributes {
|
|
|
9
9
|
is_active: boolean;
|
|
10
10
|
order_index: number;
|
|
11
11
|
config: ApiTactics;
|
|
12
|
+
event_id: string | null;
|
|
12
13
|
}
|
|
13
14
|
export type LineupPresetPk = 'preset_id';
|
|
14
15
|
export type LineupPresetId = LineupPresetModel[LineupPresetPk];
|
|
15
|
-
export type LineupPresetOptionalAttributes = 'preset_id' | 'is_active';
|
|
16
|
+
export type LineupPresetOptionalAttributes = 'preset_id' | 'is_active' | 'event_id';
|
|
16
17
|
export type LineupPresetCreationAttributes = Optional<LineupPresetAttributes, LineupPresetOptionalAttributes>;
|
|
17
18
|
export declare class LineupPresetModel extends Model<LineupPresetAttributes, LineupPresetCreationAttributes> implements LineupPresetAttributes {
|
|
18
19
|
preset_id: string;
|
|
@@ -21,6 +22,7 @@ export declare class LineupPresetModel extends Model<LineupPresetAttributes, Lin
|
|
|
21
22
|
is_active: boolean;
|
|
22
23
|
order_index: number;
|
|
23
24
|
config: ApiTactics;
|
|
25
|
+
event_id: string | null;
|
|
24
26
|
team: TeamModel;
|
|
25
27
|
getTeam: Sequelize.BelongsToGetAssociationMixin<TeamModel>;
|
|
26
28
|
setTeam: Sequelize.BelongsToSetAssociationMixin<TeamModel, TeamId>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as Sequelize from 'sequelize';
|
|
1
2
|
import { DataTypes, Model } from 'sequelize';
|
|
2
3
|
export class LineupPresetModel extends Model {
|
|
3
4
|
static initModel(sequelize) {
|
|
@@ -32,6 +33,15 @@ export class LineupPresetModel extends Model {
|
|
|
32
33
|
config: {
|
|
33
34
|
type: DataTypes.JSONB,
|
|
34
35
|
allowNull: false
|
|
36
|
+
},
|
|
37
|
+
event_id: {
|
|
38
|
+
type: DataTypes.UUID,
|
|
39
|
+
allowNull: true,
|
|
40
|
+
defaultValue: null,
|
|
41
|
+
references: {
|
|
42
|
+
model: 'Event',
|
|
43
|
+
key: 'event_id'
|
|
44
|
+
}
|
|
35
45
|
}
|
|
36
46
|
}, {
|
|
37
47
|
sequelize,
|
|
@@ -60,6 +70,16 @@ export class LineupPresetModel extends Model {
|
|
|
60
70
|
{ name: 'team_id' }
|
|
61
71
|
],
|
|
62
72
|
where: { is_active: true }
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
// At most one event lineup per team per event, so the save endpoint can upsert safely.
|
|
76
|
+
name: 'LineupPreset_one_per_team_event',
|
|
77
|
+
unique: true,
|
|
78
|
+
fields: [
|
|
79
|
+
{ name: 'team_id' },
|
|
80
|
+
{ name: 'event_id' }
|
|
81
|
+
],
|
|
82
|
+
where: { event_id: { [Sequelize.Op.ne]: null } }
|
|
63
83
|
}
|
|
64
84
|
]
|
|
65
85
|
});
|
|
@@ -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, EnergyBand, PinchCondition,
|
|
4
|
+
import { BlockingAssignment, ConditionLogic, CourtPosition, EnergyBand, PinchCondition, RotationSystemEnum, SubBackMode, SubBand, SubMode } from '../../service';
|
|
5
5
|
export interface TacticsLineupAttributes {
|
|
6
6
|
[CourtPosition.LIBERO_ZONE]?: PlayerId;
|
|
7
7
|
[CourtPosition.LEFT_BACK]: PlayerId;
|
|
@@ -36,7 +36,7 @@ export interface OffensivePreferenceAttributes {
|
|
|
36
36
|
}
|
|
37
37
|
export interface BlockingPreferenceAttributes {
|
|
38
38
|
rotation: number;
|
|
39
|
-
targets: Record<PlayerId,
|
|
39
|
+
targets: Record<PlayerId, BlockingAssignment>;
|
|
40
40
|
}
|
|
41
41
|
export interface SystemSetAttributes {
|
|
42
42
|
rotationSystem: RotationSystemEnum;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from '@jest/globals';
|
|
2
2
|
import { tacticsColumnsToApiDto, transformFromTactics, transformToTactics } from './tactics';
|
|
3
|
-
import { CourtPosition, EnergyBand, RoleEnum, RotationSystemEnum, Tactics } from '../../service';
|
|
3
|
+
import { CourtPosition, EnergyBand, NO_BLOCK, RoleEnum, RotationSystemEnum, Tactics } from '../../service';
|
|
4
4
|
import { makeCountry, makePlayer } from '../../service/test-helpers';
|
|
5
5
|
// Minimal TacticsModel-shaped object; cast through unknown since the transformer only reads columns.
|
|
6
6
|
function makeTactics(designatedSubs) {
|
|
@@ -214,6 +214,25 @@ describe('per-set tactics — service <-> row round-trip', () => {
|
|
|
214
214
|
const back = transformToTactics(attrs, roster);
|
|
215
215
|
expect(back.blockingPreferences[0].targets).toEqual({ [p4.id]: RoleEnum.OUTSIDE_HITTER, [p3.id]: RoleEnum.MIDDLE_BLOCKER });
|
|
216
216
|
});
|
|
217
|
+
// NO_BLOCK rides in the same targets map as a commit assignment, so the transformers copy it wholesale with no
|
|
218
|
+
// mapping code. This pins that: the sentinel must survive the row round-trip verbatim, mixed with a commit, and
|
|
219
|
+
// in the per-set overrides. If it ever gets translated or dropped, a player silently starts blocking again.
|
|
220
|
+
it('round-trips a NO_BLOCK assignment, mixed with a commit and in per-set overrides', () => {
|
|
221
|
+
const withNoBlock = Tactics.create({
|
|
222
|
+
lineup: { 4: p4, 3: p3, 2: p2, 5: p5, 6: p6, 1: p1, bench: [] },
|
|
223
|
+
liberoReplacements: [],
|
|
224
|
+
blockingPreferences: [{ rotation: 1, targets: { [p4.id]: NO_BLOCK, [p3.id]: RoleEnum.MIDDLE_BLOCKER } }],
|
|
225
|
+
blockingPreferenceSets: [[{ rotation: 2, targets: { [p2.id]: NO_BLOCK } }]]
|
|
226
|
+
});
|
|
227
|
+
const attrs = transformFromTactics(withNoBlock, 'team-1');
|
|
228
|
+
expect(attrs.blocking_preferences).toEqual([
|
|
229
|
+
{ rotation: 1, targets: { [p4.id]: NO_BLOCK, [p3.id]: RoleEnum.MIDDLE_BLOCKER } }
|
|
230
|
+
]);
|
|
231
|
+
expect(attrs.blocking_preference_sets).toEqual([[{ rotation: 2, targets: { [p2.id]: NO_BLOCK } }]]);
|
|
232
|
+
const back = transformToTactics(attrs, roster);
|
|
233
|
+
expect(back.blockingPreferences[0].targets).toEqual({ [p4.id]: NO_BLOCK, [p3.id]: RoleEnum.MIDDLE_BLOCKER });
|
|
234
|
+
expect(back.blockingPreferenceSets?.[0][0].targets).toEqual({ [p2.id]: NO_BLOCK });
|
|
235
|
+
});
|
|
217
236
|
});
|
|
218
237
|
describe('libero injuries — service <-> row round-trip + lenient heal', () => {
|
|
219
238
|
const country = makeCountry();
|
package/dist/esm/src/index.d.ts
CHANGED
package/dist/esm/src/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export * from './service';
|
|
2
2
|
export * from './data';
|
|
3
3
|
export * from './stat-config';
|
|
4
|
+
// Named rather than `export * from './api'`: the api module re-declares Tactics/Player/Team/Match as DTO
|
|
5
|
+
// shapes, which would collide with the service classes of the same name at the root. Consumers that want the
|
|
6
|
+
// DTO types import them from the `volleyballsimtypes/api` subpath.
|
|
7
|
+
export { collectTacticsPlayerIds } from './api/tactics-player-refs';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Country } from '../country';
|
|
2
|
+
import { Player, Rarity, Role } from '../player';
|
|
3
|
+
/** A pack targets one role, or ANY for the single free pick at the end. */
|
|
4
|
+
export type DraftRole = Role | 'ANY';
|
|
5
|
+
/** How many players an opened pack offers. */
|
|
6
|
+
export declare const PACK_SIZE = 4;
|
|
7
|
+
/**
|
|
8
|
+
* The pack sequence, in the order they are opened. One pick each, so this is also the squad: 2 setters,
|
|
9
|
+
* 4 outsides, 3 middles, 2 opposites, 2 liberos, plus one free pick, for a squad of 14 like every other team.
|
|
10
|
+
*
|
|
11
|
+
* Role-targeted rather than free choice on purpose: it makes a positionally valid squad impossible to get
|
|
12
|
+
* wrong, so nobody drafts six liberos and then cannot field a legal lineup.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DRAFT_PLAN: readonly DraftRole[];
|
|
15
|
+
/** The size of a drafted squad: one pick per pack. */
|
|
16
|
+
export declare const DRAFT_SQUAD_SIZE: number;
|
|
17
|
+
/**
|
|
18
|
+
* How far a pack's players may sit from the median for their rarity, as a fraction of the role score.
|
|
19
|
+
*
|
|
20
|
+
* The whole point of a generated draft is that the material is comparable: without this, one entrant opens a
|
|
21
|
+
* pack of duds while another opens a pack of stars and the event is decided before a ball is served. A player
|
|
22
|
+
* whose role score falls outside the band is rerolled.
|
|
23
|
+
*/
|
|
24
|
+
export declare const PACK_QUALITY_TOLERANCE = 0.08;
|
|
25
|
+
export interface PackQualityBand {
|
|
26
|
+
readonly role: Role;
|
|
27
|
+
readonly min: number;
|
|
28
|
+
readonly max: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The in-band range for a role at a rarity, measured from a sample of freshly generated players rather than
|
|
32
|
+
* from hardcoded numbers, so it tracks the generator instead of drifting away from it whenever stats are
|
|
33
|
+
* retuned. Deterministic enough in aggregate: the sample is large and the band is a fraction of its median.
|
|
34
|
+
*/
|
|
35
|
+
export declare function packQualityBand(country: Country, rarity: Rarity, role: Role, sampleSize?: number): PackQualityBand;
|
|
36
|
+
/**
|
|
37
|
+
* The players a pack offers: `PACK_SIZE` of the pack's role at the event's rarity, every one of them inside
|
|
38
|
+
* the fairness band.
|
|
39
|
+
*
|
|
40
|
+
* `pickRole` decides what an ANY pack rolls; it is passed in rather than chosen here so the caller controls the
|
|
41
|
+
* randomness (and a test can make it deterministic).
|
|
42
|
+
*/
|
|
43
|
+
export declare function generatePack(country: Country, rarity: Rarity, packRole: DraftRole, band: (role: Role) => PackQualityBand, pickRole: (roles: readonly Role[]) => Role): Player[];
|
|
44
|
+
/** The role of the pack at `index`, or undefined once the draft is complete. */
|
|
45
|
+
export declare function draftPackRole(index: number): DraftRole | undefined;
|
|
46
|
+
export declare function isDraftComplete(picks: number): boolean;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { PlayerGenerator, RoleEnum, calculateRoleScore } from '../player';
|
|
2
|
+
/** How many players an opened pack offers. */
|
|
3
|
+
export const PACK_SIZE = 4;
|
|
4
|
+
/**
|
|
5
|
+
* The pack sequence, in the order they are opened. One pick each, so this is also the squad: 2 setters,
|
|
6
|
+
* 4 outsides, 3 middles, 2 opposites, 2 liberos, plus one free pick, for a squad of 14 like every other team.
|
|
7
|
+
*
|
|
8
|
+
* Role-targeted rather than free choice on purpose: it makes a positionally valid squad impossible to get
|
|
9
|
+
* wrong, so nobody drafts six liberos and then cannot field a legal lineup.
|
|
10
|
+
*/
|
|
11
|
+
export const DRAFT_PLAN = [
|
|
12
|
+
RoleEnum.SETTER, RoleEnum.SETTER,
|
|
13
|
+
RoleEnum.OUTSIDE_HITTER, RoleEnum.OUTSIDE_HITTER, RoleEnum.OUTSIDE_HITTER, RoleEnum.OUTSIDE_HITTER,
|
|
14
|
+
RoleEnum.MIDDLE_BLOCKER, RoleEnum.MIDDLE_BLOCKER, RoleEnum.MIDDLE_BLOCKER,
|
|
15
|
+
RoleEnum.OPPOSITE_HITTER, RoleEnum.OPPOSITE_HITTER,
|
|
16
|
+
RoleEnum.LIBERO, RoleEnum.LIBERO,
|
|
17
|
+
'ANY'
|
|
18
|
+
];
|
|
19
|
+
/** The size of a drafted squad: one pick per pack. */
|
|
20
|
+
export const DRAFT_SQUAD_SIZE = DRAFT_PLAN.length;
|
|
21
|
+
/** The role a free pack rolls its choices at, so an ANY pack still offers a coherent set. */
|
|
22
|
+
const ANY_PACK_ROLES = [
|
|
23
|
+
RoleEnum.SETTER, RoleEnum.OUTSIDE_HITTER, RoleEnum.MIDDLE_BLOCKER, RoleEnum.OPPOSITE_HITTER, RoleEnum.LIBERO
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* How far a pack's players may sit from the median for their rarity, as a fraction of the role score.
|
|
27
|
+
*
|
|
28
|
+
* The whole point of a generated draft is that the material is comparable: without this, one entrant opens a
|
|
29
|
+
* pack of duds while another opens a pack of stars and the event is decided before a ball is served. A player
|
|
30
|
+
* whose role score falls outside the band is rerolled.
|
|
31
|
+
*/
|
|
32
|
+
export const PACK_QUALITY_TOLERANCE = 0.08;
|
|
33
|
+
/** Give up rerolling after this many attempts and take what we have, so generation can never spin forever. */
|
|
34
|
+
const MAX_REROLLS = 40;
|
|
35
|
+
/**
|
|
36
|
+
* The in-band range for a role at a rarity, measured from a sample of freshly generated players rather than
|
|
37
|
+
* from hardcoded numbers, so it tracks the generator instead of drifting away from it whenever stats are
|
|
38
|
+
* retuned. Deterministic enough in aggregate: the sample is large and the band is a fraction of its median.
|
|
39
|
+
*/
|
|
40
|
+
export function packQualityBand(country, rarity, role, sampleSize = 60) {
|
|
41
|
+
const scores = Array.from({ length: sampleSize }, () => calculateRoleScore(PlayerGenerator.generatePlayer(country, rarity, role, false, 1).stats, role)).sort((a, b) => a - b);
|
|
42
|
+
const median = scores[Math.floor(scores.length / 2)];
|
|
43
|
+
return {
|
|
44
|
+
role,
|
|
45
|
+
min: median * (1 - PACK_QUALITY_TOLERANCE),
|
|
46
|
+
max: median * (1 + PACK_QUALITY_TOLERANCE)
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The players a pack offers: `PACK_SIZE` of the pack's role at the event's rarity, every one of them inside
|
|
51
|
+
* the fairness band.
|
|
52
|
+
*
|
|
53
|
+
* `pickRole` decides what an ANY pack rolls; it is passed in rather than chosen here so the caller controls the
|
|
54
|
+
* randomness (and a test can make it deterministic).
|
|
55
|
+
*/
|
|
56
|
+
export function generatePack(country, rarity, packRole, band, pickRole) {
|
|
57
|
+
const role = packRole === 'ANY' ? pickRole(ANY_PACK_ROLES) : packRole;
|
|
58
|
+
const { min, max } = band(role);
|
|
59
|
+
const players = [];
|
|
60
|
+
for (let i = 0; i < PACK_SIZE; i++) {
|
|
61
|
+
let player = PlayerGenerator.generatePlayer(country, rarity, role, false, 1);
|
|
62
|
+
for (let attempt = 0; attempt < MAX_REROLLS; attempt++) {
|
|
63
|
+
const score = calculateRoleScore(player.stats, role);
|
|
64
|
+
if (score >= min && score <= max)
|
|
65
|
+
break;
|
|
66
|
+
player = PlayerGenerator.generatePlayer(country, rarity, role, false, 1);
|
|
67
|
+
}
|
|
68
|
+
players.push(player);
|
|
69
|
+
}
|
|
70
|
+
return players;
|
|
71
|
+
}
|
|
72
|
+
/** The role of the pack at `index`, or undefined once the draft is complete. */
|
|
73
|
+
export function draftPackRole(index) {
|
|
74
|
+
return DRAFT_PLAN[index];
|
|
75
|
+
}
|
|
76
|
+
export function isDraftComplete(picks) {
|
|
77
|
+
return picks >= DRAFT_SQUAD_SIZE;
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|