Dash-Encounter Telemetry — Design
Dash-Encounter Telemetry — Design
Section titled “Dash-Encounter Telemetry — Design”Problem
Section titled “Problem”A live D1 query found human players die to chargers/spiders 27.2% of the time vs. 60-70% for
Ace’s soak sweeps — real evidence humans have a technique the AI lacks. But existing telemetry
only records end-of-run outcomes (death_cause, aggregate damage_taken) — there’s no way to
see what a human actually does DURING a charger/spider encounter: do they move laterally during
the telegraph, dash through the lunge, or something else entirely. Chris: “lets try this first
but it may be worth expanding the payload later, but lets see what this tells us” — explicitly a
first pass, not a final design.
What already exists (verified against source)
Section titled “What already exists (verified against source)”- Telemetry is fire-and-forget, one POST per event, no queue/batching (
net/gameplay_telemetry.gd— a singleHTTPRequestchild node,_http.request()called once per event, errors silently ignored). This session’s earlieris_autoaddition is the most recent precedent for extending this pipeline. - Two routes exist on the worker (
telemetry/src/worker.js):POST /sample(perf, every 5s) andPOST /gameplay(run summary, run-end only). No generic per-event route exists. - Two D1 tables exist (
telemetry/schema.sql):samplesandruns. No per-encounter table. - No existing code anywhere (render, main.gd, sim) already detects “a charge encounter
started/ended” as a discrete event — this needs to be built from scratch, reusing the same
sim.enemies.dash_phase[i]/dash_timer[i]/vel[i]/pos[i]fields this session’s AI work already established as the source of truth for dash state. sim.enemiescontact-damage attribution (_hurt_player,sim/sim.gd:2753) passes only the enemy’s TYPE NAME assource, not an enemy index — so there is no reliable way to attribute a hit to one SPECIFIC tracked enemy via the existing damage-source string if multiple same-type dash enemies are active at once. This design avoids that ambiguity entirely by using its OWN geometric contact check (pilot/enemy hitbox overlap) instead of the sim’s damage attribution — deterministic and per-enemy-specific by construction.
Non-goals (first pass)
Section titled “Non-goals (first pass)”- No AI/attract-mode capture — this only matters for human sessions, since Ace’s full movement
state is already available for free via local soak tooling. Gated on
not _auto. - No true damage/HP-based “was this a real hit” check — outcome is a pure GEOMETRIC contact
check (pilot/enemy hitbox overlap at any sampled tick during the tracked enemy’s lunge). This
means a player who overlaps the enemy’s hitbox but is invulnerable (mid-dash i-frames) is still
recorded as
outcome: "contact", not distinguished from a real hit — a known first-pass simplification, not a bug.player_dashed(below) lets a later analysis pass partially disambiguate this. - No batching — one POST per resolved encounter, matching the existing fire-and-forget pattern.
A busy run could fire several small requests; consistent with existing
net/patterns, not a new scaling concern for a 2-player personal project. - No dashboard visualization in this pass — raw D1 rows only, queried by hand (as done for the death-cause analysis this session). A dashboard view is a natural later addition, not built now.
Design
Section titled “Design”Detection & capture window
Section titled “Detection & capture window”A new per-tick tracker (client-side) watches nearby BEHAVIOR_DASH enemies (charger/spider)
relative to the human-controlled pilot, using the same detection radius the AI’s flee zone
already uses (THREAT_RADIUS = 300.0, ai/auto_player.gd) — a reused, already-tuned distance,
not a new arbitrary one.
- Start: an enemy within range transitions to
dash_phase == DASH_CHARGE(or, for an Elite Charger’s chain second dash, thegrow_tchain-telegraph condition this session’s AI work already established inai/skilled_auto_player.gd’s_is_dash_telegraphing). - Sample: every ~0.1s (≈6 ticks) during the window, record the player’s position RELATIVE to
the tracked enemy (
pilot.pos - enemy.pos), plus which phase it’s currently in (charge/lunge). Capped at 40 samples as a hard safety bound (covers even a full double-dash chain with margin). - End: the tracked enemy’s lunge completes (phase leaves
DASH_LUNGEwith no further chain pending) or the enemy dies/leaves detection range. - Outcome:
"contact"if the pilot’s and the tracked enemy’s hitboxes overlapped at ANY sampled tick during its lunge phase;"clear"otherwise. - Also recorded: whether the pilot’s dash was used at any point during the window
(
player_dashed), the closest approach distance observed, whether this was an Elite chain encounter (chain), and total duration.
Payload (compact, per encounter)
Section titled “Payload (compact, per encounter)”{ "session": "...", "build": 175, "is_auto": 0, "enemy_type": "charger", "chain": false, "outcome": "clear", "player_dashed": true, "closest_approach_px": 38.2, "duration_s": 2.14, "samples": [{"t": 0.00, "dx": 150.0, "dy": 0.0, "phase": "charge"}, ...]}New server-side pieces
Section titled “New server-side pieces”telemetry/schema.sql: newencounterstable (id, ts, session, build, is_auto, enemy_type, chain, outcome, player_dashed, closest_approach_px, duration_s, samples_json).telemetry/src/worker.js: newPOST /encounterroute, mirroring the existing/gameplayhandler’s parse-and-insert shape.net/dash_encounter_telemetry.gd: new client-side tracker (aNodehosting its ownHTTPRequest, matchingnet/gameplay_telemetry.gd’s pattern exactly), ticked once per sim tick frommain.gd, gated onnot _auto.
Testing
Section titled “Testing”- Unit tests for the detection/capture logic in isolation (start/end/outcome/sample-cap
correctness) against a constructed
Simwith a known dash-enemy trajectory — same pattern as this session’sai/skilled_auto_player.gddodge/orbit tests. - The worker route gets a manual smoke test (a real POST against the dev/staging endpoint) since this project’s telemetry worker has no existing automated test suite (verify this assumption at plan-writing time rather than guessing).
- Real acceptance: after deploying, play a session (Chris and/or Toby) and directly query the new
encounterstable to confirm rows land with sane values — the same manual-query pattern used for tonight’s death-cause analysis.