initHostPeerwas one-shot: any broker hiccup/ID rejection failed silently and left a half-dead Peer that poisoned every later attempt. Now: stale peer destroyed first, 9s timeout, up to 3 fresh room codes, then an anonymous-peer fallback where the broker-assigned id becomes a (long) room code — so any machine that can JOIN can HOST.- Join accepts long fallback codes (input maxlength 48), and shows a clear error after 12s instead of "Connecting..." forever. Verified live: room claimed first try.
- Toolbar rebuilt into mode-contextual groups — only the active mode's controls show (Nodes / Walls / Obstacles / Items / Erase). Compact labeled fields, slim styled sliders with glowing thumbs, small spinner-less number boxes, compact track-name box, dynamic one-line hint per mode.
- Custom SVG icons replace all editor emojis (toolbar, header, sidebar buttons).
- Obstacles & item boxes auto-detect their layer from the nearest track node on placement (works for V2 branch nodes too); the layer box displays the detected value.
- Hard ship-stat compliance: bot top speed ≤ ship top speed (skill ≤ 1.0), nitro uses the player multiplier, rubber-banding can no longer push past the ship's ceiling, accel derives from the ship's accel stat.
- Difficulty levels (easy/medium/hard): skill bands, cornering caution, rubber-band
strength. Button in the editor header +
bots <n>/botdiff <level>commands.
- Jank retro TTS for chat: per-player 8-bit robot babble (name hashes to base pitch
92–420Hz, square/saw/triangle wave, tempo), queued with a gap, repeated messages
within 30s print silently.
tts on/offto toggle. - Missile lock warning: audio-only RWR — a two-tone growl at lock, then crunchy bit-crushed pips that speed up as the missile closes. No HUD element.
- Real friend requests:
add friend <user/id>sends a request; receiver getsaccept <name>/decline <name>(+requeststo list). No more silent mutual adds. Legacy v1 requests still accepted for back-compat. - Unique usernames via the PeerJS broker as a name registry (
claimname <name>, andname <text>auto-claims): if taken → "username already taken". Friends can be requested by username instead of the AB12-CD34 code. Claims hold while online. commandchange <cmd> [alias]— rebind any terminal command (asks interactively if no alias given;commandchange <cmd> resetto undo). Persisted.
- Item badge + upgrade cards use bold filled SVG icons (rocket, shield, mine, EMP, etc.) — high-visibility, not wireframes. World missiles were already vector-drawn.
NOTE: the 07-01 "round 2" block below (ghost replay, pads, event feed, stats, themes) was lost in a merge — the current file does NOT contain it. Restorable on request.
All existing features kept identical in behavior; this pass deepens them and adds new systems.
Original file backed up as rogue-racer (4).backup.html.
- AI bot racers (solo Test Track): kinematic racing-line followers with curvature-based
corner speed, rubber-banding, lateral wander, random items (nitro/missiles/mines), full
health/death/respawn, lap+checkpoint tracking, podium entries, per-bot engine audio.
Bot count button (
🤖 Bots: N) in the Map Editor header, persisted in localStorage. - New items: 🔧 Patch Kit (+35 HP, local), 🌀 EMP (stuns+stalls racers within 260,
shield blocks it; synced via new
emp_blastmessage). - New upgrades: ♻️ Nanobots (1.6 HP/s regen), 🧲 Magnet (pickup radius 20→52), ⏱️ Overdrive (nitro 3s→4.5s).
- Juice engine: unified particle pool (engine exhaust, boost flames, wall-grind sparks, hull smoke when <38% HP, pickup/heal/EMP bursts), persistent skid marks while drifting, on-canvas toast/banner system, procedural jingles (lap, final lap, overtake).
- Parallax starfield + nebula backdrop, faint world grid for off-track motion sense.
- Dynamic speed zoom (eases out slightly at speed; zoom slider stays the baseline).
- High-speed radial streaks, boost heat vignette, pulsing critical-damage vignette.
- Animated countdown (digit pop + ring) and a fading "GO!" burst.
- Cars: neon under-glow, front-to-back sheen, layered boost flame, animated shield ring, orbiting stun sparks, respawn-invuln blink, hull bars over damaged racers.
- Race flow: LAP x/y + FINAL LAP! banners, overtake ▲/▼ toasts with blips, WRONG WAY warning, drift combo meter above the reset bar, speed readout heats up near class limit.
- Minimap: finish-line marker, live item-box dots, glow ring around your own dot.
Purpose: Replace GitHub track storage with local folders, add a lobby track browser with a draggable queue, make the queue actually load tracks in order, and build a full in-game match flow (podium, queue advance/loop, live leaderboard, synced upgrades, between-race screen).
- Run mode: file:// (double-click) → the File System Access API is NOT available, and cross-file fetch is blocked. So real OS-folder read/write is impossible at runtime.
- Storage approach (most reliable for file://): localStorage stores the
LocalandHistory"folders" (IndexedDB is unreliable on file:// opaque origins), with Export (download .json) / Import (file picker) buttons to move tracks to/from the realSaved tracks/LocalandSaved tracks/HistoryOS folders. Drop GitHub save/load from the main flow (kept as collapsed legacy). - Laps: track default saved with the map, host can override in lobby. Default 3, range 1–20.
- localStorage TrackStore:
rr-tracks-local+rr-tracks-history;normalizeTrackRecord,getLocalTracks/getHistoryTracks,saveLocalTrack(dedupe by name),deleteLocalTrack,recordTrackHistory,exportTrackRecord. Records carrylaps(default 3) for Phase 4. - On every track LOAD (race start), push a copy into History; keep only the last 10 (FIFO auto-delete). Hooked at all 3 race-start map-load points (guest start_race, host startRace, single/play-again).
- History entries show a Save button → copies that entry into Local (persist permanently).
- Export/Import buttons (download .json / upload file) to sync with the real
Saved tracksfolders. - Relegated the GitHub save+fetch path to a collapsed
<details>in the editor sidebar.
- Right-side lobby panel (
#lobby-tracks, host-only) listing all tracks under two folders:LocalandHistory. - Drag a track from either folder into the queue (+ button also adds); folder-aware (reads OS folder when connected).
- Persist the queue; queue is the authoritative play order (existing saveMaps/sendLobbySync).
- Queue loads tracks in order (host-driven), not random. → handled in Phase 3 (advance/loop).
- After a map completes, advance to the next track in the queue (not replay same track).
hostAdvanceQueue()commitsG.queueIndex = peekNextQueueIndex()thenhostLaunchRace. - At the end of the queue, loop back to the first track.
peekNextQueueIndexwraps(i+1)%len. - Host gets a "Back to menu" button on the post-race screen (
#results-menu-btn→hostReturnToMenu). - 30s auto-advance timer (
hostBeginPostRaceTimer, broadcastspost_raceeach second); skippable when everyone readies up (#results-ready-btn→post_race_ready→hostCheckPostReady). -
startGame()hides results-screen + clears post-race timer so guests transition cleanly.
- Leaderboard panel on the left (
#leaderboardin#race-status-panel), with a count-up race timer above it (#race-timer, starts at GO,Date.now()-G.raceStartTime). - Per-player status string next to name:
C x/total mm:ss L x/total mm:ss(checkpoint x/total + split time, lap x/total + split time), updates on each checkpoint/lap. Synced viaplayer_update(addednextCheckpoint,lastCheckpointTime,lastLapTime). - Customizable lap count:
#host-laps-input(1–20) in lobby host panel →G.lobbyLaps;resolveRaceLaps(map)(lobby wins, else track default) →G.totalLaps, sent instart_race {laps}. Replaced hardcodedTOTAL_LAPSin finish check + lap HUD withG.totalLaps.
- When ANY player is choosing an upgrade, pause the game for everyone (host-authoritative;
requestUpgradePause→hostAddUpgradeChoosersets a single 5s window,game_pause/game_resumesynced). - Overlay:
"[Username] is choosing an upgrade! [Time remaining]s"(#upgrade-pause-overlay, hidden for the chooser who sees the cards; lists multiple names if several choose at once). - 5s timer to pick; on timeout the game resumes for everyone (the chooser keeps the card screen open and their car's momentum resumes). Picks before timeout resume early once all chose.
- Cinematic podium showing all finish times first (
#results-podium, top-3 pedestals with staggered rise animation; full list showsm:ss.cctimes orDNF). Finish elapsed broadcast inplayer_finished {time}(relative to each client'sraceStartTime), stored asfinishElapsedMs. - Show which map is next (
#results-next, from Phase 3). - Let each player choose their ship (
#results-ship-grid→ updatescarType, syncs viaplayer_profile/lobby_sync so the next race uses it; respects host allowed-ships). - Host can configure match settings here too (
#results-host-settings: speed class, laps, owner/vote mode — mirror the lobby controls andsendLobbySync). - Spectate option for finished players while others race (
#spectate-barwith ◀ ▶ / Free cam;G.spectateIdretargets the render camera to a still-racing player).
- file:// blocks fetch + File System Access API → all "folders" are IndexedDB; real files only via Export/Import.
- Multiplayer is PeerJS host-authoritative: queue, lap count, pause state, podium, and timers must be host-driven and synced (sendLobbySync / sendToAll). Guests mirror host state.
- Keep paintTag/base64 payloads out of high-frequency syncs (perf).
Purpose: Replace current bridge entrance/exit behavior with a robust layer transition system that supports smooth jumps, no random snapping, and consistent multi-layer gameplay.
Status: Completed and implemented. Scope: Gameplay, rendering, map editor data, map conversion, and testing.
Completed or mostly completed:
- Replaced bridge-gate transition logic with heading-based slope threshold crossing in runtime.
- Added anti-jitter hysteresis for slope thresholds.
- Added acceleration-style falling state and tuning constants.
- Removed void-triggered snap-respawn behavior from movement flow.
- Added node-level slope authoring in editor (toggle + rotate + arrow visualization).
- Added slope metadata persistence in map load/save path and custom-track generation.
- Added authored-slope runtime usage with legacy bridge endpoint fallback.
- Added first-pass visual layer emphasis for cars (off-layer dimming and hidden nametags).
- Added dynamic layered render stack (all lower layers + one upper layer) with per-layer scale/alpha/brightness transforms.
- Added wall painting in editor (mode, side, force, bounce), persisted in map JSON and custom-track generation.
- Added first-pass runtime wall behavior (solid grind/push, bouncy reflection, open pass-through).
- Added explicit legacy bridge->slope conversion in map load and track generation paths (not only runtime fallback).
- Added built-in layer regression test harness (map editor button + deterministic conversion checks).
- Expanded regression harness to validate all currently loaded custom/queued/pending uploaded maps.
Still in progress:
- Expand segment-level paint tools for faster authoring of long multi-layer runs.
- Additional playtest/tuning pass on slope crossing feel at high speed.
- Add true transition animation (not instant layer snap): interpolate car render elevation/scale/alpha over a short ramp-crossing window while keeping collision authoritative.
- Add authored slope geometry rendering (wedge/ramp silhouette) so slope nodes visibly look like inclines instead of flat overlays.
- Replace current stroke-only per-layer road rendering with connected ribbon polygons per layer to eliminate "oval segment" artifacts and restore contiguous track pieces.
- Enforce continuity at support-layer boundaries: join adjacent same-layer spans with cap stitching so no visible breaks at segment seams.
- Draw layer-connector mesh at slope thresholds using neighboring layer edge points (not a generic rectangle marker) so entry/exit visually matches road width and heading.
- Add per-layer z-order blending guard so overlap windows at slopes do not produce ghosted duplicate road blobs.
- Add editor preview mode "Show Support Layers" with per-segment tint and boundary lines to verify where each layer exists before playtesting.
- Add automated visual regression checks for: connected road continuity, slope connector alignment, and no detached oval islands around transitions.
- Fix walls not being visible gray walls on the track and stuck as the sticky not slidey walls
Make elevation transitions feel intentional and smooth by replacing brittle bridge gates with node-authored slopes that move the player exactly one layer up or down based on approach direction.
- Smooth transitions.
- No glitchy layer flips at entrances/exits.
- No random teleport/respawn snaps from elevation logic.
- Jumps/skips are created by slope + open gaps, not by forced respawn mechanics.
- Depth is readable visually: current layer is primary, above/below layers are de-emphasized.
- Layers are conceptually unbounded: ..., -2, -1, 0, 1, 2, ...
- Transition step is always exactly +1 or -1.
- No hard top or bottom gameplay cap.
- Transitions are authored at nodes as slope thresholds.
- Crossing a slope threshold checks car forward direction vs slope direction:
- Facing slope-up direction => move up one layer.
- Facing opposite => move down one layer.
- Bidirectional crossing is allowed, result depends on heading.
- No forced cooldown unless needed for anti-jitter safety.
- Collisions are same-layer only.
- Obstacle/item interaction is same-layer only.
- Checkpoint/finish trigger contact requires layer match at touch time.
- Checkpoint progress memory persists across later layer changes.
- Void/open gaps remain as jump gaps (not deletion).
- No void-driven hard respawn mechanic.
- If unsupported at current layer, car falls over time with acceleration-like behavior.
- Falling should be visually smooth and readable.
- Track walls become explicit behavior modes:
- Solid wall: grind/slow with configurable constant opposing force.
- Bouncy wall: wall collision acts like obstacle bounce.
- Open wall: no support boundary; allows falling/jumps.
- Wall mode needs regional authoring and side selection (left/right).
Add layer-transition metadata at node level.
Proposed node fields:
- slopeType: "none" | "layerSlope"
- slopeDir: number (radians or degrees, choose one and document)
- slopeArrowStyle: optional visual hint config
Track/segment layer support:
- Keep existing spline generation, but attach explicit per-segment/per-sample layer support metadata.
- Remove reliance on old bridge endpoint gate semantics.
Add support map used by gameplay queries:
- supportLayersAtSplineIdx[idx] -> Set or min/max layer info
- Used for:
- is this layer supported at x,y
- item/obstacle spawn filtering by layer
- transition correctness
Add wall behavior metadata with region support:
- wallMode: "solid" | "bouncy" | "open"
- wallSide: "left" | "right" | "both"
- wallForce (for solid) configurable
- bounceGain (for bouncy) configurable
Replace old bridge gate logic with deterministic slope crossing:
- Detect line crossing against slope threshold line at node.
- Verify crossing is within track width.
- Compute heading sign:
- dirDot = dot(carForward, slopeForward)
- Apply layer change:
- dirDot >= 0 => layer += 1
- dirDot < 0 => layer -= 1
- Apply anti-jitter guard:
- spatial hysteresis around threshold (preferred over time cooldown)
- avoid repeated flip when parked exactly on threshold
If no support for current layer at position:
- Increase fall velocity in layers/sec over time.
- Integrate accumulated fall amount.
- Drop one layer each time accumulated fall crosses 1.0.
- On support regained, settle to nearest valid layer with smooth visual transition.
Initial tuning target:
- start around 1 layer/sec effective pace
- accelerate gradually
- tune by playtesting (requested: slower, smooth feel)
Implement robust query:
- getSupportedLayerSetAt(x,y)
- support comes from track geometry + authored wall/open state
- open-wall regions intentionally allow leaving support
- Remove any void-triggered immediate respawn logic.
- Respawn should only occur from explicit damage/death rules (existing combat rules).
Given player layer N:
- Current layer N: fully opaque (main readability).
- Above layer N+1: lighter and translucent.
- Below layers N-1, N-2...: darker and opaque, progressively de-emphasized.
Requested intensity guidance:
- about 33% shift per layer step (+/-33% gamma-style effect).
Requested visual depth:
- below layers scale ~0.9 per layer step
- above layer scale ~1.1 per layer step
- align by camera anchor so slope/drop positions remain coherent visually
Implementation note:
- Keep gameplay coordinates unchanged.
- Apply camera-space transform per rendered layer.
- Ensure threshold lines and cars line up perceptually during transitions.
User direction evolved during discussion:
- Show all lower layers (progressively transformed/faded).
- Show one upper layer.
Need performance guard:
- Add render depth cap for very high |layer difference| to avoid runaway draw cost.
- Same layer: normal.
- Other layers: dim.
- No nametags for non-current layers.
Add slope authoring at nodes:
- Toggle node as slope threshold.
- Direction arrow preview.
- Numeric indicator for up/down behavior.
- Two-tone arrow style:
- white side: up direction
- black side: down direction
Add painted wall regions:
- choose wall mode (solid/bouncy/open)
- choose side (L/R/both)
- adjustable force/bounce parameters
Keep void concept as jump gap/open support area, not as forced respawn trigger.
Auto-convert existing bridge maps into slope/layer metadata:
- bridge start endpoint -> slope threshold
- bridge end endpoint -> slope threshold
- inferred elevated span -> supported higher layer region
- convert bridge3 similarly as two-step encoded support where needed
Preserve old maps with conversion pipeline at load time.
Rules:
- Touch validity requires layer match at time of crossing.
- Checkpoint memory persists through later layer changes/falls.
- Lap completion remains finish-line based only:
- crossing finish completes lap only if all required checkpoints already remembered.
- finish crossing must not wipe checkpoint progress unless lap actually increments.
Must prevent:
- rapid layer ping-pong when hovering over threshold
- getting stuck between layers at intersections
- random repositioning caused by elevation code
- desync between visual layer and collision layer
Techniques:
- crossing hysteresis region
- single-source layer authority in physics step
- deterministic order of operations:
- movement integration
- slope crossing resolution
- support/fall resolution
- collisions
Ensure player state sync includes:
- authoritative layer
- transition/fall interpolation state (if required for remote smoothness)
Remote clients must render same layer-depth rules without changing gameplay authority.
Potential hotspots:
- rendering many lower layers
- per-layer obstacle/player draws
Mitigations:
- max visible depth cap configurable
- cull layers with negligible alpha/scale contribution
- cache transformed track paths where possible
- Remove remaining legacy bridge gate behavior (runtime now prefers authored slopes).
- Implement slope threshold crossing (+1/-1 by heading).
- Add anti-jitter hysteresis.
- Implement support query by layer (bridge/spline + void/open wall behavior integrated into runtime boundary/fall flow).
- Implement acceleration-based falling between layers.
- Verify no void-triggered respawn paths remain.
- Layer-based draw transforms (scale/alpha/gamma emphasis).
- Current/lower/upper visual priority.
- Dim/hide nametags for non-current-layer players.
- Node slope tools and arrow UI.
- Wall painting modes + side selection.
- Parameter controls for wall force/bounce.
- Bridge->slope conversion on load/generation.
- Regression tests for conversion paths (automated harness in editor).
- Cross slope forward: layer +1 exactly once.
- Cross same slope backward: layer -1 exactly once.
- Idle on slope line: no oscillation.
- Closely spaced slopes: no stuck state.
- Leave support at speed: smooth delayed drop, then acceleration.
- Multi-layer fall across open gap: layer decrements over time, no teleport.
- Re-enter support during fall: clean settle, no jitter.
- Car-car collision same layer works.
- Car-car different layer does not interact.
- Obstacle/item same-layer only behavior holds.
- Checkpoints persist through layer changes.
- Finish requires remembered checkpoints.
- Finish crossing does not reset checkpoints unless lap increments.
- Current layer readability remains high.
- Lower layers darker, above lighter/translucent.
- Scale/parallax alignment keeps slopes visually coherent.
- slopeHysteresisDistance
- fallAccelLayersPerSec2
- fallInitialLayersPerSec
- fallTerminalLayersPerSec
- lowerLayerScaleStep (default 0.9)
- upperLayerScaleStep (default 1.1)
- layerGammaStep (default 33%)
- wallSolidForce
- wallBounceGain
- maxRenderedLowerLayers
Done means:
- Transitioning up/down is deterministic and non-glitchy.
- No random track snapping from elevation/void logic.
- Jumps/skips can be built intentionally with slope + open gaps.
- Layer visuals clearly communicate depth while preserving gameplay clarity.
- Existing bridge maps load into equivalent layer behavior via conversion.