Commit Graph

138 Commits

Author SHA1 Message Date
kronic 8b50753f93 Art: ArtStaging overhaul - game-cam POV system + Bathynaut character (no longer reads as a lamp)
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>
2026-07-22 20:34:36 -07:00
kronic f3aec94c48 HUD: ability bar defaults hidden until a local player exists
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.
2026-07-22 18:26:24 -07:00
kronic 5569fcd552 Fix: socket auto-recast at every cooldown reopen (raw InputBufferData IsSet = ever-pressed)
Netcode accumulates InputEvent counts on the wire; only the decoded component is delta-corrected.
AbilityFireSystem's windup resolve gated on raw IsSet, so after the first press every socket
re-fired the instant its cooldown reopened (live repro: recasts at exactly t3933/t4353 with zero
input). Gate is now a count-STEP vs the previous tick's command; missing prior command = no-fire.
Also lands the G6 ConeContactPending schedule machinery + the cone test fixture (wire-true
monotonic counts) + regression Cast_Never_Refires_When_The_Cooldown_Reopens. 430/430; live proof:
one tap -> one cast -> silence across 2+ reopens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:28:08 -07:00
kronic 040463ad07 HUD: ability/cooldown bar (sockets 1-4 + dash); socket-0 charge strip removed
AbilityBarSystem (own UIDocument, sortingOrder 49, the B5 sibling pattern): per-slot Spark
initials/name from the AbilityDatabase blob, archetype tint, drain overlay + seconds countdown +
ready flash; dash rides DashCooldown vs TuningConfig.DashCooldownTicks (Defaults() fallback).
HudSystem's single-socket blue charge strip removed (it tracked socket 0 only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:28:08 -07:00
kronic 2aebc37115 G6+G4: Cone damage-at-contact + zone fill telegraph (ZoneEffect GhostFields) + co-op saturation budget
Cone/SpecialSlam damage lands at its visual contact via ConeContactPending (knob 32, 0=legacy;
early-flush + resolve re-validate; death + BOTH class-swap paths drop armed pendings — fixes the
shipped melee death-strand in the same stroke). Zones: [GhostField] Caster/Radius/NextTick +
ZoneTelegraphSystem (Geyser latch contract on InterpolationTick; rim = true radius; arm grows,
persistent phase drains). Cone cues latch to contact (FireStartRaw, C14); TuningConfig.Defaults()
fallback at client cue sites (release-build timing fix). G4: SaturationMath ally-FX degrade
(living-enemy census, solo-exempt; enemy telegraphs structurally exempt) + CombatStressDebug +
overlay saturation rows. Reviews wf_98bf1268 (13 confirmed folded) / wf_9757d214 (5 confirmed fixed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:27:50 -07:00
kronic 0ff11fe2a9 Enemy hit-reacts: React/Stagger anim tiers off Health-drop edges (the Rukhanka-safe flinch)
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>
2026-07-21 00:28:11 -07:00
kronic ce0faafb72 HEAVY FEEL LOCKED: the A/B winner folded into defaults, clips retimed to zero contact drift
recover 36 / dmg 36 (DPS 60 held) / contact 20 (20/12/25 per step) /
grace 28 / buffer 10 / move-commit 0.25; arc 0.8, finisher hold 7,
connect kick 1.1. Clip state speeds retimed 1.0/0.85/0.88 so the VISUAL
blade contact matches the damage tick exactly (audit: zero drift; probe:
gaps 36/36/54, contacts +20/+12/+25 measured). Step-3 contact sits 1
tick under CastFacingTicks 26 (documented coupling). baseline profile =
the locked defaults; heavy-committed kept as the record.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 00:13:49 -07:00
kronic 7f032571df Weapon grip anchor: gizmo-authored WYSIWYG grip + operator's hand-tuned grip baked
WeaponGripAnchor under Hand_R in Player.prefab carries the actual axe
mesh as an EditorOnly preview (baker skips it; play-verified single axe)
so grip manipulation is the real model live in the prefab stage;
AttachMeleeWeapon reads the anchor (consts = seed/fallback). Includes
the operator's first gizmo-tuned grip (rebaked mesh matches expected
radius exactly). PlayerRigTools also carries the heavy-lock clip
re-speeds (1.0/0.85/0.88) consumed by the next commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 00:13:48 -07:00
kronic 2710d680b4 Sandbox target dummy: planted EnemyTag ghost + DevSandbox respawn spawner
GymSub bakes no WaveDirector (why sandbox enemies never spawned); the
sandbox's combat target is now a TargetDummyTag'd enemy ghost all five
EnemyAISystem passes exclude (planted; knockback stamps inert), spawned
at player+3m with 400 HP by an editor-only scene-gated server system,
dying through the normal Dying path and respawning 1.5s after the corpse.
EnemyTag-reuse audited: sandbox-only by design (cleared-checks would
count it elsewhere).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 00:13:47 -07:00
kronic 730bad3d74 Data-driven tuning bench: feel profiles (JSON gestalts + overlay A/B), asset-derived timing audit, server-measured combat probe
FeelProfileService: partial-apply profiles ride the authoritative SetTuning
path + FeelConfig reflection; Save-current captures hand-dialed state; 3
starter profiles (baseline-0720 / heavy-committed / snappier, DPS-held).
TuningAuditTools: contact truth derived from clip WindUp takes / state
speeds vs MeleeTiming (drift detector), coupling invariants, cadence/DPS,
reach-honesty ratio off the baked weapon mesh (now x0.93/x1.16, was >2x).
CombatProbe: dummy + injected mash chain, per-swing server-measured
start/contact/damage + cadence/DPS report. Verified live: snappier profile
apply -> measured 24/24/36 gaps, +12/+8/+15 contacts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 21:28:33 -07:00
kronic 2b742f0179 DebugOverlay: tuning rows for the 07-20 melee feel knobs (contact/buffer/finisher-reach)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 21:04:37 -07:00
kronic 7b65912f30 Melee feel overhaul: SwordCombat anims at heavy cadence, salvage axe, damage-at-contact + buffer + dash law (guidelines forks 1-6)
07-18/19/20 arc: LightCombo01/HeavyCombo01 clips onto Swing1-3/Slam at
~natural speed (26-tick window, 30-tick recover, DPS-held damage retune),
Menacing01 combat idle (InCombat), SM_Wep_Axe_Large_01 rigid-skinned to
Hand_R at x1.25, LANTERN FX retone + blade-smear ribbon. Forks 1-6 per
Combat_Attack_Feel_Guidelines (design review wf_000bc247): cleave resolves
at the CONTACT tick (MeleeCleavePending schedule-and-consume, knob 31,
0=legacy; connect cues moved to contact), MeleeRange 2.2 + reach-only
finisher mult 1.25 (knob 30), BufferedAttackTick GhostField input buffer
(knob 29, unlock-edge validity), dash refused pre-contact (lookup-based).
Tests: MeleeComboTests pin legacy knobs; +3 fork tests (414 green); live
server proof: HP drop exactly at swing+16 under tick batching.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 21:03:18 -07:00
kronic 7571091394 LANTERN feel pass (DR-052 + gap list): SoD facing, underwater feel, Bathynaut kit, walk/run gait, suit lamp + new Synty anim packs
- SoD facing: PlayerFacing = body-yaw only (move-facing / cast-turn / idle-hold); every fire
  direction re-sourced to FacingMath.ResolveAim (pre-code review blocking catch); TickWindowMath
  shared windows with Movement-skip; reticle/FX coupled to the damage direction; cursor-dash kept.
