Server: HealthApplyDamageSystem marks EnemyTag Dying{UntilTick} (TickUtil.
NonZero, ~54 ticks) on the lethal 0-crossing instead of instant destroy,
zeroes every live cue (replicated AttackWindup, LungeState + IsLunging bit,
KnockbackState), destroys on expiry; plain-world tests keep instant destroy
(no NetworkTime) so the suite's assertions stay meaningful.
Every consumer now ignores corpses (all confirmed entity-count/unfiltered by
the design review): EnemyAISystem all 4 passes, BossAISystem brain + summon
cap, RoomEnemyDirector room-clear + MaxAlive fit, WaveSystem cap + cleared,
CyclePhaseSystem breach wipe + DefendCleared, ThreatDirector timeout cull,
TurretFire targeting, CoreDamage drain, ProjectileDamage snapshot (corpses
are not shields), AbilityFire auto-aim candidates, debug cull + telemetry.
Wipe passes skip Dying to avoid cross-ECB double-destroys.
Client: EnemyAnimationDriveSystem finally drives the controller's IsDead
param (Health.Current<=0 is the replicated death read; corpse locomotion
zeroed); CombatFeedbackSystem moves the kill CRUNCH to the 0-crossing (the
prune-edge timing would land it ~1 s late) - the prune keeps a small
dissolve puff for corpses and the legacy full read for alive-vanish culls.
456/456 EditMode.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Portal-gated rooms: new RunLifecycle.RoomExplore loot window; room+nodes
persist past the last kill; PortalInteractRequest -> server-only PortalCommand
advances (client derives the portal pos). RunDirectorSystem moves teardown off
the InRoom edge, relocates the boss-branch/route-gate into the RoomExplore exit,
and advances an empty expedition immediately (incl. a boss clear).
Class-at-base via a shared ClassSwapUtil (meta-band strip + per-class
MetaTierState replay, so the base RPC and the dev SetClass can't drift);
ClassSelectRequest Staging-gated. Per-run PREP buffs (PrepPurchaseRequest,
PrepCatalog): once-per-run == the row's prep StatModifier band is present,
TotalOf-before-Withdraw atomic, stripped on Returning beside the boon band.
Health.Max promoted to [GhostField] (the one ghost-hash re-bake) so the boss +
floating enemy HP bars read it directly. Feel: melee cone connect-thunk (C3),
hit-stop throttle so a horde wipe can't stutter (C4), Spitter cornered-hold.
456/456 EditMode; two adversarial reviews (pre-code 13-confirmed folded;
post-impl clean bar the RoomExplore boss-clear dead-time, fixed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BossState (server-only) + BossAISystem: sole mover, seek -> telegraphed
radial slam (AttackWindup) -> phase-2 speed + swarmer summon. Excluded from
the EnemyAISystem Charger pass; knockback-immune at all 3 stamp sites;
arena-anchored spawn; scaled Health/HitRadius/AttackRange. Grunt windup now
commits in its last ~30% (whiffs out-of-range); Charger stagger roots; grunt
speed 4.2->5.2; swarmer telegraph synced. SweptMove + zone-enemy spawn-stamp
extracted to shared helpers. Zero new [GhostField].
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the server-authoritative mechanics for three new enemy archetypes on top of
the Grunt/Charger base, plus the weighted wave-composition that introduces them:
- Spitter: a ranged Husk variant (SpitterState) that holds a preferred range-band
(advance/retreat/hold via EnemyAIMath.BandVelocity) and fires a telegraphed,
dodgeable EnemyProjectile. New server EnemyProjectileMoveSystem (integrate +
store LastStep) + EnemyProjectileDamageSystem (region-filtered swept hit-test
rebuilt from LastStep — DR-018 anti-tunnelling; players use HitRadius, structures
a const radius; at-most-once destroy). Concurrent-spit soft cap, soft-fail retry.
- Swarmer: marker tag + deterministic cluster spawn (1 slot = 1 pack;
EnemyAIMath.ClusterOffset), MaxAlive counts ENTITIES so a pack defers if it
won't fit.
- 4-type weighted mix: MixBands -> ZoneEnemyMath.WaveSlots/KindForSlot/
PackSizeForSlot drives both the expedition director and (fork-4a) the base siege,
with a mandatory MaxAlive cap. Legacy WaveSize/IsChargerSlot kept + parity-tested.
- Discriminator stays component-presence (no enum in Bursted systems): query-
partition guards keep each enemy moved by exactly one EnemyAISystem pass
(sole-Position-writer). EnemyTelegraph.IsCharger -> Kind byte for the client cue.
New authoring (Spitter/Swarmer/EnemyProjectile) + expanded director authorings with
tunable mix/cluster defaults. 13 new EditMode tests (mix composition + legacy parity,
band/cluster math, projectile move + cross-region + swept anti-tunnelling regressions);
full suite green before commit.
Dormant until the prefab/subscene wiring lands (next): the new systems guard on
TryGetSingleton/RequireForUpdate, so with no prefabs wired the new types stay inert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reactivate the dormant Expedition region as a procedural combat venue.
v1 loop: walk the gate -> fight an epoch-seeded enemy wave in the
expedition -> clear -> return -> flat Ore reward (once per epoch) ->
escalated retaliation base siege.
- New sim types: ZoneEnemyTag, ZoneEnemyDirector (+ ZoneEnemyPrefab
buffer), ZoneEnemyState, ZoneEnemyMath (grunt->charger composition
by epoch). ZoneEnemyDirectorSystem (server, Burst): drip-spawns the
wave at a deterministic ring under a MaxAlive cap while a player is
out and the base is Calm; marks ClearedThisEpoch on a real clear.
[UpdateAfter(ExpeditionFieldSystem)] only (avoids a sort cycle).
- BLOCKER 1: EnemyAISystem region-filters target selection (player +
structure snapshots gain parallel region lists; no base structures /
no Core fallback for expedition husks).
- BLOCKER 3: WaveSystem, ThreatDirectorSystem timeout cull, and
CyclePhaseSystem DefendCleared + Core-breach cull all count/cull
RegionTag{Base} husks only (the breach cull was caught region-blind
by the post-impl review: a base breach wiped the live expedition
wave and spuriously paid the reward).
- BLOCKER 4: reward de-duped via CycleRuntime.LastRewardedEpoch +
ClearedThisEpoch; ExpeditionGateSystem deposits RewardOre once/epoch.
- ExpeditionFieldSystem teardown also culls zone enemies + region-
guards the clutter loop. Subscene wired with the director + roster.
368/368 EditMode green + clean netcode Play smoke. Docs: DR-040 ->
built, session log, CLAUDE.md cross-region tag-reaudit rule.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Editor-only class swap via the existing scalar dev-RPC family (new DebugOp.SetClass): F1/F2 keybind (ClassSwitchHotkeySystem), DebugOverlay '- Class -' buttons, and DebugCommandSendSystem.SetWarrior/SetRanger/SetClass statics. Server (DebugCommandReceiveSystem) swaps class in place on the spawned player: strips+re-seeds the ClassTraits StatModifier seeds, swaps the AbilityRef Fire slot, resets the ability cooldown, and heals a LIVING player to the new max (dead players skip the heal so respawn isn't raced). Server-authoritative + prediction-correct (same buffer-mutation path as GrantUpgrade); wire type unchanged so the RpcCollection hash is unaffected.
ClassTraits gains a shared Seeds core (spawn + swap can't drift), ClassSeedCount, IsClassSeed, a DynamicBuffer AppendSeeds overload, and Reapply. +3 EditMode tests (exact-count round-trip, value-equality fold, boundary/foreign-mod preservation); 351/351 green; Warrior<->Ranger round-trip Play-validated (server+client agree).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The per-player class travels on GoInGameRequest.ClassId (client reads a ClassSelection
singleton); GoInGameServerSystem seeds the class at spawn via ClassTraits (AbilityRef +
permanent trait StatModifiers on a reserved ClassSourceId; CharacterStatsRef stays Default
so the DRG-asymmetry deltas ride the replicated OwnerSendType.All buffer). AbilityFireSystem
gains the aim-directed Cone archetype: cooldown predicted both worlds, server-only cone
damage to living enemies (same-tick, SourceTick-stamped, like the melee cleave). 345/345.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Foundation for Two Classes (DR-037). New ids (CharacterId.Warrior/Ranger,
AbilityId.WarriorCone, StatTarget.MeleeDamage/MeleeRange); CharacterStatsRef.Id ->
[GhostField] so the owning client folds the right class stats; MeleeComboSystem
folds per-player MeleeDamage/MeleeRange off the replicated StatModifier buffer
(HasBuffer-guarded -> identity without class seeds, so behavior-preserving).
345/345 EditMode. Slice 2 design review + locked forks logged in the session note.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Four playtest do-now wins:
- Enemy health bars: pooled world-space Canvas, on-damage-sticky + fade,
always-on <25% HP (CombatFeedbackSystem; no new replication).
- Telegraph fix: new baked client-safe EnemyTelegraph sizes the danger-cone ramp
per enemy (0->1 ending at impact, fixes the Charger plateau); windup 18->22;
a windup scale-pulse.
- Build-mode toggle: BuildPaletteState.PaletteOpen hides the palette by default,
Tab / gamepad-Y toggles, with a discovery chip (HudSystem/BuildSendSystem).
- Charger committed-lunge tell: [GhostEnabledBit] IsLunging derived once/tick from
LungeState (the Dead idiom); the danger cone persists through the lunge.
345/345 EditMode (+3 IsLunging derive tests); Play-validated: ghost-hash change
did not break the handshake, bake correct (telegraph on all enemies, IsLunging
baked-disabled on the Charger, replicated to client), no runtime errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Structures (Turret/Wall/Pylon) reuse the combat spine: authoring bakes Health(GhostField)+DamageEvent buffer+a Destructible tag (no HitRadius -> no friendly projectile fire; no EffectiveCharacterStats -> clamp-to-0). HealthApplyDamageSystem destroys a Destructible at 0 (occupancy auto-frees). EnemyAISystem fortress-targets the weighted-nearest of players+structures via the shared EnemyAIMath.PickWeightedNearest (StructureAggroWeight TuningConfig knob, <1 prefers structures, squared factor; snapshot above the early-return so an undefended base is razed). Persistence v3: per-structure HP threaded through 5 sites (SaveData/PendingStructure/scan-guarded/BaseRestore same-ECB born-correct/WorldLauncher via SaveApply.ToPending); SaveService floor-gate [2,3] loads old saves. Loss feedback: proximity-gated StructureFeedbackSystem; CombatFeedbackSystem suppressed for structures. Pre-code review caught the DamageEvent-buffer crash blocker + 8 majors; post-code review clean. See DR-032.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
byte Archetype on AbilityDefBlob (AbilityArchetype enum: Projectile/Hitscan/Cone/Aoe), authored on AbilityDefinition + baked in AbilityDatabaseAuthoring, read at dispatch in AbilityFireSystem -- NOT folded through EffectiveAbilityStats/StatRecomputeSystem (static identity, not a tunable stat; MC-4 review BURST-1). All current abilities are Projectile (0) -> zero behaviour change; the dispatch read-point is the de-risk spike for MC-6 hitscan/cone/aoe.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Melee combo (left-click / pad-West) becomes the player's primary verb; the ranged projectile is demoted to right-click / pad-left-trigger. Predicted, owner-replicated combo Step (path-dependent -> [GhostField] anchor + absolute-write idempotency, NOT derived like the dash), server-only cleave mirroring ProjectileDamageSystem (SourceTick-stamped DamageEvent + KnockbackState), dash-cancellable movement-commit, 9 live TuningConfig knobs, and swing juice scaling with the combo step. The MC-6 archetype byte is deferred (the melee is its own verb). See DR-030.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add BefourStudios SciFi environment packs, Gabriel Aguiar VFX, and the
ShaderCrew Toon Shader embedded packages, plus combat/enemy/wave/death
gameplay systems and supporting vault docs/screenshots.
Rename 11 vendor textures from uppercase .PNG/.HDR to lowercase so the
case-sensitive Git LFS filters (*.png/*.hdr) match on case-sensitive
filesystems (Linux CI, case-sensitive macOS), not just locally where
core.ignorecase=true masks the gap. Each .meta moved with its asset so
GUID references are preserved. All ~1000 binaries tracked via LFS.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>