Skip to content
Shattered Crowns

v0.5.34-dev · 2026-08-04

Sidestep

Sword arts ride the weapon, and guns hit like guns

Three FFT-parity corrections, each measured over a fresh 10,000-battle simulator run. Sword arts are WP-carried, not Brave-carried: FFT computes Holy Sword skills from PA × WP with no Brave in the row, so Stasis Sword and Blade Storm now skip both Brave terms — and the forecast, the damage breakdown and the battle log all say so together, because a hover box printing "brave ×0.61" on an ability that ignores Brave would be the old facing lie with a new name. Guns re-raised (8/9/11/12/14/15/15 WP): the old numbers left a mediator's ultimate gun rated below its common book; chemist came along for free (45.3% → 50.9% in duels). Dual Wield pairs the same weapon class (FFT's rule) — both hands strike one tile, so a gun+knife pair either wasted the gun's reach or fired the knife across the map. And the Orator keeps all four of its weapon classes, weighted 60/15/15/10, because a two-outcome weapon roll stops being worth a reveal card.

The dancer dances, and a mutual kill has a winner

A new headless balance simulator (balance/sim/, driving the REAL match pipeline — units built by the production factory, turns by the server's own AI driver) found that thirteen abilities were unreachable by the AI: every range-0 self-centred offensive kit — all 7 dances, 6 of 10 draw-outs — was never once proposed, because the AI asked "is an enemy within reach 0?" and vetoed the caster standing in its own blast besides. Both AI tiers now aim self-centred kits at their own feet and score the ring; the dancer went from literally never dancing to 4,644 casts in 10k battles (51.4% win rate). A mutual kill now has a winner — both sides wiping on the same blow used to fall through to "no winner ever" and hang the server loop; the last killing blow takes it. Mobility reworked from a clamp into its inputs: base MOV ≤ 4, Movement slot ≤ +1, accessory +1 MOV items all rare or better — MOV 6 still exists but is a gamble you assemble, not the default state of a fast class (a flat cap had made Move +2 a 400-JP tooltip, this repo's oldest failure reintroduced by a fix).

The cursor found its voice

Three UI sounds, and the wiring is the story: AudioManager now auto-connects every BaseButton that enters the scene tree — hover and focus play a 50 ms cursor tick (one shared cooldown, so sweeping the mouse down a menu column is a ripple, not a buzz), pressing plays the confirm (a bright fifth over a click). No scene wires anything; a button clicks by being a button, current screens and future ones alike, and the smoke proves it by dropping a fresh Button into the tree and emitting pressed — silence there would mean every future menu ships mute. The third sound is the YOUR-TURN chime (a small rising bell, D5 to A5), played in battle when one of your units comes up — suppressed under fast-forward, where auto-waited turns would chime about their own emptiness. All three deliberately tiny: the correct volume for a UI sound is "barely there, until it's missing." smoke_music 194 → 205. A denied-command buzz waits on a real CommandRejected signal reaching GameState — rejections currently flow only through the log narration, and a UI pass is not the place to invent engine plumbing.

Steel follows the hand

Physical arts now sound like the weapon that delivers them — the same rule WEAPON_STYLE uses for the animation, applied to the ear: an Equip-Guns archer's Hawkeye fires like the gun it actually is. Seven new sounds (arrow twang-and-thock, gunshot with PSX grit, the rhythmic whistle of something thrown plus its shink, and the dragoon's falling whoosh into a spearhead slam), all selected by FxPlan.action_sounds — one authority returning delivery + impact + the per-hit rule (physical thunks on every connect, a dual wield twice; magic parks once per ability). Bows and guns fix the basic attack too, which had been borrowing the sword's swing. The smoke walks the registry × seven weapon classes and demands every derivable name exist; smoke_music 166 → 194. Two of its own checks needed honest fixes on the way: the SFX size floor was calibrated to cries and rejected a 0.20s shink, and the spell-parking pin referenced literals the refactor had correctly turned into data.

Every spell found its voice

