volleyballsimtypes 0.0.501 → 0.0.503

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 (81) 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/event/block.js +2 -2
  12. package/dist/cjs/src/service/event/free-ball.js +2 -2
  13. package/dist/cjs/src/service/event/in-play-event.d.ts +19 -1
  14. package/dist/cjs/src/service/event/in-play-event.js +17 -2
  15. package/dist/cjs/src/service/event/reception.js +2 -2
  16. package/dist/cjs/src/service/event/schemas/block.z.d.ts +10 -0
  17. package/dist/cjs/src/service/event/schemas/block.z.js +3 -1
  18. package/dist/cjs/src/service/event/schemas/free-ball.z.d.ts +10 -0
  19. package/dist/cjs/src/service/event/schemas/free-ball.z.js +3 -1
  20. package/dist/cjs/src/service/event/schemas/positional.z.d.ts +17 -0
  21. package/dist/cjs/src/service/event/schemas/positional.z.js +18 -0
  22. package/dist/cjs/src/service/event/schemas/reception.z.d.ts +10 -0
  23. package/dist/cjs/src/service/event/schemas/reception.z.js +3 -1
  24. package/dist/cjs/src/service/event/schemas/serve.z.d.ts +10 -0
  25. package/dist/cjs/src/service/event/schemas/serve.z.js +3 -1
  26. package/dist/cjs/src/service/event/schemas/set.z.d.ts +10 -0
  27. package/dist/cjs/src/service/event/schemas/set.z.js +3 -1
  28. package/dist/cjs/src/service/event/schemas/spike.z.d.ts +10 -0
  29. package/dist/cjs/src/service/event/schemas/spike.z.js +4 -2
  30. package/dist/cjs/src/service/event/serve.js +2 -2
  31. package/dist/cjs/src/service/event/set.js +2 -2
  32. package/dist/cjs/src/service/event/spike.d.ts +3 -2
  33. package/dist/cjs/src/service/event/spike.js +6 -2
  34. package/dist/cjs/src/service/match/box-score.d.ts +6 -0
  35. package/dist/cjs/src/service/match/box-score.js +7 -1
  36. package/dist/cjs/src/service/match/schemas/box-score.z.d.ts +12 -0
  37. package/dist/cjs/src/service/match/schemas/box-score.z.js +15 -2
  38. package/dist/cjs/src/service/player/schemas/player.z.d.ts +12 -0
  39. package/dist/cjs/src/service/player/trait.d.ts +1 -0
  40. package/dist/cjs/src/stat-config/traits.json +55 -11
  41. package/dist/esm/src/data/models/box-score.d.ts +12 -0
  42. package/dist/esm/src/data/models/box-score.js +30 -0
  43. package/dist/esm/src/data/models/views/box-score-totals.d.ts +12 -0
  44. package/dist/esm/src/data/models/views/box-score-totals.js +24 -0
  45. package/dist/esm/src/data/transformers/box-score.js +13 -1
  46. package/dist/esm/src/data/transformers/player.js +7 -1
  47. package/dist/esm/src/data/transformers/rally-event-positional.test.d.ts +1 -0
  48. package/dist/esm/src/data/transformers/rally-event-positional.test.js +54 -0
  49. package/dist/esm/src/data/transformers/rally-event.d.ts +4 -0
  50. package/dist/esm/src/data/transformers/rally-event.js +34 -6
  51. package/dist/esm/src/service/event/block.js +2 -2
  52. package/dist/esm/src/service/event/free-ball.js +2 -2
  53. package/dist/esm/src/service/event/in-play-event.d.ts +19 -1
  54. package/dist/esm/src/service/event/in-play-event.js +16 -1
  55. package/dist/esm/src/service/event/reception.js +2 -2
  56. package/dist/esm/src/service/event/schemas/block.z.d.ts +10 -0
  57. package/dist/esm/src/service/event/schemas/block.z.js +3 -1
  58. package/dist/esm/src/service/event/schemas/free-ball.z.d.ts +10 -0
  59. package/dist/esm/src/service/event/schemas/free-ball.z.js +3 -1
  60. package/dist/esm/src/service/event/schemas/positional.z.d.ts +17 -0
  61. package/dist/esm/src/service/event/schemas/positional.z.js +15 -0
  62. package/dist/esm/src/service/event/schemas/reception.z.d.ts +10 -0
  63. package/dist/esm/src/service/event/schemas/reception.z.js +3 -1
  64. package/dist/esm/src/service/event/schemas/serve.z.d.ts +10 -0
  65. package/dist/esm/src/service/event/schemas/serve.z.js +3 -1
  66. package/dist/esm/src/service/event/schemas/set.z.d.ts +10 -0
  67. package/dist/esm/src/service/event/schemas/set.z.js +3 -1
  68. package/dist/esm/src/service/event/schemas/spike.z.d.ts +10 -0
  69. package/dist/esm/src/service/event/schemas/spike.z.js +4 -2
  70. package/dist/esm/src/service/event/serve.js +2 -2
  71. package/dist/esm/src/service/event/set.js +2 -2
  72. package/dist/esm/src/service/event/spike.d.ts +3 -2
  73. package/dist/esm/src/service/event/spike.js +6 -2
  74. package/dist/esm/src/service/match/box-score.d.ts +6 -0
  75. package/dist/esm/src/service/match/box-score.js +7 -1
  76. package/dist/esm/src/service/match/schemas/box-score.z.d.ts +12 -0
  77. package/dist/esm/src/service/match/schemas/box-score.z.js +15 -2
  78. package/dist/esm/src/service/player/schemas/player.z.d.ts +12 -0
  79. package/dist/esm/src/service/player/trait.d.ts +1 -0
  80. package/dist/esm/src/stat-config/traits.json +55 -11
  81. package/package.json +1 -1
