Field report: walking onto hilly-looking colliders (the new convex rock
hulls are ramps) raised the character's Y, and with gravity zero +
SnapToGround off nothing ever brought it back down - the character
floated and its shadow drifted away.
Fix inside CharacterProcessor.PhysicsUpdate: capture the pre-update Y
and restore it (plus zero RelativeVelocity.y) after the CC sequence -
the character update itself can never change the movement plane, while
spawn/teleport writes made outside the update stay honored.
Deterministic + idempotent (rollback-safe), identical on server +
predicted client.
Verified live: player shoved into a cover-hull sloped edge decollided
~1 u horizontally with Y bit-exact at 1.0000 (the case that previously
rode up the slope); 466/466 EditMode, console clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 1.5 stabilize #2 (gate-approved incl. missing-collider adds + the
07-09 height/solid-sweep directive). New ColliderFitTools (Editor,
audit/apply, idempotent) refits the subscene Environment colliders to
the Game.unity render meshes:
- Boundary rings rebuilt 16/24/24 -> 54/82/82 segments (~3.5-4 u arcs)
around the DESIGN radii (30/52 - fitting from the current segments
ratchets inward on re-runs), inner faces fitted to ankle-band
(0.45-2.5) cliff vertices so the wall stops the player before any
visibly rising skirt.
- 8 room cover boxes -> convex MeshColliders of the actual CoverRock_*
meshes (needed Read/Write on 2 PNB rock meshes: a MeshCollider bakes
ONLY if the mesh is readable - InvalidOperationException per bake,
classic scene view still shows the collider).
- 11 landmarks: hint-matched claim-once OBB fits (cabin/tent walk-into
fixed; well/sign/artefact invisible walls removed).
- 85 adds: generic solid sweep (>=0.5 u tall, non-flora, walk-band
geometry, gated by the LOCAL fitted wall radius) - cacti, succulents,
boulders, bone piles, small rocks, craters (full set-piece blocks),
crates, tree trunk, turbine poles, dish, solar rigs. Collider tops
pinned to the visual top (cap 3): nothing solid-looking is
walk-through or walk-over. Elevated-only geometry (rotors, panels)
excluded - no phantom ground walls.
Verified: baked-CollisionWorld probes (ring closure 32/32/region, 10/10
solidity samples across base + both room slots, open ground free,
server==client parity at 323 static bodies), enemy backstop live smoke
(enemy embedded in the new crater block depenetrated out and re-engaged
across ~12 u; 4 others converged normally through ~40 obstacles),
466/466 EditMode, console clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Session log Part C (execution + validation evidence), roadmap stabilize #2
ticked, CLAUDE.md gains the MeshCollider-bakes-only-if-Read/Write gotcha
(38.4 KB, in budget), validation-harness.md gains the EditorApplication.update
guardian + EditorPrefs handoff pattern.
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>
7 plain-Entities tests: phase-1/2 HP gating, expedition-only targeting (idle on no valid target), telegraphed radial-slam AoE + radius boundary, the B4 lunge-vs-slam windup disambiguation (a lunge elapse must not slam), and knockback immunity. Values pinned to Tuning.Boss* so they track tuning. Closes the last missing-coverage gap from the audit.
466/466 EditMode tests pass (7 new).
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>
- TestWorld: shared plain-Entities fixture (Make/Make<T>/SetTick reconciling the two SetServerTick variants + Player/Enemy builders). Additive; new tests consume it (40-file migration of existing tests deliberately deferred as pure churn).
- TestAttributeGuardTests: scans *Tests.cs and fails on any parameterless public-void method missing a runner attribute — guards the swallowed-[Test] bug (B0). Confirms the suite has no other dead tests.
- PrepPurchaseSystemTests (6): the previously-untested RPC economy — afford, reject-when-broke, once-per-run, non-Staging reject, unknown-id drop, and DR-014 same-tick atomicity.
- SystemOrderingCycleTests: registers the real run/cycle/combat system set and asserts SortSystems() has no circular dependency (invisible to single-system fixtures; only throws at Play) — also de-risks the B5 splits.
459/459 EditMode tests pass. (BossAISystemTests remains queued — the most complex to author faithfully; the boss stays Play-validated meanwhile.)
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>
- Restore swallowed [Test] on EnemyAIMathTests.SlideVelocity_DegenerateNormal_DeflectsToTangent (dead regression guard for the shipped enemy-stuck-on-cover fix; now runs + passes).
- Extract shared HarvestMath.DepositYield used by ResourceHarvestSystem + MeleeComboSystem; fixes melee mining silently ignoring per-item StackMax (it hard-coded DefaultStackMax) and hoists the melee ledger-buffer fetch out of the per-target loop.
- StatMath.Apply switches on the raw byte (case (byte)ModOp.X) instead of casting to the enum inside the Bursted fold — removes the latent cross-assembly enum-in-Burst ICE trap.
- Add [Min] guards on CycleDirectorAuthoring loss-critical ints (CoreIntegrityMax>=1 so a mis-authored 0 can't bake an instant-loss core; siege sizes >=0).
456/456 EditMode tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Serena dropped from .mcp.json + .serena/ untracked (unused in practice,
flaky on Unity per CLAUDE.md's own caveat); memory stack is now three
layers. Standing auths recorded in the skill: docs auto-commit + auto-push
main->gitea (origin stays ask-first).
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>
The 07-07 knob edit to WorldFeelConfig replaced the attribute line above
ResetDefaults(), so Enabled/ChipBurstCount/Node* all stayed C# defaults and
NodeFeedbackSystem + WorldFeedbackSystem chips/SFX/micro-punch never ran.
Live-verified: root PTM 0.783 -> child LTW 1.723 (= 2.2 x 0.783), 455/455.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Full-screen URP post effect on the active PC renderer that pixelates the
scene to a fixed low-res grid and draws depth + normal edge-detection
outlines, with color posterization and tone controls, for a stylized
"3D pixel art" look. Runs via Unity's built-in Full Screen Pass feature
(no custom C#); reads camera depth/normals so it applies to DOTS entities
and GameObjects identically.
- PixelOutline.shader/.mat: pixel-snap fill + gamma-space posterize +
brightness/contrast/saturation + depth/normal outlines. Normal
threshold tuned high so outlines don't flood small characters.
- PC_Renderer.asset: FullScreenPassRendererFeature (Depth|Normal, after
post-processing).
- PostFX_Daylight.asset: tonemapping ACES -> Neutral (poppier color).
- Game.unity: brighter base atmosphere (ambient + fog pushed back) and a
stronger directional key light so the fill reads under the effect.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BLENDER (5 new/re-authored clips, all full-body, mirrored-left-arm axes
FIXED - Synty L shoulders mirror X/twist vs R, verified by axis probes):
- A_Swing_R2L / A_Swing_L2R (0.47s): horizontal slash + backhand with real
anticipation (torso wound 25 deg), 2-frame strikes, follow-through past
the target.
- A_Swing_Finisher (0.60s): overhead crash with a crouch drop into impact.
- A_Special_Slam (0.53s): two-handed ground slam - the Warrior cone finally
LOOKS like a cone attack.
- A_Fire_OneHand re-authored punchier (cock -> full-extension thrust with
torso commit -> recoil kick).
Export lesson: Blender 4.4 slotted actions - reassigning animation_data.
action does NOT bind the slot; force animation_data.action_slot before
export/render or you bake a stale pose.
CONTROLLER: MeleeSwing (one clip for everything) replaced by Swing1/2/3
keyed on new ComboStep int param (replicated MeleeCombo.Step); Fire gated
!IsCone (Ranger shot), new SpecialSlam state on IsFiring+IsCone (Warrior).
DRIVE SYSTEM: writes ComboStep from the replicated combo step and IsCone
from the replicated AbilityRef -> AbilityDatabase blob archetype (works for
remote teammates too).
SWORD: SM_Wep_Sword_01 (SciFiSpace - same pack/atlas as the soldier) as a
mesh-sub-asset child of Hand_R, grip rot (90,0,0) tuned live against the
running entity. Required RigDefinitionAuthoring boneEntityStrippingMode
Automatic -> None so the hand bone entity exists + is posed (stripped bones
never animate attachments). Play-verified: the energy blade rides the fist.
456/456 EditMode.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 'Synty share one Generic skeleton' note was WRONG - the pipeline is
HUMANOID (CharactersAvatar isHuman, muscle-curve clips). Corrected + the
Blender clip recipe added; paid for by archiving Aim-controls, the URP
m_AssetVersion blocker, VolumeProfile persistence, HDRP-convert and the
HudTheme bullets (verbatim in the gotchas archive, 2026-07-06 section).
39,959 bytes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four clips authored programmatically in Blender on the PolygonSyntyCharacter
skeleton (full-body keys per pose, numeric world-space verification of the
fall: hips 0.88->0.14m) and exported per-action FBX (Key All Bones, Force
Start/End Keying, FBX Units Scale). Imported as HUMANOID - the project's
pipeline is Human rigs (CharactersAvatar isHuman=true; CLAUDE.md's 'Generic'
note was stale), so muscle-space retargeting plays these on the player AND
every monster rig with zero bone-path matching. CopyFromOther failed on the
Blender-added 'Armature' node (hierarchy mismatch) -> CreateFromThisModel;
root motion fully baked into pose (the CC owns the transform).
- A_Death_FallBack (0.90s = exactly the 54-tick corpse window): recoil ->
fall -> flat on the back -> settle. Wired as the Death state on BOTH
controllers, replacing the crouch-idle placeholder.
- A_Fire_OneHand (0.4s): new IsFiring param + Fire state on the player
controller; PlayerAnimationDriveSystem routes FireActive to it (melee owns
IsAttacking again).
- A_Dash_Lean (0.33s): new IsDashing param + Dash state; driven from the
local player's predicted DashState window (not replicated -> remotes skip).
- A_HitReact (0.27s): imported + avatar'd, wiring deferred (needs a
replicated hit-tick source; body flash covers the read today).
456/456 EditMode; Play-validated: forced IsDead renders the soldier flat on
the ground in-game (Blender->FBX->Humanoid->Rukhanka chain proven), Fire
state enters and plays; console clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
B1 SEPARATION: pairwise soft-collision pass INSIDE EnemyAISystem (sole
enemy-Position writer preserved; O(n^2) fine at MaxAlive<=14). Knocked/
lunging enemies keep committed motion but still push neighbours; the boss is
never pushed; enemies yield a small personal radius around players (below
melee range per review B1-1); displacement goes through the swept move so
nothing shoves through walls. SeparationMaxSpeed = live knob.
B2 POISE: the windup-cancel lived in the knockback CONTINUE branches (grunt/
charger/spitter). Threshold on the EXISTING KnockbackState.Speed channel:
light melee (6) nudges without interrupting; the finisher (10.8) and cone
(8) stagger as before. StaggerKnockbackSpeed = live knob (default 7).
B4 BOSS LUNGE: a telegraphed gap-closer on its own cooldown when the target
sits outside slam reach (repositioning - the slam stays the damage beat).
BossState.PendingAttack (server-only byte) disambiguates the shared windup
elapse (review-confirmed: naive reuse would SLAM on a lunge elapse);
LungeState.UntilTick spans windup+travel so the existing IsLunging ghost bit
replicates the tell, and the client draws a travel wedge instead of the slam
ring while it is set.
B5 PARTY HP SCALE: boss Health x(1 + 0.75/extra LIVING expedition player) at
spawn (not RunParticipant - dead-respawned members park at base). Max is a
GhostField so the bar stays truthful.
B6 RUN-FAILED BANNER: silent wipes used to land players home with zero
explanation. Client detects the (in-run)->Staging edge with a launch-cached
Charge (NEVER Returning - a 1-tick transient that precedes the bank by a
tick) and shows EXPEDITION FAILED for 6 s.
B7 FIRE ANIM: the class ability finally moves the body - a stateless window
from replicated AbilityCooldown.NextFireTick minus derived CooldownTicks
(works for predicted local + interpolated remotes, no cached edges).
456/456 EditMode; live Play smoke: launch -> 5 kills credit the room clear
through the corpse window -> boon window opens -> corpses expire clean, no
exceptions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
'sand 1' shipped pure-white BaseColor - the whole desert washed out and
characters/telegraphs sat on a bright field. Project-owned copy
(Mat_Ground_Arid_Dark, 0.72/0.68/0.63) assigned to both room-slot grounds;
the Synty pack asset stays pristine. Clarity doctrine: ground darker +
lower-detail than characters.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SLICE A - rooms become places: ExpeditionBiome duplicated to sub-slot 1
(X+1500) - ground, 205 decor children, rim cliffs, ambience; collider ring +
3 landmark colliders duplicated in the subscene; NEW in-room cover at BOTH
slots (4 collidable rocks each: Environment-layer box colliders in the
subscene + matching Synty rock meshes in Game.unity, placed clear of the
landing point, portal spot, spawn ring and boss spawn). Half of all rooms
previously played on a literal void.
SLICE C - owned FX wired: VFXConfig gains Portal + EnemySpawn slots
(FX_Portal_Round_01 / FX_Dust_Big_01 from PolygonParticleFX); the portal
beacon prefers the authored effect (procedural pillar stays as fallback);
enemies get a spawn-emerge cue on the ghost add-edge (primed-scan guard
skips the connect flood).
SLICE D (teaching) - the two undiscoverable verbs are now taught: dash on
the Move step, class ability + attack on the Rooms step (scheme-aware
glyphs); How-to-Play Controls adds Class ability + Dash rows and fixes the
wrong RT glyph; Loop step 1 mentions class pick + prep; the stale '50
starting Ore' now interpolates Tuning.StartingOre.
456/456 EditMode.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Direction locked 2026-07-06: trunk fixes + full Co-op Hades conversion
(mothball tower-defense, win expedition-side, hub base, mechanic-changer
boons, 2nd ability, node types, 2nd boss, revive), depth folded in now,
Blender at hero-props+decals scope. Evidence: 9-agent analysis workflow +
live play tour (screenshots referenced by the plan doc).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The center-top phase banner read the CyclePhase label ("AT BASE") inside
expedition rooms - now shows ON EXPEDITION whenever a run is past Staging.
- The location-line switch had no RoomExplore case, so "choose your boon"
stuck through the whole loot window - added the portal-steer line.
- Storage crate floated 1u: GridOrigin.y=1 is the CC capsule-CENTER plane;
CellToWorld returns it, and the crate's mesh pivot is at its base (Turret/
Wall pivots are centered, which is why structures look grounded). The
container now sits on the terrain; same grounding for the portal beacon,
whose pos.y was the capsule plane too.
- Beacon HDR 2.6/3.4 bloomed into a white egg that swallowed the prompt -
tamed to 1.2/1.55 + slimmed the pillar.
456/456 EditMode before and after; Play smoke: crate at y=0, banner correct
at Staging.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The room-exit portal was invisible in the first playtest - players loitered
out the 30s ExploreGrace timeout. Adds RegionMath.ExpeditionPortalPos as the
single client-derivable portal-position authority (HUD prompt + beacon can't
drift), a pooled breathing HDR pillar in CombatFeedbackSystem shown only
during RoomExplore, and an in-range/out-of-range two-stage prompt.
Also repairs the comment-swallowed _portalMat initialization (NRE in
OnStartRunning killed ALL combat feedback each session and auto-paused the
editor via Error Pause). 456/456 EditMode.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DR-046 (portal-gated rooms, class-at-base via shared ClassSwapUtil, per-run
prep spend, Health.Max GhostField, C3/C4/Spitter) + the session log. CLAUDE.md
folds the batch into the core-loop bullet and corrects the now-wrong
'Health.Max unreplicated' line; paid net-zero (40921 B) by archiving the
one-time Rukhanka-samples bullet + trims into the gotchas archive.
Co-Authored-By: Claude Opus 4.8 (1M context) <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>
DR-045 (real boss, GoalTarget=2, readable-but-fair) + session log. CLAUDE.md:
core-loop demo note (GoalTarget=2, boss kit), source-gen file-using gotcha,
EB-1/2/END-1/2 bullets condensed to DR pointers to stay under the 40KB budget.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lap-1 Fabricator affordable (StartingOre 50->90); mine prompt shows IN the room;
Return no longer false-completes on death (WasOnExpedition latch + soft timeout,
never the 1-tick Returning edge); HUD keeps room/siege/ammo cues during combat
steps; Welcome needs an explicit confirm; READY panel shows FINAL DEFENSE when
goal-full; dev Force-Each-Launch hidden from public Settings. GoalProgress.Target
4->2 (~10-20 min demo). Onboarding tests updated to the new contracts (456/456).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Boss bar reconstructs the true max client-side (Health.Max unreplicated) via
Kind==Charger + expedition-pos filter; boss slam telegraph = a full ground ring
at BossSlamRadius; IsAttacking OR's IsLunging. Player body flash on hurt (distinct
red, jitter-safe threshold). Dash toward movement input when moving else facing.
Warrior cone: muzzle-cue suppressed (cone arc is its cue). Minimal finisher-only
hit-stop (PrototypeCameraRig.Hold, behind FeelConfig flag; never Time.timeScale).
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>
A remote/cold client added NetworkStreamInGame the instant it got a
NetworkId, before the gameplay subscene's ghost prefabs finished
streaming. The server's first ghost snapshot then hit a client that
couldn't resolve those prefabs -> "ghost ... ENTITY_NOT_FOUND" ->
forced disconnect ("nothing loads"). On loopback / fast LAN the
go-in-game handshake beats the ~0.4s entity-subscene stream. The host
never hit it (it shares the server's loaded subscene); every remote
join did -- MPPM virtual players and real networked clients alike.
GoInGameClientSystem now gates full-client go-in-game on the gameplay
subscene being loaded (HasSingleton<PlayerSpawner>); thin clients skip
the gate. Verified cross-process in a standalone build over loopback:
join ghost-prefab errors 15->0, disconnects 1->0, client stays
connected and resolves all server ghosts.
Also adds a -mhost / -mjoin <ip> command-line hook to MainMenuController
for headless co-op testing (drives WorldLauncher.StartSession, no UI).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Teaches the deep, interlocking loop — especially the inverted win condition
(you win by CLEARING EXPEDITIONS, not by surviving base sieges; DR-042/DR-043).
- OnboardingSystem: client-only observe-only PresentationSystemGroup overlay
(own UIDocument @ sortingOrder 60), soft-gated 10-beat coach-mark sequence
with a world-space ▶ pointer; never mutates sim / never destroys a ghost.
- OnboardingStepMath: pure, unit-tested step machine (snapshot + IsSatisfied +
scheme-aware prompts + pointer kinds + persisted-mask helpers).
- HowToPlayPanel: tabbed reference card (Controls / The Loop / Build / Threats /
Win-Lose), reachable from the main menu and the pause overlay.
- Per-client client-local state in GameSettings (TutorialHints + OnboardingMask
bitmask, additive) — a Join client keeps its own; a host save-wipe never
re-teaches. Settings toggle + menu "Replay Tutorial".
- Dev "Force Each Launch" toggle (GameSettings.ForceOnboardingEachLaunch):
SettingsService.Boot wipes the mask + forces hints on in-memory every launch
so the tutorial always replays fresh.
- HudSystem suppresses its own location hint while onboarding is active
(single prompt voice), via OnboardingState + [UpdateAfter(OnboardingSystem)].
Validated green: 20/20 EditMode; Play smoke confirmed overlay render, clean
U+25B6 pointer glyph, no system sort-cycle, and the force-wipe end-to-end.
Docs: DR-043 + session log; reusable lesson archived in the build-gotchas note.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>