Import art/VFX asset packs + game-feel systems; normalize texture extensions to lowercase for LFS

Add BefourStudios SciFi environment packs, Gabriel Aguiar VFX, and the
ShaderCrew Toon Shader embedded packages, plus combat/enemy/wave/death
gameplay systems and supporting vault docs/screenshots.

Rename 11 vendor textures from uppercase .PNG/.HDR to lowercase so the
case-sensitive Git LFS filters (*.png/*.hdr) match on case-sensitive
filesystems (Linux CI, case-sensitive macOS), not just locally where
core.ignorecase=true masks the gap. Each .meta moved with its asset so
GUID references are preserved. All ~1000 binaries tracked via LFS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 22:50:43 -07:00
parent dd0064c377
commit e362aaeb43
4830 changed files with 1293057 additions and 38 deletions
@@ -0,0 +1,53 @@
---
id: DR-009
title: First Blood — game-feel & identity slice (Husk enemy + death/respawn + combat juice + HUD + sci-fi look)
status: accepted
date: 2026-06-02
tags:
- decision
- netcode
- presentation
- enemy-ai
- game-feel
- identity
- m5
permalink: gamevault/07-sessions/decisions/dr-009-gamefeel-identity-firstblood
---
# DR-009 — First Blood: game-feel & identity slice
## Context
The project was a **systems-complete, identity-less vertical slice**: M1M5 solved every hard DOTS/Netcode problem (predicted player, data-driven abilities/modifiers, co-op, predicted physics + Character Controller, home base + shared storage) but it **looked like a debug scene, had zero feedback, and didn't play like a game** — the only "enemy" was an inert training dummy, there was no player death/respawn, no HUD, and a grep confirmed **no audio / particles / VFX / camera shake / UI** anywhere. The operator asked to "make this feel more like a game — in feel, look, and play — and give it an identity." A diagnosis established the gap was twofold: **(1) the pillars are mechanical, not fictional** (no setting/tone/art-direction) and **(2) there is no feedback layer** translating the (correct, replicated) simulation into things a player sees/hears/feels.
The operator chose: identity = **sci-fi frontier colony** ([[Identity]]); first slice = the **full game-feel pass** ("First Blood"); proceed by plan → approve → build. **context7 was reachable this session** (unlike [[DR-008_M5_HomeBase_BaseLayer_Storage]]) and was used to verify every volatile Netcode/Entities/URP API before writing (a 4-agent ctx7 verification workflow).
## Decision
1. **Identity = sci-fi frontier colony, layered over the locked [[Pillars]] with NO mechanical rework.** Captured in [[Identity]]: an off-world outpost crew holding the line against the **Husk** swarm; cyan crew / orange Husks / dark industrial world; automation = colony fabricators/drones. Only flavor, art-direction, and enemy fiction are added.
2. **Husk enemy = an OWNERLESS INTERPOLATED ghost, moved SERVER-ONLY.** Unlike the (non-replicated, server-only) training dummy, the Husk is a real ghost so all clients see it — built by **duplicating `UpgradePickup.prefab`** (an existing interpolated ownerless ghost) and swapping its authoring to `EnemyAuthoring` (keeps the `GhostAuthoringComponent`). `EnemyAISystem` runs **server-only in the plain `SimulationSystemGroup`** (interpolated ghosts are NOT predicted — must not run in `PredictedSimulationSystemGroup`), `[UpdateAfter(PredictedSimulationSystemGroup)]` (corrected from `[UpdateBefore]` in the same-day deepening pass — the predicted group is **OrderFirst**, so `UpdateBefore` is silently ignored), so a contact `DamageEvent` it appends drains the **following** tick (~16ms, imperceptible for melee) in `HealthApplyDamageSystem` (server, inside the predicted group). _(The flat `EnemySpawnSystem` mentioned below was superseded the same day by the `WaveSystem` threat director — see [[2026-06-02_GameFeel_Deepening]].)_ It seeks the nearest **living** player (writes `LocalTransform` directly — stock LocalTransform replication carries position, **no hand-written `[GhostField]`**) and strikes on a `NetworkTick`-gated cooldown. `EnemySpawnSystem` is a **persistent** server spawner that sustains `MaxAlive` Husks on a deterministic ring around the `BaseAnchor` (mirrors the one-shot `TrainingDummySpawnSystem` but refilling). Husk death reuses the existing channel: `HealthApplyDamageSystem` destroys `EnemyTag` at 0 HP (one-line addition next to the dummy hook).
3. **Player `Dead` = a LOCAL enableable component DERIVED from replicated Health — not replicated.** `PlayerDeathStateSystem` (runs in **both** worlds in `PredictedSimulationSystemGroup`, before movement/aim/fire) sets `Dead.enabled = Health.Current <= 0` and zeroes `CharacterControl.MoveVelocity` while dead. Because it is a pure function of the already-replicated, reconciled `Health` (the **same derive-don't-replicate idiom as `StatRecomputeSystem`/`EffectiveCharacterStats`**), the gate is identical on server + owner-predicted client and rollback-correct **with no replicated enabled bit** (avoids the volatile `[GhostEnabledBit]` surface). Movement/aim/fire (`PlayerControlSystem`/`PlayerAimSystem`/`AbilityFireSystem`) gain `.WithDisabled<Dead>()` (alive) composed with the existing `.WithAll<Simulate>()`. `Dead` is **baked DISABLED** on the player prefab (spawns alive, zero first-tick work). Respawn **timing is server-only** (`PlayerRespawnSystem`, `SimulationSystemGroup` `[UpdateAfter(PredictedSimulationSystemGroup)]`): schedule on first `Health<=0`, and on the due tick refill `Health` to the effective max + reposition to the deterministic base spawn slot (`PlayerSpawnMath`). `Health` (GhostField) + `LocalTransform` replicate, so the recovery reaches clients and the derived `Dead` clears.
4. **All juice + HUD = CLIENT-ONLY managed `SystemBase` in `PresentationSystemGroup` — observe, never simulate.** `CombatFeedbackSystem` edge-detects every damageable ghost's replicated `Health` (managed `Dictionary<Entity,FxCache>`, pruned each frame): a decrease → floating damage number (pooled billboard `TextMesh`) + hit-spark burst + hit SFX + camera shake; a Husk **despawn** (pruned-while-enemy = a kill) → death burst + SFX at its last position; the local player crossing to 0 → death feedback; local fire (`AbilityCooldown` advancing) → muzzle + zap. SFX are **generated procedurally** (`AudioClip.Create`), VFX use a **runtime `ParticleSystem` pool** (Sprites/Default material + HDR start colors so they bloom) — the slice ships **no binary audio/particle assets**. `HudSystem` builds a uGUI overlay **in code** (`RawImage` bars over `Texture2D.whiteTexture` + legacy `Text` with a resolved builtin font — no sprite/prefab assets) driven from the local player ghost: health bar, ability-cooldown bar, live Husk threat count, DOWNED/RESPAWNING overlay. Camera shake is a static impulse on `PrototypeCameraRig` decayed in `LateUpdate`**presentation-only, never the sim** (no `Time.timeScale` hit-stop, which would corrupt netcode). `ProjectM.Client` gains a `UnityEngine.UI` asmdef reference.
5. **Look = a URP post-processing volume + an emissive palette, on a budget.** A global `Volume` (`Bloom` threshold 1.1 / intensity 0.75, `Tonemapping` ACES, `ColorAdjustments` cool/contrasty, `Vignette`) + dark cool flat ambient + exponential fog + a cool directional key light + URP asset set to **HDR color grading** (required for ACES). Materials re-tinted with HDR emission so they bloom: crew **teal** (`M_Player`), energy-**cyan** projectiles (`M_Projectile`), hostile **orange** Husks (new `M_Husk`, isolated so re-tints don't bleed), dark-metal walls (`M_Dummy` doubles as the wall material) + ground (`M_Ground`). No real models/animation yet — palette + post-processing carry the identity.
## Consequences
- **Validated at runtime on 6.4.7 (single in-editor client).** Husks **spawn (6) and replicate server↔client (6==6)**, chase the player from the spawn ring to point-blank (seek works), and **deal contact damage** (player HP driven to 0). The **death→respawn loop works**: player `Health=0``Dead=True` → respawn scheduled (`RespawnTick`) → on the due tick **recovered at base with full HP**, `Dead=False`. The **HUD renders** (health bar, "HUSKS N" threat count, "DOWNED — RESPAWNING" overlay). The **look reads as intended**: dark metal environment, glowing cyan crew, hostile orange Husks, bloom on emissives (after fixing an initial shared-material bug where the walls glowed orange because `M_Dummy` doubled as the wall material). **EditMode 74/74** (+12: 7 `EnemyAIMath`, 5 `RespawnMath`).
- **Reused every validated pattern** — one-shot/persistent server spawner, interpolated server-spawned ghost (duplicate-an-existing-ghost), `DamageEvent``HealthApplyDamageSystem`, stock `LocalTransform` replication, server-only systems, derive-don't-replicate, `NetworkTick` cooldown gating, pure unit-tested math helpers. **No new asmdefs** beyond the `UnityEngine.UI` reference.
- **Difficulty tuned** after observing the swarm one-shot the (idle, input-less) player: Husk damage 8→5, strike cadence 36→48 ticks, speed 3.5→3.0. A real moving/shooting player can fight 6 Husks; the headless validation can't (it just stands there) — repeated death there is a test-harness artifact, not a gameplay bug.
- **Foundation for the next feel work** — the presentation pattern (client-world edge-detection on replicated state → pooled GameObject FX) and the server-AI pattern (interpolated ghost + seek/attack) generalize to more enemy types, a wave/threat director, ranged Husks, and real art/audio.
## Open / deferred
- **Auto-target on Husks** — `AbilityFireSystem`'s soft auto-target still only considers `TrainingDummyTag`; generalize the candidate set to `EnemyTag` (projectiles already hit Husks via raw-aim swept hit, so combat works; only the aim-assist snap is missing).
- **Respawn safety** — the player respawns into the swarm; add brief post-respawn invulnerability or a Husk push-back so respawn isn't instant re-death under a real player.
- **Real assets** — procedural SFX + primitive meshes + code-built FX are prototype-grade; swap for authored audio, stylized hard-surface crew/structures, and corrupted-biomech Husks once the loop is proven fun.
- **SSAO** — deferred (the URP SSAO renderer feature would deepen the dark scene); bloom + grading + emissive already deliver the read.
- **Enemy variety + threat director** — one melee Husk today; add ranged/brute/broodmaker + a wave/escalation system (the [[Identity]] roadmap).
- **Multi-client juice/HUD** — validated single-client; confirm remote-player death VFX + per-client HUD under two clients.
Expresses [[Identity]]; builds on the server-authoritative + deterministic + co-op pillars from [[Pillars]]; reuses patterns from [[DR-003_M2_Combat_Netcode_Architecture]] (DamageEvent/Health), [[DR-004_M3_DataDriven_Abilities_Modifiers]] (derive-don't-replicate, effective stats), [[DR-007_M5b_Character_Controller_Package]] (CharacterControl), [[DR-008_M5_HomeBase_BaseLayer_Storage]] (BaseAnchor, duplicate-an-existing-ghost).