Implement defensive setup scoring v3.10

Реализует финальную модель scoring из data-contracts.md v3.10:
fixed center bumpers — по last-touch; setup-bumpers — defensive trap
(награждают владельца только когда мяч принадлежит сопернику).
Self-farm запрещён. Neutral ball не даёт очков нигде.

Code changes:
- types/index.ts: BumperScoreSource union ('fixed_last_touch' |
  'player_setup' | 'ai_setup'); MatchResult.aiBumperPointsEarned +
  aiBumperHitsCount.
- Bumper.ts: новый required field source: BumperScoreSource;
  передаётся в onHit callback через bumper.source.
- scoring/MatchTracker.ts: pure helper computeCreditedTo(source, ballOwner)
  → реализует 9-cell credit matrix; recordBumperHit принимает
  (rawPoints, source, ballOwner, type, ...) и возвращает {creditedTo,
  pointsEarned}; новые getter'ы getAIBumperHits + getUncreditedBumperHits.
- scenes/MatchScene.ts:
  * Fixed bumpers создаются с source='fixed_last_touch'
  * placeSetupBumpers передаёт source per side
  * handleBumperHit (новая сигнатура: bumper, points) рутит в tracker,
    emit'ит spawn-particles + telemetry bumper_hit event с полным
    набором полей per telemetry-spec.md v3.10
  * MatchResult заполняется с AI BP/hits
  * match_end telemetry также включает aiBumperPointsEarned/HitsCount
  * HUD: BP теперь сравнительный «P x / AI y»
- scenes/ResultScene.ts: breakdown показывает обе стороны BP и hits.

Particle effects (DoD v3.10):
- 12 псевдо-частиц радиально от bumper'а (cubic ease-out, 320ms)
- цвет: player=magenta, ai=cyan, null=grey
- screenshake 80ms/0.0025 ТОЛЬКО на credited hit
  (иначе экран дрожит на каждом нейтральном отскоке)

Tests: scoring/MatchTracker.test.ts — 21 теста:
- 9-cell credit matrix (computeCreditedTo, все source × ballOwner)
- recordBumperHit с counter assertions
- многократные hits (накопление BP per сторона)
- симметрия player/AI defensive trap scenarios
- fixed bumpers через смену владения
- events log для telemetry
- recordGoal с autogoal-flag

Итого 50/50 unit-тестов (16 calculateScores + 13 flipperGeometry +
21 MatchTracker). typecheck/lint/build все зелёные.

Несоответствия с docs остающиеся:
- DoD: «12 частиц per credited side» — done; SFX bumper hit (DoD line 247)
  — TODO Phase 4 (asset pack).
- Particle effects через psecdoupartials (Arc + tween), не через
  ParticleEmitter — упрощение, визуально эквивалентно.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
aevgarik
2026-05-24 07:27:01 +03:00
parent a0ed17d814
commit bfdb24294a
8 changed files with 367 additions and 47 deletions

View File

@@ -42,7 +42,7 @@ per личность, названный тестером без подсказ
| Личность | Tells |
|---|---|
| **Defensive (Бэтти)** | Setup: 2 slingshot в углах ворот + 1 турбо центр + 1 curve + 1 пусто. Поведение: реакция надёжная (`reliabilityMul: 1.1`, capped 1.0 = почти 100 %), чуть быстрее (`reactionMul: 0.9`). AI пропускает редко; матчи длиннее обычного. |
| **Aggressive (Турбо)** | Setup: 2 турбо в центре (фарм) + 1 slingshot угол + 1 curve + 1 standard. Поведение: реагирует с большего расстояния (`triggerDistBonus: +80px`), чаще промахивается (`reliabilityMul: 0.85`). Высокий счёт у обеих сторон, быстрые матчи. |
| **Aggressive (Турбо)** | Setup: 2 турбо в своей зоне (defensive trap против мяча игрока) + 1 slingshot угол + 1 curve + 1 standard. Поведение: реагирует с большего расстояния (`triggerDistBonus: +80px`), чаще промахивается (`reliabilityMul: 0.85`). Быстрые матчи; игрок, застрявший в setup Турбо, быстро отдаёт ему очки. |
| **Trickster (Хитрый Хо)** | Setup: 4 curve bumper'а (лабиринт) + 1 турбо центр. Поведение: 15 % случайный спайк jitter (×1.3) — реакция «дёргается». Непредсказуемый счёт; необычные траектории мяча. |
| **Ghost (Эхо)** | Setup: balanced (2 slingshot + 1 турбо + 1 curve + 1 standard). Поведение: 15 % «echo double-swing» — обе стороны активируются одновременно, даже если мяч идёт только в одну. Медианный счёт; характерный двойной flip. |