Skip to content

feat(WIP, paused): 2.5D turn-based RPG + hit VFX — PSP VFX unresolved#16

Draft
doodlewind wants to merge 3 commits into
mainfrom
rpg-2.5d
Draft

feat(WIP, paused): 2.5D turn-based RPG + hit VFX — PSP VFX unresolved#16
doodlewind wants to merge 3 commits into
mainfrom
rpg-2.5d

Conversation

@doodlewind

@doodlewind doodlewind commented Jun 20, 2026

Copy link
Copy Markdown
Owner

⚠️ STATUS: DRAFT — development PAUSED, one feature unfinished. See "Known issue" below. Web is fully working; PSP has one broken effect.

What this PR set out to do

  1. (done) A new 2.5D turn-based RPG battle game (framework/games/rpgbattle3d.js): a real 3D arena + a fixed ¾ camera, two skinned KayKit Knight instances (hero blue / enemy red, CC0), and a classic command-menu turn loop (HP/MP, Attack/Power Strike/Defend/Potion, crits, floating damage numbers). Works on web and PSP.
  2. (done on web) Flashy hit VFX ("华丽的打击特效"): since the engine's 3D layer was opaque-only (cutout washed HD sprites to white), I added additive blending to the engine (OP_SET_BLEND wire op; web + PSP + 3DS const + contract) and an additive billboard that plays a CC0 explosion (StumpyStrust) on the struck fighter, with hit-stop + shake + screen-flash. Verified gorgeous in WebGL.

✅ Done & verified

  • The full RPG battle: characters, combat FSM, menu, HP/MP, crits, damage numbers — web + PSP.
  • Additive-blend engine feature wired across all hosts; bun run test green (golden + .dc3d draw-list + cross-host wire/button contract).
  • Hit VFX gorgeous on web (a crit fireball glows over the scene — see commit screenshots).

⛔ Known issue (UNRESOLVED — why this is paused)

