SNKRX Archer
Fable 5
Summary
First full unit port of the (secret) SNKRX update: the archer, finished end-to-end — verified behavior + true numbers + a locked game-wide visual language + the F5/F6 tooling that future ports run on — plus an engine patch that makes local gradients work per-glyph on text. Process documented in SNKRX-update/porting.md as the baseline for the next units. Continues SNKRX Stats (NDA 1) and SNKRX Snake (NDA 2).
Session plan (locked at start):
- Port every portable unit ONE AT A TIME — behavior, Part-8 damage, effects/sounds, and the unit's UI card all finished before the next unit. After ~3 units, distill a general process.
- Decisions: start from the five existing mini-looper units (archer first); SNKRX-vs-ours behavior differences get FLAGGED case-by-case, never silently chosen; wire the verified 25-arena seeker HP curve now (F1 ARENA dial); port SNKRX's per-character attack intervals; defensive placeholders
snake_unit_hp = 100/enemy_contact_damage = 4(real defensive curve still un-numbered, proposal Part 6). - Unit colors: per-class gradient idea evolved across the session into the final unit gradient mode (below). Red classes (rogue, nuker) re-hued (pink / orange interim) — red/orange is the enemy family.
Infrastructure (Parts A–C):
- Arena curve:
seeker_hp_curve(the verified Part-8 triplet table, 15→275hp) in enemy.lua; spawns readseeker_hp_curve[current_arena]; F1 gained anARENA ‹›field showingN (HPhp). - F6 snake inventory (
inventory.lua, new): SNKRX's party UI (buy_screen.lua CharacterPart) rebuilt on the snkrx ui toolkit. Left-edge panel, one spec-wearing cell per unit + level number; drag-to-reorder with SNKRX's exact half-row neighbor-swap algorithm (entity-keyed juice ids so springs follow units through swaps); sim pauses while open (sound_tuner gate contract). - Unit card (
unit_card): first version rejected — "the one you did doesn't have the same design sense as those as it only uses a single font, doesn't use the [ ] tags" — rebuilt in the toolkit vocabulary after studying ui_card/ui_tooltip/the gallery's meta_runs: icon tile (24px, v-centered) + Awesome title with[tier N]beside it (pixul + Awesome brackets, dy −2) +[classes][attack_type]bracket meta (attack_type from a new def field) + muted description with yellow live numbers + DMG/CD/RANGE charcoal stat chips + dashed divider + grayed pixul[Lv.3]block. Sentence style: Capitalized, final period. - Card UX iterations: appear pop (0.25 spring) — settle required BOTH pinning scale to exactly 1 once sub-pixel AND an integer pivot (fractional pivot at odd card widths put every glyph on half-pixel boundaries forever); hover persistence went safe-triangle → keep corridor (cell-to-card band over the union of vertical extents) after the triangle proved twitchy — "it shouldn't disappear if you move the mouse to the right at all".
Archer port (Part D):
- The hidden-aspd discovery: SNKRX's
t:cooldown(2, ...)literal is scaled at runtime byset_every_multiplier('shoot', aspd_m)(player.lua:1348),aspd_m = 1/Π class_aspd_m— ranger 1.5 → the SHIPPED archer rhythm is 1.33s, not 2s. Same hidden-multiplier class as Part 8's area_dmg_m; every future port derives, never reads, the interval (pre-derived: swordsman 3.33s, outlaw ≈2.02s). - Baked: damage 12 (Part 8), range 160, projectile speed 250, infinite pierce,
cooldown_jitter = 0(SNKRX is metronome-exact; the idle re-arm stays as the range-gate emulation). Kept ours (flagged, user-ruled): the v1 arrow fire sound, full cast juice. Lv.3 Bounce Shot deferred with merging. Snake speed: user picked 100 (SNKRX 75, old 120). Solo-test snake:test_unit/test_unit_countknobs (3 archers). - Snake spacing investigation: gaps flickered 1–2px. Two causes: (1) followers SNAPPED to discrete trail points (~1.7px frame steps) — fixed with exact arc-length lerp, kept as a
snake_follow_lerptoggle (true default) after A/B vs SNKRX's snap; (2) fractional spacing phase-shifts unit edges on the 480x270 grid. Trials 10 / 10.4 / 10.5 / 11 (now on an F1 SNAKE panel) → 11 default. User correction logged: SNKRX itself was pixel-grid locked, so why 10.4 looked even there is unresolved.
F5 spec lab (spec_lab.lua, new): the effect-lab inspector copied onto a live panel editing unit_defs[test_unit]'s spec tables directly (shared references → snake bodies, arrows, F6 cells restyle instantly). Pattern/color/dither/shape cyclers, conditional COLOR A/B swatch grids, scale/speed/contrast/param/param2 sliders, plus the ramp editor the template never built (STOPS 2–8, clickable stop swatches, STOP COLOR grid). EDIT body|glyph target switcher; DUMP prints a paste-ready Lua literal for baking. Preview: two rounded-rect swatches (top-left + mid, snake stays visible), the identity texts drawn exactly as the card draws them, and a composite tile+letter sample. Runs over the live game AND inside F6.
Engine patch — per-glyph local gradients (the big one):
- Discovery: glyph quads carry ATLAS UVs, so the local
linear_gradientcouldn't color text (each letter would take a near-constant shade from its atlas position). Initially proposed banded stencil text vs "engine surgery"; user challenged — "Is 2 an actually big change?" — and the code proved me wrong: the batch format already carriesaShape0..4on every vertex, and the two atlas-quad emitters (batch_add_uv_quad/_flash, anchor.c:2521) already RECEIVE (u0,v0,u1,v1) and were discarding it aszero_shape. - Fix (~20 lines total): C side writes the sub-rect into shape0; shader sprite branch remaps
quad_uv = (vUV - uv0)/(uv1 - uv0)with per-quadquad_px. Subtlety caught mid-edit:u_image_fieldsamples the texture with the same uv → spliteffect_field(world_pos, sample_uv, uv, quad_px)so luminance keeps atlas coords while patterns get quad-local, threaded through the per-cell halftone reconstruction. Zeroed shape0 = old behavior (backward compatible). Engine rebuilt; anchor.exe copied (after a device-busy retry — game was running). Propagation owed: anchor.exe + draw_shader.frag to snkrx-template / Mini Looper.
The unit gradient mode (LOCKED — the game's visual language):
- "this seems like what I'll actually want for the whole game" — every player unit + its UI wears a LOCAL dithered linear gradient: perfectly horizontal gradient line (
pattern_param = 0.25exact), lighter on top / darker on bottom, per-body and per-glyph. Archer baked:lime → green, offset 0.5243 (his F5-tuned dump).unit_spec_forgenerates it: 1 class → light/dark pair of the class color (class_gradient_pairs; lime→green is the green family's), 2 classes → the two class colors, 3 classes → dedicated 3-way mode deferred. Enemies exempt — they keep the red↔orange organic breathe. Multi-class header texts later: vertical two-color gradient, darker on bottom. - Glyph "ink" (cell level number, card icon letter): first attempt (flat darkened blend) read wrong — "Color is gray, should be a darker blue?" → root cause: deriving from color_a only while the cell reads as the blend (spec_mid_color fix), then the user chose the SNKRX idiom: same colors, both darkened. Implemented as a new universal
valuemodifier on the effect axes (u_value_mult, final-RGB multiplier in both shader paths, ≤0-reads-as-1 guard for unset uniforms, reset by effect_clear since it hits passthrough draws too).glyph_spec= the body's exact gradient +value = 0.78(≈ SNKRX's [-5] ramp shade);unit_glyph_spec_for = unit_spec_for + value; VALUE slider on F5.
Session close: previous logs renamed (SNKRX Stat Port → SNKRX Stats, SNKRX Snake Port → SNKRX Snake — "everything will be a port", no suffix repetition); the full per-unit process documented in SNKRX-update/porting.md (research pass with file:line anchors, def shape, visual workflow, sound pipeline, test checklist, the archer's flagged-differences table, roster state: 1/28 direct-hit units done; swordsman/magician/outlaw/cannoneer next).
🔒 Only the summary of this log is public. Private because this is SNKRX update.