1116efbb683259727ddd44251d86256ec8d3a8c3
26
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
eebbcc050f |
NTFS load order, a title-sorted AddOns list, and tooltips at the cursor
- addons load in the order NTFS lists a folder, case-insensitive with an underscore before the letters, the order the vanilla corpus was written against and the one that decides which addon provides a shared library (2178) - GetNumAddOns and the index form of every AddOn verb walk a title-sorted list that hides the Blizzard addons the server says to hide, and the character-select AddOns list is in title order (2175) - SetPoint and SetAllPoints raise the reference's own errors for a frame name that does not exist or an omitted relative frame instead of quietly anchoring to the parent, and an explicit nil means the screen root (2176) - a tooltip owned with ANCHOR_CURSOR follows the mouse, which pfUI's tooltip, xpbar and chat modules, pfQuest's browser and TipBuddy ask for; an omitted or unknown anchor is ANCHOR_LEFT (2176) - a $parent name handed to CreateFrame, CreateTexture or CreateFontString expands as it does in XML, so FonzAppraiser's thirty dropdowns and pfQuest's search icon get the names they asked for (2176) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
c343d236e9 |
cooldowns survive a relog, the MOTD prints, and lost channels come back
- a cooldown still running when you log out and back in keeps sweeping instead of vanishing, and another character no longer inherits the previous session's cooldowns (2116) - the server's Welcome and Patch 1.12 lines print at login in system yellow, and a character whose General, Trade and LocalDefense had gone silent for good gets them back at the next login (2119, 2120) - the stock Interface Options window loads hidden beside ours, so pfUI's options skin finds the real thing; the Auto-join Guild Recruitment Channel row has a real store, and a /reload no longer trips on its camera dropdowns (2115) - the combat log words a power gain on the display scale, so MikScrollingBattleText shows +1 Rage where it showed +10; a 1.12 predicate answers 1 or nil rather than a Lua boolean, the test ColorPickerPlus makes (2117, 2118) - a Lua error quotes a program element with a backquote the way 5.0 does, so AceLibrary's argument checks report their caller instead of raising inside the error path (2122) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
1783660496 |
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> |
||
|
|
d569261521 |
the loading tip draws, its backdrop holds, and indoor trails go warm
- the loading screen's tip of the day is on screen: it was picked and saved but never painted, and once painted it was wiped by the login round trip (2083, 2081) - the backdrop no longer switches mid-load — logging back in to an expired dungeon held its art for a second and then flipped to the continent's; the art latches at the raise, keyed on the texture, not the map id (2081, 2087) - a weapon trail indoors takes the room's warm light instead of the sky's ambient, the wearer's own light being the reference's term (2086) - the glue dialog moves out of the login screen into the shared glue layer, so the select screen's refusals and the login's errors are one widget (2084) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
ecbdf2f716 |
the leader crown updates, the trail darkens, and four dead options work
- the party-leader crown on your target's portrait only ever appeared or vanished when you re-targeted them: `PLAYER_FLAGS_CHANGED` was one of eighteen events the migrated stock windows register that nothing here produced, and the census row naming it said "TargetFrame.lua — the AFK/DND badge", which sent the first look at the wrong window — 1.12 draws no AFK/DND badge on any unit frame and build 5875 has no `UnitIsAFK`/`UnitIsDND` at all (ours are a deliberate invention, 2043; the `<AFK>`/`<DND>` the era shows is the chat line's `arg6`), and the event's sole FrameXML consumer is `TargetFrame.lua:88-95` re-running `UnitIsPartyLeader("target")`, whose descriptor leg is bit `0x1` of this very dword; the handler `0x5ee990` XOR-diffs old against new at `0x5ee9b8` and fires 407 at `0x5eea3d` with no bit test in between and ABOVE the local-player GUID gate at `0x5eea93`, once per unit token naming that GUID through `0x515e50`'s fan-out (`arg1` = the token, no `arg2`, and a zero token count skips the fan-out so a stranger nobody targets signals nothing — free here, since `fire_transitions` only ever runs per token); so the trigger is the RAW dword, `UnitState::player_flags`, the `flags`/`UNIT_FLAGS` pattern one field over, because firing off the two bits the struct decodes would under-announce AFK, DND, GM, PvP-desired, helm, cloak and both play-time bits, and `prev.is_some()` keeps a unit's first snapshot silent as its CREATE runs no notify pass; reading the whole handler found an over-fire that was inherited, not invented: benilla fired `PLAYER_UPDATE_RESTING` on ANY flags delta because wow-re's rested-XP note said in as many words that `0x5eeaf2` fires "on every flags change, not only the resting bit", and the bytes say otherwise — `0x5eead0` `f6 45 fc 20 / 74 26` is `test byte [ebp-4],0x20 ; je 0x5eeafc` and the fire sits inside that arm — so every helm toggle, leadership pass and AFK flip had been announcing a resting change to the whole UI; narrowed to the resting bit's own edge, and the same handler's `0x5eeb65 test ah,0x30` arm becomes `PLAYTIME_CHANGED`, whose consumer `PlayerFrame_UpdatePlaytime` already had all three verbs it reads; the claim is corrected in wow-re by a four-derivation round that confirmed all three arms plus the CREATE question; three tests pin it — the producer with a control on `0x400`, a bit no field decodes; the over-fire regression on the real feed, where a helm bit moving fires neither self event; and the crown itself on the stock `TargetFrame.lua`, leadership passing to a held target lighting it with no re-target and the same event for `arg1 = "player"` not — a fixture that first failed on its opening assertion because `UnitIsPartyLeader`'s second leg is a raw GUID compare with no zero guard, so a default `guid: 0` equalled the zeroed leader of an empty group (verified 1.12: it is why `UnitIsPartyLeader(nil)` answers 1 while solo); the unproduced-events census goes 18 → 16 per window, 31 → 29 whole (2078)
- the weapon swing trail darkens with the scene instead of burning at its authored colour: 2076 shipped it unlit and said why — the draw callback turns lighting ON (EGxRs `0x0e = 1` at `0x6c6847`) over vertex format 7, position + packed colour with no normal, and the state bit alone does not say what fixed-function produces from that; the bytes now do: `0x5a1e30` sets DIFFUSE- and AMBIENTMATERIALSOURCE both to `D3DMCS_COLOR1` off the format's diffuse bit, so the vertex colour IS the material; the normal is explicitly disabled rather than stale (`0x592a60` replaces the format mask instead of OR-ing it, `0x59c100` issues `glDisableClientState(GL_NORMAL_ARRAY)` when the bit is clear, and D3D's format-7 descriptor carries −1 in the normal column), so there is no N·L and the diffuse half contributes nothing; and the global `D3DRS_AMBIENT` (EGxRs id 6) has zero writers image-wide and defaults to 0, an earned zero — leaving `out = (Σ enabled lights' ambient) × authoredColour + inherited emissive`, which is why this does not take the lit emitters' `clamp(ambient + diffuse·max(N·L, 0))` term, a sun the reference does not have; `weapon_trail.rs` multiplies the trail's RGB by `WowLighting.ambient`, folded on the CPU into the vertex colours because a per-draw constant rides the vertex stream on this lane, drawing as `EffectLighting::None` and at the authored colour until the first lighting resolve; two named approximations of the class the fishing line already carries — the scene ambient rather than a per-model light collector, so an interior trail takes the exterior ambient, and no emissive, since `EMISSIVEMATERIALSOURCE` is set nowhere and reproducing it would mean inventing a constant; the other half of 2076's open list was a deviation benilla does not have: it claimed the combat-over-combat fast path (`0x5fe43c` returning at `0x5fe48b`, before the trail latch's only read at `0x5fe48e`) was unmodelled and cost trails starting one play early — both wrong, because `0x60d835` arms a one-slot field with a plain `mov` so a second proc OVERWRITES the first and firing on every request would draw superseded arms with superseded colours on 23 of the 34 type-8 kits, and because the driver's request loop already fast-paths it (0406, `select::is_combat_anim` = `0x5fcc10`'s byte-decoded set) and `continue`s without playing, so the edge never rises; a negative asserted about our own code from having written it rather than looked, shipped to wow-re where it drove a build-it verdict for something already built — now pinned by a test that names both mechanisms; and `benilla-extract charprocs` prints `CharParamOne` beside every trail row: wow-re's note justified "not read by this arm" with "every shipped type-8 row carries 20.0 there", and over the 34 kits the column takes six values (22 at 20, six at 15, two at 25, two at 0, one at 17, one at 22 — Sinister Strike's kit 399 is a 15); nothing here reads it either way, but a census that hides the one column a note asserts about cannot catch the note being wrong, and wow-re's re-measure agreed value for value and found the column live at four sites in `0x60d7c0` for other proc types (2079)
- four options that ticked and did nothing — Show Tutorials, Tip of the Day, Profanity Filter, Disable Spam Filter — get their backing, and two of the reasons they were dead were wrong: none of the four was a row in our window at all; they sat in the transcribed `UIOptionsFrameCheckButtons` declaration table beside thirteen more whose features benilla lacks, and these four are exactly the ones where the feature existed and only the setting was missing; `SHOW_TUTORIALS` is a bare `{ index = 28 }`, which an earlier census read as "an index-only entry with no store at all" — wrong, the store is the three special-case arms `UIOptionsFrame_Load` (l.239), `_Save` (l.318) and `_SetDefaults` (l.645) keep for the key, `TutorialsEnabled()` reading the acknowledged bank and `ResetTutorials()`/`ClearTutorials()` writing it, verbs benilla has had since 1976, so the row was one `func` pair away, and the reference's `~=` guard comes with it because without it a Defaults click on a page where tutorials are already on would `ResetTutorials()` and re-arm every popup the player has dismissed; that census moves out of prose into a test checked both directions, the posture the CVar defaults already hold — a name the reference's table carries that nothing registers and no listed blocker fails, and a listed name that has since been registered fails too, twelve rows listed today with the feature each waits on; the two chat filters are PCRE over shipped DBCs, not word lists: `ChatProfanity.dbc` (2289 rows off `patch.MPQ` — `dbc.MPQ`'s copy is 1512) feeds the masker `0x4a1a60` and `SpamMessages.dbc` (28 gold-seller URL patterns; `dbc.MPQ` has none) feeds the predicate `0x4a1ca0`; Blizzard PATCHED PCRE's escape table (`.rdata 0x812330`) so `\<`/`\>` alias onto `\b`, and an unpatched engine makes them literals and silently kills the whole ASCII half of the profanity list while looking correct; CASELESS folds ASCII only, so pattern and subject are both ASCII-lowercased and matched case-sensitively rather than through `(?i)`, which would match `É` where the reference matches nothing; the masker is in place and length-preserving, one token per byte cycling `!@#$%^&*` from a process-global index never reset for the session, patterns in list order not text order — wow-re's oracle transcript reproduced against the real list, "you are a twat and a faggot" → "you are a &*!@ and a !@#$%^", the later word taking the earlier characters because its row (507) precedes the other's (530); two corrections to what this tree believed: `spamFilter` is armed for every chat type EXCEPT `CHAT_MSG_FILTERED` (`0x49aad2`'s `jne` skips the disarm, which the older note read backwards, so our comments claimed the filter bit only on the one type it is exempt from), and `profanityFilter` gates INSIDE the masker at `0x4a1a66`, covering all thirteen call sites rather than the guild MOTD alone; `CHAT_MSG_FILTERED` (`0x5B`) comes with them — it was on the "never `SMSG_MESSAGECHAT` wire" list and is the server's "your message was filtered" notice `ChatFrame.lua` renders at l.1405, dropped with a warning before and delivered now; the chokepoint runs spam, then mask, then fire, in the reference's order, since a dropped line must not consume mask indices; named rather than faked: the other twelve masker sites (mail, guild, item text, the send path) and their `dl = 1` memo, and the whisper leg's `CMSG_CHAT_FILTERED` report whose body is a wow-re DEFERRED with no vmangos handler; each row sits in 1.12's own seat — Profanity Filter closes the Basic Options Display column, Show Tutorials and Show Loading Screen Tips open the Help box, Disable Spam Filter is the last link of the Advanced chat chain and value-inverted, checked ⇔ `spamFilter == "0"` (2077)
- the loading screen shows the tip of the day: `GameTips.dbc` is 74 records in the 5875 enGB chain, off `patch.MPQ`, ids unsorted (the file opens on 396 and closes on 412), so the index the client persists is a record index in load order, never an id; `gameTip` holds the NEXT index, not the one on screen — the client reads it, shows that row and stores `read + 1`, so on-disk values run 1..=74 and never 0, and the reference's own `Config.wtf` reading `SET gameTip "34"` while showing row 33 is exactly what an off-by-one anchors on; the wrap is a clamp, not a modulo (`if (i < 0 || i >= count) i = 0` at `0x46b682`), the walk is sequential, and both guards — `showGameTips` off and the roster's level-zero flag — jump past the `CVar::Set` as well as the draw, so turning tips off freezes the cursor rather than burning through the table invisibly; it draws only on the glue→world transition, because the setter `0x406630` has exactly one caller inside `EnterWorld` and neither `SMSG_TRANSFER_PENDING` arm reaches it, so the pick hangs off the loading screen's world-entry raise and a portal or worldport screen carries no tip; placement is the reference's: `s = 515/(a·1024)` with `a` = aspect × 0.75 (not W/H, which inflates the wrap width by a third at 16:9, and not the FrameXML-length idiom, which is off by 1.25 at 4:3), the block at `(0.5 − s·0.5, 0.1)`, left-justified, body `0xd7c8c8c8` with an opaque black shadow at `(+0.001, −0.001)`, laid out once per raise between the backdrop and the progress bar; the one term deliberately not applied is `yoff = (1 − a)·0.5`, which wow-re left unreconciled (`0x406a60` already applies the letterbox as a viewport and `yoff` is the same term added again inside it, a possible double count on a display narrower than 4:3) — benilla's loading screen is a 4:3 content box by construction and at 4:3 the reference's own `yoff` is zero, so the tip sits at the reference's layout on the one aspect where the ambiguity cannot bite; the `|cffffd100Tip:|r ` prefix and the trailing CR-LF are in the data and the escapes are interpreted, so the gold "Tip:" is markup and the line is built through the markup tokenizer; the cursor persists through the VM's own CVar table (`write_host_cvar`), since the config file is composed from that table and a host write stopping at the knob would reset it every launch — a live run reads `74 game tips` → `tip 0 of 74` → `tip 1 of 74` with `gameTip = "2"` left in the config, and an earlier live run is what caught a `GameTipSetting` the test harness initialised and the real app did not, a panic no unit test could see (2077)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
||
|
|
cf3189e422 |
a refused login says why, the deep ocean darkens, and the blade trails
- a refused character login left the player on a loading screen with no world behind it and no way off — reported as sitting at character select with no message, and the symptom was worse than the report: `SMSG_CHARACTER_LOGIN_FAILED` (0x41) had a name and no parse arm, so the client never heard it, and it was not at character select when the refusal landed, because the IO thread sends `CMSG_PLAYER_LOGIN` and announces the connection in the same breath (the entry's head start, 0777), so `ClientState::InWorld` was already set and the loading cover was up waiting for a snap the server had just declined to send; the wire half parses the opcode into `ServerPacket::CharacterLoginFailed` / `SessionEvent::CharacterLoginFailed` and carries the byte RAW, since vmangos sends a bare `1` and mangos-classic its own `CharLoginFailReasons`, and a wire layer that interpreted it would be picking a server; the refusal is an entry being REVOKED, not a failure to enter — `Cycle::LoginRefused` runs the same `SessionEnd::LoggedOut` teardown a `/logout` does, the cover cuts to black, `back_on_login_refused` takes the select screen back with the relist's roster underneath, and clearing `pending_pick` is load-bearing: it is 0065's reconnect memory and the relist is auto-answered with it, so keeping it would re-enter the refused character forever from behind a black screen; the byte is a 1-BASED REASON INDEX, not a status code — wow-re read `OnCharLoginResult` (`0x5aad70`): `dec eax; cmp eax,5; ja` into a six-entry jump table mapping 1..=6 onto `CHAR_LOGIN_*` 0x3e/0x3f/0x40/0x42/0x43/0x44 and SKIPPING 0x41 `CHAR_LOGIN_FAILED`, reachable only as the default, so the tempting `0x3d + byte` is right for four rows and wrong for the rest; `char_login_refusal_text` is that table, resolved against the player's own `GlueStrings.lua` and asserted against it (vmangos's `1` therefore reads "World server is down" — the server's dialect, and what the reference would say); the select screen runs the shared `login::drive_dialog` rather than a second copy of the reference's one `GlueDialog`, which found two holes — `to_select_on_roster` closed ANY dialog on an arriving roster while the refusal's own relist produces one a second later, so it would have wiped the message (the original bug one layer up; now scoped to status/queued), and `preflight`'s descriptor latch outlived the session that armed it, announcing "the avatar never streamed in" from character select, now disarmed on any disconnect; a live probe against vmangos pins the three facts this rests on — the byte arrives as 0x01, the roster still serves after it, a retry enters the world on the same socket — and `WOW_REFUSE_LOGIN=1` refuses the first pick of a run by sending a non-player guid, the one guard that needs no server state, so a path no real account can stage by hand is walkable on demand (2071)
- the ocean's depth ramp was never a placeholder, and its deep row is darker than we drew it: four files called the ADT ocean's `V = byte/255` a stand-in "pending its own RE" — the director's report had the sea fading to deep colour about 6x too slowly — on the claim that ocean takes a non-LUT UV path via `0x68d890`, which is the MAGMA vert-fill, as `benilla_adt::LiquidVertex::texcoords` had said by that same address all along; the ocean fill is `0x68d690` and it reads a depth LUT exactly as the river fill `0x68d790` does, `0x68c4c0` building both side by side (`0xc81768 = min(d/42, 1)` river, `0xc7fcd8 = min(d/255, 1)` ocean), the two fills differing in exactly 3 of 246 bytes — one `fld` displacement — so there is no second ocean-only depth term, and wow-re's cross-check found `/255` bit-exact for all 256 entries; the divisors differ because the authored bytes do — a census over every MCLQ block on Azeroth + Kalimdor, each wet vertex's depth byte against its own geometric depth, reads a river at 8.96 byte/yd (reproducing the independently-measured ≈8.5, the control) and the sea at 1.72, 5.2x gentler, so `/255` saturates at ~148 yd and `/42` at ~4.7 yd, the same ramp in yards and not a 6x slower one, while 83.4% of ocean vertices carry byte 255 outright, so the open sea is authored fully deep and the ramp is only a shore band; a test pins both divisors against the raw MCLQ bytes (swapping the ocean onto `/42` fails it), `examples/liquid_depth_census` is the measurement with named shore-band pins, `WOW_OCEAN_DEPTH_DIV` is the A/B, and the alpha-ramp deferral closes with it — `0xc7fbc0`'s `1.6·(i/63)^8` is the sky glare falloff, bound at `0x685257` inside a branch proven dead, so the linear `127 + 2·row` alpha shipped was right; and the finding nobody asked for: `0x68a830` fills the 8×64 swatch as an exact integer accumulator in BYTE space, `row(i) = c0 + floor(i·(c1−c0)/64)`, so the ramp never reaches the deep endpoint (row 63 is ~98.4% of the way), and an ocean-only tail `[0x68a9c0, 0x68aa36)` — gated on last row AND selector 0, river being selector 1 — runs RGB→HSV, `V ×= 0.9` (`0x3f666666` at `0x8102ec`), HSV→RGB and forces that row's alpha to 255, which reduces to `floor(0.9·byte)` per channel (99.03% bit-exact over all 2^24 triples, max 1/255); byte 255 ⇒ `V = 1.0` ⇒ row 63 exactly, so the darkened row IS the open sea, and benilla had rendered deep ocean ~10% too bright over most of the water in the world — an error that survived every regression diff because no capture scenario holds any ocean; `liquid.wgsl`'s `swatch_row` / `swatch_at` port the accumulator, the LINEAR/LINEAR CLAMP sampling (so the darkening ramps in across the final 1/64 of V rather than stepping) and the ocean-only tail, on the ADT lane only — the WMO arms keep the plain lerp, their opacity being a different 256-entry ramp — pinned by a test against wow-re's published byte vectors (row 62 = 12,36,41; row 63 = 11,35,40, green one ABOVE the deep endpoint; after the tail 9,31,36) that also asserts the WGSL still carries the formula; rivers move at most 7/255 from the row quantization (2069, 2074)
- the weapon swing trail — `SpellVisualKit` CharProc type 8, which benilla had dropped on the floor with the rest of the dispatcher's unmodelled cases: 34 of 1772 kits carry it, 27 are reached from a live `Spell.dbc` chain, and 16 of those name NO emitter slot at all, so the ribbon is their entire visual — kit 324 alone reaches 218 spells (Heroic Strike, Overpower, Mortal Strike, Bloodthirst, Rend) and rendered nothing beyond the swing animation, with 506 (Revenge, Riposte, Mongoose Bite), 557 (Sunder Armor), 646 (Hamstring, Wing Clip), 399 (Sinister Strike), 822 (Garrote) and 823 (Feint, Disengage) in the same shape; the proc draws nothing — `0x60d80a` latches a packed colour and a duration on the unit, and the unit's next `PlayAnimation` (`0x5fe48e`, the fields' only reader) hands both to its two weapon-hand trail objects and clears the duration, so an arm fires exactly once and a second proc on a live trail RESETS its ring rather than stacking; each object rings 128 samples of its weapon model's `$WTB`/`$WTT` markers (455 of the 571 weapon models author both) and draws them as a strip that fades from opaque at the OLDEST end of the arc to ≈0 at the blade, the alpha being a clock and not just an opacity; `weapon_trail.rs` transcribes the arithmetic rather than approximating it — the truncating `_ftol` decode (not the small-int idiom every other integer param takes, which would yield colour 0 and no trail at all), `fadeStep`'s low-byte floor (`cmp al,1`, so a ≈768 ms hitch takes a floor of 1 instead of its own huge value), the alpha-derived termination, and the ring's writer/reader modulus mismatch (`& 127` stored, `% 127` read — shipped 5875 behaviour, reachable only by Whirlwind's 10 000 ms spin); the draw state was dispatched rather than guessed and the guess was wrong in three of seven places: the callback's six EGxRs writes say `0x07 = 2` (ordinary SRC_ALPHA / INV_SRC_ALPHA — additive is mode 3, the difference between a ribbon that whites out crossing itself and a coloured smear) and `0x10 = 0` (depth test OFF: the arc draws over the world and over the swinging character, never eaten by his own shoulder), so `EffectDrawSpec::no_depth_test` is a new pipeline-key bit on the shared effect lane with every other producer explicitly false, where the state had only existed as the global `WOW_PARTICLE_NODEPTH` debug lever; the third — lighting ON over a vertex format with no normal — is a verified state that is not yet a verified appearance and ships unlit as a named deviation with the question out; the consume edge is `AnimDriver::started_anim`, not the one-shot alone, because `0x5fe2f0` is the image's SINGLE animation entry point, locomotion included — a unit that simply starts running consumes the arm, which is how Charge (kit 44, 38 spells, anim id −1) trails at all; kit 324's 600 ms duration is on screen for 1250 ms at 60 Hz, since `fadeStep` is `trunc(dt·alpha/300)` and shrinks with the alpha it decays, twice wow-re's own estimate; the warm pass gets the depth-test-off pipeline as the one combination that ships (alpha, unlit, no rasterizer settle) rather than as another factor of the cross product — 36 more warm draws per camera for pipelines nothing asks for — so the first Heroic Strike does not compile one live; and `benilla-extract charprocs` grows the trail census that found the 34/27/16 split (2076)
- a zero injury column plays nothing: benilla walked down the wound-grunt family when the selected column was 0 — crushing to critical to ordinary — on a comment with no citation ("crushing rows are often 0 in data"), a true observation about the data and a false inference about the client; `0x623490` tests the selected id once (`0x6234e6 test ebx,ebx ; je 0x62350e`, the shared epilogue) and no fallback is even reachable — the row pointer dies at `0x6234e4`, so neither the bus pick `0x623b10` nor the play `0x458890` ever sees the row to re-read a column from, and the CGPlayer twin `0x62f880` is identical; it mattered because the class-2 chance roll had just landed beside it (2073): a crushing blow skips the roll (class 9 carries threshold 100) AND fell through to the ordinary kit, so it was louder than an ordinary hit on the same creature by two routes; `sound/combat.rs` now plays the selected column or nothing — crushing → column 5, critical → 4, ordinary → 3 — and a crushing blow on a creature with no crushing kit, which is most of the 406 rows (`Skeleton`, `Silithid`, `MountainGiant` among them), is silent, the reference's behaviour rather than an omission (2075)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
||
|
|
1d7252aec2 |
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> |
||
|
|
eacf343a55 |
the player picks the realm, and a harmful spell flinches its victim
- the player picks the realm: benilla connected to whichever realm the auth server listed first — not a policy but the absence of one — with the reference's Change Realm button drawn permanently disabled and the character screen's banner reading "Connecting…", a string 1.12 does not have, invented because the realm only ever arrived as a passenger on the character list (`CharacterSelect.lua` l.66 ends that ladder with `CharSelectRealmName:Hide()` — no name, no banner); `read_realm_list` had read the flags byte, the category byte and the realm id into `_` locals and stringified the population on arrival, and all four are load-bearing; the IO thread gains a third park — logon → which realm? → world handshake → which character — publishing `SessionEvent::RealmList` and blocking on `RealmRequest::{Enter, Refresh, Abandon}` exactly as at the other two, `Enter` carrying the realm's name and not its index since the list is re-requested every 5 s (`RealmList.lua`'s `REALM_LIST_REFRESH_TIME`) off a realmd socket `Logon` now keeps; Change Realm hands the logon forward (`HeldLogon`) so a different realm costs a world dial, not a re-authentication, and a deliberate ask arms `Realms::forced` for exactly one list, because the realm remembered from last time is precisely the realm you are standing on and asking to leave — without it the button re-entered the same realm and appeared to do nothing; the auto-answer ladder is a pure `auto_answer` — `WOW_REALM`, then the remembered `realmName` CVar (which `set_realm_name` had poked into its slot in place, never reaching `cvar_changes`, so the persisted CVar whose whole job is "the last realm connected to" was absent from disk forever; it writes through `set_from_engine` now), then an unattended run takes the first realm that is up, every arm requiring the realm to be up — and the screen is `RealmList.xml` rebuilt on the same 640×512 HelpFrame plate the AddOns list uses, eighteen rows at the authored 20 px pitch, four sortable columns; the Population word is a BAND, not the float: `realm_load_stats` (0x46e510) takes the mean and `sqrt(Σ(pop − mean)² / (n − 1)) · 0.6`, `realm_load_classify` (0x46ec60) reads the sentinels first (0x20 → Recommended, 0x40 → New, 0x80 → Full) then strict `pop < mean − stddev` → Low, `pop > mean + stddev` → High, else Medium — so the same realm reads High on a quiet list and Medium on a busy one, and a lone realm is banded against the `n ≤ 1` short circuit's literal 1.0, so a local vmangos realm at 0.06 reads Low, not Medium; then wow-re's cross-check of `RealmList.cpp [0x46e3bf, 0x46fa05)` confirmed 0x01 = invalid / 0x02 = offline and that the real client does auto-connect by `realmName` without raising the list (`0x5ab800` resolves the stored name against the fresh list, its success leg calling no `SetStatus`), and corrected four things the first cut shipped: the statistics are GLOBAL — `0x46e510` walks the flat all-categories array unconditionally (`0x46e5ee fadd [esi+0x12c]`), the per-category bucketing in the same loop a side effect and never a bound, so scoping them to the tab made the same realm read a different word per tab; Recommended/New/Full never travel as flag bits at all — the parser recognises magic populations by bit pattern and rewrites them, 600.0 → 0.0 + 0x20, 200.0 → 0.001 + 0x40, 400.0 → 8.0 + 0x80, and it has to happen in `read_realm_list` before anything averages, since one realm advertised Recommended arriving as 600.0 drags the global mean past every other row; pvp/rp is a `Cfg_Configs.dbc` join on the type dword at `[realm+0x04]`, and the shipped rows make types 3 and 5 PvP and 7 RP where the 1/6/8 match had all three Normal; and the default sort is `[characters, load, name, mode]`, all ascending, written once per process by `0x46e300` — a four-key fallthrough where "ascending" characters means higher count first, a column click being move-to-front (`0x46e9b0`) that keeps the column's direction unless it was already in front — with the reference's non-antisymmetric characters comparator (its tie-breaking `strcmp` result clobbered one instruction later at `0x46e846`) deliberately not reproduced; `RealmWizard`, the `REALM_IS_FULL` confirm and the category tab strip are deliberately not built, each for a stated reason (2056, 2062)
- a re-played effect REPLACES the same record at the same attach point: the director's report that the reference draws far fewer impact effects when many mobs beat on one warrior — and named the star burst on both hands on every crit — is not a limiter but a replace, recorded in wow-re for weeks: `CEffect::AddEffect` (0x61fdd0), the path every kit slot and the melee blood spurt take, opens with `0x6208e0(owner, rec, tag)`, which walks the owner's `+0xb4` list and destroys every node carrying the same `SpellVisualEffectName` record at the same attach tag (bounded `[0x6208e0, 0x62092c]`), skipping `tag == -1`; benilla replaced only persistent instances, on the narrower `(spell_id, class)` key of the spell-id reap `0x614150`, and pushed every one-shot unconditionally — a gap seen and scoped to one caller four times (0850, 0927, 1149, 1518) without being generalised — so five attackers landing swings on one body held five blood spurts at `(UnitBlood, 0xf)` where the reference holds one, linear in the attacker count on additively-blended emitters where the reference's is flat, and near-identical in a 1v1, which is why it survived; the sharpest case is Flurry (12966–12970), applied on every critical strike, whose visual 2759 hangs record 416 — `Spells\Strike_Impact_Chest.mdx`, the gold chest-impact flare reused at the hand tags 0x15/0x16 — on both hands via cast kit 3010 over a persistent `BloodLust_State_Hand` glow from state kit 3009, and every crit stacked another pair; `spell_fx::replace_same_slot` now runs per slot at every Begin for persistent and self-terminating instances alike, keyed on the RECORD id threaded through the fx lane (`FxSlot { tag, effect, path }`, the hardcoded map becoming name → (id, path)) and not the path, because two rows may name one .mdx and the reference lets those coexist, with both exclusions kept — the world plant and an already-decaying node — and four tests pinning the key's two halves and both; the other half of the report, that the burst is too large and too long, is a separate mechanism and is not touched — and the instrument that would have measured it was lying: `m2part` read a burst emitter's `peak_rate()` as its particle count, while the reference fires on the rising edge of `enabled != 0 && rate > 0` sampled on one clock, and `Strike_Impact_Chest.m2`'s gold flare steps its gate 1 → 0 on the very keyframe its rate steps 0 → 50, so the two are never true together and that emitter emits NOTHING; the tool printed "burst of ~50 particles, life 0.50s" and a diagnosis started 2.7× over on count and 1.7× over on duration off that one line; `EmitTiming::first_burst` walks the slot's keyed span at 60 Hz and returns the first instant the predicate holds or `None`, the dump prints the real count and time or `NEVER FIRES`, and a real-asset test pins that model's emitter #0 as never and #1 as exactly 30 (2057)
- a harmful spell's impact flinches its victim: benilla had one spell-side wound path, `play_kit`'s `[8,10]` branch — the kit player's own `0x60f3ad` test — carrying the KIT's anim id, so a spell whose impact kit names no wound anim, which is nearly every one (Fireball, Frostbolt, Shadow Bolt, Sunder Armor, a mob's Strike), twitched nothing, and a kit naming CombatCritical played 10 where the client plays 9 or 8; `0x60ea70` has six callers, not the three an earlier sweep found (`grep "call 0x60ea70"` over the full objdump was the one command it never ran): the missile hand-off `0x61dc50` lays the flinch before the impact kit for every living target (`0x61dc74`, HEALTH > 0; the already-dead arm `0x60ea30` plays Death/SwimDeath instead), the instant-hit ImpactKit loop `0x6e8bf0` lays it after the kit when `0x6ea280(SpellRec) == 2` (`0x6e8c89`), the reflect kit `0x6e8cb0` does the same on the caster, and every spell-side call pushes severity 0, so the id is always `0x60ea70`'s own `(severity, engaged)` pick — CombatWound(9) engaged, StandWound(8) otherwise — never the kit column; `0x6ea280` is the spell's hostility class and `== 2` ⇔ the spell targets enemies: Targets bit `0x100` → never, `0x80` → yes, else `EffectImplicitTargetA[i]` then `B[i]` for effects 0..3 indexed into the byte tables `0x6ea338`/`0x6ea378`, whose case-0 ids `{2, 6, 15, 16, 24, 28, 53, 54}` are exactly the modern enum's `*_ENEMY` set; so `WoundAnim { entity }` loses its id and the driver resolves every spell edge as `wound_anim(0, engaged)`, `play_impact` takes an `ImpactPlay` with `flinch` — the instant hits pass `SpellDisplay::is_harmful()`, a missile arrival passes true — `SpellDisplay` gains `effect_implicit_target_a/b` (columns 82–84 / 85–87) and `is_harmful()`, and the flinch is laid as the same secondary-blend overlay as a melee wound, over a cast, a swing or a run, never replacing them; pinned against the real 5875 `Spell.dbc` — Fireball, Charge and Sunder Armor harmful through A, Frost Nova through B alone, Renew, Healing Touch, Arcane Intellect and Battle Shout not — and the driver never picks CombatCritical for a spell edge; `WOW_MOVE_TRACE` gains `fct wound trigger/evict/expire` lines, since the whole flinch lifecycle was invisible in a trace; and the report this was chased under — the warrior's swings reading as cut short by incoming impacts in a many-mob brawl — stays open, with one reference gate found on the way that benilla does not model: `0x60ea70` aborts while any attached CEffect on the victim carries `+0x2c & 0x4` (`0x60eab6`), and which kit-spawned nodes carry that bit is the question dispatched to wow-re (2058)
- other players' roots, levitation and blinks now relay to onlookers: a report that a nearby player getting rooted, water-walking or Blinking slid or stood in the wrong place until their next movement packet was exactly right — a movement mode has three legs and benilla carried two, the mover's own acked handshake (`SMSG_FORCE_MOVE_ROOT → CMSG_FORCE_MOVE_ROOT_ACK`, 0866) and the server-driven `SMSG_SPLINE_MOVE_*` twelve (1780), while the third, what observers are told once the mover acks (vmangos `MovementPacketSender.h:30-60`, broadcast by `SendMovementFlagChangeToObservers` / `SendTeleportToObservers`), fell through `parse_server` to `ServerPacket::Other` and was dropped — so a rooted player kept sliding for the ~500 ms to their next heartbeat, a Levitate on someone else was never rendered, and a Blink left them standing at the old spot; six opcodes — `MSG_MOVE_ROOT`/`UNROOT` (236/237), `MSG_MOVE_HOVER` (247), `MSG_MOVE_FEATHER_FALL` (688), `MSG_MOVE_WATER_WALK` (689), `MSG_MOVE_TELEPORT` (197) — all the ordinary `[packed guid][MovementInfo]` shape and all sharing the client's one relay handler `0x603bb0` with the 22 already carried (wow-re's opcode-handler table has thirty rows on it; the remaining two are cheat opcodes vmangos never constructs), so admitting them to `is_movement_relay` is the whole fix for five: apply/unapply rides the flags word rather than the opcode, since vmangos runs `SetHoverReal` &co. before the broadcast and the word on the wire already carries the bit's new state, and `apply_move` already commits it wholesale into the union the ground resolve reads for hover's offset and the water-walk plane — the pose the packet carries beside the mode is half the value, a rooted player's stopping place being news too, and the root arrives already post-wipe with `MOVEFLAG_ROOT` forced back on, so no wipe of ours is invented; the teleport is a relay in shape and a discontinuity in meaning, so `RelayMove::reconciles()` excludes it from the pre-fire reconcile beside the heartbeat, for the opposite reason — the heartbeat because the dead-reckon has already converged, the teleport because there is nothing to converge to, and lerping toward a Blink's destination would drag the mover across the twenty yards the Blink exists to skip, sweeping the shared capsule through every wall between — while it still rides the unit's relay chain rather than snapping at arrival, because replay is paced against the sender's own stamps and applying it early would put the player at the destination while their walk to the launch point was still replaying; a `tp` tag joins `hb`/`tr` on the `rly` trace line; and parse and decode of all six are pinned against golden bodies asserted four bytes shorter than the counter-carrying `MSG_MOVE_TELEPORT_ACK` form, so a counter is provably not being read (2061)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
||
|
|
8584b89274 |
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> |
||
|
|
e7ac8b94f3 |
the minimap and world map go stock, and the chat loses its white plate
- the minimap cluster and the battleground list are the reference's own `Minimap.xml` and `BattlefieldFrame.xml`, ours deleted, and the sixteen engine names the static probe found unbound are built, not stubbed: the battleground list and queue family (the as-group join refused client-side with no packet when the map's `MaxPlayers` is below the group), the bodyless `CMSG_BATTLEFIELD_STATUS` on world enter — benilla never sent it, so a queue joined before a reconnect was invisible — the 5.5556 yd `BATTLEFIELDS_CLOSED` leash, and the meeting-stone pair over the `0x295` five-way table with both asymmetries; the ping's lifetime is the stock Lua's now (held 5 s, faded 0.5 s), `GetPingPosition` answers two numbers always — our nil was a Lua error on every frame of the stock `Minimap_OnUpdate` — and the map-change drop the reference never had is gone (1974)
- the world map is the reference's own `WorldMapFrame.xml`, our transcription deleted; built for it: the arrow-frame family (an anonymous `Model` child at its 33.6 px footprint, drawn app-side as the minimap's own arrow sprite through one shared constant), the battleground position family over `MSG_BATTLEGROUND_PLAYER_POSITIONS` both ways (the reference's 5000 ms request throttle), and `GetPlayerMapPosition` resolving `raid1..raid40` where it knew only the party (B320); `SetupFullscreenScale` — the engine binding `0x48c270` the map's and the cinematic frame's OnShow call, which our map had stood in for with Lua — is bound to the carved law `min(0.75 · aspect, 1)` off the configured aspect, written through the frame's own `SetScale` (1.0 at 4:3 and wider, 0.9375 at 5:4); a hovered POI writes the map's own area label, not the tooltip; named gap: `WorldMapPing` draws nothing yet (1980, 1981)
- the tutorial frame is the reference's own `TutorialFrame.xml`, over a tutorial system benilla never had: two bit banks filled from `SMSG_TUTORIAL_FLAGS` — the fire-once bank `TriggerTutorial` reads and sets (never a send) and the acknowledged bank `FlagTutorial` writes and sends; there is no tutorial CVar in the binary (210 `CVar::Register` sites censused), so "disable tutorials" is every bit acknowledged; the sites this app can produce are wired (the world-enter Welcome cascade, the level-up arm, item pushes, the loot sparkle, the meeting-stone and battleground arms), the rest named; the loader learned `<TitleRegion>` — the stock frame's drag handle, a region that emits no quad; and the `TabardModel` widget kind arrives ahead of its window, five ints bounded by the `.rdata` table `{170, 17, 6, 17, 51}` and the constant 100 000 tabard cost (1976, 1977)
- the chat window drew as an opaque white plate: the chat-cache loader, gated on `InWorld` alone, restored into the boot VM 130 ms before the deferred entry load filled it, and its per-VM memo then blocked the restore that would have reached the real frames — the plate the stock Lua tints black stayed the XML's white; between the entry edge and the load there is now no VM in the world (the edge parks the boot VM, the load takes it back), so no in-world feed pushes into a VM with no interface; the perf pill reads `59 fps 7.0 ms 17.3 cpu` — the MAIN thread's milliseconds in full text and the process-wide sum dim, because the sum of every thread's CPU time had been read as a frame time (a raid read 17 on it at a solid 60 with the main thread at 7); and `WOW_PROBE_UI_ONE_TEX=1` is a pricing lever, never a look, for the draw-count ceiling a UI atlas would reach before one is built (1978, 1954)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
||
|
|
7e3a67a02e |
the unit frames are the reference's own, and the water has motes in it
- the player, party, target and pet frames are the reference's own files (`BuffFrame`, `UnitFrame`, `CombatFeedback`, `PlayerFrame`, `PartyFrame`, `TargetFrame`, `PetFrame`, in the reference's toc order) — our 2741-line `UnitFrames.xml` and our `PartyFrame.xml` are deleted; four engine verbs built for them (`PartialPlayTime`, `NoPlayTime`, `UnitPlayerControlled`, `UnitIsCivilian`, each answering 1/nil), the whole-UI load bracketed in the counted sound-suppression scope `0x48fbf0` self-brackets (the reference fires a `PlaySound` during load and throws it away), and `GetBillingTimeRested` read off `SMSG_AUTH_RESPONSE`'s third billing field rather than hardcoded; the differences the stock files bring are pinned rather than papered — `PlayerFrame` never hides, an unresolved name is BLANK not "Unknown", the target frame has no numerals at all (a bar hover pops the unit tooltip instead) - what the swap surfaced, each a hard break under the stock files: `UnitBuff`/`UnitDebuff` returned the Classic Era ten-value tuple, name first, where 1.12 returns two and three values TEXTURE first (`0x519500`/`0x5198f0`) — a raise twice per `UNIT_AURA` on any debuffed target, and all 184 corpus call sites bind the 1.12 shape; the power events are named per resource (`UNIT_MANA`/`RAGE`/`FOCUS`/`ENERGY`/`HAPPINESS` and their MAX twins) and we fired the Era pair, so no mana, rage or energy bar could live-update; `SetRaidTarget` is the engine verb and `SetRaidTargetIcon` the FrameXML toggle wrapper — we had registered the wrapper's name on the engine's body, so every raid mark in the unit popup raised - the underwater drift cloud: the reference draws a fixed 4000-mote field whenever the camera eye is inside a liquid — not a particle system, nothing spawns, ages or dies; a population scattered once into a 30 yd camera-relative cube, advected by the camera's own motion plus a gust and wrapped, so the field stands still in the world; benilla had the murk and nothing in it; a dry frame commits no draw, and the submerged one is bounded by the reference's own 666-quad cap (633–644 of 4000 measured live, which is the expected survivor count of a uniform cube through the 90° cone — an independent check on population, distribution and cone at once) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
6356bc8f43 |
Enter World stops freezing character select, and bags close by the law
- Enter World stops freezing the character screen: from the click, the select image stayed on the glass ~100 ms because the loading cover could not draw until the state-flip frame — which cost 60 ms, 57 of it render+present: the world camera's first render of a 3000-entity world on cold pipelines, plus fifteen booth cameras waking on the very frame that owes the glass a cover; the glue now yields the glass on the click's own frame and the world camera (booths with it) holds off until the cover has presented — flip frame 60 → 43 ms and off the critical path — measured by the new `WOW_FRAME_PHASES` instrument, which stamps every phase of a frame and prints the render+present remainder no in-schedule stamp can see - switching to a character you haven't clicked before no longer flashes somebody else's body in their stage — the background swapped the frame the selection changed while the character waited for its whole assembly, so whichever won is what the screen showed (53 ms of the human standing in the orc's stage, and the reverse under a stage still loading); stage and character now commit as a PAIR, loads still starting on the click and an empty stage still swapping at once — on the same cold walk, eight selections make eight rebakes, every one the right body in the frame its stage lands, where before it was twelve with four of them wrong - the bank's six bag buttons repaint again — the stock UI repaints them only on `PLAYERBANKSLOTS_CHANGED`, which benilla fired for the vault's 24 slots alone, so a bag dropped in never appeared and one moved between slots stayed drawn in the one it left; the lock clear becomes its own system ordered ahead of BOTH feeds (resolve, then push, then fire), ending the stuck-grey icon — and `GetInventoryItemCount` is carved as not-the-stack-count: an EMPTY slot answers 1 (a sentinel, not a count), a container past the slot cutoff answers a literal 0 before any lookup (the whole answer to the stray digit on the bank's bag buttons), and an equipped bag answers the SUM of its contents gated on a DBC bit our class-11 guess missed twice — including the Soul Bag, so a warlock's shard count never showed - the bags close by the law: `BAG_CLOSED` is the only thing in all of FrameXML that hides an open bag window, and benilla never fired it, so a window outlived its bag in both bands (equipped and bank); the carve pins the condition — the bag changed AND the slot was occupied — so a swap fires BAG_CLOSED then BAG_UPDATE with the slot occupied throughout, an unequip fires it with the announce suppressed for exactly that transition, and two identical empty bags exchanged between slots still close both windows, because the diff is on the bag's own guid rather than the container state; `PLAYERBANKSLOTS_CHANGED` meanwhile turns out to carry NO arguments — a census of all 485 dispatcher call sites — the slot travels in how many times it fires, and every bank button repaints from its own slot id Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3d0bc38978 |
trades answer the reference's way, and the message catalog is a table
- an incoming trade request is answered by the reference's eight-leg ladder, not by reflex — before, `CMSG_BEGIN_TRADE` went back the same frame it decoded, so any player in range could put a window on your screen mid-cinematic, mid-loading, while mind-controlled, at an auctioneer, or from your own ignore list; three legs answer with something other than "busy" (the ignore list sends `CMSG_IGNORE_TRADE`, two legs send no packet at all), Block Trades ships as the CVar and options row it is in 1.12 with its refusal on the chat line the catalog says, and the consent popup that was built came back out — 1.12 signals the trade dialog from nowhere, so asking first would be a divergence every player and addon feels, and the reference's answer to an unwanted trade is already in the ladder - the client's message catalog ships as a generated table instead of being read by eye: every refusal, notice and confirmation goes through one dispatcher taking only a message id into a 465-record registry, benilla names 163 of the keys, and the surface does not follow the key names — the byte-exact sweep settles 170 chat / 28 yellow / 267 red, none on the console arm, with the skill-up trio speaking CHAT_MSG_SKILL rather than system; a message's surface now comes from its record, not its call site, which found `ERR_SPELL_FAILED_NOTUNSHEATHED` painted red where the client paints it yellow (seventeen of our eighteen cast-fail overrides are red; that one is not, and nothing downstream could have known which id the dispatch chose) — and a new test walks the workspace's own source asserting every `ERR_` literal is a real catalog row, because an unresolvable key shows NOTHING and nobody files a bug about a toast they never saw - starting an attack stands you up — the stand sits after the reference's attackability cluster and before any range logic, so an attackable target out of reach stands you, a friendly NPC does not, and re-issuing an attack you are already making stands you too; the rest of the intended build is proven unnecessary rather than deferred: bare ground cannot stand you on any client (its two requirements are mutually exclusive in both CVar configurations), gossip is proven not to stand, and the loot leg is unproven and left alone - a kick returns you to the login screen again instead of the client reconnecting indefinitely and taking the account back off whoever kicked it: "nobody is here" is DECLARED now, never inferred — the login environment variables answer "log in without waiting for someone to type", which cannot answer "is anybody here?", so a run acts unattended only on `$WOW_UNATTENDED` (implied by a capture or rig run), with the doubt deliberately falling toward a loud probe timeout over a silent account fight; and the player-tests gate finally resolves the install that sourcing FrameXML off the chain has required, instead of failing red for its own reasons on any shell without `$WOW_DATA` exported Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ee8f9ddfa0 |
the login scene frames its art, char select remembers, DXT stays DXT
- the glue scene widens only as far as its art goes: none of the seven login dioramas is drawn wider than about 3:2 — measured per texel by `benilla-extract glueextent`, which rasterises every batch into the camera's own tan-space, painting where the reference's alpha-key test passes — so the frame zooms down to the reference's own 16:9 opening (never tighter) and pillarboxes past the aspect the art cannot fill, instead of showing the clear colour past the backdrop's slanted edges; 4:3 stays byte-identical - the character screen opens on whoever you last logged in as: the reference's `lastCharacterIndex` CVar, written at Enter World and only there — clicks and arrow keys never touch it, which is what makes the value mean "last logged in" rather than "last clicked" — restored on every character-list rebuild and clamped into range - spam-hovering tooltips stops costing 2 ms: two write sites gave up naming their node and re-derived the whole layout graph (1,812 µs against the ledger's 44, on 919 of 1,262 resolves) — the wrap pin now touches the one region it wrote, and re-pointing a tooltip at the button under the cursor retargets the node's edges in place instead of throwing the cached graph away - two corrections off the project's first GPU-bound report (a Steam Deck at 94% GPU busy on 8.4 W of a 15 W budget, the memory-stall signature): `farclip` ships the reference's registered 350 instead of the clamp's 777 — the value also sizes terrain residency, so 777 streamed, held and drew 4.8× the area — and BLP textures upload WoW's own DXT blocks verbatim the way the reference does, instead of decoding to RGBA8 at 8× the bytes of DXT1; the terrain slider still reaches 777, and the lanes that read texels on the CPU (terrain arrays, the character-skin compositor) stay decoded and say why Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
73826ca3d7 |
casting a hunter shot starts Auto Shot, and the ping wears its real art
- Serpent Sting starts Auto Shot in play: the chain built earlier the same day never fired because the in-flight record was armed only for the cast classes it refuses on, and every hunter shot is a ranged attack — the reference records every commit and discriminates at the gate; two tests now run the real ladder and reproduce the shipped symptom - the minimap ping is MinimapPing.mdx, measured: three additive quads — a 12 px spinner on the model's global clock, a static core, and a ring expanding 3→32 px on an 833 ms loop — the interim art was ~3× too big and missing the ring the eye actually reads - a mouse release goes to the frame that took the press and nothing else — the reference's up handler reads the capture alone and never the hover frame, so a press over open space delivers no release to whatever the cursor wandered onto (corrects the morning's fallback) - `WOW_MOVE_TRACE_TAGS=use` traces a right-click end to end (the cursor decision, the use packet, the spell-go answer), and the first headless hover capture parks the cursor on a chat tab so the reveal can be measured against the bare scene Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
cf581aa2ac |
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> |
||
|
|
49ba9d599b |
the bevy 0.19 excursion: graded 5.5 ms slower lane-for-lane, rolled back
- the full bevy 0.19 / avian 0.7 / egui 0.35 port landed and rendered at visual parity, then profiling matched the lanes honestly: +5.5 ms against 0.18 in our scenes, priced to three named buckets — so it was reverted the same day, with the pins mapped for a future release - back on 0.18 the client is faster than 0.18 ever ran here, keeping both performance days' wins - what the hunt left behind: the material-animation scan visits only rows that can be registry keys, and a capture that settles on an all-black image now says EMPTY instead of grading it Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
38d8502bb7 |
addon-screen polish, and item glows return to the paper doll
- every glue-screen string renders its colour markup — the addon tooltip included, which used to print its |cff… codes raw - the addon list's scroll knob finally drags, on one shared slider law: press anywhere on the track and the knob warps under your cursor and follows, exactly as 1.12's slider behaves - item effects survive the character sheet and dressing room again — a booth bake is premultiplied by construction and is no longer premultiplied twice, which had been erasing every glow hanging in the space beside the model - the login one-shot announcements wait for the in-game UI to exist before they fire Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
047dd3444e |
the wheel binds, one AddOns screen, text at the true scale
- SetBinding accepts the mouse wheel — the refusal was ours, not the client's — so wheel-bound action buttons press and release like the real thing - one addon load law: a single arbiter for "can this addon load, and why not", with the out-of-date version gate live - one AddOns screen — the character-select list, wearing the authored reference layout: roster dropdown, per-character tri-state checkboxes, force-load; the ESC-menu panel is retired, since 1.12's own menu has no AddOns row - the glyph atlas follows the window scale, so maximised text stops washing out and a title stops measuring wider than it draws; an unpositioned frame positions nothing, instead of landing at the screen origin Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
8c04f876a6 |
the UI is built per login, and stray beams stay in their rooms
- the in-game UI ends at the character screen and is rebuilt at the next login, so addons that read the character at file scope stop being frozen on whoever logged in first - everything the host remembers about a UI is keyed on that VM's identity — keybinds, macro icons and every feed survive the rebuild instead of silently pushing nothing - a ribbon trail rides the room its model is drawn by: Caverns of Time's ninety energy trails stop burning through 200 yards of rock - the visibility census gains the sky and ribbon lanes it could not see, and the logout leak probe measures after the release instead of accusing every run Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
d1140829d7 |
the interface code is ours: the transcription debt is paid
- forty files that carried the reference's literal Lua are rewritten as our own expression of the behaviour — worst identical run 61 lines down to 7 — and the README now says exactly that - the overlap gate measures the code itself across all four reference corpora, and ratchets: the number may only go down - a lost session ends at the login screen instead of silently re-authenticating and stealing the account back - a building's skybox is a real model: Caverns of Time's white ceiling becomes its painted nebula and additive stars Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
251daa14c5 |
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> |
||
|
|
b5bc569156 |
the addon arc: the AddOns folder opens
- benilla loads real 1.12 addons from Interface/AddOns — TOC, XML and Lua through one loader, with per-addon saved variables, Bindings.xml and the lifecycle events addons expect - both AddOns screens are in: the character-select list with per-addon enable state, and the ESC menu's in-game list - the API surface is measured, not guessed — a corpus harness runs hundreds of real 1.12 addons and ranks exactly which globals, templates and verbs they demand - the Lua dialect is the client's own, read off the binary — 5.0 semantics with the 1.12 additions, and UI source files are bytes, not UTF-8 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
8eea16157c |
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> |
||
|
|
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> |