Skip to content

feat(player): swap normal↔attack model by combat state (FLO-481)#124

Merged
Fl0p merged 1 commit into
mainfrom
flo-481-player-attack-model-swap
Jun 21, 2026
Merged

feat(player): swap normal↔attack model by combat state (FLO-481)#124
Fl0p merged 1 commit into
mainfrom
flo-481-player-attack-model-swap

Conversation

@Fl0p

@Fl0p Fl0p commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

What & why

Plan step 2 of FLO-474: the player now has two clearly distinct visual states driven by combat. Alongside the neutral survivor GLB we mount the strike-pose twin korovany_hero_player-attack.glb (FLO-480, drop-in twin: same bbox/scale/identity) and toggle which renders by melee state — default while idle, attack pose while a swing is in flight (windup/active/recovery).

How

  • survivorAvatar.tsmountSurvivorAvatar/wireSurvivorAvatar take an optional attack URL. Both GLBs load in parallel, are faceted + matted, and parented under the capsule at the same foot offset. Shared bbox ⇒ no scale/position correction; static swap, no animation playback (FLO-440 swap-by-URL pattern). Attack-fetch failure degrades gracefully to default-only (player visual never drops).
  • CharacterController — new registerCombatVisual(swap); setAttackPhase edge-fires the swap on the idle↔swing transition (flips twice per swing, not every frame). The default root stays the animator's node so bob/lean/lunge keep playing between swings.
  • Scenes — wired in forest, human-lands, mountains, empire (every scene with melee). controller-playground (no combat) stays default-only.

Tests

  • survivorAvatar.test.ts — attack twin mounts hidden + faceted; swap toggles default↔attack visibility; mount path loads both GLBs and registers the swap.
  • characterController.test.ts — edge-firing (exactly [true,false] across a full swing; no fire while idle).

Docs

  • public/models/README.md — two-state player wiring (FLO-481).
  • docs/guide/assets.md — combat-state swap section + attack-pose table row; cleared stale "pending engine-wiring".

Verification

  • npm run lint ✅ clean
  • npm test ✅ 952 passed (106 files)
  • npm run build ✅ built

🤖 Generated with Claude Code

Mount the strike-pose twin (korovany_hero_player-attack.glb, FLO-480) alongside
the neutral survivor GLB and toggle which renders by melee state — default while
idle, attack pose while a swing is in flight (windup/active/recovery). Gives the
player two clearly distinct combat states in every melee scene.

- survivorAvatar: mountSurvivorAvatar/wireSurvivorAvatar take an optional attack
  URL; both GLBs load in parallel, are faceted+matted, and parented under the
  capsule at the same foot offset. Drop-in twin (shared bbox) needs no
  scale/position correction; static swap, no animation playback (FLO-440 pattern).
  Attack fetch failure degrades gracefully to default-only.
- CharacterController: registerCombatVisual + edge-fire the swap from
  setAttackPhase on the idle↔swing transition (flips twice per swing, not per frame).
- Wired in forest, human-lands, mountains, empire (every scene with melee);
  controller-playground (no combat) stays default-only.
- Tests: model swap by combat state + controller edge-firing.
- Docs: public/models/README + docs/guide/assets.md two-state player section.

Co-Authored-By: Wayland <wayland@agents.flopbut.local>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Fl0p Fl0p merged commit 3d8754d into main Jun 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant