Repository navigation
D2LoD Vanilla+0.0.6-R52
Pre-release
D2LoD Vanilla+ 0.0.6 (R46 → R52)
The post-release repair era. Seven source rounds — R46 through R52, all landed 2026-10-30/31 — driven entirely by field reports against the shipped 0.0.6 build, closing five GitHub issues (#11, #10, #12, #9, #14) and shipping the two-site fix for the sixth (#8, field pass pending). The D2LoD-Vanilla-Plus-0.0.6 release page carries the 0.0.6 cut itself (build R45); everything after it lives here. Same drop-in layout throughout: each build is a rebuilt d2.exe on the 0.0.6 tree — extract over the install and go; the bundled glide3x.dll (Glide3xVK 0.4.6) is unchanged; every opt-in feature still ships OFF — with no d2vp.ini the game behaves like retail; and every build self-identifies on the first d2boot.log line (Vanilla+0.0.6-R46 … Vanilla+0.0.6-R52), which pins the exact binary before any analysis. Publish state as of these notes: the R46 build rides the D2LoD-Vanilla-Plus-0.0.6 release page, R47–R49 are tagged pre-releases, R50–R52 await the owner's tag pass — the builds and their overlays exist; the tags are the owner's ritual.
Build index
| Build | Round | What it fixes | Issue | Field verdict | Shipped as | Overlay |
|---|---|---|---|---|---|---|
| R46 | Rev 47 | Glide -3dfx first-frame divide-by-zero (texture-memory init + upload guards restored) |
#11 — CLOSED | "ok Glide i working now" | D2LoD-Vanilla-Plus-0.0.6 release page (asset D2LoD-Vanilla-Plus-0.0.6-R46.7z) |
vanilla-plus-0.0.6-R46-glide-texmem-fix.zip |
| R47 | Rev 50 | Auto Gold Pickup dead trigger (re-parented VP block) | #10 — CLOSED | "Great job. Auto Gold Pickup working again." | tag R47 |
vanilla-plus-0.0.6-R47-auto-gold-fix.zip |
| R48 | Rev 52 | 720p deposit-gold dialog widgets (wrong-branch riders) | #12 — CLOSED | "Great job. 12 Closed, R48 Field Verified." | tag R48 |
vanilla-plus-0.0.6-R48-gold-dialog-branch-fix.zip |
| R49 | Rev 53 | Unique monsters base-colored (RandTransforms dead path) | #9 — CLOSED | "R49 Field Tested, Confirmed Unique Monsters are Unique again." | tag R49 |
vanilla-plus-0.0.6-R49-track-c-unique-tint.zip |
| R50 | Rev 55 | Skeleton gold, site 1 of 2 — roster pet-recolor polarity | #8 (with R51) | necessary, not sufficient — "Necro Skeletons are still yellow" | not yet released | vanilla-plus-0.0.6-R50-skeleton-recolor-polarity.zip |
| R51 | Rev 56 | Skeleton gold, site 2 of 2 — spawn-time ownership polarity | #8 — field pass pending | pending | not yet released | vanilla-plus-0.0.6-R51-skeleton-spawn-polarity.zip |
| R52 | Rev 57 | 720p belt hover popup collapse + tooltip anchor; d2boot.log overwrite |
#14 — fix confirmed | "R52, Hover belt confirmed fix" | not yet released | vanilla-plus-0.0.6-R52-belt-hover-y-virtualization.zip |
Fixed
- (R46) Glide (
-3dfx) died ~0.5 s after the first frame with an integer divide-by-zero (0xC0000094). The merge port that carried the 0.0.6 tree to its upstream base (d6affdd, 2026-10-30) resolvedRENDERER_GLIDE_InitTextureMemory(D2Glide/Renderer/Glide.cpp) to the fork's pre-merge version of the function, silently dropping the retail texture-size assignments (nSubtileTexSize = 0x400,nFloorTexSize = 0x8000) together with the 3.5 MB VRAM gate, the debug-font hookup, the TMU≥2 perspective memory split, and two glide upload guards (the fix-3 class-slot and fix-2 stale-nodeIdx checks). The floor/subtile cache initializers then divided(nVramEnd - nVramStart) / dwSizeby an uninitialized stack slot — the exact first-frame death, right after the init-timegrChromakeyValuecall; the Vulkan wrapper (swapchain, slang chain) was never at fault. Both function bodies are restored byte-exact from the pre-port commit. Field-verified on the owner's build ("ok Glide i working now") — root cause + fix documented in #11. - (R47) Auto Gold Pickup never triggered. With
[features] auto_gold=1set, walking over gold piles collected nothing, while clicking piles still picked them up. The same merge port had re-inserted the VP [auto-gold] trigger block one brace too early insideDRAW_UI(D2Client/UI/Draw.cpp) — insideif (GFX_bShowUIFlag.HelpMenu), the full-screen help-overlay block — so the per-frameAUTO_GOLD_Tick()only ever ran while the help overlay was open; in normal play the trigger was dead, and every presence-based check saw the call as intact. The block is back at its per-frame top-level placement (the last field-verified pre-port tree,44e3518), with an invariant comment and a gate row that fails if it ever moves again. This fix named a new casualty class — the re-parented VP marker — and its house rule: presence is not placement. Field-verified on the owner's build ("Great job. Auto Gold Pickup working again") — closed in #10. - (R48) At 720p, the stash deposit-gold dialog's widgets sat misaligned. The modal's background rode the wide-layout offsets, but the edit box, OK and Cancel buttons and the spinner stayed vanilla-800-space. Third casualty class of the same merge port: wrong-branch resolution — the upstream regeneration had inverted the widget-alloc null-check shape, and the port resolved each VP-marked widget rider onto the constructor call that LOOKED like the original, now in the allocation-FAILED arm and immediately followed by the same-LHS
= 0null-out: a dead store in normal play (the raw 800-space constants ran — the field symptom) and a latent OOM null-deref. All four riders are moved into the allocation-success branches. Field-verified on the owner's build ("Great job. 12 Closed, R48 Field Verified.") — closed in #12. - (R49) Unique monsters rendered in base colors. The rolled unique/champion tint indices (9–29) resolve exclusively through the global
RandTransforms.dattable, but the transcribedPALSHIFT_GetMonsterTransform(D2Client/GAME/PalShift.cpp) early-returnednullptrfor any monster without apalshift.datentry BEFORE the palette-index switch — every random unique looked its tint up through a dead path and drew its base palette. Not a merge casualty: a baseline transcription defect, verbatim at the last field-verified pre-port tree — no marker audit could ever catch it. The unconditional early-out is deleted and behavior-identical guards re-homed inside the two entry-consuming arms; the owner's own proposed fix (a MonStats2 component memcpy) was evaluated first and retired with the 0xAC wire-width proof. Field-verified on the owner's build ("R49 Field Tested, Confirmed Unique Monsters are Unique again.") — closed in #9. - (R50) Necro skeletons spawned gold, part 1: the roster pet-recolor wrote the inverted hostility flag.
ROSTER_UpdateHostileColors/ROSTER_UpdateMercHostileColorswrote(uint32_t)!bHostileinto both the palshift index and its re-sync latch shadow — feeding every own raised skeleton the yellow HOSTILE palshift sub-table instead of bone-white sub-table 0. Baseline transcription defect, verbatim at44e3518; every upstream branch carries it, so there was nothing to cherry-pick. All four writes flipped to the raw flag. The R50 field pass proved this fix necessary but NOT sufficient ("Necro Skeletons are still yellow") — the spawn-time write below decides the first render. - (R51) Skeletons gold, part 2: the spawn-time ownership fallback carried the same inversion.
CreateObject's MonStats2Utrans==0xFFownership fallback (D2Client/UNIT/CUnit.cpp— the packet path every client-visible monster takes, single-player included) transcribednPalShiftIdx = (uint32_t)!uMercType: the same inverted polarity, at the site that runs FIRST, and single-player summons never trigger the event-driven roster recolors — so idx 1 (yellow) stuck for the unit's whole life. Spawn now writes the raw flag; both write sites agree on the raw-flag convention (own/ally = 0 → bone-white sub-table 0, hostile owner = 1 → yellow sub-table 1). Awaiting its field pass on #8 — the manifest's last open item (multiplayer note: hostile-owner summons now correctly spawn yellow; pre-fix they spawned white — the tell was inverted on both sites). - (R52) At 720p, the expanded belt hover popup collapsed on the next mouse event. The shared expanded-belt hit-test funnel
PANEL_CalculateBeltSlotIndex(D2Client/UI/Panel.cpp, ~30 call sites) had mouse X virtualized into 800-space (site-5) but compared mouse Y raw against theBelts.txt720p rect's 600-absolute bounds — at 1280x720 the real cursor sat one height-delta (60 px) BELOW the tested rect, every in-rect test failed, and the popup closed on the next mouse event (the small-HUD open path is resolution-independent and worked, hence opens-then-instantly-closes). Fix:nVirtualMouseY = nMouseY - VPRES_GetWideUiOffsetY()feeds both Y bounds — offset 0 at 640x480/800x600, retail byte-identical. Same-class secondary defect fixed in the same round: the belt tooltip anchor was raw 600-space and would have drawn 60 px high once the popup stayed open; it now rides the re-centered rect. Field-verified on the first pass ("R52, Hover belt confirmed fix") — #14 carries the record (ISSUE-14-CLOSE-COMMENT.mdis the paste-ready close comment).
Internals
- Gate ledger, 194 → 206. Six rows pin the restored glide texture-memory surface (R46 → 200), then one row per fix: auto-gold trigger placement (R47 → 201), gold-dialog riders in the alloc-success branch (R48 → 202), palshift rand-transform reachability (R49 → 203), roster pet-recolor polarity (R50 → 204), spawn ownership polarity (R51 → 205), belt hover Y virtualization + belt tooltip Y anchor + boot log overwrite (R52 → 208). At Rev 58 the two version-self-ID pins (banner + build version) were retired on owner directive — "gate is for features and fixes" — leaving 206/206; the build check is manual (first
d2boot.logline,SMOKE-CHECKLIST.mditem 0). - The suite is a manual merge audit again. It left CI at Rev 48 (between the R46 and R47 builds) on owner call — a red gate must block a merge, not a build — after a
d6affddCI run showed exactly the two glide casualties the fix rounds then healed. - Three casualty classes, three instruments. The era named and instrumented: the re-parented VP marker (
scripts/rev47_vp_placement_audit.py; rule "presence is not placement"), wrong-branch resolution (scripts/rev48_wrong_branch_audit.py), and the baseline transcription defect no marker audit can catch (R49/R50/R51 — provenance-first method,scripts/rev49_trackc_provenance.sh).docs/merge-upstream.mdcarries the war stories; merge step 4 runs the suite plus both audits. - The
d6affddmerge-port ledger closed at five healed casualties: glide texture-memory + upload guards (R46), auto-gold placement (R47), gold-dialog branches (R48) — after the sound FP constants (rev41) and the inventory shift order (rev44) before the era. d2boot.lognow overwrites every run (R52 rider, owner call: the log should never accumulate) — GENERIC_WRITE + CREATE_ALWAYS, fresh log at the first mark; hangwatch/crash writes ride the same handle. Gate rowboot: log overwrites per run.SMOKE-CHECKLIST.mdadded (R52): the per-build in-game smoke pass as a living checklist; item 13b (belt hover) is CONFIRMED and stays as the per-build regression check.- Docs shipped through the era: paste-ready close comments for every closed issue (#11, #10, #12, #9, and #14 — the last in-tree as
ISSUE-14-CLOSE-COMMENT.md), README field-status rewrites per fix, and the handoff trims to HISTORY.md (H26 = revs 40–56 + the closed tracks; H27 = the belt-hover arc).
Known issues
- Necro/minion skeletons can spawn gold (#8) — the only open item. One baseline defect, two inverted-polarity write sites, fixed across R50 (roster pet-recolor) + R51 (spawn-time fallback); awaiting its field pass on an R51+ build: raise skeletons → bone-white AT SPAWN (no roster event involved); in multiplayer, hostile-owner summons must spawn yellow. If still yellow, the next discriminator is live: force idx 2 (the TransLvl path) on a skeleton and compare its render against the idx 0/1 renders — the remaining suspect family is the palshift.dat entry parse itself (loader table order vs retail).
- Glide regression watch (closed, keep an eye): after any rebuild or wrapper pass, the H26 §4.3 step 4 checklist — world visible (not black) at the 720p stop, fps overlay reads "Video: Glide", cutscenes resize to 640x480 first (retail behavior), the 800x600 stop byte-identical.
Upgrade notes
- Extract over any 0.0.6-era install (0.0.6 through R51); saves, realm logins and
d2vp.inicarry over. Ifd2vp.iniis missing, a documented all-defaults template is written next to the exe on the next start. The bundledglide3x.dll(Glide3xVK 0.4.6) is unchanged across the whole era. - Pin the build before any analysis: the first
d2boot.logline must read theVanilla+0.0.6-Rxxyou meant to run (table above). From R52 the log starts fresh every run — old marks no longer accumulate. -3dfxis playable end-to-end again (R46): rund2vp.exe -3dfxwith the bundledglide3x.dllbeside it.auto_gold=1picks up gold piles on walk-over again (R47; each pile vanishes within ~0.2 s of entering the instant-pick radius) and coexists withalt_toggle=1.- 720p regression sweep per build: inventory cells and skill flyouts (0.0.6), deposit-gold dialog (R48), unique/champion tints (R49), belt hover popup + slot tooltip (R52 —
SMOKE-CHECKLIST.mditem 13b). 800x600 and 640x480 stay retail byte-identical. - The one outstanding field ask: #8 on an R51+ build — skeletons bone-white at spawn.
Full Changelog: D2LoD-Vanilla-Plus-0.0.6...R49 — tags for R50–R52 are the owner's release call; R52 is the current working tree.