volleyballsimtypes 0.0.500 → 0.0.502

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.
Files changed (85) hide show
  1. package/dist/cjs/src/data/models/box-score.d.ts +12 -0
  2. package/dist/cjs/src/data/models/box-score.js +30 -0
  3. package/dist/cjs/src/data/models/views/box-score-totals.d.ts +12 -0
  4. package/dist/cjs/src/data/models/views/box-score-totals.js +24 -0
  5. package/dist/cjs/src/data/transformers/box-score.js +13 -1
  6. package/dist/cjs/src/data/transformers/player.js +7 -1
  7. package/dist/cjs/src/data/transformers/rally-event-positional.test.d.ts +1 -0
  8. package/dist/cjs/src/data/transformers/rally-event-positional.test.js +56 -0
  9. package/dist/cjs/src/data/transformers/rally-event.d.ts +4 -0
  10. package/dist/cjs/src/data/transformers/rally-event.js +34 -6
  11. package/dist/cjs/src/service/competition/event-rewards.d.ts +3 -2
  12. package/dist/cjs/src/service/competition/event-rewards.js +5 -3
  13. package/dist/cjs/src/service/competition/event-rewards.test.js +9 -9
  14. package/dist/cjs/src/service/event/block.js +2 -2
  15. package/dist/cjs/src/service/event/free-ball.js +2 -2
  16. package/dist/cjs/src/service/event/in-play-event.d.ts +19 -1
  17. package/dist/cjs/src/service/event/in-play-event.js +17 -2
  18. package/dist/cjs/src/service/event/reception.js +2 -2
  19. package/dist/cjs/src/service/event/schemas/block.z.d.ts +10 -0
  20. package/dist/cjs/src/service/event/schemas/block.z.js +3 -1
  21. package/dist/cjs/src/service/event/schemas/free-ball.z.d.ts +10 -0
  22. package/dist/cjs/src/service/event/schemas/free-ball.z.js +3 -1
  23. package/dist/cjs/src/service/event/schemas/positional.z.d.ts +17 -0
  24. package/dist/cjs/src/service/event/schemas/positional.z.js +18 -0
  25. package/dist/cjs/src/service/event/schemas/reception.z.d.ts +10 -0
  26. package/dist/cjs/src/service/event/schemas/reception.z.js +3 -1
  27. package/dist/cjs/src/service/event/schemas/serve.z.d.ts +10 -0
  28. package/dist/cjs/src/service/event/schemas/serve.z.js +3 -1
  29. package/dist/cjs/src/service/event/schemas/set.z.d.ts +10 -0
  30. package/dist/cjs/src/service/event/schemas/set.z.js +3 -1
  31. package/dist/cjs/src/service/event/schemas/spike.z.d.ts +10 -0
  32. package/dist/cjs/src/service/event/schemas/spike.z.js +3 -1
  33. package/dist/cjs/src/service/event/serve.js +2 -2
  34. package/dist/cjs/src/service/event/set.js +2 -2
  35. package/dist/cjs/src/service/event/spike.js +2 -2
  36. package/dist/cjs/src/service/match/box-score.d.ts +6 -0
  37. package/dist/cjs/src/service/match/box-score.js +7 -1
  38. package/dist/cjs/src/service/match/schemas/box-score.z.d.ts +12 -0
  39. package/dist/cjs/src/service/match/schemas/box-score.z.js +15 -2
  40. package/dist/cjs/src/service/player/schemas/player.z.d.ts +12 -0
  41. package/dist/cjs/src/service/player/trait.d.ts +1 -0
  42. package/dist/cjs/src/stat-config/traits.json +55 -11
  43. package/dist/esm/src/data/models/box-score.d.ts +12 -0
  44. package/dist/esm/src/data/models/box-score.js +30 -0
  45. package/dist/esm/src/data/models/views/box-score-totals.d.ts +12 -0
  46. package/dist/esm/src/data/models/views/box-score-totals.js +24 -0
  47. package/dist/esm/src/data/transformers/box-score.js +13 -1
  48. package/dist/esm/src/data/transformers/player.js +7 -1
  49. package/dist/esm/src/data/transformers/rally-event-positional.test.d.ts +1 -0
  50. package/dist/esm/src/data/transformers/rally-event-positional.test.js +54 -0
  51. package/dist/esm/src/data/transformers/rally-event.d.ts +4 -0
  52. package/dist/esm/src/data/transformers/rally-event.js +34 -6
  53. package/dist/esm/src/service/competition/event-rewards.d.ts +3 -2
  54. package/dist/esm/src/service/competition/event-rewards.js +5 -3
  55. package/dist/esm/src/service/competition/event-rewards.test.js +9 -9
  56. package/dist/esm/src/service/event/block.js +2 -2
  57. package/dist/esm/src/service/event/free-ball.js +2 -2
  58. package/dist/esm/src/service/event/in-play-event.d.ts +19 -1
  59. package/dist/esm/src/service/event/in-play-event.js +16 -1
  60. package/dist/esm/src/service/event/reception.js +2 -2
  61. package/dist/esm/src/service/event/schemas/block.z.d.ts +10 -0
  62. package/dist/esm/src/service/event/schemas/block.z.js +3 -1
  63. package/dist/esm/src/service/event/schemas/free-ball.z.d.ts +10 -0
  64. package/dist/esm/src/service/event/schemas/free-ball.z.js +3 -1
  65. package/dist/esm/src/service/event/schemas/positional.z.d.ts +17 -0
  66. package/dist/esm/src/service/event/schemas/positional.z.js +15 -0
  67. package/dist/esm/src/service/event/schemas/reception.z.d.ts +10 -0
  68. package/dist/esm/src/service/event/schemas/reception.z.js +3 -1
  69. package/dist/esm/src/service/event/schemas/serve.z.d.ts +10 -0
  70. package/dist/esm/src/service/event/schemas/serve.z.js +3 -1
  71. package/dist/esm/src/service/event/schemas/set.z.d.ts +10 -0
  72. package/dist/esm/src/service/event/schemas/set.z.js +3 -1
  73. package/dist/esm/src/service/event/schemas/spike.z.d.ts +10 -0
  74. package/dist/esm/src/service/event/schemas/spike.z.js +3 -1
  75. package/dist/esm/src/service/event/serve.js +2 -2
  76. package/dist/esm/src/service/event/set.js +2 -2
  77. package/dist/esm/src/service/event/spike.js +2 -2
  78. package/dist/esm/src/service/match/box-score.d.ts +6 -0
  79. package/dist/esm/src/service/match/box-score.js +7 -1
  80. package/dist/esm/src/service/match/schemas/box-score.z.d.ts +12 -0
  81. package/dist/esm/src/service/match/schemas/box-score.z.js +15 -2
  82. package/dist/esm/src/service/player/schemas/player.z.d.ts +12 -0
  83. package/dist/esm/src/service/player/trait.d.ts +1 -0
  84. package/dist/esm/src/stat-config/traits.json +55 -11
  85. package/package.json +1 -1