Thirty-one spell sounds, all original synthesis, and NO per-ability table: FxPlan.impact_sound derives the sound from the same fields the visuals derive from — element picks the FAMILY (fire whoomph-and- crackle, ice shard-rain, lightning crack-and-thunder, wind gale, earth rockfall, water splash, holy chime bloom, dark minor-second drone, arcane for the element-less), magnitude picks the TIER, and tier scales count, weight and length together. Tier 1 ice is two quick shards; tier 3 is the cacophony — glass raining over a rumble into one final crash (the FFT Ice-4 memory, expressed as parameters). Heals get a rising arpeggio, buffs a two-note lift, debuffs a detuned fall, and every spell opens with a soft cast release. The element sound parks on the impact frame the FX director holds — scheduled once per ability, so an AoE is one cacophony, not three copies.

The third synth file triggered the promised extraction: shared DSP lives in tools/music/sc_dsp.py now, verified lossless by all seven shipped WAV hashes staying identical. The smoke walks the LIVE registry through the derivation: every ability must map to a sound that exists, every family must be reachable, and tier 3 must audibly outlast tier 1.

The units found their verbs

Five unit-action sounds, all synthesized through the KO-cry template (tools/music/synth_unit_sfx.py — parametric DSP, deterministic seeds, loudness authored in the synth rather than patched at play time): move (two soft footfalls — quiet on purpose, it is the game's most common verb), attack swing (band-swept whoosh, played at delivery), attack hit (bright crack + inharmonic metal partials + body thud, played at the impact frame the FX director already holds), attack miss (the same air with nothing in it, on the same beat as the MISS popup), and defend (click, CHNK, damped ring — set and hold).

The swing and hit key on the plan's TOUCH archetype — the same rule that picks the melee animation, so the ear and the arm cannot disagree; spell deliveries stay silent until the spell-SFX pass rather than borrowing a sword's thunk. Defend keys on its id (its nova archetype is shared with real spells). KO cries v3 landed alongside: the yelp contour (rise THEN dive), higher peaks, PS1 decimation crunch — approved. smoke_music 39 → 59.

The match ends in music, and the cries got sharp

Victory fanfare (~16s, D major): a trumpet flourish into the Crown leitmotif in the major — the same tune the title screen states in D minor, resolved; the bittersweet minor-sixth peak becomes a bright major sixth. Defeat sting (~16s, D minor): a lament bass under an oboe line that falls to the leading tone and never resolves, the bell tolling D over the final dominant. Both are one-shots in a new AudioManager.STINGS table — deliberately not MUSIC, where the smoke enforces looping — fired from the same winner comparison the VICTORY/DEFEAT banner makes, so they can never disagree. Sting renders trim their reverb tail (a loop needs its seam; a one-shot ending in seven silent seconds just overstays).

KO cries v2 after Joe's verdict on v1 ("bad"): quick and sharp and higher — duration halved to ~0.3s, fundamentals roughly doubled (male 330→120 Hz, female 560→230), 6 ms attack, steeper clean decay, brighter harmonic tilt, breath noise cut to an onset consonant, thud earlier and tighter. v1 chased "real human cry"; the reference's grammar is a synthy chirp, and chasing realism made it slow and low. smoke_music 30 → 39 checks (stings load, sized, never loop, and the match-end call is source-pinned).

Three battle themes, and the fallen cry out

Battle now has music: Steel and Sky (A minor, 116 — march snare and a horn call over a staccato string engine), The Broken Line (D minor, 132 — an Andalusian bass under a trombone theme, frantic wind runs, offbeat brass stabs), and Crown of Ash (C minor, 92 — brass chorale, organ, choir, a neapolitan Db). Each quotes the title theme's Crown leitmotif at its climax in its own key — three battles, one crown — and each ends its loop on the dominant so the seam resolves like a phrase. Composed in-repo through the same engine as the title theme (now shared: tools/music/sc_midi.py, extracted with the .mid hash proving it lossless). The track is picked per match, seeded on match + map, so a replay opens with the same music.

Units that fall now CRY OUT — a male or female death cry (stable per-unit coin flip until units carry a real gender) plus a body-thud, riding the new SFX bus, so the Settings dial that said "no effects in this build yet" is no longer telling the truth and its caption comes off next pass. The cries are synthesized from scratch (tools/music/synth_ko_cries.py — a falling glottal pulse train through gliding vowel formants, breath noise, lo-fi lowpass), deterministic, shipped as WAV because vorbis smears a half-second transient. smoke_music grew to 30 checks: loop flags on all four tracks, cries load and do NOT loop, the rotation reaches all three tracks, and the KO handler actually calls the cry.

A Settings page, and every dial on it is real

Main menu gains ⚙ SETTINGS: Master / Music / Sound Effects volume at 0–100%, fullscreen (the F11 toggle's twin), vsync, and the server host — which moved here from the old SERVER… dialog so there is one door to one setting. The volume dials drive actual AudioServer buses that AudioManager creates at boot (music already routes through the Music bus, so that slider is audible the moment it moves; the SFX bus carries nothing yet and the page says so — effects inherit the dial when they land). 0% is a true mute, not a very quiet number. Everything persists to user://settings.cfg. Guarded by tools/smoke_settings (20 checks), whose load-bearing one reads the AudioServer after poking the page's own slider — verified by disconnecting the slider, which fails it by name — because a slider that only moves its own label is this repo's oldest failure mode wearing a new control.

A hit breaks Charm

Charm now breaks the moment the charmed unit takes damage from any source — FFT's own rule, and the same shake Confuse has had since v0.5.10. It was never wired: the tooltip said "until it breaks" while nothing could break it, so in Joe's v0.5.33 playtest a charmed Dancer's own teammate hit her for 22 and the charm calmly ran out its full clock. In practice the breaker is nearly always the original team, since the charming side can't target its temporary recruit at all. One flag in the status registry (breaks_on_damage), which every damage site already consults; guarded end-to-end by status_integration::charm_breaks_when_the_charmed_unit_takes_damage (verified by reverting the flag — it fails by name).

Magic seeks; steel lands where it was swung

The v0.5.31 cast lock-on now applies to MAGIC only (Joe's rule from the first ultimates playtest). A charged spell, heal, buff or curse still follows the unit it was committed on — that fix stays, and your self-buffs still land after you move. But a charged PHYSICAL delivery — the dragoon's Jump, Sky Render and Dragon Dive, the knight's Blade Storm and Stasis Sword, the monk's Hundred Fists and Doom Fist, the ninja's Sudden Death, the archer's Hawkeye, Snipe and Rain of Arrows — commits to the TILE. If the target moves during the wind-up, they dodged it: the blow lands on empty ground, and against an ultimate that dodge burns the attacker's one charge. A lancer leaping to safety is a great defensive move, and now it is rewarded.

Decided by the ability's type family, not a hand list, so the next charged physical anybody authors anchors automatically. The battle log already tells you which kind you are facing: "begins charging Dragon Dive at (4,4)" is a tile you can leave; "…at Shadowblade" is a spell that will find you.

Ultimates land in slow motion

An ultimate resolving is now a cinematic: the whole world drops to 30% speed for the wind-up — the caster's animation, the delivery sprites, the sigil, everything, because it all runs on the one engine clock — the camera leans in deeper than it does for any other ability, the impact hits at the usual near-freeze, and then the world dwells slow for a beat before easing back up to full speed. If the blow KOs somebody, the moment holds a little longer still: the fatality read, watching the body fall in slow motion. Applies to every ultimate and to the long-charge eidolons (Bahamut arriving at quarter speed is exactly as intended); an AoE ultimate that downs three units lingers once, not three times.

The ability's own numbers never change — the same amount of ability time passes, it just takes longer to watch, which is why the damage number still pops exactly when the blow connects instead of drifting off the sword. Skipped entirely under fast-forward, same rule as the hit-stop: the point of fast-forward is that you are not watching. All told an ultimate now owns about two and a half seconds of the room, three on a kill — and since ultimates are one-shots as of v0.5.32, you get that moment at most once per unit per match.