Loading…
a327ex.com

SNKRX Swordsman, Magician, Outlaw, Cannoneer

Fable 5

Opus 4.8

Summary

Ported the four remaining mini-looper-survivor units (swordsman, magician, outlaw, cannoneer) into full SNKRX-faithful units — completing all 5 direct-hit units / the "easy tier" (5/28). Along the way: distilled the per-unit porting process into a general recipe, synced SNKRX-update up to snkrx-template, rebuilt the unit card header, added a random-targeting mode and the projectile+splash two-damage pattern, diverged the F5 spec lab with a unit picker, copied 062026's calmer unit-hit feedback, and added a global fixed-gradient-angle rendering option.

Session alignment + unit selection:

  • Read project_snkrx_update memory + SNKRX-update/porting.md (archer-baseline process). State coming in: archer done (1/28), porting infra in place (F1 arena dial, F5 spec lab, F6 inventory, the locked unit-gradient visual language).
  • Research pass on all 4 mini-looper survivors against E:/a327ex/SNKRX-ref/player.lua: swordsman (self-centered AoE attack(96), sensor 48, literal 3.0/warrior 0.9 = 3.33s, dmg 11 +15%/enemy), magician (attack(32) at random enemy, range 96, 2.0/mage 1.0 = 2.0s, dmg 18), outlaw (5-knife fan, sensor 96, literal 3.0/(warrior 0.9×rogue 1.1)=3.03s, dmg 14), cannoneer (projectile+explosion, sensor 128, 6.0/(ranger 1.5×nuker 0.75)=5.33s, hit 12/splash 36). Recommended swordsman; user picked it.

Swordsman port (units.lua):

  • User ruling: the swordsman is ALSO live in 062026, finished there — 062026 is the behavior reference where it differs from SNKRX. Only the SNKRX identity layer (damage, cooldown, card fields) was ported on top. Kept 062026's offset-square Cleave (64px square, 32 ahead, aimed at target) over SNKRX's self-centered circle; kept the sword_slash v3+v2 sound stack + carrier juice.
  • Numbers: damage = 11 (Part 8) + new damage_per_enemy = 2 flat crowd bonus (replaces SNKRX's +15%/enemy — flat additions always, project-wide rule; +2 gives 1-target 13 matching Part 8's "≈13"). cooldown = 3.33, range = 64. Spec/glyph_spec copied from 062026's F5-tuned literal (snkrx_yellow→yellow gradient). effect_name = 'Cleave' (SNKRX's merge effect shares the attack name), grayed.
  • Deleted swordsman_cooldown knob + swordsman_spec global; added swordsman to unit_type_order. Fixed a porting.md error: outlaw's pre-derived cooldown literal was wrong (said 2.0; the real literal at player.lua:237 is 3.0 → 3.03s).