@@ -7,12 +7,15 @@ export interface BoxScoreAttributes {
7
7
  attack_attempts: number;
8
8
  attack_kills: number;
9
9
  attack_errors: number;
10
+ attack_blocked: number;
10
11
  serve_attempts: number;
11
12
  serve_aces: number;
12
13
  serve_errors: number;
13
14
  reception_attempts: number;
14
15
  reception_perfect: number;
15
16
  reception_positive: number;
17
+ reception_ok: number;
18
+ reception_negative: number;
16
19
  reception_overpasses: number;
17
20
  reception_errors: number;
18
21
  block_solo: number;
@@ -23,6 +26,9 @@ export interface BoxScoreAttributes {
23
26
  setting_errors: number;
24
27
  setting_pass: number;
25
28
  defense_digs: number;
29
+ defense_digs_perfect: number;
30
+ defense_digs_mediocre: number;
31
+ defense_digs_bad: number;
26
32
  defense_errors: number;
27
33
  misc_points_played: number;
28
34
  misc_points_won_on_court: number;
@@ -39,12 +45,15 @@ export declare class BoxScoreModel extends Model<BoxScoreAttributes, BoxScoreCre
39
45
  attack_attempts: number;
40
46
  attack_kills: number;
41
47
  attack_errors: number;
48
+ attack_blocked: number;
42
49
  serve_attempts: number;
43
50
  serve_aces: number;
44
51
  serve_errors: number;
45
52
  reception_attempts: number;
46
53
  reception_perfect: number;
47
54
  reception_positive: number;
55
+ reception_ok: number;
56
+ reception_negative: number;
48
57
  reception_overpasses: number;
49
58
  reception_errors: number;
50
59
  block_solo: number;
@@ -55,6 +64,9 @@ export declare class BoxScoreModel extends Model<BoxScoreAttributes, BoxScoreCre
55
64
  setting_errors: number;
56
65
  setting_pass: number;
57
66
  defense_digs: number;
67
+ defense_digs_perfect: number;
68
+ defense_digs_mediocre: number;
69
+ defense_digs_bad: number;
58
70
  defense_errors: number;
59
71
  misc_points_played: number;
60
72
  misc_points_won_on_court: number;
@@ -39,6 +39,11 @@ class BoxScoreModel extends sequelize_1.Model {
39
39
  allowNull: false,
40
40
  defaultValue: 0
41
41
  },
42
+ attack_blocked: {
43
+ type: sequelize_1.DataTypes.SMALLINT,
44
+ allowNull: false,
45
+ defaultValue: 0
46
+ },
42
47
  // Serve
43
48
  serve_attempts: {
44
49
  type: sequelize_1.DataTypes.SMALLINT,
@@ -71,6 +76,16 @@ class BoxScoreModel extends sequelize_1.Model {
71
76
  allowNull: false,
72
77
  defaultValue: 0
73
78
  },
79
+ reception_ok: {
80
+ type: sequelize_1.DataTypes.SMALLINT,
81
+ allowNull: false,
82
+ defaultValue: 0
83
+ },
84
+ reception_negative: {
85
+ type: sequelize_1.DataTypes.SMALLINT,
86
+ allowNull: false,
87
+ defaultValue: 0
88
+ },
74
89
  reception_overpasses: {
75
90
  type: sequelize_1.DataTypes.SMALLINT,
76
91
  allowNull: false,
@@ -124,6 +139,21 @@ class BoxScoreModel extends sequelize_1.Model {
124
139
  allowNull: false,
125
140
  defaultValue: 0
126
141
  },
142
+ defense_digs_perfect: {
143
+ type: sequelize_1.DataTypes.SMALLINT,
144
+ allowNull: false,
145
+ defaultValue: 0
146
+ },
147
+ defense_digs_mediocre: {
148
+ type: sequelize_1.DataTypes.SMALLINT,
149
+ allowNull: false,
150
+ defaultValue: 0
151
+ },
152
+ defense_digs_bad: {
153
+ type: sequelize_1.DataTypes.SMALLINT,
154
+ allowNull: false,
155
+ defaultValue: 0
156
+ },
127
157
  defense_errors: {
128
158
  type: sequelize_1.DataTypes.SMALLINT,
129
159
  allowNull: false,
@@ -6,12 +6,15 @@ export interface BoxScoreTotalsAttributes {
6
6
  attack_attempts: number;
7
7
  attack_kills: number;
8
8
  attack_errors: number;
9
+ attack_blocked: number;
9
10
  serve_attempts: number;
10
11
  serve_aces: number;
11
12
  serve_errors: number;
12
13
  reception_attempts: number;
13
14
  reception_perfect: number;
14
15
  reception_positive: number;
16
+ reception_ok: number;
17
+ reception_negative: number;
15
18
  reception_overpasses: number;
16
19
  reception_errors: number;
17
20
  block_solo: number;
@@ -22,6 +25,9 @@ export interface BoxScoreTotalsAttributes {
22
25
  setting_errors: number;
23
26
  setting_pass: number;
24
27
  defense_digs: number;
28
+ defense_digs_perfect: number;
29
+ defense_digs_mediocre: number;
30
+ defense_digs_bad: number;
25
31
  defense_errors: number;
26
32
  misc_points_played: number;
27
33
  misc_points_won_on_court: number;
@@ -39,12 +45,15 @@ export declare class BoxScoreTotalsModel extends Model<BoxScoreTotalsAttributes,
39
45
  attack_attempts: number;
40
46
  attack_kills: number;
41
47
  attack_errors: number;
48
+ attack_blocked: number;
42
49
  serve_attempts: number;
43
50
  serve_aces: number;
44
51
  serve_errors: number;
45
52
  reception_attempts: number;
46
53
  reception_perfect: number;
47
54
  reception_positive: number;
55
+ reception_ok: number;
56
+ reception_negative: number;
48
57
  reception_overpasses: number;
49
58
  reception_errors: number;
50
59
  block_solo: number;
@@ -55,6 +64,9 @@ export declare class BoxScoreTotalsModel extends Model<BoxScoreTotalsAttributes,
55
64
  setting_errors: number;
56
65
  setting_pass: number;
57
66
  defense_digs: number;
67
+ defense_digs_perfect: number;
68
+ defense_digs_mediocre: number;
69
+ defense_digs_bad: number;
58
70
  defense_errors: number;
59
71
  misc_points_played: number;
60
72
  misc_points_won_on_court: number;
@@ -26,6 +26,10 @@ class BoxScoreTotalsModel extends sequelize_1.Model {
26
26
  type: sequelize_1.DataTypes.INTEGER,
27
27
  allowNull: false
28
28
  },
29
+ attack_blocked: {
30
+ type: sequelize_1.DataTypes.INTEGER,
31
+ allowNull: false
32
+ },
29
33
  serve_attempts: {
30
34
  type: sequelize_1.DataTypes.INTEGER,
31
35
  allowNull: false
@@ -50,6 +54,14 @@ class BoxScoreTotalsModel extends sequelize_1.Model {
50
54
  type: sequelize_1.DataTypes.INTEGER,
51
55
  allowNull: false
52
56
  },
57
+ reception_ok: {
58
+ type: sequelize_1.DataTypes.INTEGER,
59
+ allowNull: false
60
+ },
61
+ reception_negative: {
62
+ type: sequelize_1.DataTypes.INTEGER,
63
+ allowNull: false
64
+ },
53
65
  reception_overpasses: {
54
66
  type: sequelize_1.DataTypes.INTEGER,
55
67
  allowNull: false
@@ -90,6 +102,18 @@ class BoxScoreTotalsModel extends sequelize_1.Model {
90
102
  type: sequelize_1.DataTypes.INTEGER,
91
103
  allowNull: false
92
104
  },
105
+ defense_digs_perfect: {
106
+ type: sequelize_1.DataTypes.INTEGER,
107
+ allowNull: false
108
+ },
109
+ defense_digs_mediocre: {
110
+ type: sequelize_1.DataTypes.INTEGER,
111
+ allowNull: false
112
+ },
113
+ defense_digs_bad: {
114
+ type: sequelize_1.DataTypes.INTEGER,
115
+ allowNull: false
116
+ },
93
117
  defense_errors: {
94
118
  type: sequelize_1.DataTypes.INTEGER,
95
119
  allowNull: false
@@ -10,12 +10,15 @@ function transformToAttributes(boxScore) {
10
10
  attack_attempts: boxScore.attack.attempts,
11
11
  attack_kills: boxScore.attack.kills,
12
12
  attack_errors: boxScore.attack.errors,
13
+ attack_blocked: boxScore.attack.blocked,
13
14
  serve_attempts: boxScore.serve.attempts,
14
15
  serve_aces: boxScore.serve.aces,
15
16
  serve_errors: boxScore.serve.errors,
16
17
  reception_attempts: boxScore.reception.attempts,
17
18
  reception_perfect: boxScore.reception.perfect,
18
19
  reception_positive: boxScore.reception.positive,
20
+ reception_ok: boxScore.reception.ok,
21
+ reception_negative: boxScore.reception.negative,
19
22
  reception_overpasses: boxScore.reception.overpasses,
20
23
  reception_errors: boxScore.reception.errors,
21
24
  block_solo: boxScore.block.solo,
@@ -26,6 +29,9 @@ function transformToAttributes(boxScore) {
26
29
  setting_errors: boxScore.setting.errors,
27
30
  setting_pass: boxScore.setting.pass,
28
31
  defense_digs: boxScore.defense.digs,
32
+ defense_digs_perfect: boxScore.defense.digsPerfect,
33
+ defense_digs_mediocre: boxScore.defense.digsMediocre,
34
+ defense_digs_bad: boxScore.defense.digsBad,
29
35
  defense_errors: boxScore.defense.errors,
30
36
  misc_points_played: boxScore.misc.pointsPlayed,
31
37
  misc_points_won_on_court: boxScore.misc.pointsWonOnCourt,
@@ -41,7 +47,8 @@ function transformToObject(model) {
41
47
  attack: {
42
48
  attempts: model.attack_attempts,
43
49
  kills: model.attack_kills,
44
- errors: model.attack_errors
50
+ errors: model.attack_errors,
51
+ blocked: model.attack_blocked
45
52
  },
46
53
  serve: {
47
54
  attempts: model.serve_attempts,
@@ -52,6 +59,8 @@ function transformToObject(model) {
52
59
  attempts: model.reception_attempts,
53
60
  perfect: model.reception_perfect,
54
61
  positive: model.reception_positive,
62
+ ok: model.reception_ok,
63
+ negative: model.reception_negative,
55
64
  overpasses: model.reception_overpasses,
56
65
  errors: model.reception_errors
57
66
  },
@@ -68,6 +77,9 @@ function transformToObject(model) {
68
77
  },
69
78
  defense: {
70
79
  digs: model.defense_digs,
80
+ digsPerfect: model.defense_digs_perfect,
81
+ digsMediocre: model.defense_digs_mediocre,
82
+ digsBad: model.defense_digs_bad,
71
83
  errors: model.defense_errors
72
84
  },
73
85
  misc: {
@@ -37,7 +37,8 @@ function transformToBoxScoreTotals(model) {
37
37
  attack: {
38
38
  attempts: toNumberStrict(model.attack_attempts, 'attack_attempts'),
39
39
  kills: toNumberStrict(model.attack_kills, 'attack_kills'),
40
- errors: toNumberStrict(model.attack_errors, 'attack_errors')
40
+ errors: toNumberStrict(model.attack_errors, 'attack_errors'),
41
+ blocked: toNumberStrict(model.attack_blocked, 'attack_blocked')
41
42
  },
42
43
  serve: {
43
44
  attempts: toNumberStrict(model.serve_attempts, 'serve_attempts'),
@@ -48,6 +49,8 @@ function transformToBoxScoreTotals(model) {
48
49
  attempts: toNumberStrict(model.reception_attempts, 'reception_attempts'),
49
50
  perfect: toNumberStrict(model.reception_perfect, 'reception_perfect'),
50
51
  positive: toNumberStrict(model.reception_positive, 'reception_positive'),
52
+ ok: toNumberStrict(model.reception_ok, 'reception_ok'),
53
+ negative: toNumberStrict(model.reception_negative, 'reception_negative'),
51
54
  overpasses: toNumberStrict(model.reception_overpasses, 'reception_overpasses'),
52
55
  errors: toNumberStrict(model.reception_errors, 'reception_errors')
53
56
  },
@@ -64,6 +67,9 @@ function transformToBoxScoreTotals(model) {
64
67
  },
65
68
  defense: {
66
69
  digs: toNumberStrict(model.defense_digs, 'defense_digs'),
70
+ digsPerfect: toNumberStrict(model.defense_digs_perfect, 'defense_digs_perfect'),
71
+ digsMediocre: toNumberStrict(model.defense_digs_mediocre, 'defense_digs_mediocre'),
72
+ digsBad: toNumberStrict(model.defense_digs_bad, 'defense_digs_bad'),
67
73
  errors: toNumberStrict(model.defense_errors, 'defense_errors')
68
74
  },
69
75
  misc: {
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const globals_1 = require("@jest/globals");
4
+ const uuid_1 = require("uuid");
5
+ const service_1 = require("../../service");
6
+ const rally_event_1 = require("./rally-event");
7
+ (0, globals_1.describe)('rally-event compact round trip -- positional fields (coords / speed / dig quality)', () => {
8
+ const playerId = (0, uuid_1.v4)();
9
+ const roster = [playerId];
10
+ const playerIndex = new Map(roster.map((id, i) => [id, i]));
11
+ (0, globals_1.it)('packs contact/landing/speed and round-trips them, 2-dp rounded', () => {
12
+ const spike = service_1.Spike.create({
13
+ playerId,
14
+ score: 60,
15
+ target: 4,
16
+ failure: service_1.SpikeFailureEnum.NO_FAILURE,
17
+ type: service_1.SpikeTypeEnum.CROSS_SHOT,
18
+ contact: { x: 1.234, y: -0.5 },
19
+ landing: { x: -2.5, y: 6.789 },
20
+ speed: 92.456
21
+ });
22
+ const compact = (0, rally_event_1.transformToCompact)(spike, playerIndex);
23
+ (0, globals_1.expect)(compact.c).toEqual([1.23, -0.5]);
24
+ (0, globals_1.expect)(compact.g).toEqual([-2.5, 6.79]);
25
+ (0, globals_1.expect)(compact.v).toBe(92.46);
26
+ const decoded = (0, rally_event_1.transformToSpike)(compact, roster);
27
+ (0, globals_1.expect)(decoded.contact).toEqual({ x: 1.23, y: -0.5 });
28
+ (0, globals_1.expect)(decoded.landing).toEqual({ x: -2.5, y: 6.79 });
29
+ (0, globals_1.expect)(decoded.speed).toBe(92.46);
30
+ });
31
+ (0, globals_1.it)('packs dig quality on a reception and round-trips it', () => {
32
+ const dig = service_1.Reception.create({
33
+ playerId,
34
+ score: 40,
35
+ target: 3,
36
+ failure: service_1.ReceptionFailureEnum.NO_FAILURE,
37
+ type: service_1.ReceptionTypeEnum.DIG,
38
+ digQuality: service_1.DigQualityEnum.MEDIOCRE
39
+ });
40
+ const compact = (0, rally_event_1.transformToCompact)(dig, playerIndex);
41
+ (0, globals_1.expect)(compact.q).toBe(service_1.DigQualityEnum.MEDIOCRE);
42
+ const decoded = (0, rally_event_1.transformToReception)(compact, roster);
43
+ (0, globals_1.expect)(decoded.digQuality).toBe(service_1.DigQualityEnum.MEDIOCRE);
44
+ });
45
+ (0, globals_1.it)('omits the positional keys when absent, and a legacy event decodes them as undefined', () => {
46
+ const spike = service_1.Spike.create({ playerId, score: 60, target: 4, failure: service_1.SpikeFailureEnum.NO_FAILURE, type: service_1.SpikeTypeEnum.CROSS_SHOT });
47
+ const compact = (0, rally_event_1.transformToCompact)(spike, playerIndex);
48
+ (0, globals_1.expect)('c' in compact).toBe(false);
49
+ (0, globals_1.expect)('v' in compact).toBe(false);
50
+ (0, globals_1.expect)('q' in compact).toBe(false);
51
+ const decoded = (0, rally_event_1.transformToSpike)(compact, roster);
52
+ (0, globals_1.expect)(decoded.contact).toBeUndefined();
53
+ (0, globals_1.expect)(decoded.speed).toBeUndefined();
54
+ (0, globals_1.expect)(decoded.digQuality).toBeUndefined();
55
+ });
56
+ });
@@ -13,6 +13,10 @@ export interface CompactEvent {
13
13
  i?: number;
14
14
  h?: number;
15
15
  m?: number;
16
+ c?: number[];
17
+ g?: number[];
18
+ v?: number;
19
+ q?: number;
16
20
  }
17
21
  export declare function transformToCompact(evt: RallyEvent, playerIndex: Map<string, number>): CompactEvent;
18
22
  export declare function transformToBlock(event: CompactEvent, roster: string[]): Block;
@@ -10,6 +10,20 @@ exports.transformToSpike = transformToSpike;
10
10
  exports.transformToFreeBall = transformToFreeBall;
11
11
  exports.transformToSubstitution = transformToSubstitution;
12
12
  const service_1 = require("../../service");
13
+ // Rebuild the optional positional fields (contact / landing / speed / digQuality) from a compact event, for the
14
+ // event .create() inputs. Loosely typed since create() validates via its Zod schema.
15
+ function positionalFromCompact(e) {
16
+ const out = {};
17
+ if (e.c != null)
18
+ out.contact = { x: e.c[0], y: e.c[1] };
19
+ if (e.g != null)
20
+ out.landing = { x: e.g[0], y: e.g[1] };
21
+ if (e.v != null)
22
+ out.speed = e.v;
23
+ if (e.q != null)
24
+ out.digQuality = e.q;
25
+ return out;
26
+ }
13
27
  // Incident packing for the compact key 'i': values below 10 are injury severities, 10+ are knock tiers.
14
28
  function incidentToCompact(incident) {
15
29
  if (incident == null)
@@ -42,6 +56,14 @@ function transformToCompact(evt, playerIndex) {
42
56
  out.h = playerIndex.get(evt.incident.playerId);
43
57
  }
44
58
  }
59
+ if (evt.contact != null)
60
+ out.c = [evt.contact.x, evt.contact.y];
61
+ if (evt.landing != null)
62
+ out.g = [evt.landing.x, evt.landing.y];
63
+ if (evt.speed != null)
64
+ out.v = evt.speed;
65
+ if (evt.digQuality != null)
66
+ out.q = evt.digQuality;
45
67
  if (evt instanceof service_1.Block) {
46
68
  out.b = evt.blockers.map(id => playerIndex.get(id));
47
69
  }
@@ -68,7 +90,8 @@ function transformToBlock(event, roster) {
68
90
  target: event.a,
69
91
  blockers: (event.b ?? []).map(i => roster[i]),
70
92
  score: event.s,
71
- incident: incidentFromCompact(event.i, event.h, roster)
93
+ incident: incidentFromCompact(event.i, event.h, roster),
94
+ ...positionalFromCompact(event)
72
95
  });
73
96
  }
74
97
  function transformToLiberoReplacement(event, roster) {
@@ -85,7 +108,8 @@ function transformToReception(event, roster) {
85
108
  playerId: roster[event.p],
86
109
  target: event.a,
87
110
  score: event.s,
88
- incident: incidentFromCompact(event.i, event.h, roster)
111
+ incident: incidentFromCompact(event.i, event.h, roster),
112
+ ...positionalFromCompact(event)
89
113
  });
90
114
  }
91
115
  function transformToServe(event, roster) {
@@ -95,7 +119,8 @@ function transformToServe(event, roster) {
95
119
  playerId: roster[event.p],
96
120
  target: event.a,
97
121
  score: event.s,
98
- incident: incidentFromCompact(event.i, event.h, roster)
122
+ incident: incidentFromCompact(event.i, event.h, roster),
123
+ ...positionalFromCompact(event)
99
124
  });
100
125
  }
101
126
  function transformToSet(event, roster) {
@@ -106,7 +131,8 @@ function transformToSet(event, roster) {
106
131
  target: event.a,
107
132
  score: event.s,
108
133
  incident: incidentFromCompact(event.i, event.h, roster),
109
- tempo: event.m
134
+ tempo: event.m,
135
+ ...positionalFromCompact(event)
110
136
  });
111
137
  }
112
138
  function transformToSpike(event, roster) {
@@ -116,7 +142,8 @@ function transformToSpike(event, roster) {
116
142
  playerId: roster[event.p],
117
143
  target: event.a,
118
144
  score: event.s,
119
- incident: incidentFromCompact(event.i, event.h, roster)
145
+ incident: incidentFromCompact(event.i, event.h, roster),
146
+ ...positionalFromCompact(event)
120
147
  });
121
148
  }
122
149
  // FreeBall (Last Resort save) is a plain InPlayEvent with no incident of its own, so it decodes from f/t/a/s
@@ -127,7 +154,8 @@ function transformToFreeBall(event, roster) {
127
154
  type: event.t,
128
155
  playerId: roster[event.p],
129
156
  target: event.a,
130
- score: event.s
157
+ score: event.s,
158
+ ...positionalFromCompact(event)
131
159
  });
132
160
  }
133
161
  function transformToSubstitution(event, roster) {
@@ -1,4 +1,4 @@
1
- export type EventRewardBand = 'PARTICIPATION' | 'TOP_20' | 'TOP_10' | 'TOP_5' | 'TOP_3' | 'TOP_1';
1
+ export type EventRewardBand = 'PARTICIPATION' | 'FINAL_GROUP' | 'TOP_10' | 'TOP_5' | 'TOP_3' | 'TOP_1';
2
2
  export interface EventRewardRow {
3
3
  readonly band: EventRewardBand;
4
4
  /** Best final position that earns this row; PARTICIPATION has none. */
@@ -15,7 +15,8 @@ export interface EventRewardRow {
15
15
  export declare const EVENT_REWARDS: readonly EventRewardRow[];
16
16
  /**
17
17
  * The band a finish falls into. `position` is the place in the FINAL table; null means the team never reached
18
- * the final, and so does any position past the last banded row, which both count as taking part.
18
+ * the final (it went out in the group stage) and takes the participation row. The final holds MAX_FINAL_SIZE
19
+ * teams and the widest banded row (FINAL_GROUP) covers exactly that, so every finalist earns a banded row.
19
20
  */
20
21
  export declare function eventRewardBandFor(position: number | null): EventRewardBand;
21
22
  export interface EventPayout {
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.EVENT_REWARDS = void 0;
9
9
  exports.eventRewardBandFor = eventRewardBandFor;
10
10
  exports.eventPayoutFor = eventPayoutFor;
11
+ const event_schedule_1 = require("./event-schedule");
11
12
  /**
12
13
  * Best band first. A team earns its own row and every row BELOW it, which is what "cumulative" means: first
13
14
  * place collects all six.
@@ -17,18 +18,19 @@ exports.EVENT_REWARDS = [
17
18
  { band: 'TOP_3', maxPosition: 3, packs: 0, coins: 500 },
18
19
  { band: 'TOP_5', maxPosition: 5, packs: 0, coins: 1000 },
19
20
  { band: 'TOP_10', maxPosition: 10, packs: 1, coins: 0 },
20
- { band: 'TOP_20', maxPosition: 20, packs: 0, coins: 1000 },
21
+ { band: 'FINAL_GROUP', maxPosition: event_schedule_1.MAX_FINAL_SIZE, packs: 0, coins: 1000 },
21
22
  { band: 'PARTICIPATION', maxPosition: null, packs: 1, coins: 1000 }
22
23
  ];
23
24
  /**
24
25
  * The band a finish falls into. `position` is the place in the FINAL table; null means the team never reached
25
- * the final, and so does any position past the last banded row, which both count as taking part.
26
+ * the final (it went out in the group stage) and takes the participation row. The final holds MAX_FINAL_SIZE
27
+ * teams and the widest banded row (FINAL_GROUP) covers exactly that, so every finalist earns a banded row.
26
28
  */
27
29
  function eventRewardBandFor(position) {
28
30
  if (position == null)
29
31
  return 'PARTICIPATION';
30
32
  // Rows are ordered best-first, so the FIRST row the position satisfies is the narrowest band it earns.
31
- // Scanning to the end instead would always land on the widest, handing first place a "top 20" badge.
33
+ // Scanning to the end instead would always land on the widest, handing first place a "final group" badge.
32
34
  for (const row of exports.EVENT_REWARDS) {
33
35
  if (row.maxPosition != null && position <= row.maxPosition)
34
36
  return row.band;
@@ -11,11 +11,11 @@ const event_rewards_1 = require("./event-rewards");
11
11
  [5, 'TOP_5'],
12
12
  [6, 'TOP_10'],
13
13
  [10, 'TOP_10'],
14
- [11, 'TOP_20'],
15
- [20, 'TOP_20'],
16
- // Inside a 24-team final but outside every banded row, so it counts as taking part.
17
- [21, 'PARTICIPATION'],
18
- [24, 'PARTICIPATION']
14
+ [11, 'FINAL_GROUP'],
15
+ [20, 'FINAL_GROUP'],
16
+ // The final holds 24 teams and FINAL_GROUP covers all of them.
17
+ [21, 'FINAL_GROUP'],
18
+ [24, 'FINAL_GROUP']
19
19
  ])('places a finish of %i in %s', (position, band) => {
20
20
  (0, globals_1.expect)((0, event_rewards_1.eventRewardBandFor)(position)).toBe(band);
21
21
  });
@@ -26,9 +26,9 @@ const event_rewards_1 = require("./event-rewards");
26
26
  (0, globals_1.describe)('eventPayoutFor()', () => {
27
27
  globals_1.it.each([
28
28
  // position, packs, coins. Cumulative: every row from Participation down to the finish.
29
- [null, 1, 1000], // group stage: 1 pack + 1000
30
- [24, 1, 1000], // in the final but outside the top 20: still just participation
31
- [20, 1, 2000], // + top 20's 1000
29
+ [null, 1, 1000], // never reached the final (group stage): 1 pack + 1000
30
+ [24, 1, 2000], // last of the 24-team final: final group's 1000 on top of participation
31
+ [20, 1, 2000], // + final group's 1000
32
32
  [11, 1, 2000],
33
33
  [10, 2, 2000], // + top 10's pack
34
34
  [6, 2, 2000],
@@ -50,7 +50,7 @@ const event_rewards_1 = require("./event-rewards");
50
50
  }
51
51
  });
52
52
  (0, globals_1.it)('adds up to the owner-defined table for a winner', () => {
53
- // 1x pack + 1000 (participation), +1000 (top 20), +1 pack (top 10), +1000 (top 5), +500 (top 3), +1 pack (top 1)
53
+ // 1x pack + 1000 (participation), +1000 (final group), +1 pack (top 10), +1000 (top 5), +500 (top 3), +1 pack (top 1)
54
54
  const total = event_rewards_1.EVENT_REWARDS.reduce((acc, row) => ({ packs: acc.packs + row.packs, coins: acc.coins + row.coins }), { packs: 0, coins: 0 });
55
55
  (0, globals_1.expect)((0, event_rewards_1.eventPayoutFor)(1)).toEqual(total);
56
56
  (0, globals_1.expect)(total).toEqual({ packs: 3, coins: 3500 });
@@ -34,8 +34,8 @@ class Block extends in_play_event_1.InPlayEvent {
34
34
  }
35
35
  return new Block(result.data);
36
36
  }
37
- constructor({ score, playerId, target, failure, type, blockers, activeTraits, incident }) {
38
- super({ score, playerId, target, activeTraits, incident, eventType });
37
+ constructor({ score, playerId, target, failure, type, blockers, activeTraits, incident, contact, landing, speed, digQuality }) {
38
+ super({ score, playerId, target, activeTraits, incident, eventType, contact, landing, speed, digQuality });
39
39
  this.type = type;
40
40
  this.failure = failure;
41
41
  this.blockers = blockers;
@@ -33,8 +33,8 @@ class FreeBall extends in_play_event_1.InPlayEvent {
33
33
  }
34
34
  return new FreeBall(result.data);
35
35
  }
36
- constructor({ score, playerId, target, failure, type, activeTraits }) {
37
- super({ score, playerId, target, activeTraits, eventType });
36
+ constructor({ score, playerId, target, failure, type, activeTraits, contact, landing, speed, digQuality }) {
37
+ super({ score, playerId, target, activeTraits, eventType, contact, landing, speed, digQuality });
38
38
  this.type = type;
39
39
  this.failure = failure;
40
40
  }
@@ -6,11 +6,25 @@ export interface EventIncident {
6
6
  readonly severity: number;
7
7
  readonly playerId?: string;
8
8
  }
9
+ export declare enum DigQualityEnum {
10
+ PERFECT = 0,
11
+ MEDIOCRE = 1,
12
+ BAD = 2
13
+ }
14
+ export type DigQuality = DigQualityEnum.PERFECT | DigQualityEnum.MEDIOCRE | DigQualityEnum.BAD;
15
+ export interface EventCoord {
16
+ readonly x: number;
17
+ readonly y: number;
18
+ }
9
19
  export interface InPlayEventOpts extends RallyEventOpts {
10
20
  readonly target: CourtTarget;
11
21
  readonly score: number;
12
22
  readonly activeTraits: Trait[];
13
23
  readonly incident?: EventIncident;
24
+ readonly contact?: EventCoord;
25
+ readonly landing?: EventCoord;
26
+ readonly speed?: number;
27
+ readonly digQuality?: DigQuality;
14
28
  }
15
29
  export declare abstract class InPlayEvent<F, T> extends RallyEvent {
16
30
  abstract failure: F;
@@ -19,5 +33,9 @@ export declare abstract class InPlayEvent<F, T> extends RallyEvent {
19
33
  readonly score: number;
20
34
  readonly activeTraits: Trait[];
21
35
  readonly incident?: EventIncident;
22
- protected constructor({ eventType, playerId, target, score, activeTraits, incident }: InPlayEventOpts);
36
+ readonly contact?: EventCoord;
37
+ readonly landing?: EventCoord;
38
+ readonly speed?: number;
39
+ readonly digQuality?: DigQuality;
40
+ protected constructor({ eventType, playerId, target, score, activeTraits, incident, contact, landing, speed, digQuality }: InPlayEventOpts);
23
41
  }
@@ -1,14 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InPlayEvent = void 0;
3
+ exports.InPlayEvent = exports.DigQualityEnum = void 0;
4
4
  const rally_event_1 = require("./rally-event");
5
+ // The quality of a DEFENSIVE dig (positional engine, effort gating): a PERFECT dig sets up a clean in-system
6
+ // counter, MEDIOCRE is out of position, BAD is a scramble. Persisted on the Reception (dig) event's 'q' key.
7
+ var DigQualityEnum;
8
+ (function (DigQualityEnum) {
9
+ DigQualityEnum[DigQualityEnum["PERFECT"] = 0] = "PERFECT";
10
+ DigQualityEnum[DigQualityEnum["MEDIOCRE"] = 1] = "MEDIOCRE";
11
+ DigQualityEnum[DigQualityEnum["BAD"] = 2] = "BAD";
12
+ })(DigQualityEnum || (exports.DigQualityEnum = DigQualityEnum = {}));
13
+ function roundCoord(c) {
14
+ return c != null ? { x: Number(c.x.toFixed(2)), y: Number(c.y.toFixed(2)) } : undefined;
15
+ }
5
16
  class InPlayEvent extends rally_event_1.RallyEvent {
6
- constructor({ eventType, playerId, target, score, activeTraits, incident }) {
17
+ constructor({ eventType, playerId, target, score, activeTraits, incident, contact, landing, speed, digQuality }) {
7
18
  super({ playerId, eventType });
8
19
  this.target = target;
9
20
  this.score = Number(score.toFixed(2));
10
21
  this.activeTraits = activeTraits;
11
22
  this.incident = incident;
23
+ this.contact = roundCoord(contact);
24
+ this.landing = roundCoord(landing);
25
+ this.speed = speed != null ? Number(speed.toFixed(2)) : undefined;
26
+ this.digQuality = digQuality;
12
27
  }
13
28
  }
14
29
  exports.InPlayEvent = InPlayEvent;
@@ -36,8 +36,8 @@ class Reception extends in_play_event_1.InPlayEvent {
36
36
  }
37
37
  return new Reception(result.data);
38
38
  }
39
- constructor({ score, playerId, target, failure, type, activeTraits, incident }) {
40
- super({ score, playerId, target, activeTraits, incident, eventType });
39
+ constructor({ score, playerId, target, failure, type, activeTraits, incident, contact, landing, speed, digQuality }) {
40
+ super({ score, playerId, target, activeTraits, incident, eventType, contact, landing, speed, digQuality });
41
41
  this.type = type;
42
42
  this.failure = failure;
43
43
  }
@@ -1,6 +1,16 @@
1
1
  import { z } from 'zod';
2
2
  import { TraitEnum } from '../../player';
3
3
  export declare const BlockInputSchema: z.ZodObject<{
4
+ contact: z.ZodOptional<z.ZodObject<{
5
+ x: z.ZodNumber;
6
+ y: z.ZodNumber;
7
+ }, z.core.$strip>>;
8
+ landing: z.ZodOptional<z.ZodObject<{
9
+ x: z.ZodNumber;
10
+ y: z.ZodNumber;
11
+ }, z.core.$strip>>;
12
+ speed: z.ZodOptional<z.ZodNumber>;
13
+ digQuality: z.ZodOptional<z.ZodNumber>;
4
14
  playerId: z.ZodUUID;
5
15
  score: z.ZodNumber;
6
16
  target: z.ZodNumber;