Track 1. Before building any new "world responds to you" work, I checked what
already existed, because today's score was three presentation systems that were
present, enabled, and produced nothing. This is the fourth.
AmbientMotionSystem's rustle layer (flora parting as the local player brushes
past) was dead in the shipping arena via THREE independent gates, each a
leftover of the deleted biomes:
1. Root gate required a scene root literally named "BaseBiome" or
"ExpeditionBiome*". Both went with the expedition, so the loop matched
nothing at all. The seabed flora live under
Env_SeabedKit/Seabed_Flora/FloraClump_N.
2. Name regex was pure LAND flora: bush|grass|flower|fern|cactif|clover...
Nothing in a seabed matches that. Added seabed terms first; the land terms
stay only so an older scene still works.
3. Renderer gate tested for a renderer ON the matched object. The natural sway
unit is the CLUMP ROOT, which carries no renderer of its own - only
children - so the test rejected exactly the object we want to rotate. Now
accepts a renderer anywhere beneath, and skips a child whose parent already
matched so the clump tilts once instead of every stalk tilting out of its
socket.
Verified live: AmbientMotionSystem._flora.Count 0 -> 4 (the four clump roots).
304/304 EditMode green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Operator at the A0 gate: "a little too dark to play, not very readable yet."
Measured rather than guessed, and the diagnosis was NOT "add light":
seabed _BaseColor (0.08,0.12,0.15) — the floor was painted near-black, so it
reflected ~10% of anything and could never read as a surface
fog colour (0.02,0.10,0.12) — distance faded to VOID rather than to murk;
real water scatters, so far things should get lighter and lower-contrast
ColorAdjustments contrast +10 on top of ACES — the low end was crushed twice
Vignette 0.28 — ate the periphery, which is where threats arrive from
Murk pass (operator picked "murk + closer camera"):
BaseFogColor/ExpeditionFogColor -> (0.06,0.20,0.25) [WorldAtmosphereConfig
drives RenderSettings every frame, so the CONFIG is the real knob]
M_Staging_Seabed albedo -> (0.18,0.26,0.30)
contrast 10 -> 4, vignette 0.28 -> 0.16
camera Distance 17 -> 14
That fixed the void but INVERTED figure-ground, which the numbers caught:
player 0.144 | rock 0.269 | flora 0.258 | warm haze 0.368
The protagonist was the second-darkest thing on screen. The shoulder lamp is a
forward-facing spot tilted 26 deg down — it lights the seabed ahead and never
its own wearer — so the diver had no personal light at all. Raising KeyWarm
would have lifted the scenery by the same amount and left the ratio unchanged;
only a light that TRAVELS with the diver changes it. Added ~SuitGlow: a warm,
steady point light at torso height (FeelConfig.SuitGlowIntensity 5 /
SuitGlowRange 4.5, live-tunable, 0 = off). "Light is territory", made literal.
player 0.144 -> 0.320, now above rock 0.282 and flora 0.250
figure-to-murk contrast 1.32x -> 2.94x, with scene brightness unchanged
304/304 EditMode green. Awaiting the operator's eyes on the result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Self-inflicted, found by the operator the moment they pressed Play: "the camera
is so far zoomed in and shaking".
StagingAmbiance rides Env_SeabedKit.prefab, which Game.unity instantiates as
well as ArtStaging — a placement that predates this session and was harmless
while the component only pulsed flora. The camera lissajous drift added in
b407f7cd6 then wrote _cam.position every LateUpdate against
PrototypeCameraRig's follow, pinning the camera to the authored _camBase
(0, 1, -10) while the rig lerped toward its desired (2.50, 16.72, -9.01).
Measured before: camera y oscillating 1.157 <-> 13.559, max 7.43 units per tick.
Measured after: y settles 16.658..16.709 (target 16.72), span 0.051, max step
0.004 — a ~1800x reduction in per-tick jitter.
Gate on the presence of PrototypeCameraRig, not on a scene name (scene-name
checks are banned by CLAUDE.md). Verified both directions: ArtStaging has no rig
so its drift still runs; Game.unity has one so the drift is correctly inert.
Note for next time: two `refresh_unity scope=scripts` calls reported
compile_requested=false and the editor kept running the STALE assembly, so the
first verification pass showed the guard "not working" when it simply was not
loaded. scope=all mode=force was required. Confirm a behavioural fix by probing
runtime state (_cam == null), not by re-reading the source.
304/304 EditMode green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Found by running the A0 style-proof against the SHIPPING scene instead of
ArtStaging (which judges static glTF bakes on an import shader, with zero
SkinnedMeshRenderers and no Animator — it cannot answer "does the game look
like this").
Player-facing bugs fixed:
- AC_PlayerTopDown/AC_EnemyTopDown "Death" had ZERO outgoing transitions.
Any State enters on IsDead and nothing ever leaves. The player ghost is a
persistent entity, so its Rukhanka animator never resets: once you died you
rendered sprawled on the seabed permanently — sim fully respawned, moving and
fighting, visually a corpse. Added Death -> Idle on (IsDead == false).
- AmbientMotionSystem drift was still the deleted biome system: key 0 =
"meadow", MeadowMoteColor (0.62, 0.85, 0.60) = GREEN motes, 600 of them in a
36x7x24 box following the camera — the most off-palette thing in the frame.
Its x>500 region probe pointed at space now holding zero renderers. Collapsed
to one cold marine particulate; dropped the 4 dead mote colours + AridWind.
- FeedbackFx.MakeParticleMaterial built Sprites/Default with NO texture, so
every procedural particle rendered as a hard SQUARE. Added a procedural soft
dot (no Resources.Load — build-stripped; asset-free presentation is the rule).
- Game.unity carried a stock Unity "Directional Light" (warm-WHITE, 1.05, Soft)
outranking KeyWarm as a second competing shadow caster, plus 6 orphaned
LandmarkLights — four at x=1030/1530 lighting a kilometre of empty water, two
with no renderer within 12u, two of them purple. All 7 deleted. Added RimCold
for silhouette separation; key:fill set to the tuned 3.9:1; WarmPool -> Soft.
ArtStaging (kept as a lighting lab): KeyWarm 0.15 -> 0.85 — it was the only
shadow-caster yet sat BELOW GloamFill 0.22, which destroys form by definition
and is what read as "flat". StagingAmbiance was attached to nothing and its
flora wiring searched for a Game.unity parent, which is what read as "static";
it also force-played the combat one-shot pool, now restricted to looping
emitters. Flicker raised to a measured x2.11 cold swing vs x1.03 warm.
304/304 EditMode green; death-recovery, palette and particulate all verified
live in Game.unity Play.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
M1 — two systems timed INTERPOLATED ghosts against the PREDICTED tick,
the exact hazard CLAUDE.md documents and the one that is invisible on
loopback:
- EnemyDangerTelegraphSystem timed the red danger cone off nt.ServerTick,
so over a real connection the dodge tell finished ~RTT/2 + interp
buffer EARLY. The cue lied.
- PlayerAnimationDriveSystem fed the same predicted tick to RemoteDriveJob
([WithDisabled(GhostOwnerIsLocal)] — i.e. interpolated teammates), so a
teammate's swing animation desynced from their damage.
Both now use the ZoneTelegraphSystem idiom. The LOCAL drive job keeps
ServerTick: the owning player really is predicted.
M12 — the RPC leak I reproduced live during the audit. Every receiver in
this project gates on RequireForUpdate over a scene-baked singleton; in a
scene without it the receiver never runs and the request entity is never
destroyed. Netcode's WarnAboutStaleRpcSystem Consume()s but never
destroys, and is compiled out of player builds — so these accumulated
silently, and worst in a shipped build.
New StaleRpcReaperSystem (server, OrderLast, no RequireForUpdate) destroys
any unconsumed request that outlived its receiving frame. Consumed
requests are left to their owner. Verified live: a planted unconsumed
request is gone within a few frames. Three regression tests pin both
halves of the contract.
Also: HealthApplyDamageSystem and ProjectileDamageSystem now filter
.WithAll<Simulate>(). They are ServerSimulation-only so it was not a bug,
but the audit's "all predicted systems filter Simulate" reassurance was
false until now — the rule is unconditional again.
The five undisposed Allocator.Temp ECBs the audit flagged all lived in
systems the purge deleted; none remain.
298/298 green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
H2 was the audit's sharpest finding: in Game.unity every player spawned
with four ability sockets pointing at SparkIds the baked AbilityDatabase
did not contain, so all four resolved to Damage=0 Range=0 Cooldown=0.
Melee and dash were the only working combat verbs in the built game.
Cause: the 5 LANTERN Sparks were added to GymSub.unity and never to
Gameplay.unity.
- Gameplay.unity's AbilityDatabaseAuthoring now carries all 9 defs (the
4 legacy ids keep their numbers; Sparks are 5-9) with their effect
prefabs. Live-verified in Play: sockets now read
Vortex 8dmg/6range/420cd, Blink 20range/1cd, Hook & Pull
15dmg/25range/120cd, Light Zone 6dmg/5range/480cd.
- Removed 6 orphaned authoring GameObjects the purge left behind in the
subscene (StorageSpawner, StructureCatalog, ItemDatabase,
SpitterProjectileConfig, BoonCatalog, MetaCatalog) and the RoomDressing
object in Game.unity — the latter is what scattered 47 Synty desert
props into the seabed murk.
- Deleted 4 now-unreferenced prefabs: EnemySpit, Pylon, Storage, Wall.
- Frame rename (M4): FrameKind.Warrior/Ranger -> Bathynaut/Harpooner,
93 identifier sites. Byte values pinned (2/3), so no ghost-hash or save
impact. The menu said "Warrior"/"Ranger" to players three weeks after
the frames were renamed in design.
- Menu now reads LANTERN / "Light is territory — co-op descent" instead
of "PROJECT M" / "Frontier colony — co-op" (the Awakening-Engine
tagline, two directions stale).
- Removed the "Replay Tutorial" button and the Settings ONBOARDING
section: both drove coach-marks DR-051 deleted. The settings fields
still round-trip so existing settings files load unchanged.
All four project scenes + all prefabs verified free of missing scripts.
Server measured at 59.6 ticks/s against a 60 Hz target. 295/295 green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The 2026-08-06 audit found the shipping scene was still the abandoned
co-op-Hades game with LANTERN combat bolted on, and that a third of the
codebase was live code for a direction abandoned on 2026-07-13. Operator
chose deletion over freezing: "everything is saved in source control if
needed. I want the project to be clean."
DELETED (~140 source files, Scripts 335->231, Tests 77->43):
- Enemy variants + boss (H3). ChargerAuthoring / SpitterAuthoring /
SwarmerAuthoring were attached to ZERO prefabs, so LungeState /
SpitterState / SwarmerTag were never baked: ~272 lines of Bursted AI
passes, BossAISystem (261 lines) and the whole MixBands escalation
curve could not match a single chunk at runtime, while 734 lines of
green tests certified them. Both shipping enemy prefabs were already
byte-identical in stats.
- Run/room lifecycle: RunDirector FSM, RunInfo/RunMap/RoomPlan/RoomTag,
route select, portal interact, ready-check, room field/teardown.
- Meta shop, prep loadout, boons (incl. KillRewardSystem and
DashTrailDamageSystem, which existed only to serve boon flags).
- Build palette + structures, shared storage, inventory/equipment
(already recorded PAUSED in CLAUDE.md).
- The HUD panels driving all of the above (HudSystem 1168 -> 610).
KEPT deliberately: BaseGridMath + BaseAnchor (8 systems use PlotCenter
for spawn rings, respawn and dynamic light), the resource ledger +
StorageMath, the save system, region/relevancy. Three of these were in
the delete set until I checked their consumers — worth remembering that
the file-level manifest was wrong about them.
Also folds in audit finding M5: PlayerClass was a second, server-only
copy of the byte FrameId already replicates. It existed for the meta
shop; with that gone, FrameId is the single frame identity.
Harvest is now single-sink (ledger). HarvestMath keeps its shape so
LANTERN's carried-vs-banked cargo split lands in one place, not two.
295/295 EditMode green, zero compile errors. Subscene re-bake and Play
validation follow in the next commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Camera: runtime POV switcher (StagingCameraSwitcher; V/]/[ cycle, 1-4 jump) with POV_1_Game matching
the in-game rig (Pitch45/Dist13/FOV55) as the default + hero/front/close-up; deep-water SolidColor
clear kills the no-skybox corner bleed at the wide game framing.
Character (the 'reads like a lamp' fix, operator-directed): darkened the helmet to dark gunmetal with
a dim lit porthole and moved the warm beacon to the shoulder lamp (light is carried, not the head);
color-blocked the body into a brass-and-teal deep-sea-diver palette (brass armour / dark-teal
undersuit / dark-metal kit) for value contrast + identity at game scale; bulked the back tank-rig
silhouette. Master .blend keeps rig+pose+materials re-editable; posed static GLB reimported into the
connected prefab instance with instance material overrides reset to the GLB's diver materials.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fixes the empty-bar leak into ArtStaging / menu / pre-connect: AbilityBarSystem.OnUpdate
early-returns when there is no valid NetworkTime, so the 'hide when no local player' line never
ran. Root now DisplayStyle.None at build, flips to Flex only when the local player is found —
also kills any pre-connect flash in gameplay.
SwordCombat React (1.4x, 0.3s pulse) + Stagger (1.2x, 0.55s) as Any-State
states on AC_EnemyTopDown via EnemyRigTools.WireEnemyHitReacts; driven by
EnemyAnimationDriveSystem's cache (now Pos/Hp/ReactUntil/Heavy) off
replicated Health drops. Windup-honesty gate: light reacts require
!IsAttacking (a sub-poise hit never visually cancels a live windup);
the heavy tier tracks the server's B2 poise break (stagger threshold 50
= finisher staggers, light flinches at the locked tuning). Knobs:
HitReactSeconds (0=off) / HitStaggerSeconds / HitReactStaggerDamage.
Replaces the review-cut positional vibrate (Rukhanka inverse-fold no-op).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- PostFX_Lantern.asset: the ONE shared grade (ACES, bloom 0.6 cool-tinted for the
HDR gloam, vignette 0.28, faint teal filter) applied to Game, DevSandbox, and
ArtStaging; PostFX_DarkSciFi / PostFX_Daylight / Sky_DaytimeProcedural deleted.
- Env_SeabedKit.prefab: ArtStaging's environment (seabed, flora, rocks, marine
snow, caustics, warm pool, gloam fill, key light, StagingAmbiance animator)
prefab-ized and placed in Game + DevSandbox; ArtStaging connected to it.
- Scene surgery: old DevSandbox.unity deleted; Gym.unity RENAMED DevSandbox.unity
(GUID preserved -> GymSub wiring + the F1/F2 dev scripts that gate on the
'DevSandbox' name come back to life). SyntyWorld root deleted from DevSandbox;
BaseBiome/ExpeditionBiome/Slot1 deleted from Game.
- RenderSettings unified: no skybox, Exp2 teal fog {0.02,0.10,0.12} @ 0.035, flat
near-black ambient {0.03,0.055,0.08}; camera clearFlags -> solid deep-water.
- ScenePolicy.IsGameplayScene() replaces the six scene.name=="Game" string gates
(Game + DevSandbox share the dynamic look; menu/ArtStaging untouched).
- WorldAtmosphereSystem rewritten as water-column murk (LANTERN defaults; biome
variants re-meant to kelp/trench/gloam-bloom; Ground_Arid tint block deleted).
390 green; Play-verified in Game: murk values live, warm-vs-cold reads, no errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Enum renamed in StatIds.cs (byte values unchanged - serialized definitions/saves
never re-mean); all call sites + doc mentions swept (ClassTraits, PlayerAuthoring,
CharacterStatsDefinition field type, menu/UI, tests). CharacterStatsDefinition SO
CLASS name kept (asset-binding risk; deferred per plan). 390 green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deletes the M7 production systems, automation components/math, authoring, 3
machine prefabs, and 6 test files (-43 tests, 459 green). Trims the automation
paths out of BaseRestoreSystem/SaveStructureScan/BuildPlaceSystem/BuildSendSystem/
HudSystem/HudTheme/StructureCatalogAuthoring/Tuning. RuntimePlacedTag (save
marker, a keeper) re-homed into StructureComponents.cs. StructureType byte codes
stay reserved.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FrameId (replicated frame/class signal) now WRITTEN server-side at spawn for all
players; the two class-HUD readers (ClassPrepPortal, MetaShop) read FrameId (with
an AbilityRef fallback for pre-FrameId players) instead of ClassForAbility. The
gym seeds a PER-FRAME default Spark loadout via ClassTraits.FrameLoadout —
Harpooner (Ranger slot): reel/mobility/skillshots; Bathynaut (Warrior slot):
pull-in/dash/zone-control. FrameId uses ecb.AddComponent (baked on the real
player; absent on the minimal MetaSeeding test prefab). 497 EditMode green.
Deferred to a focused pass (risky wholesale refactor, "still works" via the
transition): full removal of legacy AbilityRef/AbilityCooldown/EffectiveAbility
Stats (woven through equip/class/meta/StatRecompute + tests) and the CharacterId
Bathynaut/Harpooner rename.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GymTag + GymEnemyRoster (+ GymRosterAuthoring) bake the gym singletons. New
DebugOp.SpawnEnemy (client SpawnEnemy wrapper + server receive case) spawns a
chosen enemy KIND (Drowner/Grindylow) from the baked roster near the sender —
replacing the wave-roster hack per the roadmap's Enemy-test GYM direction.
GoInGameServerSystem takes a GymTag branch: bypass the meta-catalog spawn guard
(a fresh gym has no CycleDirector) + seed a default Spark loadout on keys 1-4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Beacon left unmodulated (steady=true light); cold flora gutter on Perlin flicker
(flicker=false light) so the warm/cold read survives with hue removed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Light-only ambiance animator (caustics spin, beacon flicker, bioluminescent
pulse); self-wires by name; no material writes so nothing persists on Play exit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DebugInputInjectionSystem (#if UNITY_EDITOR) gains FireSocket(i, frames) + per-socket hold
frames + the OnUpdate socket-event sets, mirroring the legacy Fire injection. Drives the real
gather->command->prediction->AbilityFireSystem path per socket for headless validation.
Used to validate group-A live (instant-play ServerWorld+ClientWorld): socket 0&1 = a projectile
Spark, injected same-frame -> maxSrvProj=2, maxCliProj=2 (server==client parity),
distinctSpawnIds=2 with distinctSocketBits=2 (the SpawnId desync fix confirmed LIVE), both
per-socket cooldowns advanced. Netcode half of the group-A gate: PASS.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per Phase1_Combat_Gym_Build_Spec §8 (the "KEEP as-is" feel layer was really a migration):
- PlayerAnimationDriveSystem: both jobs (Local/Remote) now read SocketCooldown +
DynamicBuffer<AbilitySocket>/<EffectiveSocketStats> instead of the single AbilityCooldown/
AbilityRef/EffectiveAbilityStats; shared SocketFireAndCone helper (any-socket-firing model:
IsFiring if any socketed Spark's per-socket cooldown window is mid-fire; IsCone if any such
is Cone).
- CombatFeedbackSystem: the muzzle-flash + cone-cue blocks unified into one per-socket
fire-edge loop over SocketCooldown (per-socket uint cache); non-Cone -> muzzle flash,
Cone -> the aimed slash-arc cue. Dropped the now-dead single-cooldown latches.
L1 clean; L2 494/494 (no regression).
Deferred to the AbilityRef-removal cleanup (surfaced, NOT silent): the FrameId server-writer +
the 2 class-HUD re-points (ClassPrepPortalHudSystem/MetaShopHudSystem) + HudSystem's ability
bar. Rationale: AbilityRef is still baked + set (EquipSystem/ClassSwapUtil), so ClassForAbility
remains a valid class signal through the transition; FrameId is baked ready. No feel regression.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Client-only, procedural, zero netcode — a sibling of AmbientMotionSystem,
biome-keyed + camera-following. Three cosmetic beats: fleeing critters (ground
bugs + a few birds that dart out of the flee radius = the scatter beat,
biome-tinted, recycled at the ring edge), drifting cloud-shadow discs, and
Blight-room-only double-blink directional lightning flash + delayed thunder
(dedicated flash light -> no RenderSettings fight with WorldAtmosphereSystem).
Reuses the FeedbackFx disc/scorch primitives + procedural SFX. Live knobs in
AmbientLifeConfig. 474/474 EditMode; live-verified at base (flee confirmed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review-first netcode slice (design review wf_900e9965-8f0 -> 11 folded;
post-impl wf_5c8299f8-299 clean). A PERMANENT periodic telegraphed AoE in
Blight-biome rooms only; one new [GhostField] uint NextEruptTick.
- Geyser component + GeyserEruptSystem (server): inverted invalid-tick guard
(a baked-0 tick must NEVER erupt -> lazy-stamps born-correct instead of the
party-wiping per-tick barrage), inline both-sides gather (SourceNetworkId=-1),
reschedule = now + period (never +=), never destroyed.
- GeyserTelegraphSystem (client): absolute-tick growing warning disc +
erupt burst on the >0->=<0 crossing, latched per NextEruptTick + arm-guard
(never edge-detects the replicated field -> no phantom on 0->stamp /
relevancy re-entry). Reuses ScorchDecalSystem + DynamicLightSystem.
- Seeded in RoomFieldSystem (Blight-gated on plan.Biome, born-correct staggered
stamp from live ServerTick), GeyserFieldSpawner + authoring wired into the
Gameplay subscene. Geyser.prefab duplicated from ResourceNode (no collider).
- BuildDisc promoted to FeedbackFx (shared with the barrel fuse ring).
- 474/474 EditMode incl. the unstamped-storm regression; live no-storm end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
All procedural, client-only, observe-only in PresentationSystemGroup, zero
netcode surface (read existing replicated state). Shared FeedbackFx decal
primitives (scorch blob + crack star meshes + transparent decal material).
ScorchDecalSystem: static RequestScorch queue -> pooled fading discs
(mirrors DynamicLightSystem), wired from the barrel boom, reusable by the
geyser. CoverDamageSystem: crack accretion keyed on BlightClutter.Remaining
(Variant 4) + proximity-gated shatter-scorch. RoomDressingSystem: persistent
arena scars on a distinct hash sub-stream, torn down with dressing. Knobs in
DecalConfig. Cover cracks use decals (not URPMaterialPropertyBaseColor) because
the Synty prop shader is not Hybrid-Per-Instance.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Operator: base flora too dense + "the waving/shader doesn't look great".
Diagnosis: EVERY Synty flora prop (both biomes) uses the Synty/Foliage
VERTEX-wind shader — the transform root-tilt sway has been double-animating
all flora since 1.5, and ground-flush carpet props (Flowers_Flat,
Grass_*_Plane) visibly lift their edges when tilted.
- AmbientMotionConfig.SwayEnabled -> false (code default + the serialized
scene value): idle motion = the authored per-vertex shader wind (tips bend,
bases planted). Knob kept for A/B; walk-through RUSTLE stays as the
reactive layer the shader can't provide.
- AmbientMotionSystem: flat carpet props excluded from the flora cache
entirely — no tilt even from rustle.
- Flower density thinned deterministically: Flowers_Flat keep 40%,
Wildflowers/Sunflowers keep 50%, center (r<13) thinned harder (x0.6) —
17/38 drifts off; play/build area reads clear, flowers become ring accents.
Console clean; sway/rustle knobs remain live-tunable in the scene.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1.5b ground bundle (operator: "the ground is very plain"):
- Ground materials rebuilt: Grass/Sand base maps at real tiling (22x/26x, was a
2x smear across 90-130u) + Synty normal maps + a generated 0.5-centered macro
noise detail texture (LINEAR import — sRGB detail mul2x darkens 2.3x) with
baked rim vignette. Meadow ground moved OFF the shared Synty asset onto
project-owned Mat_Ground_Meadow.
- WorldAtmosphereSystem: per-room-biome ground tint on the expedition quads via
MaterialPropertyBlock (Meadow green / Cavern blue / Blight purple; Arid sand
default), lerped alongside the existing fog/ambient palettes.
- RoomDressingSystem + RoomDressingConfig (new, client-only observe-only):
26 biome-flavoured cosmetic props per room, scattered inside the room's ACTUAL
shape (same RoomLayoutMath authority as the server, distinct 0xD2E55 hash
stream), deterministic from the replicated run seed, torn down with the room,
colliders stripped. 47 curated prefab refs wired in Game.unity (build-safe).
- Base: 357 curated flora props re-enabled (bushes/wildflowers/grass clumps;
buildings/pond/FX stay parked) + generated worn-dirt paths storage->warpgate
and storage->base gate.
Live-verified: dressing 26/26 inside x[981,1023] z[-11,12] y=0, 11 varieties,
0 colliders; teardown on run end; Meadow tint converged to exact target; 470
EditMode green; console clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Operator feel report: the explosion radius was unreadable and the fuse
too short to react to. Two changes:
- Tuning.BarrelFuseTicks 36 -> 90 (~1.5s): long enough to READ the
danger and walk out, and better for baiting enemies onto a lit
barrel.
- WorldFeedbackSystem draws a pulsing ground DISC at exactly
Tuning.BarrelExplodeRadius under every lit fuse (keyed on the same
replicated Remaining=0-on-a-live-barrel cue as the boom split; one
shared pulsing material; removed via the existing prune path).
Verified live: disc appeared 0.07s after the pop (first fuse
snapshot), persisted to the boom at 1.54s, scale read exactly 3.25 =
the true detonation radius; 470/470 EditMode; console clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1.5 environmental hazard v1 (design-review wf_2cb10454-fdf: 13
findings confirmed + folded; Build Spec in the vault). ~25% of room
clutter seeds as EXPLOSIVE (Variant 3 on the existing replicated byte
- zero new GhostFields/prefabs/RPCs).
The FUSE is the review's fold - one mechanism closes both HIGHs:
- Pop sites (projectile sweep + isServer-gated melee harvest) do NOT
destroy an explosive: they zero the replicated Remaining + add a
server-only BarrelFuse (~36 ticks). The client sees Remaining=0 on a
still-alive barrel across snapshots -> unambiguous fuse cue, and
booms at despawn ONLY when cached Remaining<=0 - a teardown despawn
carries Remaining>0, so portal-advance teardowns can never fire
false booms (HIGH #1).
- HazardExplosionSystem (server, plain group, presence-gated on
BarrelFuse, never lifecycle-gated) detonates at ExplodeTick: radius
damage to LIVING enemies AND players (boss-slam player filter
verbatim; friendly fire = the bait mechanic), SourceTick stamped at
detonation = the authoring moment (HIGH #2: the authored-tick
contract dash i-frames negate against), SourceNetworkId=-1 (the
environment convention - a player id would consume Charger
whiff-punish windows). Fuse rides the RoomTag'd barrel -> teardown
cleans lit barrels free.
- Lit barrels are unhittable at both snapshot sites (no double-pop).
- Client: WorldFeedback caches Variant -> boom-vs-puff split (big
burst + light flash + boom SFX vs the old puff); DynamicLightSystem
gives Variant-3 barrels a red danger glow that STROBES once fused.
Verified: 470/470 EditMode (4 new: fused pop instead of destroy +
unhittable, both-sides damage w/ -1 source + authored tick, dead-player
+ radius filters, unelapsed-fuse inert); live smoke - seeded 3/8
explosive, real-sweep pop, CLIENT observed the fuse cue on the live
ghost, pinned bait enemy took exactly 26 (30->4), walk-out escape
confirmed; console clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every enemy kind now has a unique read on the existing replicated
state (client-only, zero netcode):
- Telegraph COLOURS per EnemyTelegraph.Kind: grunt orange, charger
crimson (boss shares it - shapes already differ: cone/wedge/ring),
spitter toxic-green lane, swarmer yellow. Shapes were already
per-kind; they all rendered the same HDR red.
- Windup VOICES at the onset edge (the existing _prevWindup edge +
strike-beep distance gate): grunt low thud, charger rising roar,
spitter wet hiss, swarmer chitter - kinds read by EAR before the
telegraph ramps.
- Projectile light colour by ownership in DynamicLightSystem: owned
shots stay player-cyan, un-owned (Spitter spit) glows toxic green
(EnemyProjectileColor knob).
Verified live: 12 s combat histogram shows zones rendering with
EnemyDanger_K0 (grunt) + EnemyDanger_K1 (charger) materials keyed to
real windups (screenshot); 466/466 EditMode; console clean. Remaining
distinctness depth (per-kind anim clips, creature locomotion) rides
the parallel Blender workstream.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Art look LOCKED by the operator from the artlook_* side-by-sides:
clean dark-Synty; PixelOutline._MasterEnabled=0 shipped (feature stays
dev-toggleable via F3; other fullscreen effects remain backlog).
New AmbientMotionSystem (client-only PresentationSystemGroup, the
"world alive" anti-static beat, zero netcode):
- per-biome DRIFT particles from one world-space emitter following the
camera (meadow motes / arid wind-blown dust / cavern motes / blight
spores; palette keys on camera-X region + replicated room biome,
mirroring WorldAtmosphereSystem's switch)
- idle SWAY on the ACTIVE cosmetic flora prop roots near the camera
(LOD children follow the root; probed: none static-batched)
- walk-through RUSTLE: flora brushed by the local player
(GhostOwnerIsLocal idiom) gets a decaying energy shake
Rotations write around a cached base (lossless when toggled off);
knobs in AmbientMotionConfig (scene object, code defaults when absent).
Verified live: drift palettes exact in both regions (meadow
0.62/0.85/0.60 -> arid 0.85/0.68/0.45 on region cross), flora beside
the player rocked (0,0)->(-4.0,+5.0) deg within the rustle+sway
envelope, 466/466 EditMode, console clean. Discovery for follow-up:
the BASE biome's flora props are authored INACTIVE (84 bushes etc.) -
the base has no sway targets until some are re-enabled.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dark-ambient, dynamic-light-first look (RoR2/Death Scourges reference),
built filter-agnostic so the pixel style composes on top:
- New DynamicLightSystem (client-only, PresentationSystemGroup): pooled
point lights follow projectile ghosts; pulsing portal light during
RoomExplore (same ExpeditionPortalPos authority as the beacon); soft
resource-node glow that fades with harvest-shrink; short impact
FLASHES fed by CombatFeedbackSystem's burst funnel (EmitColored +
SpawnVfx -> RequestFlash). Knobs in DynamicLightConfig (scene object,
code defaults when absent).
- Atmosphere darkened: WorldAtmosphereConfig/System palettes moved to
the dark set (base cool dusk, arid burnt dusk, per-room biome consts
darkened); scene fog switched Linear -> ExponentialSquared (the
system's per-region DENSITY writes were dead in Linear mode);
trilight ambient lowered; directional 1.9 -> 1.05 slightly warm.
- 6 landmark mood lights (warpgate cyan, artefact violet x2, survey
camp warm x2, cabin warm); URP additional-lights-per-object 4 -> 8
(was starving the existing Aether lights).
- Art-look gate material: Assets/Screenshots/artlook_{base,room}_
pixel{ON,OFF}.png over the lit scene.
Verified: lights live in Play (20 static at base incl. new landmarks;
pooled dynamics active in-room: node glows + impact flashes), fog/
ambient values confirmed live, 466/466 EditMode, console clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- New Server/PlayerResolve.TryResolve single-sources the RPC
SourceConnection -> NetworkId -> conn->player map resolve (3 sites:
ClassSelectReceive, PrepPurchase, DebugCommandReceive); EntityManager
reads keep it source-gen-safe from Bursted receivers.
- ecb.Dispose() after Playback in 9 Temp-ECB systems (explicit-lifetime
hygiene).
- The TuningConfig.GetOrDefault(ref state) variant of this tail was
REVERTED: state.GetEntityQuery in OnUpdate trips the Entities
"creates a query during OnUpdate" diagnostic per system per world
(caught in Play smoke) - the SystemAPI.TryGetSingleton idiom is
already source-gen-optimal, confirming the original B4 deferral.
Verified: 466/466 EditMode green on the final tree, console clean,
Play smoke 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 10 EditMode files: every bare trailing world.Dispose() now inside using(world){} (single-world) or a [TearDown]+List<World> (the multi-world reject-matrix tests in BoonApplyTests/RouteSelectSystemTests) — an assertion failure can no longer leak the World and mask the true first failure. No test logic changed.
- HudSystem: collapse the blank-line run before the class close; trim the extracted route-map clause from the READY-panel comment.
466/466 EditMode tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract 7 sibling PresentationSystemGroup systems (client-only, observe-only), faithfully relocating methods+fields so behavior is preserved by construction:
- HudSystem (2129->1588L): BoonModalHudSystem, RouteMapHudSystem, MetaShopHudSystem, ClassPrepPortalHudSystem — each owns its own runtime UIDocument (MenuUi.LoadPanelSettings + own sortingOrder + EnsureEventSystem), the proven EnemyMarkerSystem/OnboardingSystem pattern; no shared root, no new static bridge.
- CombatFeedbackSystem (1300->914L): RoomPortalBeaconSystem, EnemyHealthBarSystem, EnemyDangerTelegraphSystem — each owns its FX-root + mats (via FeedbackFx), self-queries enemies + self-detects its edge (health-bar LastHp; danger _prevWindup), prunes its caches each frame.
Verified: compiles clean (0 errors), 466/466 EditMode tests pass, Play world-creation clean (no ComponentSystemSorter cycle, no OnCreate exception, 0 console errors). NOTE: the final VISUAL smoke (panels appear at the right lifecycle; enemy health bars / danger telegraphs / portal beacon render; buttons live) needs a FOCUSED Play pass — the play-mode transition throttles while Unity is unfocused, so I could not drive live frames headlessly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The byte-identical ResolveCamera() (Camera.main -> PrototypeCameraRig fallback) in PlayerInputGatherSystem, BuildSendSystem, and AimReticleSystem now call one CameraResolver.Resolve(); camera-resolution policy lives in one place. Behaviour-identical; compiles clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New FeedbackFx static (MakeClip/MakeParticleMaterial/MakeBurst/PlayClip/EmitTinted/EmitAt); the 3 *FeedbackSystem copies + AmbientAudio.MakeSting now route through it via 'using static'. MakeBurst takes the FX-root + the per-use gravity/radius/sizeTail that were the only diffs between copies; MakeClip folds in noise + decay. Behaviour-identical by construction (every particle/clip param preserved exactly).
459/459 EditMode tests pass; compiles clean. VFX are presentation-only (no EditMode coverage) -> wants a Play-mode smoke to eyeball hit/death/harvest/structure bursts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CycleDirectorSpawnSystem: drop the dangling <see cref="CyclePhase.ExpeditionTicks"/> (deleted const in B2) -> plain 'initial phase delay'.
- BuildSendSystem: remove the stale '(Conveyor uses s_ConveyorDir for facing)' comment (field + hotkey deleted in B2).
Comment-only; 459/459 EditMode tests pass. Adversarial verification round (2 agents over the full diff) found no behavior regressions and no false-confidence tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Repoint CLAUDE.md testing example from the deleted HeartbeatSystemTests to HealthApplyDamageSystemTests.
- Stale deleted-system refs: ResourceFieldSpawner/ClutterFieldSpawner(+Authoring) now cite RoomFieldSystem (was ExpeditionFieldSystem); BuildPlaceRequest drops the deleted RegionTransitRequest from its 'mirrors' list.
- Stale package version: 'Netcode 1.13.2' -> 'Netcode 1.x' in ProjectileClassificationSystem + BuildPlaceRequest.
- WorldCollisionComponents: remove the duplicated EnvironmentMask summary line; FeelConfig: fix the mangled 'is hitmap)' sentence.
Comment/doc only; compiles clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- StructureCatalogAuthoring: WallCostOre -> WallCostBiomass (it bakes a Biomass cost; [FormerlySerializedAs] preserves the scene value).
- Harvester/Fabricator authoring: resource-id byte defaults reference ResourceId.Ore/.Charge instead of magic 2/4.
- RegionMath.RegionBoundaryX (= ExpeditionOffsetX*0.5) single-sources the region-flip X used by HudSystem + OnboardingSystem (was 500f in 3 places).
- CharacterComponent.DefaultGroundedSharpness single-sources the CC sharpness 15f (GetDefault, DashSystem, PlayerDeathStateSystem, PlayerCharacterAuthoring).
- InventorySlot [InternalBufferCapacity] references Tuning.InventoryMaxSlots.
- ConnectionMode enum -> byte-const class (project convention; removes the latent enum-in-Burst trap); field + one Seed() param become byte.
- Tuning.ChargerWindupTicks single-sources the Charger telegraph windup (EnemyBaker + TuningConfig.Defaults; was a bare 30 that could drift).
- (TicksPerSecond deliberately NOT added: no seconds->ticks conversion site exists; the tick-count fields are per-authoring designer tunables, so a const would be unreferenced.)
451/451 EditMode tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Delete (unreferenced in any scene, per operator decision):
- TrainingDummy* chain (authoring/spawner/system/components/prefab) + UpgradePickup* chain (authoring/spawner/systems/components/prefab); remove TrainingDummyTag from the 3 live combat queries (AbilityFire/Melee/HealthApplyDamage now key on EnemyTag) and repoint HealthApplyDamageSystemTests to EnemyTag.
- Dead RPC RegionTransitSystem + RegionTransitRequest (no sender, ungated) + its test.
- Heartbeat + HeartbeatSystem (no WorldSystemFilter, ran no-op every tick) + its test.
- BuildSendSystem dead conveyor/pylon dev hooks (s_ConveyorDir, [/] rotation, Place{Pylon,Harvester,Conveyor} statics).
- Deprecated CyclePhase alias consts (Expedition/Defend/Build/*Ticks); repoint WaveSystemTests to Siege/Calm.
- Unread FeelConfig fields (HitFlashDurationMs, RumbleHeavy) + dangling HudUi doc-comments + stale crefs.
451/451 EditMode tests pass. CLAUDE.md HeartbeatSystemTests example ref to be repointed in B7.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MenuUi FindAnyObjectByType; GA prefabAssetPath->assetPath x2; Synty sample
FindObjectsByType overload; dead death_b field removed. Real bug caught by
the sweep: the F3 pixel-art toggle used legacy Input.GetKeyDown under
Input System-only handling -> InvalidOperationException every frame in Play.
Suite green (455).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>