Card header redesign (inventory.lua) — iterated several times:

  • card_meta_runs rebuilt from the bracketed [class][attack_type] (Awesome-16 bracket glyphs, read title-sized) into a bare Ark-10 comma list (warrior, area). Tier tag [tier N] moved to small Ark, baseline-aligned (tier_dy).
  • User feedback rounds: (1) tier down a bit + smaller tags; (2) tier moved up (tier_dy = -3), tags flat (dropped per-tag class colors → all muted), tags moved up (meta_gap = -2); (3) tier 1px down (tier_dy = -2), tag line + Lv.3 block to fainter grey (vs description's text_muted) as auxiliary info; (4) Lv.3 description auto-wraps (wrap_text greedy word-wrap) so a long effect string folds instead of stretching the card width.

Magician port (units.lua):

  • Added a targeting = 'random' mode: new unit_random_enemy(u, range) helper + a 3-way branch in unit_attack_update (random / densest-pack aoe_radius / nearest). SNKRX's erratic random aim is the unit's character.
  • damage = 18, cooldown = 2.0, range = 96, rect AoE. Square downsized from SNKRX's derived 57.6 (1.5×1.2×32) to 48 (radius 24) — "too big at our scale," dropped the mage ×1.2 (magician precedent for SNKRX-size-is-a-starting-point). Kept our magic_impact cast sound (SNKRX magician is silent on cast). Spec F5-tuned + baked snkrx_blue→light_blue. Lv.3 'Quick Cast' grayed.

Template sync (the "D" task) — SNKRX-update up to snkrx-template:

  • Diffed toolkit dirs: anchor/, anchor.exe, draw_shader.frag all already identical. Real deltas: snkrx/ui/primitives.lua (+ui_status_tag alpha), snkrx/ui/widgets.lua (+ui_bar kick/fill_spec, +ui_hud_bar val_w) — both strict supersets, copied verbatim. snkrx/spec_lab.lua — new generic registry core copied in.
  • Refactored root spec_lab.lua from ~290 lines to a thin wrapper (require core + register test_unit's spec/glyph_spec + unit preview). Made card_meta_runs global so the F5 preview and the real card share one run-builder. Kept the user's request to retain the tag-line preview (drawing the new flat-grey line). Updated .claude/CLAUDE.md to the template's current edition (preserving one local working-style line).

Process distillation (porting.md rewritten):

  • Turned the archer case-study into the general RECIPE after 3 baselines. Promoted recurring rulings to §0 standing rules: flat damage additions always; re-read the cooldown literal at port time (the outlaw trap); keep-ours is the sound/juice default; SNKRX sizes are starting points (retune at our scale); the 062026-sibling reference option. Added §6 Bookkeeping (the mechanical tail: knob/spec deletion, unit_type_order, test_unit flip, rulings table, roster + memory, syntax checks). Encoded the trigger-body→targeting-mode mapping and the Area-is-a-rotated-square (side 1.5×w) geometry rule.

Outlaw port (units.lua) — first full-recipe run:

  • {warrior, rogue}, tier 2, damage = 14, cooldown = 3.03 (literal 3.0 confirmed), range = 96, no pierce (knives die on first hit). Fan formula was already algebraically SNKRX's; tuned spread through 90° (SNKRX math.pi/8) → 34° (0.15) → final 60° (math.pi/12) per user feel ("90° read too sparse single-target") — a rare ground-truth override. Speed 250. Staggered knife-throw sounds + single fan shake_push kept. Spec F5-baked snkrx_yellow→red — user waived the "avoid red for units" rule for the outlaw explicitly.

snake_unit:hit feedback copied from 062026 player:hit (snake.lua):

  • User: "the shake this one has is a bit too much." The old snake_unit:hit fired shake_shake(main_camera.shake, 4, 0.5) on every non-lethal contact = constant swarm jitter. Replaced with 062026's feedback: NO camera shake; gentler 0.2 spring pop + 0.1s flash + a contact hit_circle + 1-2 spec-mixed particles. Signature → (damage, hit_x, hit_y, source); main.lua contact handler passes the contact midpoint + the enemy. Caught a gotcha: 062026 pulls a 'hit' spring key but the snake's draw reads self.spring.main.x with no 'hit' registered — mapped the 0.2 force onto the visible 'main' spring so it actually shows.

Cannoneer port (units.lua) — first two-damage unit:

  • SNKRX mechanic confirmed at player.lua:2332 (other:hit fires on the struck enemy) AND player.lua:2208 (die() spawns the explosion Area) — directly-hit enemy takes both = 48, others take the splash. Modeled with damage = 36 (the SPLASH — the card headline + description number, matching SNKRX's card) and a new direct_damage = 12 field (the projectile hit). Attack: spawn_projectile(damage=direct_damage, speed=250) + on_deathspawn_area(damage=def.damage) at cannon_blast_radius = 48 (SNKRX's ~144-216px shrunk per magician precedent). cooldown = 5.33, range = 128. Kept densest-pack aim (aoe_radius) over SNKRX's closest (user call — better for an AoE nuke). Kept cannon boom + strong trauma blast shake. Spec F5-baked lime→pink (steered off the seeded nuker-orange, since orange = enemy family). The porting.md def template now documents direct_damage for projectile+splash units.

Mixed roster snake (main.lua SCENE):

  • Added ported_units = {archer, swordsman, magician, outlaw, cannoneer} + snake_mode ('roster' = one of each, the default mixed showcase / 'solo' = test_unit copies for porting). snake_build already cycled a roster list per-index, so n=5 lays out one of each head-first.

Spec lab: unit picker + dropped shape axis (spec_lab.lua) — DIVERGED:

  • User chose to diverge ("different projects need different tools; this one makes units faster, the template focuses one-at-a-time"). Rewrote root spec_lab.lua self-contained (no longer requires the generic core, which stays in the toolkit unused): leads with a UNIT picker (cycles ported_units) → TARGET (spec | glyph_spec) → pattern/color/dither cyclers → swatches/ramp editor → sliders → DUMP (<unit> <target> = {...} + clipboard). Shape axis removed (user won't use it). Edits remain live against the real unit_defs[...] tables.
  • Magician + outlaw spec re-bakes: both → bayer2_4 dither; outlaw's pattern_param2 (offset) set to 0.412 (copied from cannoneer per user, overriding the dumped 0.4345).

Fixed gradient angle (effect.lua + 3 draw sites) — global, on by default:

  • User wanted local linear gradients to respect the spec-lab (screen) angle regardless of object rotation, as a single global toggle (all-or-nothing). Root cause: pattern_linear_gradient projects in quad-local space (c = (uv-0.5)*quad_px), so a rotated layer_push spins the gradient with the body.
  • Implemented as Lua-side angle compensation (no shader/engine change): global effect_fixed_gradient_angle = true; effect_set(layer, spec, rotation) gained an optional rotation arg — when on + pattern is linear_gradient (kind 10), sends pattern_param - rotation/(2π) so the body's spin adds it back to the screen angle. Wired the rotating unit-spec draws: snake_unit:draw (self.r), projectile:draw (self.r), area:draw (rect shapes pass self.r, circle/polygon pass 0). Exact for square quads; backwards-compatible + generic → candidate to propagate to snkrx-template. flicker/prop/beam unwired (no ported unit uses them).

Next session (planned, saved to memory): stop porting new units — instead deepen the 5 we have into a full Lv.1 / Lv.2 / Lv.3 ability ladder per unit (a deliberate change from old SNKRX's Lv.3-only merge effect): every unit gets three abilities (current attack + a new Lv.2 + a redesigned-then-coded Lv.3), a mix of active and passive. This forces a unit_card redesign to show all three tiers AND fit on screen.

🔒 Only the summary of this log is public. Private because this is SNKRX update.