- Underwater feel: sharpness 15->6, turn 720->360, MoveSpeed 6->4.2; TuningKnob 26-28
  (0 = no-override sentinels, dev-protocol bump on DebugTuningReport); stride footsteps + silt +
  cadence floor; bubbles; underwater ambience bed + distant groans; camera drag + dev scroll zoom.
- Bathynaut kit in-engine: dome/tank/shoulder-lamp + bare head grafted (GraftSmr rigid rebase,
  RecalculateTangents); EmissiveGloamSkinned shader (Rukhanka deformation); shoulder lamp CASTS
  (warm steady spot on body yaw).
- Gait: two-ring walk/run FreeformDirectional tree (walk @0.35, run @1.0) + blended-natural
  StrideScale; additive Posture(Bank) + Lead(chest-lead) layers; idle = AnimationIdles Base;
  banking driven from facing turn rate; flat terrain (Env_SeabedKit seabed squashed - CC is planar).
- New packs: Synty AnimationIdles + AnimationSwordCombat (combat pass queued) + SyntyPropBoneTool;
  four authored clips (sway/trudge/banks/lean) + Anim_Player_Underwater.blend + suit-kit FBX.
- Validation: 411/411 EditMode green; Play smokes (server==client facing, Aim-true projectile,
  bank/stride live-sampled, lamp beam verified); pre-code + post-impl adversarial reviews applied.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:56:02 -07:00