@@ -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;
@@ -23,6 +23,10 @@ export class BoxScoreTotalsModel extends Model {
23
23
  type: DataTypes.INTEGER,
24
24
  allowNull: false
25
25
  },
26
+ attack_blocked: {
27
+ type: DataTypes.INTEGER,
28
+ allowNull: false
29
+ },
26
30
  serve_attempts: {
27
31
  type: DataTypes.INTEGER,
28
32
  allowNull: false
@@ -47,6 +51,14 @@ export class BoxScoreTotalsModel extends Model {
47
51
  type: DataTypes.INTEGER,
48
52
  allowNull: false
49
53
  },
54
+ reception_ok: {
55
+ type: DataTypes.INTEGER,
56
+ allowNull: false
57
+ },
58
+ reception_negative: {
59
+ type: DataTypes.INTEGER,
60
+ allowNull: false
61
+ },
50
62
  reception_overpasses: {
51
63
  type: DataTypes.INTEGER,
52
64
  allowNull: false
@@ -87,6 +99,18 @@ export class BoxScoreTotalsModel extends Model {
87
99
  type: DataTypes.INTEGER,
88
100
  allowNull: false
89
101
  },
102
+ defense_digs_perfect: {
103
+ type: DataTypes.INTEGER,
104
+ allowNull: false
105
+ },
106
+ defense_digs_mediocre: {
107
+ type: DataTypes.INTEGER,
108
+ allowNull: false
109
+ },
110
+ defense_digs_bad: {
111
+ type: DataTypes.INTEGER,
112
+ allowNull: false
113
+ },
90
114
  defense_errors: {
91
115
  type: DataTypes.INTEGER,
92
116
  allowNull: false
@@ -6,12 +6,15 @@ function transformToAttributes(boxScore) {
6
6
  attack_attempts: boxScore.attack.attempts,
7
7
  attack_kills: boxScore.attack.kills,
8
8
  attack_errors: boxScore.attack.errors,
9
+ attack_blocked: boxScore.attack.blocked,
9
10
  serve_attempts: boxScore.serve.attempts,
10
11
  serve_aces: boxScore.serve.aces,
11
12
  serve_errors: boxScore.serve.errors,
12
13
  reception_attempts: boxScore.reception.attempts,
13
14
  reception_perfect: boxScore.reception.perfect,
14
15
  reception_positive: boxScore.reception.positive,
16
+ reception_ok: boxScore.reception.ok,
17
+ reception_negative: boxScore.reception.negative,
15
18
  reception_overpasses: boxScore.reception.overpasses,
16
19
  reception_errors: boxScore.reception.errors,
17
20
  block_solo: boxScore.block.solo,
@@ -22,6 +25,9 @@ function transformToAttributes(boxScore) {
22
25
  setting_errors: boxScore.setting.errors,
23
26
  setting_pass: boxScore.setting.pass,
24
27
  defense_digs: boxScore.defense.digs,
28
+ defense_digs_perfect: boxScore.defense.digsPerfect,
29
+ defense_digs_mediocre: boxScore.defense.digsMediocre,
30
+ defense_digs_bad: boxScore.defense.digsBad,
25
31
  defense_errors: boxScore.defense.errors,
26
32
  misc_points_played: boxScore.misc.pointsPlayed,
27
33
  misc_points_won_on_court: boxScore.misc.pointsWonOnCourt,
@@ -37,7 +43,8 @@ function transformToObject(model) {
37
43
  attack: {
38
44
  attempts: model.attack_attempts,
39
45
  kills: model.attack_kills,
40
- errors: model.attack_errors
46
+ errors: model.attack_errors,
47
+ blocked: model.attack_blocked
41
48
  },
42
49
  serve: {
43
50
  attempts: model.serve_attempts,
@@ -48,6 +55,8 @@ function transformToObject(model) {
48
55
  attempts: model.reception_attempts,
49
56
  perfect: model.reception_perfect,
50
57
  positive: model.reception_positive,
58
+ ok: model.reception_ok,
59
+ negative: model.reception_negative,
51
60
  overpasses: model.reception_overpasses,
52
61
  errors: model.reception_errors
53
62
  },
@@ -64,6 +73,9 @@ function transformToObject(model) {
64
73
  },
65
74
  defense: {
66
75
  digs: model.defense_digs,
76
+ digsPerfect: model.defense_digs_perfect,
77
+ digsMediocre: model.defense_digs_mediocre,
78
+ digsBad: model.defense_digs_bad,
67
79
  errors: model.defense_errors
68
80
  },
69
81
  misc: {
@@ -33,7 +33,8 @@ function transformToBoxScoreTotals(model) {
33
33
  attack: {
34
34
  attempts: toNumberStrict(model.attack_attempts, 'attack_attempts'),
35
35
  kills: toNumberStrict(model.attack_kills, 'attack_kills'),
36
- errors: toNumberStrict(model.attack_errors, 'attack_errors')
36
+ errors: toNumberStrict(model.attack_errors, 'attack_errors'),
37
+ blocked: toNumberStrict(model.attack_blocked, 'attack_blocked')
37
38
  },
38
39
  serve: {
39
40
  attempts: toNumberStrict(model.serve_attempts, 'serve_attempts'),
@@ -44,6 +45,8 @@ function transformToBoxScoreTotals(model) {
44
45
  attempts: toNumberStrict(model.reception_attempts, 'reception_attempts'),
45
46
  perfect: toNumberStrict(model.reception_perfect, 'reception_perfect'),
46
47
  positive: toNumberStrict(model.reception_positive, 'reception_positive'),
48
+ ok: toNumberStrict(model.reception_ok, 'reception_ok'),
49
+ negative: toNumberStrict(model.reception_negative, 'reception_negative'),
47
50
  overpasses: toNumberStrict(model.reception_overpasses, 'reception_overpasses'),
48
51
  errors: toNumberStrict(model.reception_errors, 'reception_errors')
49
52
  },
@@ -60,6 +63,9 @@ function transformToBoxScoreTotals(model) {
60
63
  },
61
64
  defense: {
62
65
  digs: toNumberStrict(model.defense_digs, 'defense_digs'),
66
+ digsPerfect: toNumberStrict(model.defense_digs_perfect, 'defense_digs_perfect'),
67
+ digsMediocre: toNumberStrict(model.defense_digs_mediocre, 'defense_digs_mediocre'),
68
+ digsBad: toNumberStrict(model.defense_digs_bad, 'defense_digs_bad'),
63
69
  errors: toNumberStrict(model.defense_errors, 'defense_errors')
64
70
  },
65
71
  misc: {
@@ -0,0 +1,54 @@
1
+ import { describe, it, expect } from '@jest/globals';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { DigQualityEnum, Reception, ReceptionFailureEnum, ReceptionTypeEnum, Spike, SpikeFailureEnum, SpikeTypeEnum } from '../../service';
4
+ import { transformToCompact, transformToReception, transformToSpike } from './rally-event';
5
+ describe('rally-event compact round trip -- positional fields (coords / speed / dig quality)', () => {
6
+ const playerId = uuidv4();
7
+ const roster = [playerId];
8
+ const playerIndex = new Map(roster.map((id, i) => [id, i]));
9
+ it('packs contact/landing/speed and round-trips them, 2-dp rounded', () => {
10
+ const spike = Spike.create({
11
+ playerId,
12
+ score: 60,
13
+ target: 4,
14
+ failure: SpikeFailureEnum.NO_FAILURE,
15
+ type: SpikeTypeEnum.CROSS_SHOT,
16
+ contact: { x: 1.234, y: -0.5 },
17
+ landing: { x: -2.5, y: 6.789 },
18
+ speed: 92.456
19
+ });
20
+ const compact = transformToCompact(spike, playerIndex);
21
+ expect(compact.c).toEqual([1.23, -0.5]);
22
+ expect(compact.g).toEqual([-2.5, 6.79]);
23
+ expect(compact.v).toBe(92.46);
24
+ const decoded = transformToSpike(compact, roster);
25
+ expect(decoded.contact).toEqual({ x: 1.23, y: -0.5 });
26
+ expect(decoded.landing).toEqual({ x: -2.5, y: 6.79 });
27
+ expect(decoded.speed).toBe(92.46);
28
+ });
29
+ it('packs dig quality on a reception and round-trips it', () => {
30
+ const dig = Reception.create({
31
+ playerId,
32
+ score: 40,
33
+ target: 3,
34
+ failure: ReceptionFailureEnum.NO_FAILURE,
35
+ type: ReceptionTypeEnum.DIG,
36
+ digQuality: DigQualityEnum.MEDIOCRE
37
+ });
38
+ const compact = transformToCompact(dig, playerIndex);
39
+ expect(compact.q).toBe(DigQualityEnum.MEDIOCRE);
40
+ const decoded = transformToReception(compact, roster);
41
+ expect(decoded.digQuality).toBe(DigQualityEnum.MEDIOCRE);
42
+ });
43
+ it('omits the positional keys when absent, and a legacy event decodes them as undefined', () => {
44
+ const spike = Spike.create({ playerId, score: 60, target: 4, failure: SpikeFailureEnum.NO_FAILURE, type: SpikeTypeEnum.CROSS_SHOT });
45
+ const compact = transformToCompact(spike, playerIndex);
46
+ expect('c' in compact).toBe(false);
47
+ expect('v' in compact).toBe(false);
48
+ expect('q' in compact).toBe(false);
49
+ const decoded = transformToSpike(compact, roster);
50
+ expect(decoded.contact).toBeUndefined();
51
+ expect(decoded.speed).toBeUndefined();
52
+ expect(decoded.digQuality).toBeUndefined();
53
+ });
54
+ });
@@ -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;
@@ -1,4 +1,18 @@
1
1
  import { Block, FreeBall, InPlayEvent, LiberoReplacement, Reception, Serve, Set, Spike, Substitution } from '../../service';
2
+ // Rebuild the optional positional fields (contact / landing / speed / digQuality) from a compact event, for the
3
+ // event .create() inputs. Loosely typed since create() validates via its Zod schema.
4
+ function positionalFromCompact(e) {
5
+ const out = {};
6
+ if (e.c != null)
7
+ out.contact = { x: e.c[0], y: e.c[1] };
8
+ if (e.g != null)
9
+ out.landing = { x: e.g[0], y: e.g[1] };
10
+ if (e.v != null)
11
+ out.speed = e.v;
12
+ if (e.q != null)
13
+ out.digQuality = e.q;
14
+ return out;
15
+ }
2
16
  // Incident packing for the compact key 'i': values below 10 are injury severities, 10+ are knock tiers.
3
17
  function incidentToCompact(incident) {
4
18
  if (incident == null)
@@ -31,6 +45,14 @@ export function transformToCompact(evt, playerIndex) {
31
45
  out.h = playerIndex.get(evt.incident.playerId);
32
46
  }
33
47
  }
48
+ if (evt.contact != null)
49
+ out.c = [evt.contact.x, evt.contact.y];
50
+ if (evt.landing != null)
51
+ out.g = [evt.landing.x, evt.landing.y];
52
+ if (evt.speed != null)
53
+ out.v = evt.speed;
54
+ if (evt.digQuality != null)
55
+ out.q = evt.digQuality;
34
56
  if (evt instanceof Block) {
35
57
  out.b = evt.blockers.map(id => playerIndex.get(id));
36
58
  }
@@ -57,7 +79,8 @@ export function transformToBlock(event, roster) {
57
79
  target: event.a,
58
80
  blockers: (event.b ?? []).map(i => roster[i]),
59
81
  score: event.s,
60
- incident: incidentFromCompact(event.i, event.h, roster)
82
+ incident: incidentFromCompact(event.i, event.h, roster),
83
+ ...positionalFromCompact(event)
61
84
  });
62
85
  }
63
86
  export function transformToLiberoReplacement(event, roster) {
@@ -74,7 +97,8 @@ export function transformToReception(event, roster) {
74
97
  playerId: roster[event.p],
75
98
  target: event.a,
76
99
  score: event.s,
77
- incident: incidentFromCompact(event.i, event.h, roster)
100
+ incident: incidentFromCompact(event.i, event.h, roster),
101
+ ...positionalFromCompact(event)
78
102
  });
79
103
  }
80
104
  export function transformToServe(event, roster) {
@@ -84,7 +108,8 @@ export function transformToServe(event, roster) {
84
108
  playerId: roster[event.p],
85
109
  target: event.a,
86
110
  score: event.s,
87
- incident: incidentFromCompact(event.i, event.h, roster)
111
+ incident: incidentFromCompact(event.i, event.h, roster),
112
+ ...positionalFromCompact(event)
88
113
  });
89
114
  }
90
115
  export function transformToSet(event, roster) {
@@ -95,7 +120,8 @@ export function transformToSet(event, roster) {
95
120
  target: event.a,
96
121
  score: event.s,
97
122
  incident: incidentFromCompact(event.i, event.h, roster),
98
- tempo: event.m
123
+ tempo: event.m,
124
+ ...positionalFromCompact(event)
99
125
  });
100
126
  }
101
127
  export function transformToSpike(event, roster) {
@@ -105,7 +131,8 @@ export function transformToSpike(event, roster) {
105
131
  playerId: roster[event.p],
106
132
  target: event.a,
107
133
  score: event.s,
108
- incident: incidentFromCompact(event.i, event.h, roster)
134
+ incident: incidentFromCompact(event.i, event.h, roster),
135
+ ...positionalFromCompact(event)
109
136
  });
110
137
  }
111
138
  // FreeBall (Last Resort save) is a plain InPlayEvent with no incident of its own, so it decodes from f/t/a/s
@@ -116,7 +143,8 @@ export function transformToFreeBall(event, roster) {
116
143
  type: event.t,
117
144
  playerId: roster[event.p],
118
145
  target: event.a,
119
- score: event.s
146
+ score: event.s,
147
+ ...positionalFromCompact(event)
120
148
  });
121
149
  }
122
150
  export function transformToSubstitution(event, roster) {
@@ -31,8 +31,8 @@ export class Block extends InPlayEvent {
31
31
  }
32
32
  return new Block(result.data);
33
33
  }
34
- constructor({ score, playerId, target, failure, type, blockers, activeTraits, incident }) {
35
- super({ score, playerId, target, activeTraits, incident, eventType });
34
+ constructor({ score, playerId, target, failure, type, blockers, activeTraits, incident, contact, landing, speed, digQuality }) {
35
+ super({ score, playerId, target, activeTraits, incident, eventType, contact, landing, speed, digQuality });
36
36
  this.type = type;
37
37
  this.failure = failure;
38
38
  this.blockers = blockers;
@@ -30,8 +30,8 @@ export class FreeBall extends InPlayEvent {
30
30
  }
31
31
  return new FreeBall(result.data);
32
32
  }
33
- constructor({ score, playerId, target, failure, type, activeTraits }) {
34
- super({ score, playerId, target, activeTraits, eventType });
33
+ constructor({ score, playerId, target, failure, type, activeTraits, contact, landing, speed, digQuality }) {
34
+ super({ score, playerId, target, activeTraits, eventType, contact, landing, speed, digQuality });
35
35
  this.type = type;
36
36
  this.failure = failure;
37
37
  }
@@ -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,10 +1,25 @@
1
1
  import { RallyEvent } from './rally-event';
2
+ // The quality of a DEFENSIVE dig (positional engine, effort gating): a PERFECT dig sets up a clean in-system
3
+ // counter, MEDIOCRE is out of position, BAD is a scramble. Persisted on the Reception (dig) event's 'q' key.
4
+ export var DigQualityEnum;
5
+ (function (DigQualityEnum) {
6
+ DigQualityEnum[DigQualityEnum["PERFECT"] = 0] = "PERFECT";
7
+ DigQualityEnum[DigQualityEnum["MEDIOCRE"] = 1] = "MEDIOCRE";
8
+ DigQualityEnum[DigQualityEnum["BAD"] = 2] = "BAD";
9
+ })(DigQualityEnum || (DigQualityEnum = {}));
10
+ function roundCoord(c) {
11
+ return c != null ? { x: Number(c.x.toFixed(2)), y: Number(c.y.toFixed(2)) } : undefined;
12
+ }
2
13
  export class InPlayEvent extends RallyEvent {
3
- constructor({ eventType, playerId, target, score, activeTraits, incident }) {
14
+ constructor({ eventType, playerId, target, score, activeTraits, incident, contact, landing, speed, digQuality }) {
4
15
  super({ playerId, eventType });
5
16
  this.target = target;
6
17
  this.score = Number(score.toFixed(2));
7
18
  this.activeTraits = activeTraits;
8
19
  this.incident = incident;
20
+ this.contact = roundCoord(contact);
21
+ this.landing = roundCoord(landing);
22
+ this.speed = speed != null ? Number(speed.toFixed(2)) : undefined;
23
+ this.digQuality = digQuality;
9
24
  }
10
25
  }
@@ -33,8 +33,8 @@ export class Reception extends InPlayEvent {
33
33
  }
34
34
  return new Reception(result.data);
35
35
  }
36
- constructor({ score, playerId, target, failure, type, activeTraits, incident }) {
37
- super({ score, playerId, target, activeTraits, incident, eventType });
36
+ constructor({ score, playerId, target, failure, type, activeTraits, incident, contact, landing, speed, digQuality }) {
37
+ super({ score, playerId, target, activeTraits, incident, eventType, contact, landing, speed, digQuality });
38
38
  this.type = type;
39
39
  this.failure = failure;
40
40
  }
@@ -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;
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { TraitEnum } from '../../player';
3
3
  import { EventIncidentSchema } from './incident.z';
4
+ import { positionalEventFields } from './positional.z';
4
5
  const blockTypeValues = [0, 1, 2, 3];
5
6
  const blockFailureValues = [0, 1, 2, 3, 4, 5];
6
7
  const courtTargetValues = Array.from({ length: 13 }, (_, i) => i);
@@ -12,7 +13,8 @@ export const BlockInputSchema = z.object({
12
13
  failure: z.number().int().refine((v) => blockFailureValues.includes(v), { message: 'INVALID_BLOCK_FAILURE' }),
13
14
  type: z.number().int().refine((v) => blockTypeValues.includes(v), { message: 'INVALID_BLOCK_TYPE' }),
14
15
  blockers: z.array(z.uuid()),
15
- incident: EventIncidentSchema.optional()
16
+ incident: EventIncidentSchema.optional(),
17
+ ...positionalEventFields
16
18
  }).superRefine((data, ctx) => {
17
19
  // The block type IS the blocker count (NO_BLOCKER=0, SINGLE=1, DOUBLE=2, TRIPLE=3), so the two must agree.
18
20
  if (data.blockers.length !== data.type) {
@@ -1,6 +1,16 @@
1
1
  import { z } from 'zod';
2
2
  import { TraitEnum } from '../../player';
3
3
  export declare const FreeBallInputSchema: 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;
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { TraitEnum } from '../../player';
3
+ import { positionalEventFields } from './positional.z';
3
4
  const freeBallTypeValues = [0, 1];
4
5
  const freeBallFailureValues = [0];
5
6
  const courtTargetValues = Array.from({ length: 13 }, (_, i) => i);
@@ -9,5 +10,6 @@ export const FreeBallInputSchema = z.object({
9
10
  target: z.number().int().refine((v) => courtTargetValues.includes(v), { message: 'INVALID_COURT_TARGET' }),
10
11
  activeTraits: z.array(z.enum(Object.values(TraitEnum))).default([]),
11
12
  failure: z.number().int().refine((v) => freeBallFailureValues.includes(v), { message: 'INVALID_FREE_BALL_FAILURE' }),
12
- type: z.number().int().refine((v) => freeBallTypeValues.includes(v), { message: 'INVALID_FREE_BALL_TYPE' })
13
+ type: z.number().int().refine((v) => freeBallTypeValues.includes(v), { message: 'INVALID_FREE_BALL_TYPE' }),
14
+ ...positionalEventFields
13
15
  });
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ export declare const EventCoordSchema: z.ZodObject<{
3
+ x: z.ZodNumber;
4
+ y: z.ZodNumber;
5
+ }, z.core.$strip>;
6
+ export declare const positionalEventFields: {
7
+ contact: z.ZodOptional<z.ZodObject<{
8
+ x: z.ZodNumber;
9
+ y: z.ZodNumber;
10
+ }, z.core.$strip>>;
11
+ landing: z.ZodOptional<z.ZodObject<{
12
+ x: z.ZodNumber;
13
+ y: z.ZodNumber;
14
+ }, z.core.$strip>>;
15
+ speed: z.ZodOptional<z.ZodNumber>;
16
+ digQuality: z.ZodOptional<z.ZodNumber>;
17
+ };
@@ -0,0 +1,15 @@
1
+ import { z } from 'zod';
2
+ // A precise court position (metres) on an in-play event.
3
+ export const EventCoordSchema = z.object({
4
+ x: z.number().finite(),
5
+ y: z.number().finite()
6
+ });
7
+ // The optional positional-engine fields shared by every in-play event schema: where the actor contacted the ball,
8
+ // where it went, the ball speed (km/h), and the dig quality (0 PERFECT / 1 MEDIOCRE / 2 BAD). Spread into each
9
+ // event schema; absent for zone-engine events (decoded as undefined).
10
+ export const positionalEventFields = {
11
+ contact: EventCoordSchema.optional(),
12
+ landing: EventCoordSchema.optional(),
13
+ speed: z.number().finite().optional(),
14
+ digQuality: z.number().int().min(0).max(2).optional()
15
+ };
@@ -1,6 +1,16 @@
1
1
  import { z } from 'zod';
2
2
  import { TraitEnum } from '../../player';
3
3
  export declare const ReceptionInputSchema: 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;
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { TraitEnum } from '../../player';
3
3
  import { EventIncidentSchema } from './incident.z';
4
+ import { positionalEventFields } from './positional.z';
4
5
  const receptionTypeValues = [0, 1, 2, 3, 4];
5
6
  const receptionFailureValues = [0, 1, 2, 3, 4];
6
7
  const courtTargetValues = Array.from({ length: 13 }, (_, i) => i);
@@ -11,5 +12,6 @@ export const ReceptionInputSchema = z.object({
11
12
  activeTraits: z.array(z.enum(Object.values(TraitEnum))).default([]),
12
13
  failure: z.number().int().refine((v) => receptionFailureValues.includes(v), { message: 'INVALID_RECEPTION_FAILURE' }),
13
14
  type: z.number().int().refine((v) => receptionTypeValues.includes(v), { message: 'INVALID_RECEPTION_TYPE' }),
14
- incident: EventIncidentSchema.optional()
15
+ incident: EventIncidentSchema.optional(),
16
+ ...positionalEventFields
15
17
  });
@@ -1,6 +1,16 @@
1
1
  import { z } from 'zod';
2
2
  import { TraitEnum } from '../../player';
3
3
  export declare const ServeInputSchema: 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;
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { TraitEnum } from '../../player';
3
3
  import { EventIncidentSchema } from './incident.z';
4
+ import { positionalEventFields } from './positional.z';
4
5
  const serveTypeValues = [0, 1, 2, 3];
5
6
  const serveFailureValues = [0, 1, 2, 3, 4];
6
7
  const courtTargetValues = Array.from({ length: 13 }, (_, i) => i);
@@ -11,5 +12,6 @@ export const ServeInputSchema = z.object({
11
12
  activeTraits: z.array(z.enum(Object.values(TraitEnum))).default([]),
12
13
  failure: z.number().int().refine((v) => serveFailureValues.includes(v), { message: 'INVALID_SERVE_FAILURE' }),
13
14
  type: z.number().int().refine((v) => serveTypeValues.includes(v), { message: 'INVALID_SERVE_TYPE' }),
14
- incident: EventIncidentSchema.optional()
15
+ incident: EventIncidentSchema.optional(),
16
+ ...positionalEventFields
15
17
  });
@@ -1,6 +1,16 @@
1
1
  import { z } from 'zod';
2
2
  import { TraitEnum } from '../../player';
3
3
  export declare const SetInputSchema: 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;
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { TraitEnum } from '../../player';
3
3
  import { EventIncidentSchema } from './incident.z';
4
+ import { positionalEventFields } from './positional.z';
4
5
  const setTypeValues = [0, 1];
5
6
  const setFailureValues = [0, 1, 2, 3, 4];
6
7
  const setTempoValues = [0, 1, 2];
@@ -14,5 +15,6 @@ export const SetInputSchema = z.object({
14
15
  type: z.number().int().refine((v) => setTypeValues.includes(v), { message: 'INVALID_SET_TYPE' }),
15
16
  incident: EventIncidentSchema.optional(),
16
17
  // Set tempo (FIRST=0, SECOND=1, HIGH=2). Optional: absent on dumps, failed sets, and every pre-tempo event.
17
- tempo: z.number().int().refine((v) => setTempoValues.includes(v), { message: 'INVALID_SET_TEMPO' }).optional()
18
+ tempo: z.number().int().refine((v) => setTempoValues.includes(v), { message: 'INVALID_SET_TEMPO' }).optional(),
19
+ ...positionalEventFields
18
20
  });