Commit Graph
40 Commits
Author SHA1 Message Date
SamandClaude Fable 5.1 c3555b4eea the cooldown goes stock, early-Z comes back, and lists reach the end
- a size-less `<Model>` pane's rect is its file's box in layout units: the reference's geometry overrides (`0x76d080`/`0x76d0d0`) answer the bbox extent whenever no size is authored, `bboxExtent · 768·√(a²+1) · layoutScale` FrameXML units (`1280 · extent` at 4:3), written into the pane's layout input when the file's facts land, re-derived on an aspect change, and ended for good by any authored size; `SetPosition` is the same unit, which is how `0x4a7b20` centres the arrow at `½·GetWidth, ½·GetHeight` through the override — so the map arrow's sprite arm and its hand-sized 33.6 px footprint go (its rect is `0.0262 × 0.0263` layout units, 33.5 × 33.7 FrameXML units at 4:3, 41.0 × 41.2 at 16:9), and the stock `ItemAnim` pane plays the item push unadapted (`ReplaceIconTexture` on the type-14 batch, the 1000 ms clamp, the stock `OnAnimFinished`), `ContainerFrameAdapters.xml` losing 0887's Lua card and its hand-measured 42.41 × 124.72 rect — the implicit one at 16:9 — and keeping the keyring fork and the three bag verbs (2015); and the cooldown is the reference's own model: the texture-transform law `uv' = R_q((uv + t − p) ⊙ s) + p`, `p = (½, ½)`, the raw quaternion component-lerped and never normalised, is one function (`uv_transform`) with the shader's UV fold as its twin — rotation and scale tracks bake per file sequence slot beside the translation, the mat-anim table gains an affine row `[cos − 1, sin, sx − 1, sy − 1]` on `anim_slots.z` whose identity is the zero row so every static batch reads exactly what it did, and a UI tile clones the twin material per animated batch and writes both rows every frame off the pane's play head, never the world clock; the stock `Cooldown.xml` runs off the chain on 2007's two handlers, the file's four quadrant quads turned by their own rotation tracks (`0 → −90°` over each quadrant's 250 ms window, clockwise from 12 o'clock) and its alpha tracks revealing the star for the flash; retired: the native `Cooldown` widget and `SetCooldown` (an Era API 1.12 never had — `CreateFrame("Cooldown")` is the reference's unknown-type refusal now), the loader's `Model → Cooldown` substitution, the app's hand-built pie and star, and our `Cooldown.xml`; `assets/ui` is eight files (2019)
- `UpdateMapHighlight` gets its zone-level exits (B360): at zone zoom ours returned the nil tail for every cursor position, so hovering Deadwind Pass from Duskwood showed no label where 1.12 shows "Deadwind Pass"; the reference's `0x4a7fa0` runs two lookups at zone level and answers a NAME only — first the current zone's exploration-gated overlay list (the builder at `0x4a6ad9` gating on the explored byte), each row's HitRect scaled by 1/1002 and 1/668 against the normalized cursor, edges inclusive, the first hit naming its Area1, then the continent grid re-windowed through the displayed zone's own rect, naming the neighbouring zone or city and nothing for the displayed zone itself — and only the name-only tail is reachable from there, fileName nil and the six coords zero, so the stock frame hides the highlight quad, as the director's own 1.12 shot shows; and `SetVerticalScroll` stores the offset as given (B370, B363): the reference's `0x786db0` compares the new offset against the old and writes it to `[+0x328]` verbatim, `0x787100` re-anchors the scroll child off it, and the range at `[+0x320]` is never read on that path — every clamp the reference exhibits is FrameXML's, through the scroll bar's `[min, max]`; ours clamped into `[0, GetVerticalScrollRange()]`, the child's overflow past the FRAME, which is 40 px short of where `FauxScrollFrame_Update` sends the bar on the 220-tall skills list (three rows lost) and 15 px short on the 287-tall who list (one); the setter fires `OnVerticalScroll` only on a change, as the bytes do, `BenillaScroll_Step` bounds its wheel in Lua where the reference's handler does, and the ScrollFrame kind's prose stops calling its offset unpinned (2017)
- overdraw's cheap half, the sky first: since 0588 every sky element wrote `frag_depth = 0.0`, which costs a pipeline its early-Z, so on an outdoor frame every full-screen dome fragment under the ground and the buildings was shaded in full (the gradient's `asin`, `atan2` and eight table lookups at dawn and dusk) and thrown away; one shared `sky_vertex.wgsl` — bevy's mesh vertex verbatim plus `out.position.z = 0.0` — is now the vertex stage of the gradient dome, the cloud dome, the star patches and the celestial discs and glares, and the model lane's `WOW_SKY_DEPTH` branch moves from its fragment to its vertex, so no sky fragment declares `frag_depth` and the rasterizer rejects the covered screen before the shader runs; every sky `specialize` zeroes the rasterizer bias constant the `sky_order` rung had been leaking into `DepthBiasState::constant`, harmless only while the fragment overwrote its depth, and the prose beside the pin follows it; then the retained `static_gx` pass draws BEFORE bevy's opaque pass (`StartMainPass → static_gx → MainOpaquePass`) — buildings and doodads are the frame's best occluders and a terrain fragment its dearest, six samples a pixel — so walls reject the terrain behind them instead of the reverse, the pass taking the first-use clear with it and the dev depth probe ordered after it; measured on 16 capture scenarios: 14 bit-identical, `northshire-dusk` 1 px and `water-night` 11 px, attributed to exact depth ties on static/terrain silhouettes and last-bit interpolation noise; a depth prepass and near-to-far bins are declined until a journal says where the time goes (2016)
- the grass cut goes per pixel because of a quarter-mip: `0x6813d0` sets `D3DSAMP_MIPMAPLODBIAS = +0.25` on stage 0 (`0x6813f4`) once per frame before the detail-doodad pass — capture-confirmed on all 29 doodad batches, and outside the `[0x6b1000, 0x6b3200)` scan 2004 had called complete; it decides the whole look because the `clutter_cut` probe finds 6 of 31 shipped detail atlases carry a BINARY alpha pyramid (mip 0 has DXT3's 16 levels, every level below exactly two, both starting zones among them), so `a × ramp` degenerates to a global on/off and every texel of a leaf crosses at the same depth — Elwynn's survivor coverage is flat at 47.6 % across the whole 52.5–61.1 yd band, then 0.0 %, while the probe's box-filtered control erodes 47.7 → 20.9 over the same band, so the art has the gradient the shipped pyramid threw away, and `clutter_size` puts the median tuft at 0.92 yd tall, 1.03 yd deep, a full yard for the boundary plane to sweep; the escape is bilinear: full alpha needs a 2×2 all-opaque texel neighbourhood, and those run out between mip 2 and 3 (179/56/10/0/0/0 on the dominant Elwynn cell), so the reference's λ ≈ 2.9–3.3 sits past the cliff and the tuft erodes continuously — 2.2 % of a tuft's pixels leave in the last half-yard with the bias, 11.6 % without, 24.2 % one mip sharper; wgpu has no sampler LOD bias, so the clutter lane re-samples base colour at `view.mip_bias + 0.25`, keeping the sampler every other model batch shares at the reference's 0; ruled out on the way: our BLP decoder (all nine uploaded levels byte-identical against the archive's 52 793 BLP2s), mip regeneration, the `has_mipmaps` byte's value 2 (a plain boolean, an authoring marker), anisotropy and geometry; what it does not fix is resolution-dependent, and so is the reference's own look — λ with the bias is 3.22 at the capture's 648p but 2.48 at 1080p and 1.48 at 2160p, so at a modern resolution the reference itself pops leaf by leaf, and scaling the bias with the viewport is one reversible line left for a look call (2021)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:35:31 +07:00
SamandClaude Fable 5.1 73ead4e4d2 grass fades on a plane, model panes render, and the FPS journal ships
- the grass fade is a plane across the view, not a ring around the player: `0x6b2b80` texgens the camera-space position through `T(0,0,−52.5)·Ry(π/2)·S(1/17.5)`, so the fade coordinate is view-space depth, `u = (z_eye − 52.5)/17.5` — we measured `distance(eye, fragment)`, a sphere, which meets the ground as a circle centred on the player that slides as they walk and bites a piece out of every tuft it crosses, and that was the hard border; the ramp is `0x6b2320`'s quantised 64×8 table, `alpha = 4·(63 − col)`, read bilinearly as `(254 − 256·u)/255` and capped at `252/255` — plateau to 52.64 yd, zero at 69.86 yd, near grass 98.8 % opaque and never 100 — where we had a clean 1 → 0 line; the product `tex0.a × ramp` is what the 128/255 `detailDoodadAlpha` cutout tests (both stages MODULATE, `[0xc7b2fc]`, confirmed verbatim in all four apitrace captures), so the tuft eroding out at 61.11 yd is the reference's own behaviour and the "fix" that would have removed it never landed; the pass owns its render state — blend mode 2, ALPHAREF 128, cull off, depth-write on, the per-slot draw `0x6b2d60` binding nothing but the texture — where we passed the batch's authored blend and cull through, and the `clutter_state` probe counted the cost: of 322 detail batches, 2 author Opaque (no cutout, their margin drawn as a solid card) and 5 single-sided; the build gate measures the chunk's box against the frustum corner ray derived from the live projection (≈1.31× at 16:9, ≈1.45× at 21:9), not its centre plus a 24 yd margin that popped whole chunks in on relief (2004)
- a `<Model>` widget's animation state is Lua-observable and lives in the engine: a private scene clock its own `OnUpdate` advances by `trunc(elapsed·1000)` ms on visible frames only (`0x76d7f0`), an arm with an anchor the sampler re-reads every frame (`0x7121a0`), `OnUpdateModel` at the top of every paint (`0x76d1a0`) and `OnAnimFinished` from the completion callback (`0x76cdc0`); the file's sequences and bounds are facts the host hands over, `SetModel` seeds Stand now or when they land (`0x70ebd0`) with a queued arm replaying after the seed, and the XML `scale=` on a model pane is the model's scale (`CSimpleModel::LoadXML` `0x76cac0`), which the loader had never read (2007); and the pane renders its M2 — every visible pane with a file gets a cell of one render-target atlas at its device size through one orthographic camera, the root at `1280·modelScale·layoutScale` FrameXML units per model unit, the tile's `AnimationPlayer` paused and seeked to the engine's play head, the light black, particles in their own unit (`768·√(a²+1)` per model unit, neither scale), composited as one premultiplied quad at the callback rank; two corrections off the bytes: `OnUpdateModel` fires only with a model installed (`[widget+0x318] ≠ 0`) and `OnAnimFinished` fires once per arm for a looping sequence too (`0x719370`); the loader now applies a `<Model>`'s `file=` at all, which every XML-declared pane had lacked; retired: the minimap ping's byte-measured sprite, since `MiniMapPing` and `WorldMapPing` draw their own file on the pane's clock (2013)
- the stance feed fires `UPDATE_SHAPESHIFT_FORMS` only for a list change: the reference fires it at `0x4b28ff`/`0x4b2e43` from learn, unlearn and rank-replace alone, while ours fired on any change of the pushed view, and every fire runs the stock `ShapeshiftBar_Update`, which shows `ShapeshiftBarMiddle` unconditionally past two forms and `Show()`s an already-shown frame — no `OnShow`, no manage pass — so a stance switch or the category-47 cooldown arming and expiring left the middle strip drawn alone over a raised bottom-left bar; castability now rides `SPELL_UPDATE_USABLE` (`0x4b31c0`), active/texture/cooldown are pushed silently and repainted on `PLAYER_AURAS_CHANGED` and `SPELL_UPDATE_COOLDOWN`, the feed ordered before both handler sets so they read the fresh list (2009); and the loot-roll frame waits for its item template (B371): `GroupLootFrame_OnShow` paints once and nothing repaints it, and we fired `START_LOOT_ROLL` on the frame the packet arrived, against a snapshot whose name, texture and quality were still `None` — the reference's `0x61b310` ends in the item-template cache lookup `0x55ba30`, a hit firing the event there and a miss firing it from the arrival callback `0x61b460`, so the feed holds a fresh roll's event until the template answers, with two releases that are ours: a negative answer (a frame that never opens cannot even be passed on) and a 2 s deadline paid out of the roll's minute; `arg2` stays the wire's countdown, as `0x61b430` pushes it (2010)
- the FPS journal ships to players: it had registered inside the dev-only `DevToolsPlugin` and been armed by an environment variable, so nothing we shipped could have written a row on the one GPU-bound frame in the corpus, a Steam Deck's at 94 % busy; `/console fpsJournal 1` — a CVar of our own, persisted like every CVar — opens `benilla-config/Diagnostics/fps-journal.csv` and appends one row a second, `0` closes it mid-run, and `WOW_FPS_JOURNAL=<path>` stays as the harness lever; eight GPU columns appended, `gpu_ms` then `gpu_opaque, gpu_static, gpu_transp, gpu_glow, gpu_post, gpu_ui, gpu_other`, read off bevy's `RenderDiagnosticsPlugin`, which times passes where the device offers `TIMESTAMP_QUERY_INSIDE_PASSES` (Vulkan and DX12) and leaves the cells empty on an Apple GPU, empty being the honest value; `static_gx`, the four `ffx_glow_*` passes and `ui_gamma_decode` open diagnostic spans so a city's own draw is named in the split; a fresh file opens with the adapter, backend, driver and `gpu_spans yes|no` line; and a probe's `/console` line, which skips the stock `ChatEdit_ParseText` and had landed in a parser arm that knew only `reloadUI`, now forwards to `ConsoleExec` and writes a CVar the way the typed line does (2008)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:33:58 +07:00
SamandClaude Fable 5.1 72e78ebc92 a 40-man raid costs less per frame, and the money frames go stock
- a 40-man raid cost +5 to +8 ms of CPU per frame — a population, not a hot function — and three cuts landed where it was measured: the rig palette upload sends one `write_buffer` per run, bridging the gaps parked bodies leave between live rigs (calls 93 → 3); a built material becomes an asset when something visible binds it, not when a spawner builds it — 9.9k material assets were alive for 36 drawn batches, so a material is realized only once bound and visible, readers that clone before binding doing it themselves through one new function in the engine's wall (172 → 173): a 17× cut in live GPU objects; and a body's batches spawn as material groups — a geared body stood as 12–27 mesh parts, its skin composite drawn as five separate batches, and two visible batches now join when nothing downstream can tell them apart: parts per body 17 → 12, the Stormwind raid leg 17.0–21.0 → 15.65–15.82 `cpu_ms` (1929, 1940)
- the instruments that priced it, all on the probe line: wgpu's own resource census, because a sampled profile put roughly half of the pass encoder in usage trackers sized to the device's live buffer and texture counts — a term that scales with residency, not with what is drawn, and nothing printed the number; the active camera count, since every view is its own pass set; the UI's live texture-identity runs, one 2D draw each, which is the number a UI change moves, not the quad count; and parts and materials per dressed body on the dress census, the ceiling on merging a body's batches read off a real body instead of guessed from a model's batch table (1940)
- the coin kit retires: every remaining money display — quest progress, the mail purse and postage, the trade recipient, the tooltip, the quest log, the trainer's purse and cost — moves onto the reference's `MoneyFrameTemplate`, each seat already matching ours; the progress panel's required-money flow is ported in full — `QuestFrame.lua:168-190` colours the FRAME red/white AND the LABEL black/dark-grey when the quest costs more than you hold, where ours had neither colour; the mail postage takes the reference's name and label, trade and mail issue no explicit update because the frames repaint on their own events, and the trainer's cost label is shown or hidden with its `local COSTS_LABEL` shadow deleted; with the trainer converted the hand-built coin trios have no consumer, so their templates and the `BenillaMoney_*` helpers are deleted and `MoneyFrame.xml` is the only money kit (1937)
- the crowd-control ladder's aura exemption exists, and it is a join between two `Spell.dbc` records — each arm asks whether the spell being cast is itself an immunity covering the aura that blocks it (Ice Block cast while stunned), the school arm shifting because `School` is an index and `EffectMiscValue` a mask; what the scan mostly decides is WHICH message a refusal carries — a blocking aura the cast does not counter renames the reason to "Can't do that while %s" (1941); bevy_audio leaves the build — its plugin had been disabled since 0530, but disabling a plugin does not remove a crate, and rodio, a second cpal and an Ogg/Vorbis decoder were still linked into a client that mixes through kira (1932); and cpal has four stream errors, not two — a routine ALSA underrun was treated as "the device is gone", tearing the stream down on every hiccup; now only a lost or invalidated device rebuilds (1939)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 20:53:37 +07:00
SamandClaude Fable 5.1 b41acbf98a Linux and Windows get their sound back, and emotes refuse while moving
- every build off macOS has been silent since the 09-02 snapshot (B356): the non-macOS device layer was a stub that opened nothing, and nothing had ever compiled the client for another target; that layer is now the same output stream over cpal — one seam, not two stacks: the ring, the render thread and the rebuild-on-device-change loop are one code on every platform, only the device file differs; the audio thread never allocates, a lost or changed default device is noticed at kira's own cadence, and realtime is `SCHED_FIFO` on unix and MMCSS "Pro Audio" on Windows, either refusable, the ring turning a refusal into a degradation; and the two instruments this bug says were missing exist — a Linux and Windows cross-compile under `clippy -D warnings`, and a live output test that opens the real device and asserts the ring never ran dry, because compiling is not sounding (1920)
- an emote that requires standing still now refuses on the move: `CheckEmoteEligible`'s `0x4000` arm writes an out-param that `DoEmote` turns into a red "You can't do that while moving!" and aborts; 20 emotes of 78 can raise the line, read off the shipped `Emotes.dbc`; SWIMMING is not in the mask, so a swimmer holding still emotes fine, and `IsSelfControlled` is TRUE for an ordinary player, so a feared or confused player emotes away happily (1904); and the cast path gains its equipped-item rung — the button greyed and pressing it still sent: the reference short-circuits to "fits" on four gates, not one, the miss being a zero `EquippedItemSubClassMask`, which means no item required at all, and the rung sits above the crowd-control validator, so a player both stunned and missing the weapon is told about the weapon; that ladder has six arms, not three — CHARMED, FLEEING and CONFUSED join (1925)
- the quest reward's money is the reference's own widget: the tail of the shared `QuestFrameItems_Update` drives `questState.."MoneyFrame"` through `MoneyFrame_Update`, and the two hand-built coin trios become one `MoneyFrameTemplate` instance each at the reference's anchors, which our coins already used; the progress panel stays on coins, being the required-money flow, not the reward one (1928); and the flight master's `!` joins the questgiver sweep, because the green `!` and the gold `!` are the SAME slot, `unit+0xb2c`, torn down once before both queries re-issue; the mouseover re-ask is refuted — the only taxi-status sender has one live caller — and the two queries are not symmetric: the questgiver's goes out on every sweep, the taxi one only when the unit's key moves or on a full sweep, as the wire shows at Dungar Longdrink: 68 questgiver status queries against 1 taxi-node one (1918)
- `GetSpellTabInfo` out of range answers `nil, nil, 0, 0` — the reference returns four values on every path that returns (`0x4b3ce0`), and ours answered a single nil copied from another binding's shape; it matters because stock `SpellBookFrame_OnLoad` calls it unconditionally and then does `id > (offset + numSpells)`, which a nil raises on and (0+0) makes true, hiding every button on the page — the behaviour to match; the argument raises the reference's usage string when absent, and the index truncates toward zero THEN decrements (1931); the perf probe gains a `WOW_PROBE_UNCAP=vsync` arm, because the uncapped present mode is a request, not a schedule — measured, it taxes the number it was meant to pin (11.80 `cpu_ms` uncapped against 14.65 under it), so the default stays uncapped; and the last three test temp paths are keyed on the pid, after concurrent runs raced on one fixed shared file

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 20:52:22 +07:00
SamandClaude Fable 5.1 9f204d894b a bot walks on the ground, and a stunned cast never leaves the client
- a bot floating a body-height above the ground in mid-stride (B357): cmangos's Playerbots moves its bots as Player objects under `SMSG_MONSTER_MOVE`, and our ground clamp skipped anything that was not a Unit, so a splined Player kept the wire Z verbatim — and a spline is a straight chord, which over a hollow leaves the body in the air; kind is not a question the reference asks (`0x616620` runs every registered mover once per frame, reading only its own flag word), so one rule grounds every Unit plus a Player from the moment a server spline first moves it, never the body we steer; and a re-sent identical wire pose no longer leaves a unit floating (1921); the body we STEER is a mover too — the reference hands a ride's spline displacement to the same vertical-zeroing step as walking, so the spline owns XY and facing and the ground owns Z, closing a +0.47 yd gap at mid-chord on an Elwynn slope, endpoint included (1927)
- the client refuses a stunned, silenced or pacified cast itself, in the requirement validator `0x6094f0` whose mounted and moving legs we already model, before any packet — and the three arms are not symmetric: STUNNED refuses everything, SILENCED and PACIFIED run only where `Spell.dbc PreventionType` declares them, so a silence stops casts and leaves melee abilities alone and a pacify does the reverse; the leg sits above the mounted block and below the equipped-item requirement, so stunned and missing the weapon you are told about the weapon; `PreventionType` is column 165, Auto Shot alone telling it from its look-alike neighbour; the preflight banner splits movement blockers from ability blockers, since a disarm or a pacify touches no movement gate; and the refusal sink is an explicit parameter rather than a ladder field, because a resource reachable twice from one system panics on the first live frame (1903)
- a disarm greys what needs the hidden hand: the spell equipped-item test is `0x5f0c50`, walking all 23 equipment slots, rejecting a deprecated or genuinely broken item, and taking its slot mask from `AttributesEx3` (main-hand-only, off-hand-only, else every slot) — DISARMED strips the hidden hand's bit out of that mask, so a main-hand-only ability whose one allowed hand is the one the disarm took has an empty mask and dies, Heroic Strike greyed with its tooltip line red; using a worn weapon while disarmed answers `ERR_CANT_USE_DISARMED`, the client's own refusal at rung 15 of the item-use ladder's 20, above the bind-confirm arm and never a bag click (1903); and two stale claims go — `UNIT_FLAG_STUNNED` no longer says the idle selectors bail on it (0889 had refuted that at the bytes), and the charter's `ShowPetition` arm is rung #9 of that same ladder, read rather than reasoned
- `UpdateSpells()` is twelve bytes and all of them are the event: it ends in a jump into the event signal with 0x104 = `SPELLS_CHANGED` in hand, whose repaint is FrameXML's alone, so a no-op would not have been faithful and our own repainting stand-in was wrong in both directions at once; it fires synchronously as the reference's does, at the cost of a fire-path refactor rather than a next-tick queue; `PlayerHasSpells()` is a hard-coded 1, no branch, no store read, pinned to answer 1 on an empty spellbook, where a plausible implementation diverges silently (1924); and three integration harnesses loaded every document against an empty base, so a self-sourcing chain file loaded as an empty shell with every handler missing — none of their lists could fail, so the fix lands with a guard watched failing first, `FadingFrame.xml`, one relative script reference and nothing else (1923)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 20:51:44 +07:00
SamandClaude Fable 5.1 ef7140c294 an item on the cursor is a sale, and an animation's sound has a place
- right-clicking a vendor with an item on the cursor sells it: `0x5df5d0` asks `GetCursorItem` before anything else and a non-zero answer takes the arm to `CMSG_SELL_ITEM` instead of `CMSG_LIST_INVENTORY`, where benilla sent the list unconditionally — the whole stack, to the clicked vendor, with no merchant-window gate anywhere in the arm; the clear is `SetCursorItem(0, …)` rather than `ClearCursor`, which fires `CURSOR_UPDATE` and leaves the source slot greyed until the server's inventory update (1914); and the NPC you are talking to is not clickable: `0x5f0130` refuses a right-click on the NPC whose window is already open, against a global with exactly two writers — the interaction setter, whose fourteen callers are every one a window opener, and its clear — so the first click always gets through and no double-click is eaten, where benilla re-sent the arm's opcode on every click (1866)
- a Hated or Hostile questgiver's `!` now disappears, every `!` blinks off and back on when you resurrect, and the minimap no longer dots a corpse, your own pet or an untrackable unit: the sweep tears down on HOSTILITY, not on the flag — `0x5eb134` is a convergent block reached from two branches, so three incompatible readings had each seen one path — and an escort giver's `!` clearing with its flag is the field watch instead, which B257 now rests on; the dots gain `0x4eaa90`'s three preconditions (1906); the quest log's rebuild is all-or-nothing — the reference makes every rebuild a no-op until its template misses are answered, so a cold log shows empty and then lands in one step, never our `"..."` row per in-flight quest — sorted level then title, case-insensitively, with the `id == 0` group first under the reference's own "Missing header! (quest designers)"; and `GetQuestLogTitle`'s `isHeader`/`isCollapsed` answer 1/nil, not booleans
- an animation event has a place, not just a time: the M2 event kernel (`0x719370`) places each fired key through its bone, and benilla carried the when and the what and never the where, so every animation-event sound played at the model root — 149 of 244 `$DSL` records sit off their model's origin, out to 67.6 yd on `Maraudon_Waterfall01.m2` — and the point belongs to the record, not the tag, since 75 models author one tag twice; doodad, GameObject and shake sounds now sound where the key is (1904); and the unit dispatcher's arms do not agree about the point they are handed — footsteps and emotes at the event point (the footstep's material foley stays at the origin), `$TRD` at the origin, `$CSD` at attachment 17 rather than its head-mounted records, `$CSS` at the event point on a hit and attachment 1 on a miss — with `0x623b90`'s attachment-else-bias resolver transcribed table and all (1915)
- `GetSkillLineInfo` out of range answers the reference's thirteen-value tuple, numbers in the rank slots, not a single nil — the stock skill window adds two of those slots unguarded straight off `SetSelectedSkill(0)`, so one nil raises the moment it drives us; one shape serves every out-of-range case because the bounds test at `0x4d3675` is a single unsigned compare, and our source had asserted the opposite (1919); the outbound wire is traced by name (`out`, mirroring `in`), a line pushed only after a successful write — a 5-second visit to the Goldshire poster reads as 87 lines (1901); the binding shape gate seats a player and checks the `Unit*` verbs it had been skipping, coverage 220 → 249, all agreeing with the reference (1911); and a UI capture scenario opts the UI in itself instead of photographing a UI-less world and exiting 0, the opt-in answering `false` in a player build that has no harness

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 20:51:17 +07:00
SamandClaude Fable 5.1 766395116c a disarmed fighter punches, and the quest log tags its elite rows
- a disarmed fighter swings bare-fisted: `UNIT_FLAG_DISARMED` was read nowhere but a dev banner, and the reference applies it inside `GetWeapon` on the `visFlag == 0` path, so the swing lands on AttackUnarmed; the RE cross-check (nine workers converging byte-for-byte) corrected the first reading twice — disarm hides exactly ONE weapon, main hand first (`5ec28d je 0x5ec2aa`), so a disarmed dual-wielder keeps swinging its dagger; and the weapon comes OFF the hand: the field-change reflex `0x5ff580` detaches a drawn main-hand weapon when the bit goes up, while a stowed one stays frozen on the back; one pure ladder feeds every unarmed-view site of the 82-site `visFlag` census — parry, the sheath machine, the melee sounds (a disarmed punch does not clang like a sword), Heroic Strike greying out, and the Attack button showing `Spell-Reset` with the weapon still equipped (1863)
- a quest-log row reads `(Elite)`, `(Dungeon)`, `(Raid)` or `(PvP)`: `QuestInfo.dbc` was never parsed and `GetQuestLogTitle`'s third return was a literal nil; the table is seven rows read out of `patch-2.MPQ`, SPARSE (1 Elite · 21 Life · 41 PvP · 62 Raid · 81 Dungeon · 82 World Event · 83 Legendary — vanilla's 1 is "Elite", not TBC's "Group"), and the base `dbc.MPQ` copy has only four, so a chain stopping short would lose Dungeon, World Event and Legendary; the row gains the reference's own right-flush tag string and its precedence — a finished quest's FAILED/COMPLETE word OVERWRITES the tag — replacing the " (Complete)" we appended to the title; the RE round confirmed the tag path and found the arity bug: six values ALWAYS (`mov eax,6`), the level a NUMBER 0 even on a header, where we gave one nil; the reference's title shrink stays out: an explicit width is a WRAP width here (1873)
- the death thud is silent indoors, and that is the content: BOTH the footstep and the thud lookups key on `TerrainType.SoundID`, and TerrainType 10 "None" has SoundID 0 — the footstep table has a row there for 17 classes and `DeathThudLookups` has none, so the floor that creaks underfoot swallows the body that lands on it; 10 075 of 10 299 WMO materials (97.8%) are TerrainType 10 and 694 of 815 WMO roots have no surface that can thud, every inn and tavern among them, the Kharanos inn included — the code was always right and the prose was not; the RE round also confirmed the strict `depth > 2.0` sense and the size-class column, pinned a sound id of 0 as silence, and placed `0x6005d0` as the loot sparkle, not a second sound; `thudcensus` prints all 121 buildings that do thud rather than a head, because a truncated list cannot answer "does THIS one" (1870)
- a questgiver's overhead marker follows its rider on and off the mount: the reference re-runs the attach from the `UNIT_FIELD_MOUNTDISPLAYID` watch handler, and benilla picked the slot once at build, relying on a teardown the reseat rework no longer does (B199), so the seat rode at the WRONG slot; `overhead_slot` is now the one definition of the pick (`0x6074c0`) and the marker rebuilds when it moves (1871); and the chat menu's kit was ours overwriting the reference's — `UIMenu.xml` loads earlier on the chain and our `ChatFrame.xml` carried the same kit under the SAME ten names, faithful enough that deleting it changes nothing; nothing ever opened the chat menu, and now a test does; an event census holds the gap between registered and fired events at exactly eighteen, failing on a surprise AND on a quietly gained producer — features not yet built, none a split pair (1869)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 20:49:33 +07:00
SamandClaude Fable 5 f9e75a79fc the ghost world gets its own sky, and a corpse cannot steer
- a dead player's sky is DeathClouds now — `lightSkyboxID` was named in the schema and read by nobody and `LightSkybox.dbc` was never loaded at all, so a ghost kept the ordinary gradient dome; the shipped data has exactly 5 of 426 LightParams rows carrying a skybox id, every one of them DeathClouds.mdx, reached from the death profile (slot 4) of all 374 Light rows and gated on the ghost bit, with a hardcoded weight of 1.0 that skips the WMO skybox outright; it resolves off the same (map, camera position) seed the atmosphere uses, so the ghost sky and the death-profile fog it hangs over switch on the same frame — and the lane sheds its `wmo_sky` name for `skybox`, no longer being WMO-only
- a corpse cannot steer: the reference's two movement-input predicates share a precondition benilla never modelled — `UNIT_FIELD_HEALTH > 0` — so at zero health the keyboard turn and the right-drag close together, exactly as they do inside an Ice Block, while the ghost (health 1) keeps moving; the wow-re §5 round then corrected three byte trails (the mouse turn has its own eight-conjunct predicate, the supposed on-taxi test is the Knockdown animation lockout — set on exactly one of 208 AnimationData rows — and the "charm bit" is the far-sight latch) and added three verified health gates we lacked: the jump IS health-gated, sit and stand are refused while the body reads dead (a feigner too), and a follow tears down only when BOTH predicates drop — we had that wrong in both directions
- the walk-gait cross-check folds back confirming `min(walk, run)` and the speed-only animation channel (by a 2,304-function closure census), and finds the trap we lacked: every incoming spline re-authors the gait, and the spline's RUNMODE flag is an INVERSE — a spline marked as a run clears walk mode, one not so marked SETS it — so a charge, taxi, knockback or flee hand-off leaves the body in whatever gait its path was authored with, applied once on the spline's start edge; the post-worldport zero-walk fallback is retracted on a re-run (the ctor's zero is a transient thirty instructions wide, never observable), leaving the plain minimum the bytes compute — the better failure mode too, since a fallback makes a walker silently RUN if a walk speed ever goes missing, where the transcription makes them visibly stop

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 20:18:16 +07:00
SamandClaude Fable 5 693c26d9c4 held weapons pick, book images size to their art, the panel opens again
- a unit's pick geometry is every model chained to it — body, held items, mount — one candidate per root resolving to the same unit, which is how the reference registers its pick (it walks the attachment tree and registers each model); Naxxramas' floating weapons could only be selected by their name plate before (no body, no candidate), and every armed unit gains its gear as clickable surface (our own body 12 → 21 tested parts); the broad phase stays the body's sphere, so a very long polearm on a small body is the named residual
- the selection ring's writer has two branches and we had transcribed one: a model whose stand box is degenerate (zero extent in x and y) rings at the literal 1.2 × scale — 2.7 yd under a trigger creature — instead of `sqrt(0.5·sqrt(dx²+dy²))` = 0 clamped to an invented 0.05 floor; both invented fallbacks are gone
- an HTML book image with no width/height is its art's texel square (one texel = one FrameXML unit, bit-for-bit the `<AbsDimension>` converter), not the rest of the page — *A Treatise on Military Ranks* drew its crest twice too big and stretched to the page's aspect; a texture-size oracle answers off the same decoder the renderer draws with, and an unfloated height-less `<IMG>` reserves its art's height in the flow; 797 shipped textures swept, exactly two zero-size axes move and neither is visible
- the dev debug panel killed the client on open with "Attachments have differing sample counts": Bevy's 2-D depth texture is keyed on the render target alone, so two Camera2ds on one window at different MSAA counts cannot both be valid, and the egui overlay camera had silently kept the default 4× after the player-UI camera went to Off; it now says `Msaa::Off`, and a preflight check reports any two 2-D cameras that disagree on a shared target, every run

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 17:16:04 +07:00
SamandClaude Fable 5 cbf77812d7 the cave hides the lake, the lifts come back, the bolt gets its width
- from inside a building the open-world water outside draws only through a doorway you can see, as terrain and trees already did — the Caverns of Time lake is gone from the cavern's ceiling: there was never a second visibility lane for open-world liquid (the old deferral said "it already has an authority"; a sweep of every visibility writer found none, so Bevy's frustum test was the lake's only gate), an ADT liquid layer is tagged exterior scene at spawn and the existing window cull owns it, one 33 yd chunk layer at a time — which the reference's bytes then confirmed is its unit too (`CChunkLiquid`, reachable only from the per-window walk, a sealed room's supply discarded not deferred); a building's own pool also rides the Map Objects toggle now, so switching buildings off no longer leaves the canals hanging in the air
- the Thunder Bluff elevators and every other Kalimdor lift were invisible for the whole session whenever you logged in on that continent: the lift arm snapshotted the current map one stage before the login frame had written it, so every lift compared itself against map 0 and hid as off-map — a lift carries no map at all now (it is only ever streamed to players standing on its own map), the off-map term stays for the boats it was written for, and `WOW_LIFT_CENSUS` prints each transport's arm state, cycle and visibility (13 transports, 11 hidden → 0)
- the lightning across Razorgore's room and Chain Lightning's mob-to-mob hop drew as hairlines: the chain renderer took each hop's cross-section as the world-horizontal perpendicular, where the reference takes `(−d.y, d.x, 0)` in eye space — the ribbon is view-plane aligned and keeps its authored 2 × halfWidth yards at every angle, so a long level beam watched from its own plane no longer thins to a line (the "a hop seen end-on genuinely collapses" note was wrong and is gone)
- a display that names a model builds even when it draws no batch — Naxxramas' Unholy Axe / Swords / Staff are an invisible stalker holding a Monster weapon, and the held weapon had nowhere to hang; the same gate was keeping 43 emitter-only GameObjects dark (house and blacksmith smoke, Ironforge's lava steam, Stratholme's embers, every firework), and a name sits at the authored PlayerName attachment instead of at the feet; and a vendor's foot-shuffle on turning to you now blends the whole way in and runs to its own clip window — a 500 ms step on 1100 of 1130 shipped shuffle records — where the latch had tested the gap left against a band sixty times too wide, and nothing ever ended the step

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 17:15:08 +07:00
SamandClaude Fable 5 db86a9c7ad the zone counters line up, and the ballista shine holds still
- the world-state readout — the top-centre counters a battleground or Silithus keeps up — takes the reference's real geometry: every icon those rows name is a sprite authored into the upper-left corner of a power-of-two canvas (`AllianceTower` is 16×16 of ink at (3,2) in a 32×32 file), nothing anywhere crops it, and the reference compensates geometrically — a 42×42 slot hung 6 units off the row's left edge with the label seated 10 above its centreline, taken as one system; our snug 20×20 box stripped exactly that compensation, so the art drew at half size floating away from its own `0/200` label — and an empty icon column now reserves its rect instead of sliding the text left, which straightens the readout's left edge
- the dynamic icon is a second slot, not a swap: `Icon` and `DynamicIcon` feed two different regions and shipped content never mixes them, so the flag is a 32×32 button off the row's right edge with an ADD-blend flash overlay pulsing 0.5 s in / 0.5 s out, lit only at `uiState == 2` — the flag-taken value; before, any non-zero state replaced the static art, which fires on the ordinary state 1 and hid a faction shield that should never have moved, and the pulse's alpha ramp is now pinned by a test, not just its art
- the dev cost pill yields the band: the readout is the game, the pill is scaffolding, and a stock 1.12 UI has no free corner to move house to — so it steps below whatever the readout draws, reading `WorldStateAlwaysUpFrame:GetBottom()` on its own 4 Hz cadence and only while it is drawing, rather than mirroring the frame's row pitch in Rust; the probe's cost is stated at the site (an edge read settles the layout — up to one graph solve, ~47 µs, four times a second)
- the Hearthglen ballista's bolt heads and shields stop flickering: a shine layer is a second batch over its base's own triangles, and ours split the pair across lanes — the base rode the retained lane's f64 CPU bake while the env-mapped, additive, no-depth-write shine stayed on the entity path's f32 GPU rotate, and a few ULPs between coplanar surfaces turns the depth test into a per-pixel coin flip re-rolled by every camera move; a batch now names the M2 skin section it draws, neither member of a shared section diverts into a consolidator (depths equal by construction), and a corpus census bounds the cost — 1,080 of 9,691 models draw a section more than once, 264 such sections across 221 `World\` doodads — with toggling pixels at the pin dropping 0.26% to 0.14%, matching the consolidators-off arm bit-for-bit

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 15:07:21 +07:00
SamandClaude Fable 5 a53a58128c opening a chest kneels you, and outdoors elects what it draws
- the loot kneel finally fires on a chest (the reported gap): a chest never sends `CMSG_LOOT`, so the client-local loot latch gains its second arm on `SMSG_SPELL_GO` with the loot response as an admission gate — you kneel before the window opens and hold it while it is up, like the reference
- the outdoor draw election lands: the 1.12 client elects every scene object per frame, and a unit behind you or in a room the portal flood can't see is neither drawn, animated nor particle-ticked — at the Goldshire pin drawn submeshes go 365 → 49 and live particles 569 → 138, camera stamp bit-identical; `WOW_NO_DRAW_ELECTION=1` reverts. No ms pricing yet — counts only, the machine was shared
- standing-NPC facing and docked transports go write-on-change instead of dirtying their transforms every frame
- the options window gets a ground: a black fill inside the rope drops the world's bleed-through onto the page from 40% to 12%, so the small text reads against snow and noon water

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 17:59:10 +07:00
SamandClaude Fable 5 6d1e13d147 there are three water renderers, and WMO water gets its two
- WMO water stops running the terrain-liquid program: the material key names WHICH renderer a pool takes, and the two WMO arms land — Blackfathom Deeps' interior pools take their authored per-vertex opacity and the material's own body colour (34, 38, 57 there — the dark blue-grey in the reference shot) instead of coming out opaque and daylit on the zone's river swatch
- the exterior WMO arm bands at sub-17, not sub-16 — teal, not olive — which is what the Stormwind canals actually read as
- a hole corner's authored height no longer counts as a height, and a corpus-wide instrument reports which WMO water arm every pool takes
- the ADT/Felwood golden sweep is bit-identical: open-world water is provably untouched

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 17:55:04 +07:00
SamandClaude Fable 5 8709dd6bc9 dark overcast skies, a talk gesture, and NPCs that face you
- the Tainted Scar sky reads its dark red overcast instead of almost pure white: a light-band row with no keyframes now commits the client's own constant — black for colour rows, +0.0 for scalars — instead of an invented neutral-daytime palette; Blasted Lands authors cloud density 0.85, so the cloud gradient IS the sky there, and sky pixels go `rgb(255,255,255)` → `rgb(145,55,25)` while the terrain control stays bit-identical and the blessed capture sweep holds MAE 0.000
- an NPC you interact with turns to face you and holds the bearing while the window is open — client-side, a per-frame facing-goal chain whose smoother halves the remaining angle each frame (π settles in 8–9 frames); the server sends nothing, and a patrolling guard or a creature in combat is unaffected
- sending a chat line plays the talk gesture, like every 1.12 client: a say reads its last byte (`?` question, `!` exclamation, else talk), a yell always shouts, a laugh-word laughs — five gestures resolved from Emotes.dbc flag bits, and talking stows a drawn weapon
- a standing capture viewpoint pins the Tainted Scar sky for regression

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 17:52:20 +07:00
SamandClaude Fable 5 e34600ff27 the client boots with no install, and the idle egui lane goes quiet
- with no WoW install the client boots to the login screen instead of panicking on frame one: three world systems demanded install-gated resources their own startup never inserts
- `WOW_DATA=` — set and empty — means there is no install, in any build; the cursor says "no client data" once instead of thirty-one times
- the per-frame `bevy_egui pass output has not been prepared` ERROR is gone: a gated context runs an empty pass itself, so the idle lane keeps its savings and the log stays silent
- ECS diagnostics name their system in a dev build — no more `<Enable the debug feature to see the name>` in a panic

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 19:06:09 +07:00
SamandClaude Fable 5 8522c65937 the lava bubbles move again, and the bonfire keeps its wood
- Blackrock Mountain's lava bubbles animate at last: their sprite's flip-book channel was baked from a sequence they never play — UV and tint loops now bake per sequence, and a placement whose sequences disagree gets its own material
- a bonfire's logs were vanishing while its flame burned on: a body planted just under its own floor now claims the room above it, and the flame card and fire sound follow the body's cull instead of outliving it
- a WMO prop's animation host stops being cull-tested as an object in its own right
- WOW_TILE_RADIUS's doc now states its real default, 2, and why

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 17:27:28 +07:00
SamandClaude Fable 5 802845981f the Rookery eggs hatch, and the bubble finds its height
- UBRS's Rookery eggs hatch on approach now: the trap's despawn-anim packet was silently dropped and the entity freed the instant the destroy landed — the packet is parsed, and the reference's refcount pin holds the egg alive through its 2.7 s burst before it goes
- the AQ drakes' crawl is the server's doing: vmangos flies cyclic paths at walk speed, and the client's ride measures exact against the wire — the motion is inspectable now, down to a per-spline trace
- the chat bubble sits at the height 1.12 gives it: a latched constant from the model's stand box, replacing the posed-attachment anchor the RE just refuted
- the trigger-creature render gate folds back from the binary, byte citations corrected

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 17:26:18 +07:00
SamandClaude Fable 5 287b109773 the bubble's pale lines, and the trigger NPC finally hides
- the pale lines through the speech bubble were its backdrop atlas bleeding across its own slices under magnification; the slices now inset, verified per row against the real backdrop bytes
- a bubble that never appears can say why: all seven silent refusals report themselves under the bubble trace
- the invisible trigger creature goes invisible at last — there is no server flag to consume; its model is authored empty, and the black slab people kept meeting was our own placeholder cube. a display that names a model now draws exactly what that model builds, including nothing
- a census probe lists which streamed entities are standing as placeholder cubes, and on which displays

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 17:22:57 +07:00
SamandClaude Fable 5 7b8e5f99bd lava creeps again, and sealed rooms empty of outdoor bodies
- lava and slime surfaces creep on the building path — the two liquid types the reference animates, and only those
- particle blend 1 is AlphaKey: the rock elemental throws tumbling chips instead of flat pale squares
- standing in a sealed room, outdoor creatures are never submitted to the scene at all — the reference's own election, not a hide
- an animation clip's arm frame fires no event keys, matching the client's walker rule frame for frame

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 19:23:04 +07:00
SamandClaude Fable 5 961654afdb the map's invisible walls are read from the bytes at last
- the terrain's impassable-chunk flag is parsed and fenced the way the binary fences it — all four sides, floored, rising only — so movement stops exactly where 1.12.1 stops
- a vertex normal authored as all zeros is legal content: the normalize is guarded, and the flat-black sleeves and skirts get their lighting back — a corpus sweep found 73 affected models nobody had filed
- Caverns of Time's asteroid belts turn: a rigid bone needs no rig

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 19:22:35 +07:00
SamandClaude Fable 5 0058888def birds stop blinking out, and the version check is answered
- an animated doodad is culled by its authored box, not its bind pose — the one-yard bird flying a 64-yard circuit stops vanishing with the camera angle — and the fix survives the engine's own bound recompute
- the logon proof answers the server's version challenge instead of sending zeros, so a strict realmd stops rejecting benilla as a modified client
- two instruments land: a frame-by-frame visibility trace for one model, and a probe that classifies whether a realmd enforces the version check

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 19:09:40 +07:00
SamandClaude Fable 5 6b980b9efb the player build: one binary that carries its content
- a player build ships beside the dev one — dev chords and probe surfaces go dark, and a gate enforces what a player can still reach, by medium not by module
- the binary carries its own shaders and its shipped UI, finds the WoW folder by itself, and keeps its local state in one benilla-config folder
- the glue screens get a capture shutter, and the engine stops depending on the protocol crate

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:18:02 +07:00
SamandClaude Fable 5 7b4bd8af4a quest timers, honest interiors, and snow rebuilt from the bytes
- timed quests get their countdown — a Quest Timers box under the minimap and a Time Remaining line in the quest log, kept against the server's own clock
- interiors go honest: footsteps stop crunching the terrain's snow through a building's floor, and tavern voices are authored dry, so the fake cave echo is gone
- snow is the reference's again — point-sprite flakes at the era's pixel size, and a spawn slab that leans into your run so a runner no longer outruns the field
- Stormwind stops drawing Elwynn through its walls, a looted quest crate's lid settles, cold zones puff visible breath, and the collect-quest toast fires only on a real advance

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:00:02 +07:00
SamandClaude Fable 5 50aedb8b05 world books open, and anvils stop offering a cursor gear
- clicking a book in the world opens it and turns its pages: letters and world books share one reader
- the cursor's object rules are the client's own 31-row strategy table now, so decorative objects stop pretending to be usable
- trainer and tradeskill windows draw each row's icon by the reference's own law, and the trainer's filter dropdown toggles again
- water color follows the zone's light blend, so lakes stop snapping between area tints

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:03:35 +07:00
SamandClaude Fable 5 9f4595597d fishing becomes whole: the bite, the line, the splash and the reel
- the whole loop plays: the line arcs rod tip to bobber, the bite splashes with its own sound, and clicking the bobber reels in
- the verdicts read like 1.12: yellow "your fish got away" lines, the reel-in sound, the fishing loot window
- "You feel rested." prints on the transition, rest states come from the client's own Exhaustion table, and level 60 trades the XP strip for the max-level rail
- the Skills pane lists what the real client lists: the hidden rows, the sort, gray numberless single-rank lines

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:03:16 +07:00
SamandClaude Fable 5 8549f14dd6 footprints on snow and sand, and the drunk effect staggers in
- drink enough and the screen blurs and your walk meanders, the camera carried along with the stagger; the underwater blur ships in the same lane
- boots leave prints on snow and sand that fade away in about six seconds, paws and hooves included
- key bindings move into the Options window with the rest of the settings, and action buttons abbreviate their hotkeys the way the client does
- the pet bar rearranges by drag, a running pet spell shows its active icon and a click cancels it, and spell ribbons follow their verified physics

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 19:05:51 +07:00
SamandClaude Fable 5 77dc8f8ead pets get their action bar, macros arrive, and hunters draw the bow
- a hunter's or warlock's pet has its action bar now: attack, follow, stay, the pet's own spells with cooldowns and autocast sparkles
- the macro system lands: the macro window, the icon chooser, /cast and /macro, and macros dragged onto the action bar
- hunter shots look right at last: the bow draws and releases on the shot, and the arrow lays a spark trail its whole flight
- the options window re-dresses in 1.12's own art with the classic dialog look, and gets a working search box and clickable steppers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 19:03:13 +07:00
SamandClaude Fable 5 994be22f5c outdoor lava flows, and Undercity gets its lost corridor back
- lava renders outside Blackrock at last: the mesher was dropping every open-world lava and slime surface, and 28 river mouths get their missing sea back in the same sweep
- Undercity's broken corridor returns, geometry and color both: a one-byte file overrun dropped the whole room, and a paint job one record short was being thrown away entire
- collision goes one-sided like the real client: walls stop you from the front and let you pass from behind
- the Booty Bay entrance is wood and rope again instead of a black cutout, glass picks up a real reflection, and models far from the map's center stop trembling

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 19:02:47 +07:00
SamandClaude Fable 5 a03cb5d5d2 Chain Lightning draws its chain, and the Controls page lands
- the beam is on screen: Chain Lightning arcs outward hop by hop and Drain Life ropes from hand to victim — a write-order edge was erasing the finished geometry every frame, and a tripwire now guards the whole class
- the Controls page lands (sticky targeting, auto loot, invert mouse), UI scale waits for the era Apply button, and the terrain-distance slider retires
- with auto-loot in effect a lootable corpse shows the triple-pouch cursor, the real client's tell
- the character-screen freeze before a loading screen is gone: the shader warm-up hides behind a fully presented cover and tears down only its own roots

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 18:54:58 +07:00
SamandClaude Fable 5 060aa574fb dark doorways light up, and the Audio and Graphics pages land
- a building's transition corridors carry the reference's bright-doorway fade: the black tunnel between sunlit outside and a lit interior is gone
- the Options window gets its first real pages: seven 1.12 sound rows on Audio and the two 1.12 Video Options sliders on Graphics, all live CVars
- terrain culls like the real client: the ground is one-sided, so from underneath you see through it exactly as 1.12.1 does
- the beam system's data plane lands: 48 live chain, drain and funnel beams read off the real patch chain, with a byte-exact wire golden — the renderer follows

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 18:54:47 +07:00
SamandClaude Fable 5 eb239ddf25 effect clocks run true, and Orgrimmar's ground items stop strobing
- spell shimmer timing is right: shared clocks tick together, each cast anchors its own
- the flickering ground items in Orgrimmar are fixed, a transparent draw-order tie
- static scenery answers the mouse again, and the freeze debuff's crackle rings at the target
- bug reports moved to Discord, GitHub issues are closed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 18:00:04 +07:00
SamandClaude Fable 5 59b4a4f4ce buff timers tick, ground spells find the floor, stuck drags let go
- buff durations count down properly: the bar polls the remaining time every frame and a server-sent span reads as real seconds
- ground-targeted spells find the floor: Consecration's and Flamestrike's hovering discs ride the decal projector, and Frost Nova's births ride its expanding ring (the emitter params are per-frame tracks now)
- a spell or action stuck on the cursor lets go on a left click, and a macOS hardware-cursor drift heals itself in-process
- a stealthed weapon fades right: the fade twin's cutout follows the source blend, so an opaque batch never alpha-tests mid-fade

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 17:01:51 +07:00
SamandClaude Fable 5 3fbfebd604 stealth you can see, item glows, and smooth character-select skies
- stealth reads as stealth now: the body fades to the reference's 30 % and creeps with the proper stealth walk and stand poses
- the item and enchant glow subsystem exists: permanent weapon glows and enchant visuals show in the world and at character select
- the character-select background's colour banding (the reported "16-bit" look) is fixed at the render target, and the tauren stage's black corners with it
- aura body tints got a real per-instance channel, so a spell can tint the whole unit without touching its neighbours

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 19:40:54 +07:00
SamandClaude Fable 5 d3e05f32e5 the world only pays for what's on screen, and the leak is gone
- terrain draws per chunk now, and one authority culls everything outside: buildings, billboard cards, particle emitters
- the fps and memory creep on long flights is fixed twice over: material animations only tick what something draws, and far-away art is released by distance (with a 30 s dwell floor so fast cameras don't thrash it)
- the second crash ever reported (a cast racing a despawning unit at flight speed) is fixed, with a regression test that reproduces the exact panic
- every build now prints which commit it is, so a bug report can say exactly what it ran

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 19:39:56 +07:00
SamandClaude Fable 5 34fdbd1d16 the keyring lands, keys open doors, and a building can own the sky
- The keyring is built. Your first key makes the keyring button appear by
  the bags, opening the window the original keeps its dungeon keys in — the
  server had been sending the keys all along; nothing had ever shown them.
- Keys open their doors now. The client used to cast the open as a bare
  spell, which the server rightly refuses; it now sends the item-use the
  original sends, carrying which key from which slot. The tooltip plays
  along: "Locked" turns the soft green when something you carry opens it,
  and a key stops inventing a "Use: Opening" line it never had.
- A building can own the sky: a WMO can name its own painted skybox, and
  Stratholme's burning city now draws its near-black-to-red dome instead of
  the zone's khaki gradient. The exact rule for when it applies settles in
  the next snapshot.
- Signpost tooltips follow the cursor again — the tooltip anchor forks per
  object class, corner for doors, cursor for signs — and the Blasted Lands
  lightning stops striking the same spot for ever: a placed doodad re-rolls
  its animation variation every play-window, like the original.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 19:54:18 +07:00
SamandClaude Fable 5 7823f6a47a underwater only when you're in the water, and druid forms work
- A pool belongs to a room now. Uldaman read as deep underwater because a
  mushroom cave's pool 186 yards up claimed every position beneath it forever;
  Undercity's NPCs swam on dry stone because Tirisfal's lake — 95 yards of rock
  overhead — still answered for them, and the camera took the whole screen
  underwater with it. Every liquid query (player, camera eye, every unit) is now
  scoped to the building that owns the water, verified against the real 1.12.1
  files at both reported spots.
- Druid forms work. A live display change — shapeshift, GM morph, revive — was
  only ever read at spawn; now the model swaps instantly and a size change eases
  over the real client's two seconds. A bear-form druid shows no weapon, exactly
  like the original: only a character body attaches held items.
- 126 NPCs across the world were bald and earless. An amulet's display row sits
  in their head slot, and its "tuck the hair under the helm" mask fired with no
  helm to tuck under. A mask now needs an actual helm model; the gnome suppliers
  get their pigtails, ears and earrings back.
- The pouring lava in Blackrock Mountain and the Great Forge came back. A prop
  that spans several rooms (a lava fall is named by up to 19) was culled with
  only the first room that names it, so it blinked in and out by camera angle.
  A prop now draws while any of its rooms is visible.
- The Undercity entrance lightshafts dim to their authored 10% instead of
  flooding the throne room with blinding white columns.
- An indoor pool fogs with its room now, instead of hazing with the outdoor
  scene while the wall beside it hazed with the room. Magma and slime take fog
  too — "fullbright" in the data meant unlit, never unfogged.
- Backing the camera out through the Deadmines entrance swirl no longer blanks
  the whole dungeon: the sealed pocket behind the portal has a detail-only
  floor the room-finder couldn't stand on, so it declared you outside. A third
  fallback leg now names the room from the camera-collision faces.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 19:37:17 +07:00
SamandClaude Fable 5 0d945c4fe8 particles keep time, and the maps with no terrain appear
- Twenty of the game's forty-three maps are not made of terrain at all — Molten
  Core, Onyxia's Lair, Dire Maul, Gnomeregan, Uldaman, the Deeprun Tram and both
  Blackrocks among them. Their whole world is one building named by the map file,
  and that line was being thrown away, so those maps loaded nothing and a teleport
  in fell forever. The building is now placed as an ordinary WMO placement — so
  collision, props, liquid, interior lighting and portals all come along on the
  existing path — verified on seven of the twenty against the server's own
  independent floor heights.
- Particles keep the model's time. Emitter rate and on/off now bake per animation
  sequence and follow the clip that is actually playing, so a quest object's
  explosion choreography no longer burns at full roar while the object idles. And
  the flipbook cell ramp is computed the way the reference computes it: sheets
  that play backwards no longer crash the client (a certain Winterspring brazier
  fired this on every approach), the tail streak reads its own ramp instead of the
  head's, and the per-segment repeat count works — the Insect Swarm's wings
  finally flap.
- A culled dungeon room takes its furniture with it: lanterns, crates, barrels and
  cobwebs now cull with the WMO group that owns them, instead of hanging lit in
  the void where their room used to be. The group's own liquid goes along too.
- Under the floor: the archive chain pins that a file the patch deletes can never
  fall through to the stale copy in a base archive — a silent failure mode that
  would serve deleted content looking perfectly healthy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 19:22:09 +07:00
SamandClaude Fable 5 2c55790ec1 portals work, GameObjects animate, and the tent stops flickering
- Area triggers exist now, so portals work: every teleport pad, dungeon entrance
  and enter-a-place quest volume reports the reference's own way — one exit latch,
  first containing volume on the map wins, a sphere or a yawed box. Verified live:
  Rut'theran to Darnassus, the Deadmines worldport, and a level-gated entrance
  refusing with the server's own "You must be at least level 10 to enter."
- GameObjects animate. Every animated object arms its idle the moment it goes live
  (doors and chests used to flash bind pose for a frame), a missing rest pose is
  remapped the way the reference remaps it instead of playing nothing — the
  Ahn'Qiraj gate's root tangle stood frozen for want of a Closed clip — and the
  state machine covers the verified twenty of thirty-one object types instead of
  three: fourteen hundred spawns' worth of traps, goobers, books and questgivers
  now change pose off the wire.
- The flickering tent is still. Cloth you can see from both sides is authored as
  two reversed copies of the same sheet, and the real client backface-culls each
  copy from its wrong side so exactly one covers any pixel; we drew every building
  batch two-sided, which left the winner to floating-point noise, flickering while
  the camera crept. WMO materials now honor their cull flag.
- A sweep of long-standing visual bugs: effects respect the far-clip wall — a
  ferry's deck lanterns were drawing from 7,000 yards away; the night-elf eye glow
  goes out in first person instead of leaving two orbs burning in mid-air; the
  goblins' stray white hair tuft is gone (a duplicated table key resolved last-wins
  where the client scans first-wins); lava and slime finally have their own
  submerged view — the dense fiery orange and dense green of the real thing, off
  two fixed lighting rows nothing in the zone data points at; and the far-horizon
  band overlaps the detailed terrain instead of leaving a strip of sky at lowered
  view distance.
- Two caught at the keyboard: the backpack icon greys instead of vanishing while
  the escape menu is up, and the world map is a real panel now — it can no longer
  open over the menu, and ESC closes it first like everything else.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 19:20:51 +07:00
SamandClaude Fable 5 c1ba444119 the micro menu, the ping meter, and the PvP flag
- The main menu bar's two empty recesses fill. The micro menu — the eight arched
  buttons: character, spellbook, talents, quest log, socials, world map, main menu,
  help — sits in the dark panel between the page arrows and the bags, transcribed
  from the reference's own layout: the character button wears your portrait, a button
  holds itself down while its panel is up, and the talent button appears at level 10.
  The latency meter fills the one slot the bar art leaves transparent for exactly it,
  tinted green/yellow/red on the reference's 300/600 ms thresholds and drawn UNDER
  the bar art, seen through its window, the way 1.12 actually does it. The socials
  and main-menu buttons find their panels one snapshot later; help still waits.
- The UI explains itself on hover: 1.12 ships its detailed two-line tooltips ON — a
  FrameXML default, not the opt-in extra it is often remembered as — so the micro
  buttons, your own portrait, the XP bar, the five bag buttons and the ping meter
  all answer a hover with the reference's own explanations.
- The PvP flag, end to end: crossed swords on the player, target and party frames,
  driven by the two descriptor bits the wire actually carries, and `/pvp` speaks —
  the yellow toast plus the system chat line, the OFF sentence being the one that
  warns the flag lingers for five minutes. It really does linger: turning it off
  waits out the server's timer, which looks like a bug and is the reference.
- Quest markers stopped freezing. The grey `!` over a questgiver above your level
  goes gold the moment you ding, an NPC re-entering view is asked afresh instead of
  wearing a session-old answer, and the marker drops when the questgiver bit goes
  off — the re-ask triggers mirror the reference's own set, plus one named addition
  (your own level changing).
- Water and heights: swimming downhill no longer bounces between swim and fall ten
  times a second — the surface line is a two-sided constraint, so the swimmer
  settles with the river instead of being left behind by it. And collision height
  is per-unit now, read from the unit's own model: a gnome used to swim with a
  third of a yard of water over her head because five depth lines were quoting a
  human's number.
- A round of pixel honesty in text and chat: the newest chat line no longer wears a
  wider drop shadow than every line above it (shadows move in whole pixels now),
  keeps its descenders, and hover boxes stop painting over the chat body. The chat
  input's history browse survives programmatic edits, a failed script chunk warns
  instead of vanishing, and recall deepens to 64 lines. Idle animation got honest
  too: a model's idle is Stand resolved through its own lookup table — never
  file order, which had the duel flag bobbing on its spawn clip — and an armed idle
  is culled on the box its animation authors, not the bind pose, so the flag stops
  vanishing at most camera angles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 19:17:47 +07:00
SamandClaude Fable 5 2ad0043523 benilla: initial public release
A from-scratch World of Warcraft 1.12.1 client in Rust + Bevy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:27:01 +07:00