The hit VFX renders as a white box on PSP (it's perfect on web). The additive billboard goes through the PSP native render path (renderNativepost OP stream), and that path isn't applying the texture + additive blend to a dynamic rigid quad — it draws a bare white quad.

Fixes attempted (committed):

  • buildFlat only registers dynamic nodes once, while the VFX node started hidden → never drawn. Fixed by keeping it registered and hiding via scale=0. Now the node draws on PSP (progress).
  • ✅ VFX textures re-baked to power-of-two (64×64) — the PSP GE requires pow2 (48px → white). Web handles NPOT.
  • ✅ Quad given a normal so GE lighting is deterministic.
  • Still a white box after all three → the native post-stream rigid textured+additive OP_DRAW path needs deeper work (texture sampler + blend state for that specific draw path, which was built for skinned characters, not rigid sprites).

▶️ To resume

  • Debug the PSP native post OP_DRAW for a rigid textured mesh: confirm OP_BIND_TEXTURE/apply_texture actually enables Texture2D for it, and that OP_SET_BLEND (sceGuBlendFunc Add/Fix) takes effect — likely the textured rigid path in runtime/src/gfx3d.rs (vs. the static sceneAdd path which does texture correctly).
  • Or render VFX via the static-scene textured path instead of post.
  • The web path is the reference (works); match its result on PSP.

🤖 Generated with Claude Code

doodlewind and others added 3 commits June 20, 2026 14:41
A new game, framework/games/rpgbattle3d.js: a 2.5D JRPG duel — a real 3D arena rendered
with a fixed 3/4 camera, two skinned KayKit Knight instances (hero tinted blue, enemy
red), and a classic command-menu turn loop.

- Asset: KayKit "Character Pack: Adventurers" Knight by Kay Lousberg — CC0 (public
  domain), committed under assets/vendor/kaykit-knight/ (+ LICENSE + CREDITS). ONE model
  is baked and instanced twice (hero + tinted enemy) to stay in the soldier-class memory
  budget (the baked module is 0.64 MB, Fox-class).
- Bake: framework/bake/bake-gltf.ts gains a clip allow-list (asset packs ship dozens of
  clips) + bakeRpgHero() baking only the 5 combat clips (Idle, Walking_A, Attack, Hit,
  Death) at 128px / boneLimit 8 -> assets-rpg-hero.ts.
- Combat (deterministic, ctx.rng + frame timers): INTRO -> PLAYER (command menu) ->
  WINDUP/IMPACT/RECOVER -> ENEMY (AI) -> ... -> VICTORY/DEFEAT. Stats (HP/MP/ATK/DEF/SPD),
  SPD turn order, Attack / Power Strike (MP) / Defend / Potion, a damage formula with
  seeded variance + 12.5% crit, floating damage numbers, attacker lunge + target hit
  flash, HP/MP bars + nameplates drawn over the 3D pass.
- Golden: framework/test/golden.ts gains a deterministic rpgbattle3d SPEC; the .dc3d
  draw-list golden captures the skinned characters (the software oracle is vertex-colour
  only, so the .png shows arena + HUD; WebGL/PSP show the models).

bun run test green (golden + .dc3d + cross-host button contract). Verified in WebGL: hero
left / enemy right facing each other, Power Strike lands a crit, HP/MP update.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Flashy melee hit effects for rpgbattle3d, built on a new engine feature.

Engine — additive blending for the 3D layer (it had only opaque rendering, so cutout
sprites washed to white blobs):
- g3d.ts: OP_SET_BLEND wire op (0x0008) + CommandEncoder.setBlend(); Material.blend='add'.
- scene3d.ts: brackets additive nodes with OP_SET_BLEND in BOTH paths — the OP-stream
  emit() (web/raster) and the native walkDynamic() (PSP, routed through the post stream).
- web/engine.js: gl additive (blendFunc ONE,ONE, premultiplied) + depth-write off.
- runtime/src/gfx3d.rs: sceGuBlendFunc(Add, Fix, Fix) + depth mask off on the PSP host.
- runtime-3ds + contract.ts: OP_SET_BLEND declared for cross-host wire parity (3 hosts,
  16 wire constants, contract green). raster3d skips it (oracle ignores textures anyway).

VFX:
- bake-vfx.ts: bakes CC0 sprite sheets into PREMULTIPLIED RGBA frames (so additive
  reproduces the soft glow). assets-vfx.ts = VFX_BOOM (22f) + VFX_SPARK (9f).
- Assets (CC0, committed under assets/vendor/vfx/ + CREDITS): StumpyStrust "Explosion
  Sheet" + kurohina "Spark effect".
- rpgbattle3d.js: a camera-facing additive billboard plays the explosion at the struck
  fighter on IMPACT (bigger on a crit), plus hit-stop (FSM freeze), a shake bump, and a
  screen-edge flash. Verified gorgeous in WebGL (a crit fireball glows over the scene).

bun run test green (golden + .dc3d + wire/button contract). Deterministic: VFX timing is
frame-stepped, hit-stop is fixed-length.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mic-node registration

Partial fixes toward the additive hit VFX rendering on PSP (works on web; renders as a
white box on PSP). NOT yet resolved — development paused here.

- bake-vfx: VFX frames baked at 64x64 (power-of-two; the PSP GE requires pow2 textures —
  48px rendered white). Web is unaffected (handles NPOT).
- rpgbattle3d.js: the VFX billboard stays VISIBLE and is hidden via scale=0 between hits,
  so the PSP native buildFlat (which walks once) registers it as a dynamic root; quad gets
  a normal so GE lighting renders it deterministically. After these, the node DRAWS on PSP
  but still as an untextured/unblended white quad — the native post-stream rigid textured+
  additive draw path needs deeper work.

bun run test green (web golden + .dc3d + wire/button contract). See the PR description for
status + the original goal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@doodlewind doodlewind changed the title feat: 2.5D turn-based RPG battle with a skinned protagonist model feat(WIP, paused): 2.5D turn-based RPG + hit VFX — PSP VFX unresolved Jun 20, 2026
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