kronic 210d307406 LANTERN realignment P5: Bathynaut suit body onto Player.prefab (in place)
New PlayerRigTools.BuildBathynautPlayer (menu: ProjectM/Animation) - forks the
proven EnemyRigTools recipe: strips the Synty-soldier skeleton+SMRs from
Player.prefab IN PLACE (GUID preserved; ghost/authoring/CC/Animator/
RigDefinitionAuthoring untouched), flattens the SpaceSoldier_HelmetArmor suit
body under the root, keeps the -0.90 feet offset, re-slots
M_SuitFrame_Bathynaut_Animated on every SMR, adopts the shared CharactersAvatar
(AC_PlayerTopDown muscle clips retarget). 17 inactive Synty variant SMRs stripped.
Both frames share this visual until per-frame models land; the brass-dome/tank
kitbash attachments are a follow-up /art-dev export.

Play-verified in DevSandbox: suit renders + idles correctly under the LANTERN
murk, Rukhanka rig baked with all 9 drive params, dev overlay alive in the
renamed scene, no console errors.

=> Movement/animation tuning is now UNBLOCKED (the operator's next priority).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 16:31:07 -07:00
kronic c58439d33a LANTERN realignment P3: one look everywhere + scene surgery
- 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>
2026-07-15 16:12:00 -07:00
kronic 77de740b63 LANTERN purge B7: rename CharacterId -> FrameKind (frame terminology)
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>
2026-07-15 15:51:48 -07:00
kronic 4a8220ad3e LANTERN purge B6: delete the legacy single-ability path (sockets are THE ability model)
AbilityRef, AbilityCooldown, EffectiveAbilityStats, DefaultAbility deleted.
GoInGameServerSystem seeds the per-frame 4-socket Spark loadout UNCONDITIONALLY
(was gym-only); ClassSelectReceiveSystem + DebugOp.SetClass swap FrameId +
re-seed sockets + zero SocketCooldown; ClassSwapUtil.Apply drops newAbilityId;
EquipSystem weapons become stat-sticks (GrantedAbilityId removed from the item
blob/authoring); StatRecomputeSystem folds CharacterStatsRef + sockets only;
HUD cooldown bar reads socket 0 of SocketCooldown/EffectiveSocketStats; class
HUD readers are FrameId-only (ClassForAbility/AbilityFor deleted);
DebugModifierInjectionSystem drops CycleAbility.

390 tests green; Play-verified: a non-gym spawn gets frame=2 with Sparks
[7,8,6,9] and no console errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 15:47:12 -07:00
kronic b34945c2d2 LANTERN purge B3+B5: delete the cycle/core/win-lose spine + onboarding; save epoch v7
Deletes CyclePhaseSystem, GoalReachedSystem, CoreDamage/CoreRestore, ThreatDirector,
CoreIntegrity/GoalProgress/RunPhase/RunOutcome/ThreatState components,
CoreVisualFeedbackSystem, and the whole Client/Onboarding slice (+6 test files).

Keepers reworked: RunDirectorSystem (UpdateBefore attr + launch guard + goal/threat
bank removed; sole SaveRequest raiser now), CycleDirectorSpawnSystem (ledger/meta
host only), WaveSystem UNGATED (waves run wherever a WaveDirector is baked),
EnemyAISystem core-fallback stripped, AmbientAudioSystem reworked (bed + run cues;
no CycleState gate), MusicSystem RunInfo-only, HudSystem big trim (goal meter, core
bar, siege banner, terminal banner, outcome flash, onboarding hook all gone),
MetaShop/ClassPrep/AimReticle siege gates dropped, DebugOverlay/ops re-meant
(SpawnWave=force next wave, EndSiege=quiet arena; SetCalm/AdvanceGoal/SetHeat
retired, bytes reserved), TuningConfig Core knobs retired (ids 20-23 reserved),
StorageMath.DrainFraction deleted, HowToPlay copy rewritten.

Save epoch v7 (fresh epoch, operator-approved): SaveData drops goal/core/outcome +
conveyor/machine-IO fields; MinLoadableVersion=7; PendingSave/PendingStructure
trimmed; RollTerminalCampaignForward deleted; SaveStructureScan signature slimmed.

390 tests green; Play world-creation clean (player + waves live, no exceptions).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 15:27:12 -07:00
kronic 835dace213 LANTERN purge B2: delete EB-2 turret defense
TurretFireSystem, TurretAuthoring, Turret.prefab, Turret component, turret cap,
B hotkey, HUD Charge chip + out-of-ammo cue, catalog entry, Tuning consts, tests
(-7, 452 green). StructureType.Turret byte + ResourceId.Charge stay reserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 14:40:12 -07:00
kronic a0f6d4a5c4 LANTERN purge B1: delete the automation chain (Harvester/Conveyor/Fabricator)
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>
2026-07-15 14:32:11 -07:00
kronic aeb979f8d5 LANTERN P1 step 8: light-as-territory relevancy prototype (★)
Per connection, marks every ENEMY ghost OUTSIDE the player's lamp radius
IRRELEVANT (monsters in the dark aren't replicated — the gamma test). Modelled
on the proven RegionRelevancySystem: SetIsIrrelevant so players/global ghosts
stay relevant for free. Shared-set discipline — RegionRelevancySystem is the SOLE
clearer (runs every tick); LightRelevancySystem is [UpdateAfter] it and only ADDs
light hides via TryAdd (no clear; defensive against a region+light overlap dup).
Gated on GymTag (LANTERN-only; legacy region relevancy untouched). Pure decision
in LightRelevancyMath (+3 EditMode tests, 500 green). Per-player dim/bright
(variable radius) + the L3 gamma-test verification are follow-ups; the ★adversarial
netcode review is recommended before this ships.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 08:51:24 -07:00
kronic 342a39a540 LANTERN P1 step 5 + §8: two-frame identity via FrameId + per-frame gym loadouts
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>
2026-07-15 08:45:45 -07:00
kronic 8f099c32ba LANTERN P1 step 6: manual-aim windups (SoD readable telegraphs)
AbilityDefBlob/AbilityDefinition gain WindupTicks. AbilityFireSystem's socket
loop resolves each socket at the historical command (serverTick - WindupTicks)
via GetDataAtTick — a pure fn of replicated data on both worlds (Build Spec §4/
DB-6: no local fireAtTick that would diverge under reprediction; fireCount from
that command, spawn uses the current tick's aim/pos; history gap = no-fire).
WindupTicks set on the 4 non-Blink Sparks (Blink is movement, resolved by
BlinkSystem). 497 EditMode green (no regression). Client telegraph VFX + the
reprediction L3 test are operator-eyes / netcode-world follow-ups.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 08:35:20 -07:00
kronic b92cc7196b LANTERN P1: enemy-test gym tooling — on-demand spawn + GymTag clean-spawn
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>
2026-07-15 00:01:13 -07:00
kronic 36950ca21d LANTERN P1 Group B: 5-Spark systems — Aoe/Hitscan dispatch + reel/zone/decoy
Aoe/Hitscan archetype dispatch in AbilityFireSystem (both-worlds cooldown;
server-only ecb.Instantiate — the client-never-instantiates invariant; ZoneEffect
vs DecoyTag chosen by prefab membership). Harpooner REEL as a homing state
(ReelState + ReelSystem re-aim the hit Husk's KnockbackState toward the caster's
live position each tick; EnemyAISystem stays sole Position writer), stamped by
ProjectileDamageSystem on a Reel-flag hit. ZonePulseSystem (enemy-only periodic
AoE, caster attribution, no friendly fire, Vortex pull, born-0 lazy-stamp).
Decoy aggro in EnemyAISystem + DecoySystem despawn. AbilityDefBlob gains
EffectFlags (Reel) + DurationTicks. +3 ZonePulseSystem EditMode tests (497 green).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 00:00:41 -07:00
kronic 4302687be4 Art/anim: EnemyRigTools + Grindylow + Bathynaut suit-frame builders
BuildGrindylow (SciFiSpace Alien, gloam-blue) = 2nd creature (A1 'twice'
repeatability); BuildBathynautFrame (armored+helmeted soldier, warm-neutral)
= suit-frame preview proving the suit body skins via Rukhanka.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 22:21:06 -07:00
kronic cb224be81b Art/anim: skinned Drowner enemy via EnemyRigTools (LANTERN)
EnemyRigTools.BuildDrowner (menu ProjectM/Animation) — SciFiSpace Crew_Male
body (same rig/atlas/avatar as the player) through the proven DR-023 path:
EnemyDrowner.prefab = ownerless interpolated ghost + AnimatedLitShader
(Skinned-Palette) gloam material + AC_EnemyTopDown + RigDefinitionAuthoring.
Structurally identical to the shipping werewolf/charger enemies -> animates
in-game via the same EG/Rukhanka path. (Renders only in the baked ECS world,
not a plain scene; ArtStaging keeps the static bake for art review.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 22:00:38 -07:00
kronic 9ca27e626a Client: StagingAmbiance — steady beacon, gloam flicker (readability cadence)
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>
2026-07-14 21:42:32 -07:00
kronic 3103214e76 Client: StagingAmbiance dev component (ArtStaging Play-mode juice)
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>
2026-07-14 21:31:59 -07:00
kronic 52818d0dd6 LANTERN P1 Group B: Blink Spark (Movement archetype) + BlinkSystem
Per Phase1_Combat_Gym_Build_Spec §6 (the Blink Spark = a second, socketed dash):
- AbilityArchetype += Movement (falls through AbilityFireSystem; a sibling system drives it).
- AbilityId += the 5 Phase-1 Sparks (DecoyWisp/HookPull/Vortex/Blink/LightZone).
- BlinkState: predicted, non-replicated blink window (DashState sibling).
- BlinkSystem: velocity blink (never a LocalTransform write), triggered by the Movement-
  archetype socket, dash-wins precedence ([UpdateAfter(DashSystem)]), sole writer of its
  socket's SocketCooldown row; reads socket loadout/cooldown by entity via lookups (query at 6
  args, under the 7-cap). Baked BlinkState on the player.
L1 clean; L2 494/494. Dead until a Blink Spark def is socketed (ability-database SO = next).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 11:45:06 -07:00
kronic 58c2eaca58 LANTERN P1: dev injector — socket-fire injection (enables the L3 netcode validation)
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>
2026-07-14 11:29:08 -07:00
kronic 21f609c6c4 LANTERN P1 step 2.5: client feel layer migrated to the socket kit
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>
2026-07-14 11:13:05 -07:00
kronic 12f86c86de LANTERN P1 step 2: AbilityFireSystem socket restructure + SpawnId repack
The netcode core of the 4-socket kit (Phase1_Combat_Gym_Build_Spec §1,§2,§5; review NP-1/
RS-1/DB-1/DB-3):
- PlayerInput: +4 Socket0..3 InputEvents + Burst-safe GetSocket(i); legacy Fire kept vestigial.
- PlayerInputGatherSystem: gather sockets from keyboard 1..4 (+gamepad RT/LB/RB); socket 0 also
  fires on the legacy primary (right-click / pad LT) as a bridge.
- AbilityFireSystem: query dropped to 4 type args (PlayerInput/PlayerFacing/LocalTransform/
  GhostOwner) + BufferLookup<AbilitySocket>/ComponentLookup<SocketCooldown>/BufferLookup<
  EffectiveSocketStats> (the 7-arg-cap fix); loops 4 sockets; per-socket cooldown + per-socket
  effective stats; Cone + Projectile dispatch per socket (predict-spawn Projectile-only).
- ProjectileSpawnId.Pack: pure Burst-safe key owner14|socket2|fireCount12|fork4 so same-tick
  multi-socket projectiles never collide; AbilityFireSystem uses it.
L1 clean; L2 494/494 (+5 ProjectileSpawnId tests: distinct-per-socket, fork, owner, bit-ranges,
count-wrap). L3 two-player + rollback is the group-A gate (after step 2.5). Note: the client
feel layer (muzzle/anim) still reads the legacy cooldown until step 2.5; sockets bake empty
until content (step 4), so nothing fires in-game yet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:59:45 -07:00
kronic b143eb6bb5 LANTERN P1 step 1b: per-socket effective-stats fold (additive)
Per Phase1_Combat_Gym_Build_Spec §7 (additive; legacy single EffectiveAbilityStats + its
fold stay until steps 2/2.5 migrate readers, then retired):
- EffectiveSocketStats: 4-wide derived (non-replicated) per-socket effective-stats buffer.
- PlayerAuthoring bakes it (4 rows).
- StatRecomputeSystem: a SECOND fold loop (separate query, under the 7-arg cap) folds each
  socket's SparkId blob base with the SHARED StatModifier band (uniform; per-Spark warping =
  Phase 4) into EffectiveSocketStats[i]; empty socket (SparkId 0) -> zeroed default.
  Fixed CS1654 by writing through a mutable local copy of the buffer handle.
L1 clean; L2 489/489 (new SocketFold_Folds_Per_Socket_With_Uniform_Band).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:43:31 -07:00
kronic 67095024b9 LANTERN P1 step 1: 4-socket kit data model (player-ghost re-bake)
Additive socket data model per Phase1_Combat_Gym_Build_Spec §3 (no behaviour; legacy
AbilityRef/AbilityCooldown left intact for the step-2 migration):
- AbilitySocket: cold per-socket loadout buffer (EquipmentSlot-modelled, [GhostField] byte
  SparkId, SendToOwnerType.All, 4 rows baked empty).
- SocketCooldown: hot owner-predicted per-socket cooldown (4x [GhostField] uint + Burst-safe
  switch indexer; SendToOwnerType.All for remote ability-feel parity with AbilityCooldown).
- FrameId: replicated [GhostField] byte frame/class signal (SendToOwner; the class source the
  socket model needs since no single ability id maps 1:1 to a class).
Baked onto the player ghost in PlayerAuthoring. L1 clean compile + domain reload.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:35:58 -07:00
kronic 24800f4bcb Attack Boon Changes 2026-07-13 18:30:41 -07:00
kronic 6dcd8a243d World: critters + weather beats — AmbientLifeSystem (Phase 1.5b bundle 4)
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>
2026-07-12 14:41:35 -07:00
kronic 4febf3dfe2 Hazard: Blight geyser — permanent periodic both-sides AoE (Phase 1.5b bundle 3)
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>
2026-07-12 13:53:48 -07:00
kronic 649f656833 Decals: explosion scorch pool + cover damage-cracks + room-arena scars (Phase 1.5b bundle 2)
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>
2026-07-12 12:58:33 -07:00
kronic 9670465e25 World: flora feel fix — kill double-animation (root sway off, shader wind is the idle motion) + thin flowers
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>
2026-07-11 23:12:34 -07:00
kronic 2fc5ae9a10 World: ground bundle — macro-blend ground materials, per-room dressing scatter, biome ground tint, base flora + worn paths
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>
2026-07-11 22:47:44 -07:00
kronic eb7f572168 Fix: barrel fuse shows its blast radius + burns ~1.5s (was 0.6s)
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>
2026-07-11 17:13:13 -07:00
kronic bbf418e779 Hazard: destructible cover — carve-able rocks that genuinely block until broken
Phase 1.5 long-term #8, the LAST 1.5 item (design review wf_e14dd739-069:
both HIGHs + the distinct MEDs verified before the session limit ate the
duplicate verifiers; unverified leftovers reasoned-closed in the spec).

Cover = a BlightClutter ghost (Variant 4) with a baked Environment-layer
BoxCollider: the runtime ghost's collider joins BuildPhysicsWorld ->
blocks the player CC and the enemy sweep while alive, and the gap opens
the instant the entity dies. Both hit paths already break it (the
clutter chassis); 8 hits to carve.

Review folds:
- HIGH: the 07-07 anti-stuck nudge would phase enemies THROUGH cover
  after ~1.5s (it exists to prevent room soft-locks). The nudge is now
  COVER-AWARE: a SphereCast identifies the blocker; a live BlightClutter
  ghost suppresses the phase (no soft-lock possible - the blocker is
  destructible); static-geometry wedges still nudge.
- HIGH: yield==durability made tanky cover an economy faucet. Deposit
  is DECOUPLED from the decrement at both hit sites: ScrapPerHit=0 =
  breaks in Remaining hits, yields NOTHING; fractional POSITIVE yields
  still credit >=1 (the B1 immortal-sink test pinned this - zero means
  zero, positive floors to 1).
- MEDs: HitRadius 1.2 (visual core, not a shot-eating disc), NO cover
  in Boss rooms (no boss depenetration backstop), scatter keep-out >=7u
  from the room origin (player landing + portal), Variant=4 explicit
  (the clutter block's explosive reroll cannot leak).
- Old static layer removed: 8 WorldColliders_RoomCover colliders
  (subscene) + the RoomCover_Visuals root (Game.unity).

Verified live: 3 cover ghosts spawned with SOLID baked colliders;
BLOCK - NudgeUntilTick stayed 0 across 3.5s of forced zero-progress
(old code arms at 1.5s); DEPENETRATE - an enemy shoved inside a live
rock exited to 1.7u (backstop holds against ghost colliders); CARVE -
the rock died to the real sweep and the spot probed OPEN. 470/470
EditMode; console clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 16:15:38 -07:00
kronic cd607ae156 Hazard: exploding barrels — fused explosive clutter, friendly-fire bait
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>
2026-07-10 19:41:47 -07:00
kronic a335ca3f21 Feel: attack distinctness — per-kind telegraph colours, windup voices, spit light
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>
2026-07-09 23:17:43 -07:00
kronic 53bc21143d Feel: breakable clutter reads as Hades-style barrels, one-hit pop
The BlightClutter destructible-ghost chain (per-room seeding, unified
DR-018-safe sweep, melee + projectile breaking, personal-inventory
drops) already existed end-to-end - the roadmap item's gap was FEEL:

- Visual: prefab Model mesh swapped SM_Env_Rock_Chunk_01 (read as
  scenery) -> SM_Prop_Barrel_01 (FantasyKingdom, same proven atlas
  material). Base-pivot barrel offset -1 on the Model child (scatter
  y=1 is the capsule plane - the base-pivot float gotcha).
- One-hit pop: prefab Remaining 8 -> 2 (both hit paths decrement by
  max(1,(int)ScrapPerHit)=2, so one swing/shot shatters); authoring
  default kept in sync.
- Density: MaxClutterPerRoom 6 -> 8.

Verified live: 8 barrels seeded in a combat room, grounded (screenshot);
an injected projectile through the real ResourceHarvestSystem sweep
popped one barrel in ONE hit (8 -> 7) and credited 2 Biomass; 466/466
EditMode, console clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 22:14:43 -07:00
kronic 90d4bed381 Feel: ambient motion layer + art look locked (clean dark-Synty, pixel OFF)
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>
2026-07-09 21:01:14 -07:00
kronic 414f9ff62a Feel: Phase 1.5 lighting/atmosphere pass — dark ambient + dynamic lights
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>
2026-07-09 20:37:50 -07:00
kronic c58a17b8af Fix: planar Y-lock in CharacterProcessor — no more climbing/floating on sloped colliders
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>
2026-07-09 20:06:05 -07:00
kronic 5382cbf341 World: collision-fidelity pass — fit walls/cover/landmarks to visuals + solid sweep
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>
2026-07-09 19:45:40 -07:00