Spaces:
Running
Skill Upgrade Plan — impressive unique VFX + powerful AoE forged skills
Goals (from the ask):
- Forged skills get impressive graphics when used in combat (today they look plain).
- The coding model makes each skill visually unique (not just mechanically).
- Most skills are powerful and hit all units in their range (AoE).
Why forged skills look plain today (grounded findings)
- VFX is config-driven:
combatRenderer.buildSkillPlay(id, def)readsdef.skillFx[skillId] = { animUrl, effects:[{url,cell}] }. Canon skills get this from the sandbox's skill configs; forged skills (id 9000+) have noskillFxentry, so oncastthe renderer falls through toplayAttack()— a basic sword swing — andspawnEffects(undefined)is a no-op. - AoE is target-centric:
resolveScope()supportsself/party/nearby/area/target_and_adjacent/ adjacent_to_target, butarea/nearby= target + units withinADJACENT_GW=140of the TARGET. There is no "all enemies within the caster's range" scope. Forged skills also don't set anyscope, so each effect hits the single auto-target. - Asset palette is rich:
web/assets/effects.json(228 entries) has element sheets — fire, ice, nature, poison, shock, petrification, plusattackarcs (slash/pierce/shot) andhitpops — all minifantasy strips with{cell, rows, cols}. Great raw material for element-themed VFX. - The Game wires VFX per actor:
comboBattler.spawnHerobuildsdefsById.P0 = {name, profession, ...sheets}and passes it tocreateCombatRenderer. AddingdefsById.P0.skillFxis the hook for the hero's forged-skill VFX.spawnEffectspositions effects at the caster's body centre.
Phase A — Visual identity in the forge IR (model makes each unique)
Extend the Skill-Forge output so the coding model emits a visual block alongside the mechanics.
Cosmetic, so a bad value just falls back (never crashes the resolver).
New IR fields (skillSchema.js → validateSkill):
visual: {
element: 'fire'|'ice'|'lightning'|'nature'|'poison'|'shadow'|'holy'|'arcane', // → tint + sprite sheet
shape: 'slash'|'nova'|'ring'|'beam'|'pillar'|'projectile'|'shockwave', // procedural motion
color: '#rrggbb', // accent (validated hex)
intensity: 1..3, // particle count / scale / flash
}
All whitelisted/clamped. element maps to an effects.json sheet; shape+color+intensity drive
a procedural layer (Phase B). The model picks them to match the flavor → every skill is visually
distinct (element × shape × color × intensity is a huge space), and consistent with its name/lore.
Prompt (FORGE_SYSTEM) gains: "Also choose a visual that fits the skill's theme: an element,
a shape (how it manifests), an accent color, and intensity 1-3." Keep the JSON-only contract.
Phase B — Render impressive, unique VFX
Two composited layers, wired by giving the hero's forged skills a skillFx entry at spawn.
B1 — Element sprite layer (reuse existing pipeline).
- Build
ELEMENT_FXmap: element → aneffects.jsonURL (fire→fire sheet, lightning→shock, etc.). - In
comboBattler.spawnHero, constructdefsById.P0.skillFxfrom the equipped forged skills:skillFx[skill.id] = { animUrl: null, effects: [ ELEMENT_FX[skill.visual.element] ] }.buildSkillPlayalready loads these;processLog'cast' already callsspawnEffects. So forged skills immediately stop looking like a basic swing.
B2 — Procedural Pixi VFX layer (new src/render/skillVfx.js).
A small, asset-free effect generator drawn into the combat root (world space), parameterised by
visual: an expanding tinted ring/nova (shape nova/ring/shockwave, radius = the skill's AoE
range → reads as "everything in here is hit"), a beam/line to the farthest target, a pillar,
or a projectile burst. Color = visual.color; particle count/scale/duration scale with
intensity. Big skills also trigger a brief screen flash + shake (reuse the existing
addShake/juice hooks). This guarantees uniqueness + scale even when no sprite matches.
B3 — Hit feedback on every struck unit.
When an AoE skill resolves, pop a small element hit sprite (from effects.json hit category) +
the yellow skill-name float on each affected enemy — so the player sees the whole area get hit.
Wiring: comboBattler's processLog 'cast' hook (it already runs per cast) calls
skillVfx.play(caster, skill.visual, rangeRadius, targets). The renderer change is additive.
Phase C — Powerful + hit all units in range (AoE)
C1 — New engine scope (teamBattle.resolveScope):
case 'area_around_caster': {
const r = e.range || DEFAULT_AOE // effect-supplied radius (field units)
return b.actors.filter(x => x.alive && x.team !== src.team && dist(src, x) <= r)
}
Allow effects to carry a clamped range. (Also extend damage/heal/... op validators to accept
scope + range, not just apply_condition.)
C2 — Forge defaults to powerful AoE:
- Schema: when the model omits scope, default most forged skills to
area_around_caster(a config flag, e.g. attack/spell categories get AoE by default;self/allytargets don't). - Prompt: "Most skills should be POWERFUL and hit ALL foes in range — prefer an area scope and meaningful damage/conditions."
- Numbers: raise the forged damage clamp (these are a hero's signature ultimate-ish skills), with a balance ceiling. AoE radius derived from the skill's reach/element (clamped to ~the on-screen ranged reach so it's readable, not whole-map).
C3 — Readability tie-in: the AoE radius from C1 drives the Phase-B nova scale, so the visual ring exactly covers the hit area — the player sees who's caught.
Files to touch
| File | Change |
|---|---|
tiny-army/web/skillSchema.js |
visual block + validation; scope/range on more ops; AoE-by-default; higher damage clamp |
tiny-army/web/skillForge.js |
FORGE_SYSTEM prompt: emit visual + prefer powerful AoE |
auto-battler/src/engine/teamBattle.js |
area_around_caster scope + range on effects |
auto-battler/src/render/skillVfx.js (new) |
procedural element VFX (nova/ring/beam/pillar/projectile) |
auto-battler/src/render/comboBattler.js |
build defsById.P0.skillFx from forged visuals; call skillVfx.play on cast; AoE-scaled nova + flash |
auto-battler/src/render/combatRenderer.js |
(light) expose a per-target hit-effect helper for AoE pops |
Sequencing
- C (engine AoE + power) first — biggest gameplay impact, smallest surface; verify in-engine
that an
area_around_casterskill damages all foes in radius. - A (visual schema + prompt) — model emits
visual; validate; re-forge produces visual params. - B1 (element sprites) — forged skills stop looking plain (quick win, reuses pipeline).
- B2/B3 (procedural VFX + AoE nova + hit pops) — the "impressive + unique" payoff.
Decisions (locked 2026-06-13)
- Power/AoE: signature-strong, capped — most forged attacks/spells are AoE + high damage, ceiling so they can't trivially one-shot a Champion boss.
- VFX: both element sprites + procedural nova/beam scaled to the AoE.
Open decisions
- VFX approach — element sprites only, procedural only, or both composited (recommended: both — sprites give theme, procedural gives unique scale/motion + guaranteed coverage).
- AoE default — make most forged attacks/spells AoE (recommended), or only when the model opts in.
- Power level — how strong? Recommend "signature strong": higher damage + AoE, but capped so a forged skill can't trivially one-shot a Champion boss.
- Should canon (non-forged) skills also get the new procedural VFX? Optional follow-up — they already have authored sprites.