Commit Graph
8 Commits
Author SHA1 Message Date
SamandClaude Fable 5.1 057a3061c4 glue chrome in the box, a knockdown holds, and loot closes on a step
- on a 21:9 display the login, select and create chrome lays out on the boxed scene instead of the black bars beside it; at 16:9 nothing moves (B377, 2091)
- a <Model> tile draws only inside its own cell of the shared atlas, so the pet bar's autocast shine no longer bleeds a golden bar onto a bag slot's cooldown (B379, 2093); a slider's first value always fires its handler, so Atlas's options load (2095)
- a knockdown plays out: the reference locks the base animation for Knockdown, LiftOff and Land until the clip ends, and benilla re-picked Stand ten milliseconds in (2096)
- the loot window closes and releases the corpse on your first step, strafe, turn or jump, the reference's movement-start guard; a distance leash shipped first and was a mislabelled census row (2094 → 2097, B381)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:24:48 +07:00
SamandClaude Fable 5.1 976b380d3c unselectable units take no target, quieter wounds, a realm-list dialog
- a NOT_SELECTABLE unit takes no target: Stratholme's Baron Rivendare, pre-event, is spawned by vmangos with `UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_SPAWNING`, and where the reference can neither damage nor target him benilla refused the damage (`CanAttack` already read bit 25) and took him as a target anyway — most visibly through the ATTACKERSTATEUPDATE self-defence acquire, which hands you your attacker the moment he opens on you with no click and no hover involved, and every other writer (left-click, right-click, TAB, `TargetUnit`/`/target`/`/assist`, the attack auto-acquire) had the same hole; the reference's gate is the third early-out of `SetSelection` `0x493540` (`0x4935ee`/`0x4935f3`): the resolved object's `+0x58` vcall to `0x60be60` `IsSelectable` = `!(UNIT_FIELD_FLAGS & 0x02000000) || UNIT_FIELD_CREATEDBY == your guid`, sitting BEFORE the dedup so a refusal is a complete no-op — no `CMSG_ATTACKSTOP`, no `CMSG_SET_SELECTION`, the target already in hand survives — and skipped entirely for an unresolved object, which is what keeps an out-of-range party member selectable off the roster; `target::relations::is_selectable` transcribes it whole, the CREATEDBY clause (your own totem stays clickable, somebody else's does not) and the xor-eax base stub every non-unit vtable carries at `+0x58` included, and `scan::commit` runs it first behind a mandatory `target_store` parameter so none of its eight call sites can forget a gate it does not compute; the hover is the SAME predicate at a second site, byte-verified by the wow-re round this dispatched: the hover grader `0x4828d0` calls slot 21 directly on the pick's winner at `0x482982`, and a false answer jumps to `0x482090(0,0)` + `ResetCursor` `0x523d30`, skipping the publisher `0x492890` that holds the mouseover globals' only two writers image-wide — so no tooltip, no cursor, no emissive brighten and no `mouseover` token, all from one rule; it is a GRADER, not a filter, and the difference is observable — a census over the pick itself (`0x480a50`, `0x480d90`, `0x713cb0`, `0x481190`, `0x480df0`) finds zero reads of the unit flags against four inside `CanAttack`, so the flagged unit wins the trace and is then thrown away, which `Hovered::refused` carries with the pick's distance kept so a trigger stalker in front of a chest takes the pick and the chest behind it does not inherit the mouseover; graded at `update_hover`'s publish rather than in a system of its own, because `drive_mouseover_tooltip` is ordered only after the net stage and a second system would have bought a one-frame name plate for a unit that must never have one; the V-plate suppression turns out to be load-bearing, since a plate hover publishes the mouseover directly (`0x7cb869 call 0x492890`) with no grading at all; and a left-click on a refused unit no longer clears what you had — the grader had dropped it into `select_on_click`'s arm beside "clicked the sky", where the reference's deselect lives in the terrain and NOTHING legs only: the down-edge pick `0x481f00` still found the object, so the release takes the object leg `0x4925d0` whose tail hits `SetSelection`'s early-out and changes nothing, the reasoning the corpse guard beside it already carried and which gets its first test here (2060)
- three things a landed hit showed that the reference does not: a lootable corpse took blood, flinch, impact sounds and a floating number — the victim dispatcher `0x624530`, which owns every victim-side consequence of a swing, opens with `test byte [[victim+0x110]+0x224],1` (`UNIT_DYNAMIC_FLAGS` bit `0x1`) and jumps past all four — but the obvious fix, gating the victim, is wrong: benilla fans that dispatcher out into one message emitted on three occasions and the reference gates the DISPATCHER, and an image-wide census of the number emitter `0x6243e0` (exactly three call sites, zero tail-jumps, zero address-takes, earned against a positive control) puts `0x624566` inside the gate but the `SMSG_ATTACKSTOP` flush `0x624ea7` and the supersede flush `0x625893` outside it, so a lootable victim still floats a number for a superseded record; gated: the impact-tag dispatch (record still consumed first, as `0x6247d0` clears the GUIDs before calling) and the unresolved-attacker fallback `0x625a6d`; ungated: both flushes and the whiff slow-down, `0x624ca0` being its own function past the dispatcher's end; the flush's own predicate `0x625e40` reads an unnamed `[rec+0xac]` and stays unbuilt rather than guessed (2066); a crit played a full CombatCritical clip on a fighting unit — vmangos sends `SMSG_EMOTE(EMOTE_ONESHOT_WOUNDCRITICAL)` to the victim of every damaging crit (`Unit.cpp:1601`), `Emotes.dbc` resolves it to AnimID 10, and on the director's 90 s brawl trace that was 7 one-shots plus 9 fast-path entries on the warrior cutting its own swing (`fastpath cur=87 req=10`), 19 across all units; the reference's shared emote player `0x5fcd20` refuses at `0x5fcd96` when `0x60ecd0` is true — `UNIT_FIELD_FLAGS` bit `0x800` OR an auto-attack target `[+0xc48]` — and 1469 had built the flag half alone, dormant on vmangos (its `0x800` is `PET_IN_COMBAT`; a fighting player or creature carries `IN_COMBAT 0x80000`, a bit the client never tests), so `emote_anim::play_eligible` takes `engaged` and both producers, `SMSG_EMOTE` and the chat gesture, pass `Has<Engaged>` through one `PerformerQuery`: a crit on a fighting unit shows only as the wound overlay, and the wound trace lines carry `unit=` (2067); and an ending effect's particles popped — the teardown `0x6203e0` that both the completion callback and the same-slot replace reach hides the node and queues it rather than freeing it, and whether that is visible turned on a half no wow-re note covered, dispatched rather than assumed: the draw path's own admission test is the live-particle count (`0x7b4b46 mov eax,[esi+0x64]`) and the model enqueues onto two scene lists on two fields — MESH on the visible flag (`0x719207`), PARTICLE on the aggregate `CM2Model+0x3d8` (`0x71922c`), set while any particle lives and the same word `0x61f680` frees on — so for `Strike_Impact_Chest` a node destroyed at 0.10 s still shows ~30 particles at 0.20 s, gone by 0.334 s, and benilla cut up to 0.30 s of a 0.334 s effect; the three points where an effect ENDS — expiry, the same-slot replace, the reap's immediate destroy — now hand their emitters to the drain by the instance root they already carry as anchor (`drain_instance_emitters`, `ParticleEmitter::drain_on_owner_loss`) and the root still despawns, the reference dropping the mesh the same frame; flipped at the ending rather than set at spawn because the other way an owner vanishes is the model dtor (gear change, display swap, stream-out), where the reference frees synchronously and draining that case is what stranded ghost clouds in the air (0826/0833), and the collateral path still frees (2070)
- a creature whose template carries `DO_NOT_PLAY_WOUND_ANIM` takes no flinch and no parry, and the wound grunt is rolled: the creature template's `type_flags` carries the bit at `0x8`, benilla consumed the two bits either side — `0x10 NO_FACTION_TOOLTIP` in the unit tooltip, `0x20 MORE_AUDIBLE` in the parked-event gate — with neither reader naming its bit, and `wound.rs` carried the gap in a doc comment claiming "no template cache" that had been false since the cache landed; the reference refuses the flinch inside `0x60ea70` itself (`0x60ea9f`, so every trigger is covered) through the getter `0x6125f0`, whose null-record leg fails OPEN, so `names::type_flags` names all eight bits (a call site could say `& 0x10` and look right — that is what let it be wrong) and the wound trigger refuses a flagged victim as its first gate, keyed off the descriptor's `OBJECT_FIELD_ENTRY` the way `0x60b160` keys the cache; animation only, since no creature-type mechanism suppresses the blood spurt — a flagged creature bleeds (2068); the vocal half was dispatched rather than built, because wow-re's published "makes no pain sound" called the flag's second consumer `0x60ec00` a pain-sound dispatcher while three of its own notes said otherwise, and the cross-check came back with all seven questions VERIFIED: `0x60ec00` is an ANIMATION dispatcher — `GetWeapon(0,0)`, the mainhand subclass through the 18-entry remap `0x60ec98` and jump table `0x60ec84` to Parry1H/2H/2HL/Unarmed, handed to `0x5fe2f0` = `PlayAnimation`, whose 40 call sites push literals in `[16, 202]` and which reaches no sound band — so the flag gates two animations and no sound, and the parry now rides it (victimState 3 only; Dodge 30 and Block 24 reach `PlayAnimation` directly from `0x624a90`/`0x624a74`, and our subclass map agrees with the raw remap on all 18 rows); nothing suppresses the injury vocal — of the fourteen functions in the wound-vocal chain the only one dereferencing `[unit+0xb30]` is `0x623b70`, testing bit 5 for a bus — but the ordinary grunt IS rolled: `0x623490` runs the class chance roll at `0x8626d4[2] = 60` for a creature and `0x86424c[2] = 30` for a player, by the VICTIM's type, while classes 3 (critical) and 9 (crushing) carry 100 and always sound, so `INJURY_CHANCE_CREATURE`/`INJURY_CHANCE_PLAYER` thin the grunt under sustained melee where benilla had played all three every time — 40 grunts per 100 too many on a creature, 70 on a player — and a crushing blow's class 9 punches through a server-pushed object sound's suppression, which stops at class 8 (`0x6234bb`/`0x6234bf`/`0x6234c4`); a pet gets a template record at last, since `NameCache::resolve` routes a `HIGHGUID_PET` guid to the pet-NAME query and a tamed unit had none — 18 of the 471 flagged templates on the world DB carry a `pet_family` — so the create arm also asks by the descriptor's entry, the reference's own key (`[[unit+8]+0xc]`); 2068's claim that templates are queried lazily is corrected, the create arm having warmed the cache for every streamed unit all along, at the reference's own timing (`0x60afb0` → `0x60b157`, on the create path) rather than as the convenience its comment called it; and the skeletons of the report still flinch, and should — neither vmangos nor cmangos flags them (2 of 62 templates in each), `Skeleton.m2` authors CombatWound and CombatCritical, so what changes for them is the grunt's rate, not its existence (2073)
- the realm list is a dialog, not a screen: the first live run of 2056's realm selection reported no hover highlight, Cancel and the X throwing you out of the session instead of back to character select, the character screen gone from behind it, and then a login that hung on "Connecting" forever with nothing in the log — three of the four are one mistake, since `GlueScreenInfo` names all seven glue screens and `RealmList` is not among them: `RealmList.xml` is a `frameStrata="DIALOG"` frame with a full-screen 75%-black quad that `Show()`s over whatever screen is up and `RealmList_OnCancel` only `Hide()`s it, while 2056 had built it as a `ClientState`, so Cancel had to pick somewhere to go, the screen behind was torn down, and — the one that mattered — a roster arriving while that state was up moved nothing because `to_select_on_roster` only ever leaves `Login`; Okay looked inert while the IO thread walked on to the character park, the next Cancel answered a park two ahead of the thread, and every later login queued behind a `pick_rx.recv()` that was never coming, silently, reproduced with a scripted walk before anything changed; the realm list is a `shown` flag now, served in place by the character park over `select!` — refreshing on the realmd socket the cycle still holds, since `RequestRealmList` (`0x46b8d0`) begins `COP_GET_REALMS` on a socket closed only at world entry (`0x46b70c`) and only `ConnectToRealm` (`0x46b210`) drops the world connection — and leaving only when a realm is picked, `HeldLogon` and the resumed cycle gone with it; Cancel sends one `Abandon` and the park that hears it decides, which is the reference's own shape: `RealmListDialogCancelled` (`0x46ed20` → `0x46b810`) compares the current screen name against "login" and returns immediately when it differs, tail-jumping `CLoginMgr::Cancel` (`0x5b3320`) only from the login screen; the hover was never a realm-list oversight — `Hilight` was "the owning screen's to drive", four screens drove it, two folding selection into the same expression and one scoped so the `GlueCloseButton` X on every panel could never light, and the realm list made five by having none — so one owner now, `glue_hilights`, lit by hover or `LockHighlight` and walking UP to the nearest enclosing button because a modal dialog's root is itself a button, with `art_swaps`, `glue_button_visuals`, `glue_hilights` and `sync_outlines` registered once in `GluePlugin` instead of hand-copied per screen; the IO thread names each park it stops at, a logout comes back through the same park a first login does, so the policy ladder puts the realm the player is currently on ahead of the persisted `realmName`, which benilla writes only at world entry, the reproduction is now a smoke leg (Change Realm → Okay → Change Realm → Cancel → Okay against the live server) that fails on the old code by construction, and two divergences are stated: the X abandons like Cancel because our thread parks on the question rather than polling for it, and Change Realm shows the list already in hand at once rather than waiting a round trip behind a status dialog (2072)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 16:08:46 +07:00
SamandClaude Fable 5.1 7fe4b37c2f guild lines are message ids, mail sends again, and the shadow fades in
- the guild lines are message ids: `ui_guild/lines.rs` carried 34 English constants and two hand-rolled fill helpers and pushed composed text onto the chat log — 2045's largest ratchet row and the one file its rule had no route for, because `UiError`'s doc claimed the 1.12 formats use at most one `%s` and one `%d` and the type was built to match, while `ERR_GUILD_PROMOTE_SSS` is "%s has promoted %s to %s." — a wrong sentence in a doc had become a wrong type, and the wrong type had made a file's worth of drift structurally necessary; `CGGameUI::DisplayError` (`0x496720`) is cdecl varargs — `add esp,4` bare, `add esp,8` with one string, `SMSG_GUILD_INVITE` pushing two with the stack check exact at `0x30 + 0x60 = 0x90`, and `SMSG_GUILD_EVENT`'s emitter tail `0x5e745f` passing one, two or three off `strCount` — so `UiError` carries an ordered heterogeneous `Vec<FillArg>` (`S(String) | D(i64)`, since `ERR_USE_LOCKED_WITH_SPELL_KNOWN_SI` is String-then-Integer) behind four constructors naming the reference's call shapes (`key`, `s`, `strings`, `args`), twenty sites migrated; what the catalog knew and composed text could not carry: `ERR_GUILD_NAME_INVALID`/`ERR_GUILD_NAME_EXISTS_S` are kind 2 — the red line — and went to chat, `ERR_GUILD_CREATE_S`/`ERR_INVITED_TO_GUILD_SS` carry the LEVELUP cue and `ERR_GUILD_PERMISSIONS` (type_tag `0x3e`) is one the reference speaks (1815), all three silent; the default arm was missing outright — `0x5e745a` pushes `0x69` = `ERR_GUILD_INTERNAL` for `0x09` and every id past `0x0d`, catalog row 105 the whole time, so `TABARD_CHANGE` had been silent under a comment calling the key unsettled; a short `strCount` is passed SHORT (0 and ≥ 4 pass zero strings) so a starved specifier reads "…promoted Furor to %s.", not "…to ."; `/ginfo` deliberately stays off the queue — `GUILD_NAME_TEMPLATE`/`GUILD_INFO_TEMPLATE` are not catalog rows, `0x5e6fb0` resolves each token through the VM and emits chat itself, and a non-row key through `Shown::keyed` would turn a chat line red — so it waits on the guild feed and goes out unkeyed, the enUS month-day-year swap with it; the `_S` arity is asserted from the key names, 22 of 22; the ratchet row is deleted, 208 over 31 files remain (2054)
- a unit's blob shadow asks the UNIT for its alpha: the lane gathered live `RenderFade`s off a unit's part entities and read "no part is fading" as 1.0 — true of a settled unit and exactly backwards for arrival, where a streamed unit holds `PendingAppearFade` with no `RenderFade` anywhere until the world is shown, so its parts were deliberately invisible while the shadow drew fully opaque; measured on a probe login: alpha 1.000 from t 0.61 to the arm at t 3.51, then 0, then the ramp — 2.9 s of dark oval under no creature, the symptom `model_render_alpha`'s own "pending ⇒ 0" law (0827) already named for emitters and ribbons; `model_fade::UnitRenderAlpha` is the one door, composed from the ROOT where `UnitAppearFade` tells pending from settled, published as one question rather than its three inputs so a caller cannot reassemble the mistake, and `DespawnFade::armed()` retires the `-1.0` sentinel from every caller; the world API wall goes 177 → 178; and wow-re settled what the shadow's alpha IS, four questions VERIFIED: it reads `CM2Model+0x180` through `0x710ca0`, not `+0x19c`; that slot carries the appear and despawn ramps, the self fade AND the CharProc-14 aura — `0x60d180` drives the same `StartAlphaFade` the appear fade does, so a stealthed unit's shadow now dims with its body — and not the per-batch M2 colour/weight tracks; and the reference draws nothing under a not-yet-shown object either (`+0x180 = 1.0 · 0.0`, written 30 bytes before the read), so this is fidelity, not taste; three module-doc claims corrected against it: the dead `0x683ea5` gate, the registration handoff (GameObjects and DynamicObjects never cast; units, players and corpses always do), and 1277's `InheritedVisibility` gate, which the reference lacks and stays ours on purpose (2055)
- the mail stationery list was empty for the whole session, and every send with it: the five `dropping command — not connected` lines at login, once named, were `ItemQuery` for all five `Stationery.dbc` rows — `feed_mail` carried no run condition and asked from the login screen before the socket existed, the asks evaporated in the io thread, and `Items::template` had already latched all five in its ask-once `pending` set, cleared on disconnect and never on connect, so they were never re-asked and the stock `SendMailFrame_Reset`'s row-1 selection left every send silently unsent; the list now waits for a self player, and `release_ask_once_latches_on_enter` clears the pending sets at world enter, the counterpart the disconnect teardown never had — a redundant re-ask costs one packet, a wrong latch costs a dead feature nobody can see is dead; and three more log lines answer for themselves: a control character is answered before the shape — a newline is a line break the markup parser consumes, not a missing glyph — so the warn stays meaningful for a glyph a face genuinely lacks; the blend-check's one-frame run is the specialization tick catching up with a material swap, a real login showing four entries clear 26 ms later, so the warn waits for a run to SURVIVE the tick and a one-frame run is recorded at debug; and the audio starve line carries the wall span it covered beside the time it counted and the position it saw advance, because a 1.0 s window still closed 0.756 s of wall after the stream started on `Time<Real>` — if they disagree the caller's clock is still wrong, if they agree the starvation is real

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:41:57 +07:00
SamandClaude Fable 5.1 f1a1b68c1a strings become keys, hidden tiles park, and the water stops breathing
- a reference string is a key, never a Rust literal: display text is a key into `GlobalStrings.lua` in-game or `GlueStrings.lua` on the glue screens, resolved at runtime from the player's own install, and a literal is legitimate only as a fallback beside a lookup — the reference's own `UNKNOWNOBJECT` shape — because a re-typed sentence costs the wording nobody diffed, the surface and the voice a composed string has no catalog record to read off (1770, 1815), and localization; the tripwire in `reference_strings.rs` is the check a comment could never be: it walks the workspace, normalises both sides and fails on any literal matching a shipped value — 273 re-typed literals over 34 files, after calibration threw out three false-positive classes (a path-mangling bug, the 85 fallbacks beside a real lookup, and `#[cfg(test)]` modules whose fake tables match by construction — the first run's 378 would have enshrined ~50 phantom sites); `ALLOWED` is a per-file ratchet whose counts may only go down and whose stale rows fail too, regenerated from a clean tree at 253 over 33 files — the first baseline was captured mid-edit and the land gates failed on a row claiming drift in a file that had none — then 252 over 32 as `ui_trade`'s row retired behind a neighbouring conversion to `ERR_TRADE_BLOCKED_S` (2042); stated in its own doc: it cannot find an INVENTED sentence, which matches nothing; and `benilla_ui::strings::fill` is the one template filler — ordered `%s`/`%d`, positional `%N$s`, `%%` collapsing, a starved specifier copied through visibly so a mis-modelled template looks wrong rather than plausible — replacing eight hand-rolled copies that disagreed, one of them `str::replace` filling every `%s` with the same argument, latent only because no message on that queue carried two yet (2045)
- two families convert, and both assert the identifier, not the sentence: the character-create result line resolves its key off the player's chain with the literal as fallback, and three bugs the 23-arm literal table had hidden come out — `0x36` re-typed without its second sentence ("Please try again during off peak hours."), `0x50` invented (there is no `CHAR_NAME_CONSECUTIVE_SPACES` in 1.12, so it takes the default arm), and `0x4B` naming `CHAR_CREATE_NAME_IN_USE` where the code means `CHAR_NAME_RESERVED` — both read "That name is unavailable" in enUS, so no assertion on displayed text could ever have caught it, and a sentinel test that rewrites `CHAR_NAME_RESERVED` in a copy of the real table proves `0x4B` moves while `0x31` does not; the ratchet's first row retires, 21 → 0; and `SMSG_PARTY_COMMAND_RESULT` returns the catalog message the reference's own jump table names — handler `0x5e68b0`, `dec eax; cmp eax,7; ja <silent>; jmp [4*eax+0x5e6a14]`, results 1–8 onto `ERR_BAD_PLAYER_NAME_S` … `ERR_IGNORING_YOU_S` plus `ERR_INVITE_PLAYER_S` (`0x3a`) and `ERR_LEFT_GROUP_YOU` (`0x42`) on result 0 — so text, surface and sound come off one row: `ERR_PLAYER_WRONG_FACTION` (`0xff`) is the red line where the other nine are chat, and three inputs the reference is DELIBERATELY silent on (result ≥ 9, result 0 outside op {0, 2}, an invite with an empty name) stop printing an invented "Party command failed (N)."; tests assert the message id, since no English distinguishes a right key from a wrong one here, and `ui_party` goes 22 → 12 (2045)
- a hidden `<Model>` tile is parked: every orthographic pane shares the atlas's one camera, deliberately kept active whenever any cell is packed because it is the camera that clears the sheet, so `booth_frozen` — exact for every booth ever built, one camera one scene — answered "drawn" for every pane that had left the paint list, and a hidden autocast-shine pane's four spline emitters kept integrating and pushing quads for the whole 600-frame linger at a cell the packer had since given away; `sync_tiles` gains a sixth stage that parks any live tile with no cell this frame — `AnimParked` on the root, which holds the pose evaluator, the compose, the palette write and the global-sequence bone writes, and `ParticleEmitter::set_frozen` on every emitter — with `scene_frozen` stating the freeze law in one place and the park verdict pinned to the composite by a test, so a repack or a full atlas cannot freeze a visible pane's cloud; the bone global sequences move onto the pane's clock, where the material rows have been since 2019: the kernel's Phase B cursor is `(sceneClock − attachSnapshot) % duration` off the scene that owns the instance (`[model+0x2c]`, the snapshot at `CM2Model+0x68` stored at `0x70eae1`), and a `<Model>` widget owns a private `CM2Scene` at `CSimpleModel+0x314` that only its own advance at `0x76d863` moves — so the ping's spinner holds while hidden and resumes where it stopped, with no accumulator of ours; measured with a cooldown pane holding the atlas alive: twelve hidden shine panes cost 54 effect items / 63 transparent draws before, 34 / 43 after, the pool held rather than drained by design; and the particle census's `gated=` column, which printed `fade.is_some()` and read backwards as the gate verdict, is renamed `has_fade=` (2046)
- a doodad is not an entity: `[def+0xa4]` has exactly four writers image-wide — ctor `0x6a7d73` (0.0), WMO doodad-set commit `0x695bb3` (1.0), ADT MDDF commit `0x6b01bb` (1.0), MCSH refresh `0x698cb4` (0.5) — and the 2.5-producer `0x69e280` and the ramp `0x69e770` are reachable only through the entity-attached `[obj+0xe0]` path, which contradicted the §6 both lit-ground sites cited and a capture with gains up to 2.5, so the premise was marked contested at both sites first (the shader's own note told a future session to cut `min(I, 1)` and `LIT_T` together on its strength); wow-re then settled it — two C++ classes inline the same base and share the offset: `CMapDoodadDef` (vtable `0x810a74`, `"WDOODADDEF"`, size `0x198`, serving ADT MDDF and WMO MODD) and the WENTITY light node (vtable `0x810810`, size `0x108`, off `[obj+0xe0]`) that `0x69e770` ramps at 3.3333/s; on the doodad class `+0xf8`, the field the ramp reads its target from at `0x69e894`, is `m[2][3]` of the world matrix — it has no target field — and six trace frames put 111 MDDF placements over 356 draws at gains only in {0.5, 1.0}, none changing, against an entity walking out of shadow at 0.5 / 0.81 / 1.09 / 1.3367 and rising; so ADT map doodads join the fixed-1.0 family with the exterior WMO props, `ShadeSel::Lit` is entity-only, the retained lane's Matte arm drops its prop requirement, and no pixel changes — the cap was faithful for trees all along and lifting it is now an entity-only look change for the director to call; `WORD_SHADE_LIT` is left as a documented dead bit (2050); and the far water's 1.25 s breath is flattened: the director's recording autocorrelates at lag 75 @ 60 fps = 1.250 s (r = +0.60), the 30-frame ocean flipbook at 24 fps, whose frames do not share a mean — alpha 55.985..57.628, DXT3's 4-bit alpha swinging 1.64 at mip 0, 3.72 at mip 5, 6.38 at mip 6 — so at distance the mipped sheet moves together, 0.8..1.84/255 on screen, fogged out at `farclip` 300 and visible at 777; wow-re proved the reference breathes too (`0x68abab` creates the flipbook with `applyGlobal = 1`, the upload hands the driver `Levels = 9`, LOD bias 0, the loop period `[0x8102b8]` 1.25 s over 30 frames), so ours was faithful and it is flattened anyway by the director's eye: each frame's DC equalised per level and per channel at texture-build time as an exact integer redistribution in ±1 steps on a coprime stride (1.64 / 3.72 / 6.38 / 0.555 → 0.000), water and ocean only since magma and slime take the sheet as their body colour, `WOW_LIQUID_DC=raw` restoring the authored art for the A/B (2051)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:41:02 +07:00
SamandClaude Fable 5 dddf16e963 the auction house opens, the Honor tab lands, and a day of fixes
- the auction house end to end: right-clicking an auctioneer opens Browse/Bids/Auctions on the server's reply — search, the category tree from your own DBCs, the reference's 50-a-page pager, multi-key sorting (directions corrected at the bytes), bid/buyout, the sell slot with deposit and duration, cancel with its fee popup; prices sit on the reference's own money-frame kit in lined-up columns, the twenty auction messages split refusals-red / outcomes-to-chat, and a won or sold auction reads as a proper mail receipt — the window's Lua re-worded in our own terms after the overlap ratchet caught the first cut leaning on the reference's text
- the PvP honor system whole: the Honor tab and the inspect page over a real wire model, with current rank and highest-ever rank correctly two different fields
- a day of reported fixes: WASD survives a /logout re-entry, addon settings save on a window close and Cmd+Q (exactly once — a mac close could fire the tail twice), clams open again, random-enchant items name themselves on every surface, the loot window's rows hold their seats with legible counts, idle players stop sinking through building floors, the shooter re-pulls between Auto Shots, portraits loosen ~19% to the client's own aspect-1.0 matrix, and big creatures thump the camera off the CameraShakes table
- character select clicks like 1.12 — captions whiten on hover, buttons fire on release, Humans face straight ahead off the stage bone's real yaw, the facing is per-character, and hunter/warlock pets stand beside you at their family-ramp size with their own emitters (the imp's flames), the Voidwalker's sequence-hidden shackles culled
- the smaller player-facing set: nameplate on/off rows in Options with the CanAttack category gate (emissaries stop plating), the bonus bar slides up with igBonusBarOpen, the camera's framing height glides on a shapeshift, the open letter evicts the centre seat, the far-band fog hull stops printing a pale horizon stripe at low view distance, the guide arrow is gold with per-source rim art, error text fades through its shadow too, and /convertraid converts the party
- README: the auction house and PvP honor move out of "not built yet"

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 19:04:27 +07:00
SamandClaude Fable 5 9662553528 addons come alive: Bagnon draws, hovers and clicks
- Lua 5.0's generic-for and nested long strings are restored in a forked VM — 60 corpus addons stopped dying on one deleted opcode
- a day of demand-driven carving takes the 218-addon corpus from 24 addons surviving a session start to 114, with 144 loading without a single error
- Bagnon draws a real bag and answers real gestures — hover tooltip, pickup, use, drag — because our frames wear the reference's names, and the reference's own ContainerFrame.lua runs off the player's install instead of being transcribed
- the physics broad phase leaves: nothing in the client reads a contact, and it was measured at up to 41% of main-thread samples in real sessions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:19:08 +07:00
SamandClaude Fable 5 bdef9cd790 benilla-world: the engine becomes its own crate
- the world engine splits from the game into a benilla-world crate — 112 files, and the wall is ratcheted to zero names of the game inside the engine
- the engine's public doorway is counted and gated at 32 items, and the render materials, shaders and asset cache move down to benilla-assets
- the game's files leave the engine's asset root — game:// is its own space — and a spike binary boots the engine with no game attached
- weather rides along: snowfall emission is denominated in the reference's frame cap, and a long teleport retires the stranded precipitation field instead of leaving it behind

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:13:44 +07:00
SamandClaude Fable 5 3b6975fdef the pet gets its frame, flooded rooms fill, and macros really fire
- the pet gets its own unit frame beside yours, and an Attack order no longer latches as the standing command
- five rooms that always read bone dry hold their water now, the Deeprun Tram's flooded stretches among them
- macros fire through the client's own chat door, and the macro window fits its tabs and scrolls its icon chooser
- the options window grows a Nameplates page and its first dropdown, and the name-display switches apply live in the world

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 19:03:43 +07:00