- Faction-controlled systems now spawn only opposing-faction hostiles (never pirates/neutrals), and local-faction fighters join the player as allies during the fight.
- Rework patrol, bounty, and escort side-quests to the faction war model: patrols are always in-system vs one seeded rival; bounties target a rival's member in a hostile neighbor gated on jump-gate access; escorts rescue a trapped transport in hostile space and deliver it back through the gate into friendly territory.
- Implement per-faction reputation standing (setFaction/changeFaction) with save persistence; faction-owned places now resolve standingFor() via the controlling faction.
- Job boards on faction worlds post only faction-context missions with generic war-voice descriptions ({enemy} placeholder); neutral boards unchanged.
- Update all dev test suites (encounters, bounty-hunts, escort, escort-flow, reputation, sidequests) to cover the new faction rules and gate locks.
- 8 new enemy types (frames 50–57) split 4/4: combo seekers fire flank-angle homing missiles with capped turn-rate; elementals throw darts that bloom into growing, damage-ticking electrified zones
- Balance follows the faction rule (2× damage, 3.5× armor vs pirate raider), documented in combat.json comments
- New `flank_barrage` and `shock_dart` abilities in EnemyAbilities.js with shared `stepShock` frame step wired into CombatSystem.update
- Generalized `_combatBootMode` to accept any id from `factionIds()` so new factions need no code change
- Painted frames 50–57 in ships-enemy.png/.psd; added capital-mindweave-surface-01 reference art
- Tests: dev/zenith.test.mjs (Node), dev/zenith-arena-browser.mjs (Playwright), updated combat.test.mjs roster and sheet-art checks, extended cdp-faction-arena.mjs with zenith tab
- Register 8 Apex (frames 30-37) and 8 Mindweave (frames 40-47) in
data/combat.json enemyTypes at 2x damage / 3.5x armor vs the pirate,
each with a distinct weapon pattern and ability kit
- New shared skill layer (js/combat/EnemyAbilities.js): cloaked ambush,
flank strafe, blink reposition, illusion decoys, telekinetic waves;
wired into CombatSystem update/collide with cloak gating on fire,
targeting, and player skills
- Add sweep/spiral/shell weapon patterns in js/combat/Weapons.js
- New ?combat=apex / ?combat=mindweave duel arena with an in-game
DuelPicker (keys 1-8 / click, 0 = whole pack); plain ?combat demo
unchanged
- Paint the 16 missing spritesheet frames (ships-enemy.png) so the
fighters are visible; add a PNG alpha regression test guarding against
blank cells
- Add dev/cdp-faction-arena.mjs CDP smoke check and extend
dev/combat.test.mjs with faction roster, balance, ability, duel, and
sheet-pixel checks
- Combat bands split at level 6 (5–7), 9–12, and 13+ (9–12 cap) with
weighted pirate rosters; compose() now draws `pirateTypes` from the band
roster and GameScene uses it when spawning one-shots.
- Patrol encounters gain tiered bands (levels 6+) with mixed rosters,
fatter rolling waves (up to 20 deep / 6 concurrent), contact-count odds
overrides, and per-tier `xpScale` dials; legacy array rosters normalize
to the weighted table.
- Bounty hunts replace the stubbed marksman band with tuned tiers
(marksman 6–10, deadeye 11–14, apex 15+): growing pickets and escorts,
escort roster mixes, elite armor/damage scales 3×–4×, and `xpScale`
trims so payout pace tracks the curve.
- Tests updated to assert the new band shapes, roster draws, XP scaling,
and tier resolution; skills-bar screenshots refreshed.
- Raise a new GameOverOverlay (scrim, semi-transparent "GAME OVER" headline, Return to Main Menu / Continue) when the player's hull breaks in combat
- Add _playerWentDown beat that freezes the world and owns the screen, gating input and the update loop while active
- Route the death decision through a skill seam (_playerDeathRespawns / _syncDeathFlowRespawn) so a future respawnOnDeath skill can override; default ends the run
- Wire Continue to resume the newest save (SaveManager.latest -> prepareLoad) then scene.restart, mirroring the menu's Continue
- Update combat.json playerShip comment to reflect the real death flow replacing the placeholder respawn in the main game
- Add toxic-takeoff-01.mp4 asset
- Add canvas-drawn comet head, arrival fireball, and damage-radius ring textures in Fx.js, replacing the old single-dot streak/ring combo
- Switch all ram-related (and general glow) sprites from ADD to SCREEN blend to fix the "dark box" artifact over semi-transparent nebula backgrounds
- Introduce ember trail particles that linger ~1 s behind the hull during the dash, plus a flickering comet head that rides the ship and is released on impact
- Add `ramBlast()` impact sequence: ignition flash, blooming fireball, exact damage-radius ring, shock ripples, and debris sparks
- Wire in the heaviest camera shake beat (`ram`) and layer the data's sfxEvent under a thunder boom at impact
- Bump skill fx `ringLifeMs` to 480 so the damage-radius ring lingers long enough to read
- Add dev CDP harnesses (`cdp.mjs`, `ram-shot.mjs`, `ram-video.mjs`) and reference stills/video for the charge's three key frames
- New `data/skills.json` (registered in manifest) owns every number/string for the
per-class skill trees, type taxonomy, seat table, bar look, and window copy;
code only reads behavior off it so adding a third skill is a data edit.
- Pure skill model + ledger (`js/skills/SkillModel.js`, `SkillState.js`): rank
points from the class bank, spend-through-unlock edges, seat bindings, save
round-trip via `record.playerSkills` (old saves load as an empty ledger).
- Combat runtime in `CombatSystem.js`: cast pipeline with per-skill recharge
clocks, energy spend/regen (now on by default), Ramming Charge dash + arrival
blast (4x..12x weapon damage by rank), Reinforced Plating hull pool re-rolled
from base so it never compounds; charge passes through the pack and settles once.
- UI: full-screen Skill Tree console (`js/ui/SkillWindow.js`) opened from the ship
console's SKILL TREE door (lit when the class owns a tree), combat bar seats draw
icon/rank/cooldown/affordability states with click-to-assign and right-click clear,
keys 1-8 cast or place.
- Evidence: new Node + Playwright suites (`skills.test.mjs`, `skills-combat.test.mjs`,
`skills-bar-browser.test.mjs`, `skills-window-browser.test.mjs`,
`skills-devdoor-browser.test.mjs`) plus screenshots in `dev/shots/`; README and
PROJECT_NOTES document the layer and dev doors (`?skill`, `?skilltree`).
- New "Escort" contract type on neutral job boards (minLevel 3): the player
links a persistent transport + 1-2 escort fighters at a local waypoint,
flies them through a seeded 1-2 hop gate route to a planet/station, and
delivers with the sky clear.
- TransportShip extends NeutralShip with a lapsed break-off window and a
`persistent` flag so CombatSystem's active/neutralOnly gates skip it; the
convoy never holds the jump gate or landing door open.
- Dedicated escort EncounterDirector (9-18s rolls, 85-100% chance) fires
mixed-fighter waves ~5x more frequently than normal encounters; one wave at
a time via the patrol contact's law.
- Transport loss fails the contract; landing on a planet resets it with a
fresh rendezvous in the current system and full-armor transport, same
destination.
- 20 unique escort dossier descriptions, reward band raised to 120-175 CR.
- Three new test suites: escort.test.mjs (model), escort-flow.test.mjs
(scene flow), escort-browser.test.mjs (real browser boot + board render).
- New EncounterDirector (js/combat/Encounters.js): pure, data-driven clock that rolls pirate / neutral / mixed encounters in neutral non-home systems on a ~45–90s interval at 50–60% chance, one at a time with a 30–60s cooldown, never during a live wave run or on a downed player.
- Neutral ally ships (js/combat/NeutralShip.js) on spritesheet frames 20–27: escort the player for a per-ship 10–15s window, engage the nearest pirate in mixed fights, then break off and fly off-screen; player-safe by construction (never targeted or damaged by ally bolts/seekers).
- Pirate targeting generalized so hostiles can draw aggro onto escorts when the player is out of range; escort and pirate bolts/missiles collide with each other but not the player.
- Group size scales with combat level (1–2 low → 4–5 high, bands in data/combat.json); encounter hostiles are one-shots so the fight ends when the sky clears.
- New 'volley' weapon pattern (wide staggered-speed fan) and owner-tagged bolts/missiles for ally vs enemy routing in Weapons.js.
- Neutral roster config + encounters section added to data/combat.json; EnemyTypes gains neutralTypeConfig/Ids/SheetList.
- Tests: dev/encounters.test.mjs (cadence, gate, weights, size bands), dev/neutral.test.mjs (roster, escort AI, targeting, no friendly fire), plus combat/xp test updates; CDP probes and encounter-hud-check for in-browser HUD verification.
- Raiders now roll an attack comm on-screen (50%) or on rolling-wave arrival (20%), and a defeat comm on kill (20%), driven by per-type config in combat.json
- New bounty-attack comm fires when the wanted leader's fight stands up in GameScene._fireBountyEngagements
- Ship 12 pirate speech clips, 3 enemy video clips, and register three new senders (pirate, pirate_defeat, pirate_bounty) in comms.json
- Wire onComm seam through CombatSystem so the scene can arm CommsHub triggers; rolls are no-ops when the seam is absent
- Add combat, bounty-hunt, and comms test coverage for the new roll logic, trigger arming, asset presence, and roster count
- New hostiles frames 1–7 (bomber → warden) with distinct stats and
monotonic difficulty ramp in data/combat.json
- Shared js/combat/Weapons.js dispatch for lance, spray, burst, rapid,
ring, missile, and combo patterns used by both combat contexts
- New dodgeable heat-seeking Missile (capped turn-rate homing) plus pure
geometry helpers in Combat.js
- Rewire ?combat boot to startWaves('combat_demo'): 7 rolling waves, one
type each, 6 ships per wave, 3 concurrent, replacement per kill
- Add missile_fire SFX voice and extend tests/CDP demo checks for all
patterns and the full wave run
- Level 1 now costs 5 kills of the base craft instead of 10
- Total XP to reach level 80 drops from ~17.4M to ~8.7M
- Update XpManager default, progression data, tests, and README to match
- Kills grant the hostile type's own `xp` (the roster sets XP-per-threat;
the curve lives in data/progression.json, 100 x n^2, capped at level 80)
- Each crossed level fires one `levelup` event: +3 hull / +2 shields with
a full heal, cyan burst + a distinct voice, and a kill-feed line — the
placeholder toughness the skill layer will subscribe to later
- Combat bar gains an XP row below the orbs (level, live gold fill,
banked / next cost; hidden when the snapshot carries no xp, so the
range bar keeps its old face)
- Kills show a gold "+N" reward floater; the level-up's SFX is the new
8th combat event (solar_flare_01)
- XpManager is pure and tested headless (node dev/xp.test.mjs); the
ledger saves as { level, xp } and old saves load fresh at level 0
- dev hooks (xp/grantXp), sfxcheck (range seven, main eight), shot
captures, and README all follow along
- Add inView() pure geometry helper to test if a craft's visual circle is at least partly within the camera's visible rect
- Gate enemy fire, auto-fire target selection, currentTarget lock, and bullet collision checks behind craftVisible() so off-screen hostiles hold lances, can't be engaged, and are immune to tracers until they fly into view
- Extend pickTarget() with an optional qualifies predicate to narrow the candidate pool (used for the on-screen rule)
- Document the rule in data/combat.json and CombatSystem class comments
- Replace dot tracers with elongated glowing bolts (trail band + core streak + head bloom)
and a directional muzzle flare in both the range and main-game contexts.
- Add enemy impact reads: core flash, directional sparks along the travel vector,
cyan arc ripple on shield hits vs orange shards on hull hits, white craft hit-flash,
and a polished damage-number pop.
- Add player shield-sphere strike effect: full-sphere outline + bright arc centered on
the laser's incoming bearing, radial ripple, spark burst, and a shield-collapse cue
when shields break to hull.
- Add subtle world-camera shake on player hits, enemy kills, and shield-break while
keeping the HUD/combat bar perfectly still via the fx-ui camera split.
- Expose new visual knobs (bolt size/trail, muzzle, arc span, shake intensity) in
data/combat.json; fix Fx.update destroying ships on tintFlash expiry.
- Remove the gate-to-capital midpoint origin logic (_pirateAmbushOrigin) and
the startWaves opts.origin seam; wave ships now materialize off-screen
around the player from a random direction, matching the ?combat pack.
- Update combat.json, comms.json, quests.json comments to reflect the new
spawn behavior (off-screen around player, streaming in on chase AI).
- New main-story quest granted after 'Special Delivery' is claimed AND its payoff comms are ACKed all the way through (unlocks.acked gate)
- Three-step checklist: speak with Trestle (trestleQ4 warning beats), defend from a rolling wave of six raiders at the gate-to-capital midpoint, return to the Neutral Capital World
- CombatSystem.startWaves gains an origin option so ambush enemies materialize in a ring around a specified point rather than off-screen around the ship
- CommsHub fires a new onAck hook before onEnd so the scene can record acknowledgments and run its grant pass in the same tick
- QuestState.isUnlockMet / grantUnlocked accept a gates.acked set; GameScene supplies it from the comms acked-ledger
- SaveData captures/restores comms.acked, combat.done, and story.capitalReturned; resetRunState clears all three on New Game
- Combat is now built for every scene (not just ?combat demo); the hostile pack spawns only in the sandbox boot
- Test suites updated: quest unlock gating, ambush wave shape, roster count 25→28, save round-trip of new ledgers
- Replace vertical gauges with draining hull/shield/energy orbs
(hull with overlapping shield on the left, energy on the right).
- Share the main game's CombatBar in the combat range and add
range energy tracking with optional regen.
- Make gauge hit flashes shape-aware: circle orbs get a soft bloom,
rects keep the legacy white pulse.
- Update combat data and assets for the new orb-strip storyboard.
- Waves.js: pure wave runner (rolling/fixed) + CombatSystem.startWaves
for quest-step combat; named scripts (probe/skirmish/raid) ship in
combat.json, driven from the console until the quest hook lands
- EnemyTypes.js: named hostiles (raider/bomber) deep-merged onto the
base craft, with per-type textures preloaded via enemySheetList()
- Hostiles now materialize off-screen (camera edge + game.spawnLead)
and fly in on their chase AI instead of popping into view mid-fight
- Wave ships are run-owned: no self-respawn, kills feed rolling
replacements, and the combat bar stays up through the lead and gaps
A shared readability layer (js/combat/CombatHud.js) for both combat
contexts — the training range and the main game's ?combat:
- Target/lock indicator: rotating brackets on the current target (the
auto-fire target — one rule, one target, js/combat/Combat.js#pickTarget).
- Threat cues: pulsing markers above each live hostile.
- Off-screen arrows: edge arrows toward hostiles outside the camera
(zoom-aware via a context-supplied worldToScreen).
- Kill feed: newest-first fading entries, top-right.
- Gauge flash: the combat bar's shields/hull gauge flashes on the hit
it took (CombatBar.gaugeRect feeds the shared CombatHud.flash).
Pure rules stay in Combat.js (pickTarget, feedPush, offscreenArrow),
covered by dev/combat.test.mjs. The main game re-anchors the pack
around the ship once its start position settles (CombatSystem
.resyncAroundShip + a short settle window in GameScene), so the demo
pack sits in auto-fire range instead of kilometers off.
All combat rules tests pass (0 failures); CDP-verified in both
/combat and index.html?combat.
- js/combat/Combat.js: new pure handleFeel() step (smoothed turn
velocity, ramped/decaying throttle, instant brake) with EXACT
analytic integration of the eased turn — frame-rate independent by
construction (dev/combat.test.mjs: feel block, 0 failures).
- data/combat.json: combat.player tuning surface — thrust 2000, drag
1.8 (now actually overrides ship.drag in the range), rotSpeed 6.4,
rotApproach 20, thrustRamp 18, thrustDecay 30, brake 0.9.
- js/combat/CombatScene.js: updatePlayer runs the shared feel step.
ALSO FIXES WASD: this Phaser build's addKeys() object form only
resolves single KeyCodes names, so {up:'W,UP',...} put every name on
ONE shared Key with keyCode undefined and isDown polling could never
see a key-press (WASD was dead). Now keys drive off the working
keydown-/keyup- event API (the main game's pattern) with real Key
objects registered for capture; window-blur clears held keys.
- dev/combat-flycheck.mjs: CDP flight-feel check — thrust 60% in ~300
ms, 30° turn in ~150 ms, sustained spin ≥ rotSpeed, stop from full
in ~300 ms (headless ~24 fps budgets; PASS).
- Add CombatSystem: owns the hostile pack, tracers, player armor/energy,
auto-fire (nearest-in-range), swept-hit collisions, rams, respawn — all
driven by data/combat.json `game.*` / `energy` sections
- Add CombatBar (Diablo 4 style): vertical shields + hull gauges on the
left, energy gauge on the right, eight skill seats (keys 1–8) in the
middle; shown in place of the action deck while combat is live
- `?combat` now boots the normal game with the pack spawning when the
player starts (mouse steers, cannon auto-fires); the standalone WASD
range remains at `/combat` / `combat.html`
- GameScene wires the system into its update loop, solid constraints, and
click-through gate; exposes `window.__ORBIT_COMBAT__` for DevTools
- Extend combat tests to cover CombatSystem lifecycle (spawn, auto-fire,
hits, rams, armor, respawn, cast stub, destroy)
- New `combat.html` + `/combat` dev-server route + `index.html?combat`
boot a fixed arena where the player ship fights a pack of enemy craft;
all tuning lives in `data/combat.json` (thrust, hulls, shields, lance,
rams, respawns).
- Add `js/combat/` modules: CombatScene, EnemyShip (standoff-ring AI +
lance), Bullet, Fx, StarDrift, and pure rules in Combat.js (armor
damage, orbit step) exercised by the new `dev/combat.test.mjs`.
- Extract shared `awaitFonts` into `js/utils/Fonts.js` so both the game
and sandbox boots use one copy.
- Preload the enemy spritesheet in GameScene so the texture is ready when
combat ships in; add `ships-enemy.png` (+ `.psd`) art.
- Expose `window.__COMBAT__` (reset, spawnEnemy, player, kills) for
devtools tuning; R resets the range, P freezes it.