Post-codex-review-5: align event-log с telemetry spec + UX polish

- MatchTracker (codex #1 Low): event-log поля переименованы под
  telemetry-spec.md v3.10 — ballOwner→ballOwnerBeforeHit,
  matchTimeSec→matchTimeSeconds (для bumper_hit и goal events).
  Future-proof для batched emit через .getEvents(). Тест assertion
  обновлён под новые поля.
- Touch zone labels (codex #2 Low): BOOST 14→18px, alpha все
  лейблы 0.5→0.7 для mobile readability.
- Test comment (codex #3 editorial): «player получает hits через
  ai_setup» → корректная формулировка про defensive trap
  (атакующий игрок кормит AI как защитника).

50/50 tests, typecheck/lint/build .

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
aevgarik
2026-05-24 08:13:32 +03:00
parent bfdb24294a
commit 32b894ec08
3 changed files with 13 additions and 9 deletions

View File

@@ -579,7 +579,7 @@ export class MatchScene extends Phaser.Scene {
.text(sideW / 2, zoneY, '◀ L', { .text(sideW / 2, zoneY, '◀ L', {
fontFamily: 'monospace', fontSize: '20px', color: '#ff006e', fontStyle: 'bold', fontFamily: 'monospace', fontSize: '20px', color: '#ff006e', fontStyle: 'bold',
}) })
.setOrigin(0.5).setAlpha(0.5); .setOrigin(0.5).setAlpha(0.7);
// Right zone // Right zone
const rightX = GAME_WIDTH - sideW / 2; const rightX = GAME_WIDTH - sideW / 2;
@@ -594,7 +594,7 @@ export class MatchScene extends Phaser.Scene {
.text(rightX, zoneY, 'R ▶', { .text(rightX, zoneY, 'R ▶', {
fontFamily: 'monospace', fontSize: '20px', color: '#ff006e', fontStyle: 'bold', fontFamily: 'monospace', fontSize: '20px', color: '#ff006e', fontStyle: 'bold',
}) })
.setOrigin(0.5).setAlpha(0.5); .setOrigin(0.5).setAlpha(0.7);
// Booster zone (центр, узкая) — Phase 2 placeholder // Booster zone (центр, узкая) — Phase 2 placeholder
const boosterZone = this.add.rectangle(GAME_WIDTH / 2, zoneY, boosterW, zoneH * 0.5, PALETTE.accent, 0.05); const boosterZone = this.add.rectangle(GAME_WIDTH / 2, zoneY, boosterW, zoneH * 0.5, PALETTE.accent, 0.05);
@@ -606,9 +606,9 @@ export class MatchScene extends Phaser.Scene {
}); });
this.add this.add
.text(GAME_WIDTH / 2, zoneY, 'BOOST', { .text(GAME_WIDTH / 2, zoneY, 'BOOST', {
fontFamily: 'monospace', fontSize: '14px', color: '#ffbe0b', fontStyle: 'bold', fontFamily: 'monospace', fontSize: '18px', color: '#ffbe0b', fontStyle: 'bold',
}) })
.setOrigin(0.5).setAlpha(0.5); .setOrigin(0.5).setAlpha(0.7);
} }
private buildHUD(): void { private buildHUD(): void {

View File

@@ -99,7 +99,8 @@ describe('MatchTracker.recordBumperHit — credit и point-counters', () => {
describe('MatchTracker.recordBumperHit — стэк очков и событий', () => { describe('MatchTracker.recordBumperHit — стэк очков и событий', () => {
it('многократные hits корректно накапливают BP per сторона', () => { it('многократные hits корректно накапливают BP per сторона', () => {
const t = new MatchTracker(0); const t = new MatchTracker(0);
// Player атакует — player получает 3 hits через ai_setup (по 3 pts slingshot) // Player атакует (ballOwner=player) — мяч застревает в AI-setup,
// AI фармит как защитник (defensive trap). Player BP не растёт.
t.recordBumperHit(3, 'ai_setup', 'player', 'slingshot', 0, 0); t.recordBumperHit(3, 'ai_setup', 'player', 'slingshot', 0, 0);
t.recordBumperHit(3, 'ai_setup', 'player', 'slingshot', 100, 0.1); t.recordBumperHit(3, 'ai_setup', 'player', 'slingshot', 100, 0.1);
t.recordBumperHit(10, 'ai_setup', 'player', 'turbo', 200, 0.2); t.recordBumperHit(10, 'ai_setup', 'player', 'turbo', 200, 0.2);
@@ -137,7 +138,7 @@ describe('MatchTracker.recordBumperHit — стэк очков и событий
expect(events[0]?.data).toMatchObject({ expect(events[0]?.data).toMatchObject({
source: 'fixed_last_touch', source: 'fixed_last_touch',
bumperType: 'standard', bumperType: 'standard',
ballOwner: 'player', ballOwnerBeforeHit: 'player',
creditedTo: 'player', creditedTo: 'player',
pointsEarned: 5, pointsEarned: 5,
}); });

View File

@@ -115,16 +115,18 @@ export class MatchTracker {
this.uncreditedBumperHits += 1; this.uncreditedBumperHits += 1;
} }
// Event-log использует имена полей из telemetry-spec.md v3.10 bumper_hit event,
// чтобы при future batched-emit можно было пробрасывать .getEvents() напрямую.
this.events.push({ this.events.push({
type: 'bumper_hit', type: 'bumper_hit',
timestamp: now, timestamp: now,
data: { data: {
source, source,
bumperType, bumperType,
ballOwner, ballOwnerBeforeHit: ballOwner,
creditedTo, creditedTo,
pointsEarned, pointsEarned,
matchTimeSec, matchTimeSeconds: matchTimeSec,
}, },
}); });
@@ -139,6 +141,7 @@ export class MatchTracker {
} else { } else {
this.aiGoals += 1; this.aiGoals += 1;
} }
// Event-log поля совпадают с telemetry-spec.md goal_scored event.
this.events.push({ this.events.push({
type: 'goal', type: 'goal',
timestamp: now, timestamp: now,
@@ -146,7 +149,7 @@ export class MatchTracker {
scorer: scoredBy, scorer: scoredBy,
isAutogoal, isAutogoal,
ballOwnershipBeforeGoal: this.ballOwner, ballOwnershipBeforeGoal: this.ballOwner,
matchTimeSec, matchTimeSeconds: matchTimeSec,
}, },
}); });
} }