THE GRIMOIRE

a living reverse-engineering reference — Diablo II 1.13c · The Esoteric Path

6 open questions10 total questions 53 findings450 symbols & hooks 0 decoys

confidenceverified ours, vs the binary trusted a named expertsourced community canon unverified unconfirmed

Modules 7

Preferred imagebases (no ASLR on this 1.13c toolchain). RVA = VA − imagebase.

moduleimagebasesizeloadfile
D2Client0x6FAB00000x135000fixedC:/Diablo II/D2Client.dll
D2Common0x6FD500000xA9000fixedC:/Diablo II/D2Common.dll
D2Game0x6FC200000x122000relocatesC:/Diablo II/D2Game.dll
D2Win0x6F8E00000xCF000fixedC:/Diablo II/D2Win.dll
D2gfx0x6FA800000x21000fixedC:/Diablo II/D2gfx.dll
D2Glide0x6F8500000x1A000fixedC:/Diablo II/D2Glide.dll
glide3x0x100000000x10F000relocatesC:/Diablo II/glide3x.dll

Open Questions 10

The live frontier — what we don't yet know. Experts, start here.

OQ.d2dx_runtime_gamesizeopenquestionopen

DISPUTES finding.d2dx_gamesize_960. What is D2DX's ACTUAL runtime gameSize in-game (panel open)? Workflow inferred 960 (narrow). But forcing 1280 via SetCustomResolution (D2DXWiden) SHRANK the menu (black margin) -> implies native was LARGER than 1280, contradicting 960. Need a runtime read (probe gameSize / grSstWinOpen args), not source inference.

OQ.units_shift_leftopenquestionopen

Units/NPCs shift LEFT on inventory-open (they must NOT shift). Source unknown: the ctx+0x0C widen, D2DXWiden (now reverted), or the double-motion-smoothing (d2fps+D2DX both on). Re-check with D2DXWiden off. → PROGRESS 2026-07-12: terrain shift CONFIRMED = -320 (1x) via offX(0x6FBCC418)->ctx+0x04; scrollX untouched. Shared tile projection is 1x for all layers, so the objects' extra -320 is downstream/unit-specific. STILL OPEN: unit-layer re-read of offX (theory a) vs motion-smoothing (theory b). NEXT: xref offX reads -- if the unit/PASS2 path reads 0x6FBCC418 too, theory (a). ==> 2026-07-12 xref: offX read 173x, 1 write (0x6FB5AF7C). Attempt A (offX=0) un-shifted floor+units but NOT walls (separate source) + broke clicks. Shift is MULTI-SOURCE + hit-test-coupled; units lean theory (a). Naive offX-kill is OUT.

OQ.one_clip_or_twoopenquestionopen

ONE clip (D2Glide 640) or TWO (D2Glide 640 + a D2DX target)? Panel-open probe=640, no-panel probe=1296 => the D2Glide clip is panel-mode-dependent. Whether D2DX ALSO clips once geometry passes 640 is blocked by OQ.d2dx_runtime_gamesize.

OQ.menu_res_regressionopenquestionopen

D2DXWiden(1280) regressed the menu resolution. Is forcing 1280 wrong for the low-res menu, or is the game's native size >1280? Ties to OQ.d2dx_runtime_gamesize. If D2DXWiden is re-applied, scope it IN-GAME only.

OQ.d3d11_rt_width_unmeasuredopenquestionopen

DISAMBIGUATOR (council chat+clau demanded, never run): what is D2DX's ACTUAL D3D11 world render-target width + viewport, panel-open vs closed? Pairs with the panel-gated D2Glide submit probe to triangulate the stop point: (submit 640 + RT 1280)=>upstream D2Glide; (submit 1296 + RT 960)=>D2DX extent; (submit 1296 + RT 1280)=>wide-but-retagged (inspect MRTs). Build: hook ID3D11DeviceContext::Draw or read D2DX gameSize; or log grSstWinOpen incoming res arg (feature_d2dx already hooks it).

OQ.floor_sublayer_residualopenquestionopenverifiedThe Esoteric Path

v1.12.0: after authoring the no-panel lens (offX=0/ctxL=0/ctxR=DispW each frame), the WORLD, WALLS and UNITS un-shift correctly -- but 1-2 FLOOR sub-layers still sit at the shifted position. So a floor sub-layer projects through a source our per-frame ctx authoring does NOT cover (a cached projection, a pre-PASS1 draw, or an offX re-read in the tilesub/floor-sublayer path 0x6FAF4307). Minor visual residual. Find via which draw path emits the lagging layer. Polish -- lower priority than the black. ==> IDENTIFIED (v1.12.2): it's the play-area rect playAreaW (0x6FBA9E14) -- the floor draws into it AND the hit-test reads it; narrowed to ~640 on panel-open (EP's 2022 HOM/4-tile-buffer notes). Authoring playAreaW=DispW/frame is the test. ==> v1.12.3: nViewXOffset was ALREADY 0 at our pre-PASS1 hook (no-op) -> the floor shift is COMPUTED INSIDE PASS1, not a pre-authorable global. See finding.floor_shift_internal_to_pass1. Council consult queued. ==> v1.12.6 [PROBE glidevtx count=0 @0xBEFC]: 0xBEFC is NOT the floor's live submit path. The floor's actual grDraw site is unidentified. See finding.floor_shift_ledger. Next = unfiltered return-addr logger.

OQ.d2dx_retag_blackopenquestiontestingverifiedThe Esoteric Path

THE LAST BARRIER TO ZERO BLACK: with the world un-shifted, floor geometry reaches x>=640 (probe band[960,1280)=66-104) and D2DX renders it BLACK. Confirmed m2's prediction: D2DX SurfaceIdTracker retags world batches with minx>=gameSize/2(=640) as USER_INTERFACE while ScreenOpenMode&1 -> the UI projection drops the world verts. Fix (council m2): runtime hot-patch the minx>=640 retag compare in glide3x during the world draw, restore after. We CANNOT rebuild D2DX. First step: read the D2DX source (bolrog/d2dx SurfaceIdTracker.cpp) to nail whether the retag drops COLOR or only writes metadata (m5 disputed) -> then locate + hot-patch it. ==> CORRECTED 2026-07-13 (read D2DX source): the surfaceId retag is METADATA ONLY (SetSurfaceId on vertices, for AA/motion) -- it does NOT relocate color (m5/chat was right, m2 wrong). The real clip is D2DX RenderContext::UpdateViewport, which sets the game VIEWPORT + SCISSOR to _gameSize.width (=960 for our desktop). World pixels x in [960,1280] are scissored -> black. FIX = widen _gameSize to 1280 via SetCustomResolution (our feature_d2dx.c / D2DXWiden). It failed alone earlier ONLY because the world was still SHIFTED (geometry never reached [960,1280]); with v1.12.0 no-shift the world fills [0,1280], so no-shift + D2DXWiden TOGETHER should fill the dead zone. RT is >=1024 (max(1024,w)); if a thin [1024,1280] band stays black, the RT alloc is the next tweak.

OQ.panelopen_640_cull_vs_clampopenquestionclosed

Panel open: is the D2Glide floor submit clamped at 640(=DispW/2) by the [esp+0x38] per-subtile CULL (NOP-able @0x856889) or by a VERTEX-X CLAMP elsewhere in 0x856840's projection? v1.11.2 gated probe (GlideUnclamp=1) settles it: submit max ~1280 => cull is the lever; ~640 => clamp is elsewhere. → ANSWER It's the CULL. v1.11.2 gated probe (mode=1): GlideUnclamp=1 -> panel-open D2Glide floor submit max 1264-1296 with 338 verts IN [960,1280); GlideUnclamp=0 was 640. The NOP defeats the cull (640->1264). The remaining black is a DROP at/after D2DX -- geometry reaches the dead zone, D2DX doesn't paint it.

OQ.glideunclamp_no_changeopenquestionclosed

Why did v1.9.1/v1.11.0 GlideUnclamp (NOP the cull narrowing 0x856889/0x85689E) show NO visual change if 640 is the cull? Either the NOP widens the submit but a downstream layer hides it, or 640 comes from a clamp the NOP doesn't touch. Depends on OQ.panelopen_640_cull_vs_clamp. → ANSWER GlideUnclamp DOES widen the submit (640->1264, 338 verts into the dead zone) -- never 'no effect'. The visual didn't change because the widened geometry is dropped downstream at/after D2DX.

OQ.walls_shift_sourceopenquestionclosed

BLOCKER (council wave-5 m1+m4): walls/roofs shift via a SEPARATE source, NOT offX (attempt A: offX=0 un-shifted floor+units but not walls). The authored no-panel view-state is INCOMPLETE until this source is named. Find it: read-breakpoint on the wall projection input during PASS2 (0x6FB3B110), or resolution-differential (vary DisplayWidth, scan for the value that tracks it AND encodes panel-open). Last unknown before the view-state-authorship patch. ==> TESTING (v1.12.0 attempt.I_noshift_lens): authoring ctxL=0 will show if walls read the ctx (un-shift) or a PASS2 recompute (stay shifted). ==> CLOSED (v1.12.0): the walls UN-SHIFTED with our ctxL=0 authoring -> the walls read the render context, NOT a separate source. Authoring the no-panel lens covers them.

Findings 53

Verified conclusions and recorded corrections.

finding.panelworld_clip_in_d2dxfindingverified

2026-07-12 (v1.10.0 probe): the panel-world dead-zone clip is IN D2DX. Floor geometry crosses grDrawVertexArrayContiguous at max 1296 (138 verts in [960,1280)) yet the dead zone is BLACK. NOT D2Client (issues 1236), NOT D2Glide (submits past 960). The [640,960] tiles render via the SAME path, so the [960,1280] verts are valid, not degenerate -> D2DX clips them. Fix belongs at D2DX via a runtime glide3x hook from esoteric.dll (rebuild not available here).

finding.d2dx_gamesize_960_is_the_clipfindingverified

2026-07-12 (workflow d2dx-panel-clip-fix, source+bytes): D2DX renders the world into its NARROW _gameSize={960,720} (Metrics.cpp desktop 1280x720). Clip cutoff == 960 == _gameSize.width: framebuffer sized to gameSize (SetFramebufferSize), UpdateViewport sets RSSetViewports+RSSetScissorRects to that rect, GameVS.hlsl NDC.x/960 -> x>=960 off-RT+out-of-scissor+NDC>1, rasterizer-clipped. The SurfaceIdTracker retag (minx>=gameSize/2) is a RED HERRING (surfaceId only feeds FXAA, color write unconditional). Fix = force gameSizeWide via SetCustomResolution(1280,720).

finding.panelopen_clip_is_d2glide_640findingsuperseded

[SUPERSEDED 2026-07-12: the 640 was the CULL with GlideUnclamp=0. Gated probe with GlideUnclamp=1 -> submit 1264, 338 verts in the dead zone, still black => the binding clip is a D2DX-downstream DROP, not D2Glide 640.] CORRECTION 2026-07-12 (v1.10.0 probe, PANEL-OPEN frames): with a panel open, the D2Glide live drawer 0x6F856840 submits floor geometry to grDrawVertexArrayContiguous at max screenX ONLY 640 (=DisplayWidth/2, the mode-1 right bound), band[960,1280)=0. D2Client still ISSUES to 1280 (TILEBAND). So the binding panel-open clip is D2Glide 0x856840 at 640, NOT D2DX. My earlier 'crosses at 1296 -> D2DX' read used NO-PANEL frames (before [WIDEN] mode=1) by mistake -- the harness was right, I read the wrong frames. finding.panelworld_clip_in_d2dx and finding.d2dx_gamesize_960 describe the NO-PANEL/second clip; the panel-open lever is here. Likely TWO clips: D2Glide 640 (panel) + D2DX 960 target; both need fixing. v1.9.1 cull-NOP showed no visual change -> re-testing GlideUnclamp WITH the probe to see if it widens the submit past 640 (cull-skip) or the 640 is a vertex-X clamp elsewhere in 0x856840.

council.wave4_consensusfindingnoted

Council wave-4 (chat m5 + clau m6 converge): the panel-open dead zone is the 960-WIDE RENDER-TARGET EXTENT, not a surfaceId routing/split. D2Glide announces 960 gameSize to D2DX at grSstWinOpen; SGD2FreeRes widened D2 to 1280 without updating that; D2DX allocates a 960 world RT and clips the 1280-coord floor. Fix they gave = D2DXGetConfigurator->SetCustomResolution(1280,720) before grSstWinOpen. STATUS: we BUILT this (feature_d2dx.c / D2DXWiden) -> 122 successful calls but it REGRESSED the menu res and did NOT fill. So their consensus fix is tested-and-insufficient.

council.wave4_gem_retagfindingnoted

Council wave-4 dissent (gem m4): the drop is the UI-RETAG. D2DX SurfaceIdTracker:88 retags world batches with minx>=640 as USER_INTERFACE when a panel is open; retagged verts get the UI orthographic projection (wrong for world coords) and render into the void = black. Fix he gave = hot-patch the minx>=640 Jcc in glide3x to fail ONLY during the floor draw call, then restore. NOTE: clau m6 REFUTES this - [640,960) is retagged too yet renders fine, so a rule that fires on visible geometry can't be the blackout rule.

council.wave4_mandatory_measurementfindingnoted

Council wave-4: BOTH chat m5 and clau m6 demanded a PRE-PATCH measurement we never ran -- read D2DX's ACTUAL D3D11 world render-target width + viewport (RSGetViewports width; OMGetRenderTargets->GetDesc width/height) at the first Game-pass Draw, plus grSstWinOpen incoming args. chat's decision tree last branch: 'viewport 1280, RTV 1280+, constants 1280, still cutoff at 960 => your binary differs; next hook is ID3D11DeviceContext::Draw, inspect bound VS/PS and MRTs on the first missing floor batch' -- NOT SurfaceIdTracker.

reframe.one_cause_view_statefindingnoted

Doug reframe 2026-07-12: the dead zone, the panel-open camera SHIFT, and the world-vs-units DESYNC are ONE cause -- opening a panel CHANGES THE WORLD VIEW-STATE (offX=-320 + play-area narrowing). Clean target: render the world in the NO-PANEL view-state (no shift, full width) and composite the panel on top = ONE view-state (matches the ledger META-LESSON). Neutralize the change AT ITS SOURCE (the panel-open handler 0x6FB5AEC0 writing offX @0x6FB5AF7C), leaving the mode flag 0x6FBCC414 intact for culling (attempt A proved mode is load-bearing).

reframe.keep_shift_retiredfindingnoted

RETIRED CONSTRAINT: 'keep the native -320 left-shift' (offX=0x6FBCC418, written @0x6FB5AF7C). Preserved through attempts A-H. Doug 2026-07-12 REVERSES it: wants ZERO shift on panel-open (world stays put, panel composites on top). 'both moves are inappropriate when opening the inventory if we have the world behind as we want.'

finding.shift_desync_is_halffindingobserved

Doug confirms 2026-07-12: panel-open, the WORLD shifts HALF as much as characters/items (units = ~2x world). Ledger theories (both unconfirmed): (a) multi-layer shift (floor vs wall/roof/units = separate draw fns), (b) double motion-smoothing (d2fps + D2DX both interpolate the camera move -> 2x on moving units, 1x on static terrain). Exactly-2x favors (b). MEASURE before patch: log offX(0x6FBCC418) + ctx+0x24 world-scroll + one unit screenpos, panel-open vs closed.

finding.clip_is_d2dx_drop_confirmedfindingconfirmedverified

CONFIRMED 2026-07-12 (v1.11.2 panel-gated probe, mode=1, GlideUnclamp=1): floor geometry REACHES the dead zone at the D2Glide->D2DX seam -- submit max 1264-1296, 338 verts in [960,1280) -- yet renders BLACK. The dead-zone black is a DROP at/after D2DX, NOT a D2Glide clip (GlideUnclamp already carries the submit to 1264). Independently reproduces council wave-4 (geometry crosses into D2DX in the dead zone, black). Next diagnostic = the council's demanded D2DX world-RT-width read (extent 960 vs UI-retag). SEPARATE from the world/objects SHIFT desync.

finding.terrain_shift_is_offx_ctxLfindingconfirmedverified

CONFIRMED 2026-07-12 (v1.11.3 [SHIFT] snapshot, standing still): opening a side panel shifts the FLOOR/terrain by exactly -320 (1x = -DispW/4), and the WHOLE shift is offX(0x6FBCC418) 0->-320 driving ctx+0x04 0->-320. scrollX(ctx+0x24) is NOT touched by the panel (log changes there = the player walking). Terrain pan (ctxL - scrollX) moves -320. So the terrain-shift LEVER is offX->ctx+0x04. The objects-2x is a SEPARATE source: the shared tile projection is only 1x, so units (PASS2, a separate draw fn) pick up an EXTRA -320 downstream, NOT the camera/ctx. Which source (unit re-read of offX vs motion-smoothing) is unmeasured.

finding.offx_read_173_one_writefindingconfirmedverified

CONFIRMED 2026-07-12 (d2re xref 0x6FBCC418): offX is READ at 173 sites across D2Client, but WRITTEN once (0x6FB5AF7C, =-DispW/4=-320 on panel-open). So the surgical lever is the single write, not the 173 reads (blanket-patching reads = ledger attempt A = infeasible). CROSS-REF attempt A (held offX=0): un-shifted FLOOR + UNITS but NOT walls/roofs (a SEPARATE shift source) AND broke click-to-move (the hit-test reads offX). CONCLUSION: the panel shift is MULTI-SOURCE (offX for floor/units; a different global for walls/roofs) and HIT-TEST-COUPLED. Units read offX twice -> the 2x (theory a favored; not proven vs motion-smoothing). Naive offX=0 is OUT (already failed). The reframe goal (render world in no-panel view-state) needs an architecture decision, not a single patch.

council.wave5_view_state_authorshipfindingnoted

Council wave-5 (all 3 CONVERGE): kill the panel shift by AUTHORING a coherent NO-PANEL WORLD VIEW-STATE for the whole world-draw -- NOT per-value patching, NOT a mid-frame ctx swap (that's the C-G scheduling trap: 173 readers read at different times, no single safe instant). Two flavors: m1(chat)=post-hook the panel-transition routine (holds offX write 0x6FB5AF7C); after it runs, canonicalize the world-camera outputs (offX=0, wall/roof offset=0, ctx+0x04=0, ctx+0x0C=DispW) and HOLD persistent; keep ScreenOpenMode=1/2 (UI+input stay live). m2(gem)+m4(clau)=STATE-SPOOF: hook the world-draw, temporarily set the panel-state flags to 0 (lie 'no panel open'), call original (D2 naturally zeroes offX + wall offsets + hit-test), restore flags before the panel compositor 0x6FB739E0 -> ONE hook, hit-test solves itself, UI intact. Hit-test coherent BY CONSTRUCTION (render + mouse ray read the same authored state).

council.wave5_d2dx_retag_is_the_blackfindingnoted

Council wave-5 CRITICAL LINK (m2 gem): the SHIFT fix and the dead-zone BLACK are the SAME downstream issue. Unshifting the world pushes valid geometry into x>=640; D2DX SurfaceIdTracker:88 retags world batches minx>=640 as USER_INTERFACE while a panel is open -> the UI orthographic projection drops those world verts -> black/corrupt right half. So authoring the no-panel view in D2Client is NECESSARY BUT NOT SUFFICIENT: we must ALSO defeat the D2DX minx>=640 retag during the world draw (runtime hot-patch the compare in glide3x, restore after). Defeating that retag IS the fix for the black.

attempt.I_noshift_lensfindingconfirmedverifiedThe Esoteric Path

ATTEMPT I (v1.12.0, council wave-5 recipe): author the NO-PANEL world lens each frame before PASS1 -- in the existing widen hook (0x6FAF4302), when a panel is open set offX(0x6FBCC418)=0, ctx+0x04(draw_left)=0, ctx+0x0C(draw_right)=DispW, dirty|=1; ScreenOpenMode left TRUE (culling+UI live). Grounded: the panel handler 0x6FB5AEC0 writes ONLY offX+mode from DisplayWidth (no separate wall global), so this run also DIAGNOSES OQ.walls_shift_source. OUTCOMES: (a) floor+walls+units all un-shift (character/waypoint bisected by panel) => walls' source IS the ctx, blocker closed; (b) floor+units un-shift but walls stay => walls recompute in PASS2 (0x6FB3B110) => name that next; (c) clicks coherent? (offX=0 persistent should self-solve per m1/m2); (d) x>=640 goes black => D2DX SurfaceIdTracker retag confirmed = the 2nd knob (m2). ==> RESULT (v1.12.0, WIN): [NOSHIFT] fired once (offX -320->0, ctxL -320->0, ctxR->1280) and HELD every frame (offX 0->0). The WORLD UN-SHIFTED -- character/waypoint bisected by the panel, walls came with it, clicks fine. Geometry reaches the dead zone (probe band[960,1280)=66-104, TILEBAND max=1277). TWO residuals remain: (1) the dead zone is still BLACK = the D2DX retag (m2's 2nd knob); (2) 1-2 FLOOR sub-layers still shift (a source our ctx authoring doesn't cover). The multi-source shift is otherwise SOLVED by authoring the no-panel lens each frame.

finding.playarea_governs_floor_and_hittestfindingtestingtrustedEsoteric Path (Doug, 2022 notes) + Necrolis + Mir Drualga (phrozen_code Discord)

THE FLOOR + THE MOUSE HIT-TEST are governed by the SAME play-area rect: playAreaW (0x6FBA9E14, RVA 0xF9E14) / playAreaH (0x6FBA9E18), narrowed to ~640 on panel-open. This is the '4-tile buffer'/Hall-of-Mirrors region: the world isn't rendered there by default. SOURCES (all trusted): EP's own d2-re-reference v0.8 note (playAreaW view struct, 'same rect governs mouse hit-test -> the non-clickable black') + our diag.c RD(0xF9E14); phrozen_code Discord 2022-11 -- Necrolis: 'd2dx might be doing it via a clipping rect' (= the RenderContext viewport/scissor we re-found); Mir Drualga (SGD2FreeRes author): 'those black rects are hardcoded in SGD2FreeRes to hide the HOM from not adding assets'; EP: 'a 4 tile buffer... also appears on the left if you open the character stats pane'. FIX (v1.12.2): author playAreaW=DispW each frame -- the bottom-void fix on the horizontal axis. Should make the floor cover the dead zone AND make click-to-move work standing still (currently only works while moving).

finding.floor_shift_internal_to_pass1findingconfirmedverifiedThe Esoteric Path

THE FLOOR SHIFT IS INTERNAL TO PASS1 (0x6FB3B0C0), not a pre-authorable global. Authoring the no-panel lens each frame BEFORE PASS1 (offX=0, ctxL=0, ctxR=DispW) un-shifts WALLS + UNITS perfectly (they read ctxL) but the FLOOR still shifts. Ruled out, each a NO-OP at our pre-PASS1 hook: playAreaW(0x6FBA9E14)=already 1280; nViewXOffset(D2FloorTileParamsStrc 0x6FB9A9CC, EP's 'terrain shift')=already 0 ([FLOORPARAMS] never fired). Yet the floor is ISSUED full-width (TILEBAND tiles reach screenX 1278, 637 in [960,1280)) -- so it's drawn wide but at the SHIFTED position. The floor-flush projection we RE'd (screenX=ctx+0x04 - ctx+0x24 + tileAbsX) SHOULD use our ctxL=0; since the floor is shifted anyway, the FLOOR uses a different scroll/offset OR its collected tileAbsX carry the shift. Candidate: the DrawGame scratch scroll (0x6FBA9F60/5C) the passes overwrite. Need to hook INSIDE PASS1 or find the floor's internal horizontal source.

council.wave6_floor_hook_too_latefindingtestingtrustedcouncil wave-6 (chat m1, gem m2, clau m4)

CONVERGENCE: the floor's shift is applied at a point our PASS1-call hook (0x6FAF4302) MISSES -- we zero the globals AFTER the floor grabbed the shifted value. Candidates: (m4/clau) the floor SNAPSHOTS the scratch scroll at the DrawGame prologue (0x6FAF42E0 mov[0x6FBA9F60],ecx / 0x6FAF42E6 mov[0x6FBA9F5C],edx from ctx, at the TOP of DrawGame, before our hook) OR bakes the shift into tileAbsX at room COLLECTION (tilesub pre-projects); (m2/gem) the floor's D2Gfx draw-offset (ctx+0x04) is set early via D2Gfx_SetDrawOffset before our hook -> hook HIGHER; (m1/chat) the floor submits the CORRECT unshifted X and D2Glide applies -DispW/4 INTERNALLY via its nScreenPanels arg in the live drawer 0x6F856840 -> force nScreenPanels=0 for the floor path. DECISIVE delta (m1): track one floor tile's submittedX (D2Client) vs vertexX (D2Glide) panel-open vs closed -- clientDelta=-320 => upstream (D2Client); glideDelta=-320 => D2Glide. v1.12.4 tests the scratch-scroll hypothesis (author 0x6FBA9F60/5C = ctx + log the delta). UNIFYING PIN (m4): both the floor AND the click are a value = DispW-320 that our hook doesn't reach; one resolution-differential scan collects EVERY DispW-320 address.

council.wave6_menu_and_click_answersfindingnotedtrustedcouncil wave-6 (gem m2, clau m4)

TWO side-answers for later. (1) MENU shrink from SetCustomResolution(1280): no native menu-exclusion; scope it DYNAMICALLY by D2 client state -- hook the game-enter/exit (state 0x01 menu / 0x03 in-game) and toggle SetCustomResolution(800 menu / 1280 in-game), or gate it in the top-level render dispatch. (2) CLICK-into-black-only-while-moving (m4): it's the click-to-move path-VALIDITY / acceptance rect, a separate rect derived from DispW-320, refreshed only by motion (stale when standing still). Fix: widen the acceptance rect (find via the resolution-differential DispW-320 scan); if clicks then register but won't path, force the walkable-region refresh independent of motion.

council.wave6b_d2dx_motion_predictionfindingtestingtrustedcouncil wave-6b (chat m10, gem m11, clau m12)

NEW LEADING THEORY (m10, corroborated by m12): the floor/roof shift is a D2DX POST-SUBMIT bug, not a D2Client origin. (1) D2DX SurfaceIdTracker retags world batches with minx>=gameSize.w/2(=640) as USER_INTERFACE when a panel is open. (2) In OnBufferSwap, D2DX applies the player MOTION-PREDICTION offset ONLY to surfaceId!=UI && cat!=Player -- so the UI-mislabeled floor/roof batches SKIP the camera offset while the walls get it -> they slide apart AS the prediction changes with click-to-move = 'follows the mouse'. Isolated batches (the palace doorway) most susceptible. Ruled out upstream: offX held 0, floor still shifts (D2Client camera not the source). m12: the mouse-tracking PROVES live-read (not baked at collection); the black split MIRRORS sides with the panel = a compositing/retag split, not a clip. DEFINITIVE TEST (m10): d2dx.cfg [optouts] nomotionprediction=true -> if the slide stops, confirmed. FIX (m10, keeps motion pred): in the grDrawVertexArrayContiguous hook, temporarily set ScreenOpenMode(0x6FBCC414)=0 for WORLD-TILE calls only (filter by return addr: D2Glide+0xBEFC DrawFloor, +0x67AB/+0x67B9 DrawWall1/2), restore after -> D2DX won't retag the floor as UI. Start with +0xBEFC.

finding.floor_vs_wall_grdraw_sitesfindingconfirmedverifiedThe Esoteric Path (verified) + chat (council)

THE FLOOR AND WALLS SUBMIT FROM DIFFERENT D2Glide CALL SITES (verified via disasm of the call to the grDraw wrapper 0x6F856026): DrawFloor -> 0x6F85BEF7 call (return 0x6F85BEFC = D2Glide+0xBEFC); DrawWall1 -> 0x6F8567A6 (return 0x67AB); DrawWall2 -> 0x6F8567B4 (return 0x67B9). Our glidevtx probe was filtering return-addrs [0x6840..0x6B40] = the WALL calls, so all our floor-drawer RE + the FloorCompX seam-compensation were hitting the WALLS, not the floor -- which is exactly what Doug saw (the correctly-placed tiles moving by +320). CORRECT floor filter = 0xBEFC. chat's public-1.13c-table mapping (DrawWall1/2=0x67AB/0x67B9, DrawFloor=0xBEFC) is CONFIRMED for our build. v1.12.6 + INI: glidevtx arg=0xBEFC, FloorCompX=320 -> compensate the FLOOR at its seam.

finding.floor_shift_ledgerfindingopenverifiedThe Esoteric Path

FLOOR+ROOF SHIFT LEDGER (evidence-backed; walls+units are FIXED, floor+roof still shift when a panel opens). Each source RULED OUT with version+log: * WALLS/UNITS fixed: v1.12.0 authoring no-panel lens (offX=0/ctxL=0/ctxR=DispW) pre-PASS1. PROVEN: [NOSHIFT] log fires each frame + visual (walls/units snap into place). * offX (0x6FBCC418): RULED OUT v1.12.5. PROVEN: [SHIFT] 'offX 0->0' held every frame, floor STILL shifted. * playAreaW (0x6FBA9E14): RULED OUT v1.12.2. PROVEN: [NOSHIFT] 'playAreaW 1280->1280' (already full, no-op). * nViewXOffset (D2FloorTileParamsStrc 0x6FB9A9CC): RULED OUT v1.12.3. PROVEN: [FLOORPARAMS] NEVER logged (condition fvx!=0 false -> already 0 at our hook). * scratch scroll (0x6FBA9F60/5C): RULED OUT v1.12.4. PROVEN: [SCRATCH] NEVER logged (already == ctx scroll). * D2Glide drawer 0x856840 vertex math: RULED OUT by DISASM -- vertexX = baseX + subtileX; the 640 midpoint feeds ONLY the cull bounds ([esp+0x30]/[esp+0x38], already NOP'd), no -DispW/4 on the vertex. * D2DX motion prediction: RULED OUT -- d2dx.cfg nomotionprediction=true -> no change (Doug); + the shift is STATIC not dynamic (Doug correction), which motion-prediction wouldn't produce. * grDraw site 0xBEFC (chat's 'DrawFloor'): RULED OUT v1.12.6. PROVEN: [PROBE glidevtx] arg=0xBEFC -> count=0 (0 hits). 0x6F85BEF7 IS a call site on disk but is NOT on the live floor path in our build. VERIFIED-live: WALLS submit via grDraw returns 0x67AB/0x67B9 (disasm: 0x6F8567A6/B4 call the wrapper 0x856026; probe with arg=0x6840 fired in v1.11-1.12.5). 0x6840==walls is INFERRED (Doug: compensating that filter moved the CORRECTLY-placed tiles) + chat labels them DrawWall1/2. OPEN: the FLOOR's actual LIVE grDraw return address is UNIDENTIFIED (not 0x6840=walls, not 0xBEFC=count0). RIGOROUS NEXT: an UNFILTERED return-address logger (probe arg=0) that dumps every distinct grDraw return addr + its vertex-X range + count -> identify the floor by its signature, THEN compensate/fix that exact site.

council.wave7_grdraw_seam_deltafindingtestingtrustedcouncil wave-7 (chat, gem, clau)

CONVERGENCE: measure the floor batch's vertex X at the grDraw seam, panel-CLOSED vs panel-OPEN. dMinX=-320 => D2Client submits the floor shifted (UPSTREAM); dMinX=0 => D2DX shifts it (DOWNSTREAM). Also capture the caller return-addr to NAME the floor's live site (our 0xBEFC guess fired 0x). chat: D2DX's 0xBEFC 'DrawFloor' is only an IdentifyGameAddress label, not the live path; forward is unchanged (OnDrawVertexArrayContiguous copies x/y verbatim). gem: if downstream, read D2DX's VS projection matrix via a D3D11 VSSetConstantBuffers vtable hook (matrix[12]=X translation). clau: DISSENT-then-agree -- 'mouse kills it' argument uses the RETRACTED mouse-tracking premise (Doug corrected to STATIC), so its mouse-sweep isn't our discriminator; BUT clau's mouse-INDEPENDENT argument is strong: walls render correct through the SAME D2DX pipeline, so if D2DX shifted world batches walls would shift too -> predicts dMinX=-320 (client-side). clau CAUTION taken: do NOT do the ScreenOpenMode=0 mask (attempt-A anti-pattern: fixes readers who look, misses others, desyncs hit-test). IMPLEMENTED v1.12.7 [Probes] Survey=1 + glidevtx arg=0 -> [SURVEY] rows per caller, closed vs open.

finding.live_grdraw_callersfindingopenverifiedThe Esoteric Path (v1.12.8 [SURVEY])

THE LIVE grDraw CALLERS (v1.12.8 Survey, panel-CLOSED only so far): D2Glide+0x6A55 (n=87710, x=-32..1296, the dense one), 0xA344 (n=16767, x=0..1151), 0xA555 (n=5340, x=303..1152), 0x704A (n=895, x=1216..1296), 0x9AF4 (n=293, x=11..788). CORRECTION (rigor): finding.floor_vs_wall_grdraw_sites claimed walls=0x67AB/0x67B9 (from disasm of call sites 0x6F8567A6/B4) -- but those return-addrs DO NOT APPEAR in the survey, so they do NOT fire on the live path. The disasm found call sites that aren't reached. The REAL callers are the 5 above. Still OPEN: which is the FLOOR (need panel-OPEN data -> the caller with dMinX~-320). v1.12.8's run was the menu run (open[n=0] everywhere); a park+toggle run is needed. NOTE the survey only counts IN-GAME frames (eso_probe_frame runs from the DrawGame hook, not at the menu).

finding.menu_worldctx_gatefindingconfirmedverifiedThe Esoteric Path (v1.12.9 [D2DXWiden] log)

MENU FIX. v1.12.9 grSstWinOpen log: the IN-GAME open = res=4099(0x1003) with worldCtx=0x214439AC (NON-NULL); the MENU opens = res=7 and res=4097 with worldCtx=NULL. The world render ctx (*0x6FBCC3C4) is set at the in-game open and null at the menu -> gate is worldCtx!=null. FIX v1.13.0: eso_d2dx_set_wide calls SetCustomResolution(1280) only when worldCtx!=null -> menu keeps native size, in-game world stays widened. (grSstWinOpen arg1=window handle; the resolution is arg2=[esp+0x2C].)

finding.survey_floor_is_downstreamfindingtestingverifiedThe Esoteric Path (v1.12.9 [SURVEY] open data)

SURPRISE: the floor shift is likely DOWNSTREAM in D2DX, not at the D2Client->grDraw submit. Survey open-vs-closed, 5 live callers: 0x6A55 (floor, n=828558, closed x=-32..1311 / open x=-32..1296 => dMinX=0, dMaxX~-15: STABLE, NOT shifted at submit); 0x09AF4 (dead-zone FILL, open x=640..1280 n=22082 = the world we enabled behind the panel, WORKING); 0xA555 (dMinX=-84); 0xA344 (dMinX=33); 0x704A (dMinX=16). NO CALLER submits shifted by -320. So the floor is submitted un-shifted and D2DX shifts it downstream => chat/gem's branch, AGAINST clau's client-side prediction (reconciled: D2DX shifts only floor-category batches, not walls). CAVEAT: aggregate min/max is confounded by the changing tile set on open, but the ABSENCE of any -320 across all 5 callers is robust. nomotionprediction=true gave no change, so it's a D2DX transform OTHER than motion-prediction. NEXT: specific-tile track OR gem's D3D11 VSSetConstantBuffers matrix read.

finding.screenshiftx_is_floor_shiftfindingtestingtrustedThe Esoteric Path (SGD2FreeRes + mir + planqi ingest 2026-07-18)

BREAKTHROUGH: the -320 FLOOR/ROOF shift source is ScreenShiftX = D2Client+0x11B9A0 (mir names it 'ScreenShiftX'; planqi names 0x11B9A0 'PanelOffsetX -- horizontal shift applied when side panels are open'). SGD2FreeRes REPLACES vanilla SetScreenShift with Sgd2fr_D2Client_SetScreenShift (set_screen_shift/replacement.cpp L54-63; hook D2Client+0xC39F6 in 1.13c, found via write-BP on ScreenShiftX/Y): screen_shift_x = display_width/2 - 640/2 => at 1280 wide = 640-320 = the centering shift. The floor/tile projection reads ScreenShiftX. Our no-shift lens zeroed offX (GeneralPlayAreaCameraShiftX 0x11C418) + ctx draw_left -- fixing WALLS+UNITS -- but NEVER touched 0x11B9A0, so FLOOR/ROOF stayed shifted. That is the exact residual. NEXT: author 0x11B9A0=0 each frame alongside offX (or neutralize the SetScreenShift hook @0xC39F6); x32dbg live-read of 0x11B9A0 panel-open expects the -320-class value.

reframe.d2dx_no_per_category_transformfindingconfirmedtrustedThe Esoteric Path (bolrog + Necrolis/d2dx-annihilus source read 2026-07-18)

OVERTURNS finding.survey_floor_is_downstream. Full source read of D2DX (bolrog + Necrolis fork): D2DX applies NO per-category floor-vs-wall positional transform -- TextureCategory::Floor(4)/Wall(6) are never used to offset geometry (GameHelper::RefineTextureCategoryFromGameAddress only TAGS batches for AA). The ONLY post-capture world shift is UnitMotionPrediction (D2DXContext.cpp OnBufferSwap ~408-436): UNIFORM across all world geometry (floor AND wall), skipped only for surfaceId==USER_INTERFACE(16383). We already set nomotionprediction=true -> no change. RenderContext::UpdateViewport sets ONE uniform viewport+scissor; Constants CB carries only screenSize/invScreenSize, no translate. => the -320 floor shift is NOT downstream in D2DX; it is upstream (ScreenShiftX). The survey's dMinX 'downstream' read was an artifact of the confounded aggregate + not having neutralized 0x11B9A0.

finding.sgd2freeres_deadzone_architecturefindingconfirmedtrustedThe Esoteric Path (SGD2FreeRes source read 2026-07-18)

The panel/dead-zone architecture SGD2FreeRes imposes (clean-room from its develop-branch source): (1) SetGeneralDisplayWidthAndHeight -> GeneralDisplayWidth/Height (0xDBC48/4C). (2) SetScreenShift (hook D2Client+0xC39F6) -> ScreenShiftX/Y (0x11B9A0/4) centering = the -320 floor shift. (3) SetTileCullingBound (hook D2Client+0x8A4D0; CullingSpec{flags@0x0, draw_window_rect RECT@0x4}) -> tile draw + cull windows. (4) DrawLeft/RightScreenBackground (hook D2Client+0xC3AA6) -> paints the BLACK dead-zone: d2gfx::DrawRectangle(0,0,width/2,height) 'to stop transparent-DC6 cheaters' + tiles 800BorderFrame around the 640 play area; stock bg draws NOP'd at +0x271ED / +0x270F1. => black perimeter = DrawScreenBackground; floor shift = SetScreenShift. To reveal the world behind the panel: neutralize DrawScreenBackground and zero ScreenShiftX.

finding.live_rpm_2026_07_18findingconfirmedverifiedThe Esoteric Path (live elevated RPM/WPM session 2026-07-18 on running Game.exe)

LIVE against the running 1.13c binary (esoteric.dll mod ON), via elevated ReadProcessMemory/WriteProcessMemory. (1) worldCtx (D2Client+0x11C3C4) CONFIRMED null@menu, non-null in-game (0x214739D4) -> verifies finding.menu_worldctx_gate vs the binary. (2) FALSIFIES finding.screenshiftx_is_floor_shift: ScreenShiftX (0x11B9A0) = +320 CONSTANT in-game (SGD2FreeRes centering, re-written per-frame), identical in mode 0/1/2; a live freeze of 0x11B9A0=0 did NOT move the floor (only a border element flickered) -> the FLOOR does NOT read ScreenShiftX. (3) PANEL-DELTA SCAN (12 globals + render-ctx struct fields, panel-closed vs inventory vs character): opening a panel changes NO horizontal shift global -- ScreenShiftX, offX(0x11C418; our lens holds 0), OffsetPtX(0x11C1F8=0), ctxL(0), ctxR(1280), playAreaW(0xF9E14=1280) ALL constant across mode 0/1/2. Only ctxScrollX(=global 0x119960) and mouse offsets vary, and that is CAMERA MOVEMENT (character walking), not the panel. (4) CONCLUSION: the floor residual shift is STATIC (matches Doug: 'always shifted, not sliding') and is driven by NO authorable global on panel-open -> it is INTERNAL to the floor pass (PASS1 / FloorFlush 0x9ED50), a constant floor-vs-wall X-base difference. Re-confirms finding.floor_shift_internal_to_pass1; global-poke approach exhausted. NEXT: x32dbg breakpoint inside FloorFlush 0x9ED50 with a panel open -> read the X base the floor computes and compare to the wall pass (PASS2 0x8B110). RPM cannot observe internal computed values.

finding.d2floortileparams_floor_shiftfindingtestingtrustedThe Esoteric Path (Discord corpus mine 2026-07-19)

CORPUS-CONFIRMED floor-shift mechanism -- converges with finding.floor_shift_internal_to_pass1. The FLOOR is a SEPARATE drawer from walls/roofs (Necrolis: MULTIRES_DrawGroundTiles vs DrawLowerWallTiles/DrawUpperWallTiles/DrawRoofTiles; SwineFlu confirms floor is a distinct recursive fn per Necrolis's reversed code at d2mods.info t=59615). Floor horizontal placement = D2FloorTileParamsStrc @ D2Client+0xEA990 (0x6FB9A990, sizeof 0x44): nScreenPanels(+0x20)=which panels open, pView(+0x24), clip nXMin/Max nYMin/Max(+0x2C..0x38), nViewXOffset(+0x3C = D2Client+0xEA9CC)/nViewYOffset(+0x40). Necrolis: 'anyone chasing the D2DX floor -320 shift should hook the FILL of this struct and watch nScreenPanels -> nViewXOffset.' Struct filled INSIDE the floor path (1.10f fill 0x6FAA3330 -- rebase to 1.13c by xref of writes to +0xEA990). EXPLAINS why our lens fails on the floor: pre-PASS1 hook authors the render ctx (walls read it -> fixed) but nViewXOffset is set LATER internally from nScreenPanels; our ledger's 'nViewXOffset already 0' was a PRE-fill timing error. FIX: hook the struct fill / DrawGroundTiles and zero nViewXOffset (or force nScreenPanels=0 for the floor pass only) so floor matches the unshifted walls. Cull rect = D2RoomCullArgsStrc{pView,pCullingRect,pClientRoom,bTilesOnly} (Necrolis).

finding.floor_shift_is_ctx_scrollxfindingconfirmedverifiedThe Esoteric Path (5-front disasm assault 2026-07-19, direct from 1.13c D2Client.dll)

ROOT CAUSE PINNED. The floor+roof static -320 shift is ctx[+0x24] (scrollX), NOT ctx[+0x04] and NOT any global we ruled out. TWO PROJECTION FAMILIES: (1) FLOOR+ROOF flush @0x6FB4ED50 (D2Client+0x9ED50): screenX = ctx[+0x04](drawL) - ctx[+0x24](scrollX) + tileAbsX -- NO offX term. (2) WALLS+UNITS project via offX=ScreenXShift 0x6FBCC418 (RVA 0x11C418) which our lens zeroes -> they re-center. ctx = *0x6FBCC3C4 (RVA 0x11C3C4) = the SAME struct the lens writes (confirmed: both callers of tile drawer 0x6FB4EC10 -- 0x6FB3B224 in PASS2 + 0x6FB4EE7A -- push edi=ctx; PASS1 0x6FB3B0C0->DrawRooms 0x6FB2A9F0 on same ctx). So ctx[+0x04]=0 DOES reach the floor -> not the residual. THE 320 ENTERS ctx[+0x24] at DrawGame 0x6FAF4230: scrollX = scrollCenterX(thunk 0x6FABC1D2 on view *0x6FBCBBFC=playerBase) - (R-L)/2 (0x6FAF4270-79), written 0x6FAF42F8 'mov [esi+0x24],ecx' (esi=*0x6FBCC3C4) right before PASS1 call 0x6FAF4302. Lens moves ctx[+0x04]/[+0x0C] SYMMETRICALLY (-320,960->0,1280) so R-L stays 1280, (R-L)/2 stays 640, scrollX UNAFFECTED -> panel camera re-center +DispW/4=320 survives in ctx[+0x24]. Floor pan = 0-(scrollX_nopanel+320) = static -320. DispW/4=1280/4=320. Roof same path. SUPERSEDES finding.screenshiftx_is_floor_shift, finding.survey_floor_is_downstream, finding.d2floortileparams_floor_shift (that struct was 1.10f). FIX: correct ctx[+0x24] to hold no-panel pan before PASS1 (extend lens; ctx[+0x24]-=DispW/4 for open-panel side, sign per ScreenOpenMode 0x6FBCC414). Floor+roof only. Confirm sign/magnitude via one live RPM of ctx[+0x24] panel-open vs closed standing still.

finding.floor_shift_sgd2_tilecullingfindingconfirmedverifiedThe Esoteric Path (live disasm 2026-07-19, SGD2FreeRes.dll in the running process)

DEFINITIVE ROOT CAUSE of the floor/roof panel-shift. It is in SGD2FreeRes.dll (loaded @0x739C0000), NOT D2Client -- D2Client's whole floor path is 100% VANILLA (verified by live-vs-disk patch map: DrawGame 0x44230, scroll write 0x442F8, floor flush 0x9ED50, floor drawer 0x9EC10, DrawRooms 0x7A9F0, PASS1 0x8B0C0, PASS2 0x8B110 all byte-identical to disk). SGD2FreeRes hijacks SetTileCullingBound: D2Client+0x8A4D0 is patched 'jmp 0x739C16B0' (trampoline) -> real fn 0x739D21D0. Inside 0x739D21D0 a PANEL-CONDITIONAL block: 0x739D2212 call 0x739D64A0 (nonzero when a side panel open); 0x739D221A je 0x739D2230 (skip if none); 0x739D221C add [ebx+4],0xFFFFFF60 (top-=160); 0x739D2223 add [ebx],0xFFFFFEC0 (LEFT-=320=DispW/4=0x140); 0x739D2229 add [ebx+8],0x140 (RIGHT+=320). ebx = the tile-cull/draw window (arg+0x14). => on panel-open SGD2 moves the floor's tile window by +/-320. The vanilla flush (screenX = ctx+0x04 - ctx+0x24 + tileAbsX) then faithfully draws the shifted tiles; walls read offX (our lens holds 0) so they don't move -> floor vs walls split by 320. RECONCILES the paradox that render ctx (drawL/scrollX) measured FLAT open-vs-closed (floor_scan same-position: both drawL=0 scrollX=-208, delta 0): the shift rides in tileAbsX collected from the shifted cull window, NOT in the ctx. Other SGD2 hooks confirmed live: SetScreenShift 0xC39F6 -> 0x739D1F80; DrawScreenBg 0xC3AA6 -> 0x739C10B0; stock left/right bg NOP'd @0x271ED/0x270F1. FIX target: neutralize/adjust the panel block at 0x739D221C or its gate je@0x739D221A (NOP-test flips je->jmp). CONFIRMED SHIFT (Doug's 3 screenshots: no-panel centered, inventory=world pushed left, character=pushed right).

finding.floor_shift_ledger_v2findingconfirmedverifiedThe Esoteric Path (attempt/failure ledger 2026-07-19)

FLOOR-SHIFT HYPOTHESIS LEDGER (chain of superseded theories, each with why it fell): (a) ScreenShiftX D2Client+0x11B9A0 -- FALSIFIED live (WriteProcessMemory froze it to 0, floor did not move; only a border flickered). (b) 'D2DX shifts floor-category downstream' -- FALSIFIED by reading bolrog+Necrolis D2DX source (NO per-category transform; only uniform UnitMotionPrediction, and nomotionprediction=true gave no change). (c) D2FloorTileParamsStrc.nViewXOffset @0x6FB9A990/0x6FB9A9CC -- FALSIFIED: that instance address is 1.10f, ZERO refs in our 1.13c DLL (xref + immediate byte-search); the struct LAYOUT transfers but the 1.13c instance differs. (d) 'ctx+0x24 scrollX carries +320' (5-front on-disk disasm assault) -- FALSIFIED live: floor_scan same-position closed-vs-open showed drawL=0/scrollX=-208 in BOTH (delta 0). (e) CONFIRMED: SGD2FreeRes SetTileCullingBound live patch shifts the tile-cull window +/-320 on panel-open (finding.floor_shift_sgd2_tileculling). META-LESSON: (a)-(d) all came from analyzing the ON-DISK vanilla DLL. The shift is in a MOD's live patch. Doug flagged the layered-mod interaction; the live-vs-disk patch map resolved it. Also: a same-position live read (stand still) is required -- 20-min-apart readings are confounded by camera movement.

finding.freeze_scrollx_flush_livefindingconfirmedverifiedThe Esoteric Path (live WPM freeze test 2026-07-19, Doug visual+screenshot)

FREEZE-scrollX TEST (hammered ctx+0x24 = *(D2Client+0x11C3C4)+0x24 from -240 to -600, 3439 writes/30s): BOTH floor AND walls flickered/slid (Doug: 'a lot of flickering on the walls and floors'). CONCLUSIONS: (1) the D2Client flush 0x6FB4ED50 / render-ctx path IS the LIVE world render -- SGD2FreeRes does NOT draw the floor via its own renderer; the vanilla screenX = ctx+0x04(drawL) - ctx+0x24(scrollX) + tileAbsX is what shows. Rules IN the D2Client path. (2) CORRECTS finding.floor_shift_is_ctx_scrollx: walls do NOT use offX-not-scrollX -- BOTH floor and walls read ctx+0x24. The world renders as a unit through this ctx. (3) By elimination: drawL+scrollX are FLAT panel-open-vs-closed (floor_scan same-position delta 0), the flush is live, so the panel shift can only ride in tileAbsX (the COLLECTED tile coords). Leading theory: our no-shift lens forces drawL=0 AFTER DrawRooms already collected tiles using the game's shifted drawL (the council-wave-6 'hook too late'). ALSO: NOP of SGD2 SetTileCullingBound panel-block (0x739D221C) had ZERO impact -> that cull-widen is not the shift. NEXT: read tileAbsX/screenX at the flush (codecave probe or hardware BP) panel-open vs closed; and re-check WHEN our lens sets drawL vs when DrawRooms reads it.

finding.floor_shift_measured_320findingconfirmedverifiedThe Esoteric Path (Doug's waypoint experiment + template-match 2026-07-19)

OBJECTIVE PIXEL-PRECISE MEASUREMENT of the floor shift. Setup (Doug): Barbarian standing on the Act1 encampment waypoint, no merc, no effects. The waypoint = co-located FLOOR marker (brown cross-diamond) + UNIT markers (blue swirls). Captured game frames closed vs open, template-matched each marker: waypoint brown-cross (FLOOR tile) moved LEFT 480 screen-px (dy 0, conf 0.56); blue swirl (UNIT) 0px (conf 0.82); campfire embers (OBJECT) 0px (conf 0.68); walls 0 (prior overlay). Screen is 1920w upscaled by D2DX from game-render 1280 (DispW) => factor 1.5 => 480 screen-px = 320 GAME-px = exactly DispW/4. DEFINITIVE: the FLOOR-TILE LAYER shifts exactly DispW/4 = 320 game-px LEFT on panel-open; units/objects/walls hold at 0 (offX layer, our lens already fixes them). Confirms Doug's exact claim to the pixel. RECONCILES the whole investigation: the render ctx (drawL/scrollX) reads FLAT open-vs-closed because the 320 rides in the collected floor tileAbsX, NOT the camera scalars; the earlier full-floor overlay aliased the 320 (=~5 tiles) but the waypoint's non-periodic cross revealed it. Method note: capture the game window via PIL ImageGrab (D2DX/D3D content grabs fine); measure in GAME-space = screen/1.5. FIX TARGET: un-shift the floor tile collection (tileAbsX) by DispW/4. Ruled out as the source: ScreenShiftX 0x11B9A0 (poke null), SGD2 SetTileCullingBound cull-block 0x739D221C (NOP null), the render ctx (flat). It is in the floor tile-coordinate computation (DrawRooms / the floor pass tileAbsX).

Structs 115

Reversed data layouts — offsets verified against the live binaries.

struct.D2Client_RenderContextstructconfirmedverifiedThe Esoteric Path — verified vs the 1.13c binary
D2Client

The world render context. Instance pointer = *0x6FBCC3C4. It IS the world view-state: the tile projection reads screenX = (+0x04) - (+0x24) + tileAbsX, screenY = tileAbsY - (+0x28) + (+0x08). A side panel writes +0x04 = offX = -320 (the shift) and narrows +0x0C to the play-area edge (the clip). See finding.terrain_shift_is_offx_ctxL and reframe.one_cause_view_state.

offfieldtypenote
+0x00dirtyu32geometry-dirty bit; |=1 forces mirror/cull rebuild for a wider window
+0x04draw_lefti32draw-window L; equals offX (0 no-panel, -320 panel-open) -> the SHIFT
+0x08draw_topi32draw-window T
+0x0Cdraw_righti32draw-window R; iteration bound = 1280 no-panel, 960 panel-open -> the CLIP
+0x10draw_bottomi32draw-window B
+0x24scroll_xi32scroll origin X (camera); NOT touched by the panel
+0x28scroll_yi32scroll origin Y
struct.UnitAnystructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

The universal client-side game-entity struct; every player, monster, item, and object shares this layout, with a union selecting the type-specific data block.

offfieldtypenote
0x00dwTypeDWORD
0x04dwClassIdDWORD
0x08pMemPoolvoid*
0x0CdwUnitIdDWORD
0x10dwModeDWORD
0x14pPlayerDataPlayerData*union @0x14
0x14pItemDataItemData*union @0x14
0x14pMonsterDataMonsterData*union @0x14
0x14pObjectDataObjectData*union @0x14
0x18dwActDWORD
0x1CpActAct*
0x20dwSeed[2]DWORD
0x28dwInitSeedDWORD
0x2CpPathPath*union @0x2C
0x2CpStaticPathStaticPath*union @0x2C
0x30_3[5]DWORD
0x44dwGfxFrameDWORD
0x48dwFrameRemainDWORD
0x4CwFrameRateWORD
0x4E_4WORD
0x50pAnimDataDWORD*
0x54pGfxInfoGfxInfo*
0x58pGfxInfo2GfxInfo*
0x5CpStatsExStatListEx*
0x60pInventoryInventory*
0x64pLightLight*
0x68dwLightRadiousDWORD
0x6CnPalShiftIdxWORD
0x6EUpdateTypeWORD
0x70pUpdateUnitUnitAny*
0x74pQuestRecordDWORD*
0x78bSparkyChestBYTE
0x79_6a[3]BYTE
0x7CpTimerArgsDWORD
0x80dwSoundSyncDWORD
0x84_7[2]DWORD
0x8CwXWORD
0x8EwYWORD
0x90pEventDWORD
0x94dwOwnerTypeDWORD
0x98dwOwnerIdDWORD
0x9C_8[2]DWORD
0xA4pHoverTextHoverText*
0xA8pInfoInfo*
0xACptCombatvoid*
0xB0dwLastHitClassDWORD
0xB4_9[4]DWORD
0xC4dwFlagsDWORD
0xC8dwFlags2DWORD
0xCC_10[5]DWORD
0xE0pChangedNextUnitAny*
0xE4pRoomNextUnitAny*changed 14.12.11
0xE8pListNextUnitAny*
0xECpMsgFirstpMsg*
0xF0pMsgLastpMsg*
struct.PlayerDatastructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Per-player data block (name, quest flags, waypoints, assigned/switch skills, trade state) hung off UnitAny.pPlayerData; copied from the serverside layout.

offfieldtypenote
0x00szName[16]char
0x10QuestsFlags[3]QuestFlags*
0x1CpNormalWaypoint[3]Waypoint*
0x28ptAiGeneralvoid*
0x2C_1[2]DWORD
0x34KillCounterWORD*Monster
0x38_1bDWORD
0x3C_2[4]DWORD2[2] is merc related
0x4CdwTradeTickDWORDmby InactivityTime
0x50nTradeStateDWORD
0x54_3DWORD
0x58dwAcceptTradeTickDWORD
0x5CpTradevoid*
0x60_4[3]DWORD
0x6CdwBoughtItemIdDWORD
0x70dwRightSkillDWORD
0x74dwLeftSkillDWORD
0x78dwRightSkillFlagsDWORD
0x7CdwLeftSkillFlagsDWORD
0x80dwSwitchRightSkillDWORD
0x84dwSwitchLeftSkillDWORD
0x88dwSwitchRightSkillFlagsDWORD
0x8CdwSwitchLeftSkillFlagsDWORD
0x90_5[3]DWORD
0x9CpClientDataClientData*
0x100_6[50]DWORD
0x168GameFrameDWORD
struct.MonsterDatastructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Per-monster data block (txt record, gfx components, name seed, unique/champion/minion bit-flags, enchantments, AI pointer, name) hung off UnitAny.pMonsterData.

offfieldtypenote
0x00pMonStatsTxtMonStatsTxt*
0x04Components[16]BYTE
0x14NameSeedWORD
0x16(anonymous bitfield)BYTE : 1 (x8)fOther, fSuperUniq, fChampion, fUnique, fMinion, fPossesed, fGhostly, fMultishot (each 1 bit)
0x17dwLastModeBYTE
0x18dwDurielDWORD
0x1CanEnchants[9]BYTE
0x25_1BYTE
0x26wUniqueNoWORD
0x28pAiGeneralAiGeneral*
0x2CwName[28]wchar_t*
struct.ObjectDatastructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Per-object data block (object txt record, interaction type, owner name) hung off UnitAny.pObjectData.

offfieldtypenote
0x00pObjectTxtObjectTxt*
0x04InteractTypeBYTE
0x05_1[3]BYTE
0x08_2[8]DWORD
0x28szOwner[16]char
struct.ItemDatastructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Per-item data block (quality, seeds, owner, flags, prefixes/suffixes, body & inventory location, socket linkage) hung off UnitAny.pItemData.

offfieldtypenote
0x00QualityNoDWORD
0x04LowSeedDWORD
0x08HighSeedDWORD
0x0COwnerIDDWORD
0x10InitSeedDWORD
0x14CommandFlagsDWORD
0x18ItemFlagsDWORD
0x1C_1[2]DWORD
0x24ActionStampDWORD
0x28FileIndexDWORD
0x2CItemLevelDWORD
0x30ItemFormatWORD
0x32RarePrefixWORD
0x34RareSutfixWORD
0x36AutoPrefixWORD
0x38MagicPrefix[3]WORD
0x3EMagicSutfix[3]WORD
0x44BodyLocBYTElocation on body = xpos of item (only if on body) (spread)
0x45InvPageBYTE0 = inventory, 3 = cube, 4 = stash (spread)
0x46_2[2]BYTE
0x48EarLvlBYTE
0x49InvGfxIdxBYTE
0x4AszPlayerName[16]char
0x5A_3[2]BYTE
0x5CpNodeInvInventory*
0x60_4DWORD
0x64pNextItemUnitAny*
0x68NodePosBYTE
0x69NodePosOtherBYTE
struct.PathstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Movement/position path for an active (moving) unit: current and target coordinates, owning rooms, collision flags, path type, target unit, and facing direction.

offfieldtypenote
0x00xOffsetshort
0x02xPosshort
0x04yOffsetshort
0x06yPosshort
0x08xUnknownDWORD16 * (wInitX - wInitY) <- Mby AutomapX
0x0CyUnknownDWORD8 * (wInitX + wInitY + 1) <- Mby AutoampY
0x10xTargetshort
0x12yTargetshort
0x14_2[2]DWORD
0x1CpRoom1Room1*
0x20pRoomUnkRoom1*
0x24_3[3]DWORD
0x30pUnitUnitAny*
0x34dwFlagsDWORD0x40000 -> PATH_MISSILE_MASK
0x38_4DWORD
0x3CdwPathTypeDWORD
0x40dwPrevPathTypeDWORD
0x44dwUnitSizeDWORD
0x48_5[2]DWORD
0x50dwCollisionFlagDWORD0x1804 <- bFlying, 0x3401 <- bOpenDoors, 0x3C01 <- Cannot Open Doors, 0x804 <- Ghost, 0x1C09 <- Player
0x54_5dDWORD
0x58pTargetUnitUnitAny*
0x5CdwTargetTypeDWORD
0x60dwTargetIdDWORD
0x64bDirectionBYTE
struct.StaticPathstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Simplified position record (size 0x20) for units that do not move (items/objects): owning room plus offset and grid position.

offfieldtypenote
0x00pRoom1Room1*
0x04xOffsetDWORD
0x08yOffsetDWORD
0x0CxPosDWORD
0x10yPosDWORD
0x14_1[2]DWORD
0x1CdwFlagsDWORD
struct.InfostructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Skill container hung off UnitAny.pInfo: mem pool plus pointers to the first, left-hand, and right-hand active skills.

offfieldtypenote
0x00pMemPoolDWORD*
0x04pFirstSkillSkill*
0x08pLeftSkillSkill*
0x0CpRightSkillSkill*
struct.SkillstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A single skill entry in a unit's skill linked list: txt record, next-link, mode, level, and flags.

offfieldtypenote
0x00pSkillsTxtSkillsTxt*
0x04pNextSkillSkill*
0x08SkillModeDWORD
0x0C_1[7]DWORD
0x28dwSkillLevelDWORD
0x2C_2[2]DWORD
0x30dwFlagsDWORD
struct.SkillInfostructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Skill identifier record referenced by Skill.pSkillInfo; holds the skill id. NOTE: absent from D2Structs_113D.h (which uses SkillsTxt* directly) — this definition comes only from the d2bs D2Structs.h.

offfieldtypenote
0x00wSkillIdWORD
struct.Room1structsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Active/in-memory room node holding collision map, units, and nearby-room links for a level.

offfieldtypenote
0x00pRoomsNearRoom1**pptVisibleRooms
0x04_1DWORD
0x08_1svoid*
0x0C_1bDWORD
0x10pRoom2Room2*
0x14_2[2]DWORD
0x1CpUnitChangedUnitAny**
0x20CollCollMap*
0x24dwRoomsNearDWORDdwVisibleRooms
0x28nPlayerUnitsDWORD
0x2CpActAct*
0x30_4DWORD
0x34nUnknownDWORD
0x38_5[4]DWORD
0x48pClientsClientData**
0x4ChCoordsRoomCoords
0x6ChSeedD2Seed
0x74pUnitFirstUnitAny*
0x78nNumClientsDWORD
0x7CpRoomNextRoom1*
struct.Room2structsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Static/preset room descriptor (DRLG layout) linking to its Room1, level, tiles, and preset units.

offfieldtypenote
0x00_1[2]DWORD
0x08pRoom2NearRoom2**
0x0C_2[2]DWORD
0x14hSeedD2Seed
0x1C_2bDWORD
0x20pType2Infostruct { DWORD dwRoomNumber; DWORD* _1; DWORD* pdwSubNumber; }*inline anon struct: dwRoomNumber //0x00, _1 //0x04, pdwSubNumber //0x08. points to 0x70 struct if dwPresetType == 1, 0xF8 struct if == 2
0x24pRoom2NextRoom2*
0x28dwRoomFlagsDWORD
0x2CdwRoomsNearDWORD
0x30pRoom1Room1*
0x34dwPosXDWORD
0x38dwPosYDWORD
0x3CdwSizeXDWORD
0x40dwSizeYDWORD
0x44_3DWORD
0x48dwPresetTypeDWORD
0x4CpRoomTilesRoomTile*
0x50_4[2]DWORD
0x58pLevelLevel*
0x5CpPresetPresetUnit*
struct.LevelstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A single DRLG level (map area) with its rooms, size/position, seed, presets, and warp/room-center coordinates. sizeof = 0x230.

offfieldtypenote
0x00dwDrlgTypeDWORD1 - maze, 2 - preset, 3 - wilderness
0x04dwLevelFlagsDWORD
0x08_1[2]DWORD
0x10pRoom2FirstRoom2*
0x14(anonymous union)union { LvlMazeTxt* pMazeTxt; PresetData* pPreset; WildernessData* pWilderness; }pMazeTxt for dwDrlgType==1 (RANDOM MAZE); pPreset for dwDrlgType==2 (PRESET MAP); pWilderness for dwDrlgType==3 (RANDOM AREA WITH PRESET SIZE)
0x18_2DWORD
0x1CdwPosXDWORD
0x20dwPosYDWORD
0x24dwSizeXDWORD
0x28dwSizeYDWORD
0x2C_3[96]DWORD
0x1ACpNextLevelLevel*
0x1B0_4DWORD
0x1B4pMiscActMisc*
0x1B8_5[2]DWORD
0x1C0dwLevelTypeDWORD
0x1C4hSeedD2Seed
0x1CCpLevelPresetsLevelPreset*
0x1D0dwLevelNoDWORD
0x1D4_6[3]DWORD
0x1E0(anonymous union)union { DWORD RoomCenterX[9]; DWORD WarpX[9]; }
0x204(anonymous union)union { DWORD RoomCenterY[9]; DWORD WarpY[9]; }
0x228dwRoomEntriesDWORD
0x22C_7DWORD
struct.ActstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Per-act runtime container holding the act's environment, town level, map seed, root room, and DRLG misc pointer.

offfieldtypenote
0x00_1aDWORD
0x04pEnviromentActEnvironment*
0x08dwTownLvlDWORD
0x0CdwMapSeedDWORD
0x10pRoom1Room1*
0x14dwActDWORD
0x18hTile[12]DWORDnot sure, some inline struct of 0x30 size seems fit
0x48pMiscActMisc*
0x4C_4DWORD
0x50_5DWORD
0x54_6DWORD
0x58_7DWORD
0x5CpMemPoolvoid*
struct.ActMiscstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

DRLG-level misc/generation state for an act (aka Drlg): seeds, tile pointers, callbacks, level list, and back-pointer to Act. sizeof(0x48C).

offfieldtypenote
0x00hSeedD2Seed
0x08_1DWORD
0x0CpTileDWORD*
0x10_1g[15]DWORD
0x4cpfnCallBackDWORD
0x50_1a[16]DWORD
0x90pVisDrlgVis*
0x94dwStaffTombLevelDWORD
0x98_2aDWORD
0x9CpGameGame*
0xA0_2[236]DWORD
0x450nDiffLvlDWORD
0x454fnCallbackDWORD*
0x458InitSeedDWORD
0x45C_2d[4]DWORD
0x46CpActAct*
0x470_3[2]DWORD
0x478pMemPoolvoid*D2PoolManager*
0x47CpLevelFirstLevel*
0x480nActDWORD
0x484_5DWORDTomb Levels Related 66 + rand(7)
0x488fnCallback2DWORD*
struct.PresetUnitstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A preplaced (preset) unit within a Room2 — class/type id and tile position, linked as a list.

offfieldtypenote
0x00_1DWORD
0x04dwClassIdDWORD
0x08dwPosXDWORD
0x0CpPresetNextPresetUnit*
0x10_3DWORD
0x14dwTypeDWORD
0x18dwPosYDWORD
struct.RoomTilestructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A tile linking a Room2 to a neighboring room (warp/connection), stored as a linked list.

offfieldtypenote
0x00pRoom2Room2*
0x04pNextRoomTile*
0x08_2[2]DWORD
0x10nNumDWORD*
struct.StatstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A single stat entry: sub-index, stat index, and value.

offfieldtypenote
0x00wSubIndexWORD
0x02wStatIndexWORD
0x04dwStatValueDWORD
struct.StatExstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A stat vector/array container: pointer to a Stat array plus counts (size 0x8).

offfieldtypenote
0x00pStatStat*An Array[wStatCount]
0x04wStatCountWORD
0x06wnBitSizeWORD
struct.StatListstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A node in a unit's linked list of stats (size 0x40).

offfieldtypenote
0x00pMemPoolvoid*
0x04pUnitUnitAny*
0x08dwOwnerTypeDWORD
0x0CdwOwnerIdDWORD
0x10dwFlagsDWORD
0x14dwStateNoDWORD
0x18ExpireFrameDWORD
0x1CSkillNoDWORD
0x20sLvlDWORD
0x24StatsStatEx
0x2CpPrevLinkStatList*
0x30pNextLinkStatList*
0x34pPrevStatList*
0x38fpStatRemovevoid*
0x3CpNextStatList*
struct.StatListExstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Extended stat list container tracking base/full/mod stats for a unit or item (size 0x64).

offfieldtypenote
0x00pMemPoolDWORD
0x04_1DWORD
0x08dwOwnerTypeDWORD
0x0CdwOwnerIdDWORD
0x10dwListFlagsDWORD
0x14_2[4]DWORD
0x24BaseStatsStatEx
0x2CpLastListStatList*pointer to the last pStatList of the StatListEx owner (aka item owner in case list ex belongs to item)
0x30_3DWORD
0x34pListExStatListEx*pStatListEx - pointer to owner StatListEx (if this one is owned by a item, this points to the item owners list)
0x38pNextListExStatListEx*
0x3CpMyLastListStatList*
0x40pMyStatsStatList*
0x44pOwnerUnitAny*
0x48FullStatsStatEx
0x50ModStatsStatEx
0x58StatFlagsBYTE*pt to an array
0x5CfpCallBackvoid*
0x60ptGameGame*
struct.InventorystructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A unit's inventory: item linked list plus cursor/owner/count data.

offfieldtypenote
0x00dwSignatureDWORD
0x04pMemPoolBYTE*
0x08pOwnerUnitAny*
0x0CpFirstItemUnitAny*
0x10pLastItemUnitAny*
0x14_1[2]DWORD
0x1CdwLeftItemUidDWORD
0x20pCursorItemUnitAny*
0x24dwOwnerIdDWORD
0x28dwItemCountDWORD
0x2C_2[4]DWORD
0x3CCorpseNumberDWORD
struct.InventoryLayoutstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Layout/geometry of an inventory grid panel (slot sizes and screen bounds). Not present in D2Structs_113D.h; taken from d2bs D2Structs.h, which has no offset comments.

offfieldtypenote
SlotWidthBYTE
SlotHeightBYTE
unk1BYTE
unk2BYTE
LeftDWORD
RightDWORD
TopDWORD
BottomDWORD
SlotPixelWidthBYTE
SlotPixelHeightBYTE
struct.GfxCellstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A single frame/cell of a DC6/DCC sprite graphic (dimensions, draw offsets, pixel data length).

offfieldtypenote
0x00flagsDWORD
0x04widthDWORD
0x08heightDWORD
0x0Cxoffsint
0x10yoffsint
0x14_2DWORD
0x18lpParentDWORD
0x1ClengthDWORD
0x20colsBYTE
struct.CellContextstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Rendering context binding a unit/component to its cell file and current graphics cell and facing direction (size 0x48).

offfieldtypenote
0x00nCellNoDWORD
0x04_0aDWORD
0x08dwUnitDWORD
0x0CdwClassDWORD
0x10dwModeDWORD
0x14_3DWORD
0x18_4DWORD
0x1C_5BYTE
0x1D_5aBYTE
0x1E_6WORD
0x20_7DWORD
0x24_8DWORD
0x28_9DWORD
0x2CszNamechar*
0x30_11DWORD
0x34pCellFileCellFile*also pCellInit
0x38_12DWORD
0x3CpGfxCellsGfxCell*
0x40directionDWORD
0x44_14DWORD
struct.GfxInfostructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

Graphics draw-info block holding the draw-offset table pointer and palette-shift index for a sprite.

offfieldtypenote
0x00_1DWORD
0x04_2DWORD
0x08_3DWORD
0x0C_4DWORD
0x10_5DWORD
0x14_6DWORD
0x18_7DWORD
0x1C_8DWORD
0x20_9DWORD
0x24_10DWORD
0x28_11DWORD
0x2C_12DWORD
0x30pGfxDrawOffsetsGfxDrawOffset*
0x34_13DWORD
0x38nPalShiftIdxDWORD
struct.LightstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A dynamic light source in the game world (type, static lightmap validity flag and map pointer). Only a forward declaration exists in 113D, so the d2bs body is used.

offfieldtypenote
0x00_1[3]DWORD
0x0CdwTypeDWORD
0x10_2[7]DWORD
0x2CdwStaticValidDWORD
0x30pnStaticMapint*
struct.AutoMapLayerstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A layer of the automap holding linked lists of floor/wall/object/extra cells (sizeof = 0x1C). Verbatim source spelling is AutoMapLayer.

offfieldtypenote
0x00nLayerNoDWORD
0x04fSavedDWORD
0x08pFloorsAutoMapCell*
0x0CpWallsAutoMapCell*
0x10pObjectsAutoMapCell*
0x14pExtrasAutoMapCell*
0x18pNextLayerAutoMapLayer*
struct.AutoMapCellstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A single automap tile node (cell number, pixel position, weight) linked as a binary tree via pLess/pMore. Verbatim source spelling is AutoMapCell.

offfieldtypenote
0x00fSavedDWORD
0x04nCellNoWORD
0x06xPixelWORD
0x08yPixelWORD
0x0AwWeightWORD
0x0CpLessAutoMapCell*
0x10pMoreAutoMapCell*
struct.ControlstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A UI control/widget (button, textbox, etc.) with position, size, handler pointers, linked text and edit state. Only a forward declaration exists in 113D, so the d2bs body is used.

offfieldtypenote
0x00dwTypeDWORD
0x04_1DWORD*unsure? definitely a ptr but not obvious, usually points to 6 when dwType is 6 I think
0x08dwDisabledDWORD
0x0CdwPosXDWORD
0x10dwPosYDWORD
0x14dwSizeXDWORD
0x18dwSizeYDWORD
0x1C_2DWORD*some sort of function (maybe click?); preceding source comment: I think _2 thru _9 are a handler table of some sort
0x20_3DWORD
0x24_4DWORD*some sort of function
0x28_5DWORD*
0x2C_6DWORD
0x30_7DWORD*ptr to something...
0x34_8DWORD*another random ptr... mostly dead ends when I examined them
0x38_9DWORD
0x3CpNextControl*
0x40_10DWORD
0x44unkStateDWORD_11 0 when button avail to be clicked 1 when greyed - still need to look at this more
0x48pFirstTextControlText*
0x4CpLastTextControlText*
0x50pSelectedTextControlText*
0x54dwSelectEndDWORD
0x58dwSelectStartDWORD
0x5CwText[256]wchar_tunion member, anonymous struct: Textboxes
dwCursorPosDWORDunion member, anonymous struct: Textboxes (no offset comment in source)
dwIsCloakedDWORDunion member, anonymous struct: Textboxes (no offset comment in source)
0x5C_12[2]DWORDunion member, anonymous struct: Buttons (overlaps textbox members at 0x5C)
0x6CwText2[256]wchar_tunion member, anonymous struct: Buttons
struct.WaypointstructsourcedD2 community headers — D2Ex2/D2Structs_113D.h (jankowskib) + d2bs (noah-)

A single waypoint activation record (flags bitfield). Only a forward declaration exists in 113D, so the d2bs body is used.

offfieldtypenote
0x00flagsBYTE
struct.CullingSpecstructsourcedtrustedMir Drualga (SGD2FreeRes)
set_tile_culling_bound

Struct the game passes to the tile-culling function. Layout verified by static_assert: flags at 0x0 (uint32_t), draw_window_rect (RECT) at 0x4, tile_culling_window (RECT) at 0x14 (20). RECT is 16 bytes (left/top/right/bottom int32). | src: struct CullingSpec { uint32_t flags; RECT draw_window_rect; RECT tile_culling_window; }; static_assert(offsetof(CullingSpec, flags)==0); static_assert(offsetof(CullingSpec, draw_wi

struct.Constants_shader_constant_bufferstructsourcedtrustedbolrog (D2DX)
d2dx

Full layout (32 bytes, static_assert): float screenSize[2]={0,0}; float invScreenSize[2]={0,0}; uint32_t flags[4]={0,0,0,0};. NO translate/offset/textureOffset field exists. Therefore the GPU-side constants apply NO per-batch or per-geometry positional translate; any floor-vs-wall positional difference must originate in the CPU vertex data, not the CB. | src: float screenSize[2]; float invScreenSize[2]; uint32_t flags[4]; // static_assert size==32

struct.MetricsTableEntry_baked_gameSize_tablestructsourcedtrustedbolrog (D2DX)
d2dx

struct with 3 Size fields: desktopSize, gameSize, gameSizeWide. 26 rows (desktop -> game / gameWide): 1280x720->960x720/1280x720; 1360x768->1024x768/1360x768; 1366x768->1024x768/1366x768; 1280x800->1066x800/1280x800; 1536x864->1152x864/1536x864; 1440x900->1200x900/1440x900; 1600x900->1200x900/1600x900; 1280x1024->640x480/640x512; 1400x1050->700x525/700x525; 1680x1050->700x525/840x525; 1920x1080->720x540/960x540; 2560x1080->720x540/960x540; 2048x1152->768x576/1024x576; 1600x12

struct.Batch_16_bytesstructsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Packed 16-byte batch key. uint32 _textureHash @0; uint16 _startVertexLow @4; uint16 _vertexCount @6; uint16 _textureStartAddress @8 (byte addr/256 stored as (addr>>8)+1); uint16 _startVertexHigh_textureIndex @10 (VVVV + 12-bit texIndex); uint8 _textureHeight_textureWidth_alphaBlend @12 (HHHWWWBB); uint8 _isChromaKeyEnabled_gameAddress_paletteIndex @13 (CGGGPPPP); uint8 _textureCategory_primitiveType_combiners @14 (category in bits 5-7); uint8 _textureAtlas @15 (bits 0-2). No | src: uint8_t _textureCategory_primitiveType_combiners; // TTT.PPCC (GetTextureCategory = value>>5)

struct.Vertex_16_bytes_int16_screen_coordsstructsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

int16 _x,_y,_s,_t; uint32 _color; uint16 _paletteIndex_atlasIndex (palette<<12 | atlas&4095); uint16 _isChromaKeyEnabled_surfaceId (bit14 chroma | surfaceId&16383). Position is int16 screen-space; ctor asserts x,y in [INT16_MIN,INT16_MAX]. AddOffset(x,y) does _x+=x,_y+=y — mechanism the MoP loop uses. | src: int16_t _x; int16_t _y; int16_t _s; int16_t _t; ... inline void AddOffset(int32_t x,int32_t y){ _x += x; _y += y; }

struct.TextureCategory_enumstructsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Unknown=0, MousePointer=1, Player=2, LoadingScreen=3, Floor=4, TitleScreen=5, Wall=6, UserInterface=7, Count=8. Floor and Wall distinct values but geometry-identical. | src: Floor = 4, TitleScreen = 5, Wall = 6, UserInterface = 7

struct.GameAddress_enumstructsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Unknown=0, DrawWall1=1, DrawWall2=2, DrawFloor=3, DrawShadow=4, DrawDynamic=5, DrawSomething1=6, DrawLine=7, Count=8. Index into gameAddresses_* tables. | src: DrawWall1 = 1, DrawWall2 = 2, DrawFloor = 3, DrawShadow = 4, DrawDynamic = 5

struct.CellFilestructsourcedsourcedd2bs community (noah-)
D2 client (DC6/sprite file)

Sprite/cell file container. dwVersion 0x00; an anonymous struct at 0x04 { WORD dwFlags; BYTE mylastcol; BYTE mytabno:1 }; eFormat 0x08; termination 0x0C; numdirs 0x10; numcells 0x14; GfxCell* cells[255] at 0x18 (array of pointers to decoded GfxCell frames). | src: DWORD dwVersion; //0x00 | struct{WORD dwFlags;BYTE mylastcol;BYTE mytabno:1;}; //0x04 | DWORD eFormat; //0x08 | DWORD termination; //0x0C | DWORD numdirs; //0x10 | DWORD numcells;

struct.AutomapLayer2structsourcedsourcedd2bs community (noah-)
D2 client (automap)

Small automap layer variant: _1[2] 0x00; nLayerNo at 0x08. | src: DWORD _1[2]; //0x00 | DWORD nLayerNo; //0x08

struct.ItemPathstructsourcedsourcedd2bs community (noah-)
D2 client

Item position path head: _1[3] 0x00; dwPosX 0x0C; dwPosY 0x10 (DWORD tile coords). 'Use Path for the rest.' | src: DWORD _1[3]; //0x00 | DWORD dwPosX; //0x0C | DWORD dwPosY; //0x10

struct.ObjectPathstructsourcedsourcedd2bs community (noah-)
D2 client

Object position path head: pRoom1 0x00; _1[2] 0x04; dwPosX 0x0C; dwPosY 0x10. 'Leaving rest undefined, use Path.' | src: Room1* pRoom1; //0x00 | DWORD _1[2]; //0x04 | DWORD dwPosX; //0x0C | DWORD dwPosY; //0x10

struct.CollMapstructsourcedsourcedd2bs community (noah-)
D2 client (collision/map)

Collision map grid. dwPosGameX 0x00; dwPosGameY 0x04; dwSizeGameX 0x08; dwSizeGameY 0x0C; dwPosRoomX 0x10; dwPosRoomY 0x14; dwSizeRoomX 0x18; dwSizeRoomY 0x1C; pMapStart 0x20 (WORD*); pMapEnd 0x22 (WORD*). Game vs Room coordinate spaces both present; pMapStart is the walkability grid used for map rendering/pathing. | src: DWORD dwPosGameX;0x00 | DWORD dwPosGameY;0x04 | DWORD dwSizeGameX;0x08 | DWORD dwSizeGameY;0x0C | DWORD dwPosRoomX;0x10 | DWORD dwPosRoomY;0x14 | DWORD dwSizeRoomX;0x18 | DWORD dwS

struct.ObjectTxtstructsourcedsourcedd2bs community (noah-)
D2 client (TXT)

Object type record. Render-relevant: nOrientation at 0x14C (draw orientation) and nAutoMap at 0x1BB (automap icon index). Also szName 0x00, wszName 0x40, nSelectable0 0xC4, nSubClass 0x166, nParm0 0x178, nPopulateFn 0x1B2, nOperateFn 0x1B3. | src: BYTE nOrientation; //0x14C | DWORD nAutoMap; //0x1BB

struct.RosterUnitstructsourcedsourcedd2bs community (noah-)

Party roster entry. szName[16] 0x00; dwUnitId 0x10; dwClassId 0x1C; wLevel 0x20; wPartyId 0x22; dwLevelId 0x24; Xpos 0x28; Ypos 0x2C; dwPartyFlags 0x30; szName2[16] 0x66; pNext 0x80. Xpos/Ypos are party-member map coords (minimap/render). | src: DWORD Xpos;0x28 | DWORD Ypos;0x2C | DWORD dwLevelId;0x24

struct.DyPosstructsourcedtrustedNecrolis (d2-rs)
d2interface

Dynamic (moving) entity position. #[repr(C)]. Fields in order: linear_pos: LinearM2d<FU16>; iso_pos: IsoP2d<i32>; target_pos: [LinearM2d<u16>; 3]; room: Option<NonNull<Room>>; _padding1: [u32;4]; entity: NonNull<Entity>. This is the struct apply_pos_change mutates (ptr in ESI). | src: #[repr(C)] pub struct DyPos { pub linear_pos: LinearM2d<FU16>, pub iso_pos: IsoP2d<i32>, pub target_pos: [LinearM2d<u16>; 3], pub room: Option<NonNull<Room>>, pub _padding1: [u32;

struct.StaticPosstructsourcedtrustedNecrolis (d2-rs)
d2interface

Static (non-moving) entity position. #[repr(C)]. Fields: room: Option<NonNull<Room>>; iso_pos: IsoP2d<i32>; linear_pos: LinearM2d<u32>; _padding1: [u32;3]. | src: #[repr(C)] pub struct StaticPos { pub room: Option<NonNull<Room>>, pub iso_pos: IsoP2d<i32>, pub linear_pos: LinearM2d<u32>, pub _padding1: [u32; 3], }

struct.EntityPosstructsourcedtrustedNecrolis (d2-rs)
d2interface

#[repr(C)] union: s: Option<NonNull<StaticPos>> | d: Option<NonNull<DyPos>>. Which arm is valid depends on Entity.kind. | src: #[repr(C)] pub union EntityPos { pub s: Option<NonNull<StaticPos>>, pub d: Option<NonNull<DyPos>>, }

struct.EntitystructsourcedtrustedNecrolis (d2-rs)
d2interface

#[repr(C)]. Render-relevant fields: kind: EntityKind; class_id:u32; mem_pool; id:u32; state:u32; data; act_id:u32; dungeon; rng:Rng; seed:u32; pos: EntityPos; anim_frame_def; seq_frame_count:FU8; seq_frame:FU8; seq_anim_rate:FU8; anim_state:u32; frame:FU8; frame_count:FU8; anim_rate:u16; frame_event:u8; anim_data; gfx_info; _padding0:[u32;1]; stats; inv; light; light_width:u32; _padding1:[u32;30]; next_entity: Option<NonNull<Entity>>; next_in_room: Option<NonNull<Entity>>; _p | src: pub kind: EntityKind, pub class_id: u32, ... pub pos: EntityPos, pub anim_frame_def: *mut (), pub seq_frame_count: FU8, pub seq_frame: FU8, pub seq_anim_rate: FU8, pub anim_state:

struct.RoomstructsourcedtrustedNecrolis (d2-rs)
d2interface

#[repr(C)]. connected: Option<NonNull<NonNull<Room>>>; _padding1:[u32;3]; data:u32; _padding2:[u32;4]; connected_count:u32; _padding3:[u32;9]; pos: LinearM2d<u32>; size: M2d<u32>; _padding4:[u32;9]. pos/size are the room world placement used to resolve iso/linear coords. | src: #[repr(C)] pub struct Room { pub connected: Option<NonNull<NonNull<Room>>>, pub _padding1: [u32; 3], pub data: u32, pub _padding2: [u32; 4], pub connected_count: u32, pub _padding3

struct.BaseAddressesstructsourcedtrustedNecrolis (d2-rs)
d2interface

pub struct BaseAddresses { pub client: usize, pub common: usize, pub fog: usize, pub game: usize, pub gfx: usize, pub win: usize } | src: pub struct BaseAddresses { pub client: usize, pub common: usize, pub fog: usize, pub game: usize, pub gfx: usize, pub win: usize, }

struct.COLLIDE_tile_collision_flagsstructsourcedtrustedNecrolis (phrozen_code Discord)
D2Common

Full per-subtile collision bitmask enum. Notably COLLIDE_BLOCK_PLAYER 0x0001 = 'black space' in Arcane Sanctuary / cliff walls (the void/dead-zone tiles); COLLIDE_ALTERNATE_FLOOR 0x0010 (some floors set it); COLLIDE_BLANK 0x0020 returned for invalid subtile; COLLIDE_WALL 0x0004. Directly relevant to floor/void rendering behavior. | COLLIDE_BLOCK_PLAYER = 0x0001, // 'black space' in arcane sanctuary, cliff walls etc ... COLLIDE_ALTERNATE_FLOOR = 0x0010 ... COLLIDE_BLANK = 0x0020,

struct.D2Renderer_function_table_OpenD2_reimplstructsourcedsourcedeezstreet (cla$$ics) (phrozen_code Discord)
OpenD2

Reverse-engineer's abstracted renderer interface mirroring D2's draw model: RF_Init/Shutdown/Present, RF_RegisterTexture, RF_SetTexturePixels(tex,pixels,nPalette), RF_DrawTexture(tex,x,y,w,h,u,v). Notes a TODO for palshifted-cel drawing. Useful mental model of the D2 blit pipeline. | struct D2Renderer{ void (*RF_Present)(); ... void (*RF_DrawTexture)(tex_handle texture, DWORD x, DWORD y, DWORD w, DWORD h, DWORD u, DWORD v); // TODO

struct.DC6_frame_decode_DC6_LoadImagestructsourcedsourcedeezstreet (cla$$ics) (phrozen_code Discord)
D2CMP

DC6 image load layout: frames indexed dir*dwFrames+frame, each DC6Frame has dwWidth/dwHeight and dwNextBlock set to running pixel offset; the file offset/pointer table lives outside the dc6 body. Relevant to cel/graphics decoding. | memcpy(&pImage->pFrames[dwFramePos], gpDecodeBuffer + pFramePointers[dwFramePos], sizeof(DC6Frame)); pImage->pFrames[dwFramePos].dwNextBlock = dwTotal

struct.Control_D2Win_UI_controlstructsourcedsourcedMnw1995 (phrozen_code Discord)
D2Win

D2Win UI control struct: dwType +0x00, pCellFile +0x04, dwDisabled +0x08, pos X/Y +0x0C/0x10, size X/Y +0x14/0x18, Initialize fn +0x1C, CallFunc (button) +0x28, CallBackFunc +0x34, pNext +0x3C, dwMaxLength/ButtonFlag +0x44, pFirstText/dwScrollEntries +0x48, pLastText/dwScrollPosition +0x4C, pSelectedText +0x50, wText[0x1A] +0x5C, pChildControl +0x90 | struct Control { DWORD dwType; //0x00 ... Control* pChildControl; //0x90 };

struct.UnitAny_1_13c_layout_gfx_render_membersstructsourcedsourcedmisiek1294 (phrozen_code Discord)

UnitAny: dwType 0x00, dwTxtFileNo 0x04, pMemPool 0x08, dwUnitId 0x0C, dwMode 0x10, union pPlayerData/pItemData/... 0x14, dwAct 0x18, pAct 0x1C, seeds 0x20/24/28, pPath 0x2C, dwGfxFrame 0x44, dwFrameRemain 0x48, wFrameRate 0x4C, pAnimData 0x50, pGfxInfo(D2GfxDataStrc*) 0x54, pGfxInfo2 0x58, pStatsEx 0x5C, pInventory 0x60, pLight 0x64, dwLightRadius 0x68, nPalShiftIdx 0x6C, wX 0x8C, wY 0x8E, dwFlags 0xC4, dwFlagsEx 0xC | D2GfxDataStrc* pGfxInfo; //0x54 ... Light * pLight; //0x64 ... WORD nPalShiftIdx; //0x6C

struct.UnitAny_0x6C_unionstructsourcedtrustedNecrolis (phrozen_code Discord)

pUnit+0x6C is a union: bool bInteracting / char nPL2Shift (palette-shift index). Confirmed by disasm MOV BL,[ESI+6C] as pPlayer->nInteract | union { bool bInteracting; char nPL2Shift; }; //+6C

struct.D2GFXDataStrc_gfx_anim_descriptor_sizeof_0x4structsourcedsourcedmisiek1294 (phrozen_code Discord)

Necrolis authoritative gfx data struct: pCellInit +00, pCellFile +04, nFrame +08, nDirection +0C, nMaxDirections +10, nMaxFrames +14, fFlags +18, fState +1C, nComponent/fItemFlags +1D, nUnitType +20, nUnitIndex +24, nMode +28, nOverlay +2C, union{dwToken +30, dwComponent +34, dwArmorType +38, dwMode +3C, dwHitClass +40} , pName +44. This is UnitAny->pGfxInfo (+0x54) | struct D2GFXDataStrc //sizeof 0x48 { D2GFXCellStrc* pCellInit; //+00 ... const char* pName; //+44 };

struct.AnimToken_COF_animationstructsourcedsourcedeezstreet (cla$$ics) (phrozen_code Discord)

AnimToken{ D2TokenType tokenType; union{ cof_handle plrCof[PLRMODE_MAX][WC_MAX]; monCof[MONMODE_MAX]; objCof[OBJMODE_MAX]; } }. COFs load with token; .dcc directions loaded lazily per direction needed | struct AnimToken { D2TokenType tokenType; union { cof_handle plrCof[PLRMODE_MAX][WC_MAX]; ... } };

struct.AltItem_expanded_item_display_draw_array_elestructsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

Struct for the expanded on-screen item-display (ALT-highlight) draw array, gptAltDrawArray. Fields: nLeft/nTop/nRight/nBottom rect (+0x00..+0x0C), Unit* pItem (+0x10), wchar_t wszName[128] (+0x14), nRectColor (+0x114), nRectDrawMode (+0x118), nColor (+0x11C). Whist gives a full 1.13c patch table hooking D2Client at 0x58E59/0x59089/0xCDBB9 (max-count patches to 0x7F), and 0x58E83/0x58F07/0x5909D/0x59448/0x58F94/0x58F2 | struct AltItem { int nLeft; int nTop; int nRight; int nBottom; Unit* pItem; wchar_t wszName[128]; int nRectColor; int nRectDrawMode; int nColor; };

struct.D2CellFileStrc_DC6_cell_file_headerstructsourcedtrustedNecrolis (phrozen_code Discord)
D2CMP

Cell-file (sprite) header, sizeof 0x18 + frame data: dwVersion(+00), fCellFlags(+04), nEncodeType(+08), dwPaletteOffset(+0C), nDirections(+10), nFrames(+14), D2GFXCellStrc* pFrames[](+18). Necrolis clarifies 24-bit DC6's aren't really 24-bit - they carry their own PCX-style palette, and the 'zeros' field is the offset to that internal palette. He states he has mapped out most of D2CMP's ordinals. | struct D2CellFileStrc ... DWORD dwPaletteOffset; //+0C ... 24bit DC6's aren't 24bit, they just have their own palette

struct.D2COFStrc_D2COFLayerInfoStrc_animation_COF_lstructsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

COF animation file layout. D2COFLayerInfoStrc sizeof 0x9: nComponent(+0), bShadow(+1), bSelectable(+2), bOverrideTrans(+3), nTransLevel(+4), hitclass szHitClass[4]/dwHitClass(+5). D2COFStrc sizeof 0x1C: nLayers(+00), nFrames(+01), nDirections(+02), nVersion(+03=0x14), dwUnk(+04), then a union of Yoffset/Xoffset box OR nXpos/nWidth/nYpos/nHeight rect (+08..+14), nAnimRate(+18), nArmType(+19). Trailing: pLayerInfo[nLay | struct D2COFStrc //sizeof 0x1C ... BYTE nAnimRate; //+18 BYTE nArmType; //+19

struct.COF_Header_layoutstructsourcedtrustedLectem (phrozen_code Discord)
file/COF

COF file header: layers,frames,directions,version(=20),unknown1(bitfield e.g. loopAnim),bbox xMin/xMax/yMin/yMax(int32),animRate(int16,256),zeros(int16). Governs animation component layers & bounding box. | uint8_t layers; ... int32_t xMin; int32_t xMax; int32_t yMin; int32_t yMax; int16_t animRate;

struct.DCC_Header_layoutstructsourcedtrustedLectem (phrozen_code Discord)
file/DCC

DCC file header: signature(0x74),version(6),directions(max32),framesPerDir,padding0[3],tag(=1),finalDc6Size. Sprite-encoding header. | uint8_t signature; ///< Magic number for DCC files, must be 0x74

struct.Cell_UnitInfo_draw_flagsstructsourcedtrustedNecrolis (phrozen_code Discord)
D2Common

Item cell draw flags: CELLFLAG_UNIQUE=0x01, CELLFLAG_OVERLAY=0x02, CELLFLAG_CLIENT=0x08, CELLFLAG_SET=0x10. Drive item graphic/overlay drawing. | CELLFLAG_UNIQUE = 0x01, CELLFLAG_OVERLAY = 0x02, CELLFLAG_CLIENT = 0x08, CELLFLAG_SET = 0x10

struct.Dialog_vtable_deposit_stash_dialogsstructsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Dialog function-pointer table used by deposit dialogs: pfCreate+00, pfDraw+04, pfMouseScroll+08, pfClickDown+0C, pfClickUp+10, pfMouseMove+14, pfNumpadMove+18, pfKeyScrollUp+1C, pfKeyScrollDown+20, pfKeyScrollRight+24, pfKeyScrollLeft+28. Panel dialog draw+input dispatch. | tDialogDraw pfDraw; // +04 ... pfKeyScrollLeft; // +28

struct.AnimDataRecordstructsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Common

AnimData.d2 record: char szAnimDataName[8]@0x00, DWORD dwFrames@0x08, DWORD dwAnimSpeed@0x0C. Frame count + speed for animations. | char szAnimDataName[8]; //0x00 DWORD dwFrames; //0x08 DWORD dwAnimSpeed; //0x0C

struct.DrlgAct_0x18_DrlgTileDatastructsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Common

1.13c: pDrlgAct + 0x18 holds a DrlgTileData structure (level tile data). | DrlgTileData DrlgTileData; //0x18

struct.D2MonSeqTxtstructsourcedsourcedMnw1995 (phrozen_code Discord)
D2Common

Monster sequence record: WORD wSequence@0x00, BYTE nMode@0x02, nFrame@0x03, nDir@0x04, nEvent@0x05. | WORD wSequence; //0x00 BYTE nMode; //0x02 BYTE nFrame; //0x03 BYTE nDir; //0x04 BYTE nEvent; //0x05

struct.D2InventoryGridInfoStrcstructsourcedsourcedMnw1995 (phrozen_code Discord)

Layout of an inventory grid panel: grid cell counts (X/Y), the pixel bounds of the grid (left/right/top/bottom) and per-cell box width/height. This is the geometry of a side panel's item grid. | int nGridLeft;//0x04 ... int nGridBottom;//0x10 BYTE nGridBoxWidth;//0x14 BYTE nGridBoxHeight;//0x15

struct.D2_Tile_TileHeader_DT1structsourcedtrustedNecrolis (phrozen_code Discord)

Layout of the DT1 floor/wall tile header used by the map renderer, including per-tile collision bitmap (25 bytes), runtime cache index, block offset, light direction, roof height, dimensions, type/style/sequence, and 24-bit transparent color. Direct floor/tile rendering data. | uint8_t fCollisionInfo[25];//+28 ... uint16_t wCacheIndex;//+42 - runtime only ... uint32_t dwBlockOffset;//+48

struct.D2SubTileStrcstructsourcedtrustedNecrolis (phrozen_code Discord)

Sub-tile (tile component) descriptor for DT1 rendering: screen X/Y, grid X/Y, pixel format, encoded length, and offset/pointer to the pixel data block. | struct D2SubTileStrc //sizeof 0x14 { short nXpos;//+00 short nYpos;//+02 ... uint16_t wFormat;//+08 int nLength;//+0A

struct.DT1_file_HeaderstructsourcedtrustedLectem (phrozen_code Discord)

On-disk DT1 tile-library header layout for both current (v7) and older (v4) formats; v4's skipColor is the RGB skipped in D2CMP CelIteratePixels. Floor/tile file format. | uint32_t version; ... if(version == 7){ char libraryName[260]; uint32_t numTiles; uint32_t firstTile; }

struct.Rooms_Act_active_room_fields_GetActiveRoomsstructsourcedsourcedmisiek1294 (phrozen_code Discord)

Level/room bookkeeping offsets; nRooms increments on room init while nActiveRooms tracks active count. GetActiveRooms(Act* pAct, int dwLevelNo). Relevant to map streaming around the play area. | int nRooms;//0x004 ... int nActiveRooms;//0x00C ... GetActiveRooms ... -10662

struct.D2ObjectsTxt_frame_fieldsstructsourcedsourcedmisiek1294 (phrozen_code Discord)

Objects.txt runtime record has a frame-count array around offset 0xD8-0xD9 (pad0xD8 then dwFrameCnt[7]); relevant to object animation frame rendering. | BYTE pad0xD8;//0xD8 DWORD dwFrameCnt[7];//0xD9

struct.D2FloorTileParamsStrcstructsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

The parameter struct passed to the D2Client floor/tile-drawing routine. Directly names the render-pipeline fields we care about: nScreenPanels at +0x20 (which side panels are open), pView (D2ViewStrc*) at +0x24, bDrawEdgeTiles at +0x28, tile bounds nXMin/nXMax/nYMin/nYMax at +0x2C..+0x38, and the crucial nViewXOffset (+0x3C) and nViewYOffset (+0x40, typo'd nnViewYOffset). This is the screen-shift/dead-zone control bl | struct D2FloorTileParamsStrc //sizeof 0x44 - D2Client.0x6FB9A990 { ... int nScreenPanels; //+20 D2ViewStrc* pView; //+24 ... int nViewXOffset; //+3C

struct.TileGfxDatastructsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2gfx

Whist's (incomplete) definition of the struct passed to the d2gfx tile-drawing ordinals; only szDT1FileName mapped at +0x058 (the DT1 tile file name), first 0x58 bytes unknown. | struct TileGfxData { DWORD __000[88]; //0x000 char* szDT1FileName; //0x058 };

struct.InventoryGridstructsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client/D2Common

Layout of the inventory-panel grid array (Inventory+0x14 -> pInvInfo points to array of these). Fields: pFirstItem +0x00, pLastItem +0x04, nWidth +0x08, nHeight +0x09, ppItems (grid cell pointer array) +0x0C. Grid index enum: BODYLOC=0, BELT=1, INVENTORY=2. | struct InventoryGrid { Unit* pFirstItem; //0x00 ... BYTE nWidth; //0x08 BYTE nHeight; //0x09 ... Unit** ppItems; //0x0C };

struct.D2GameViewStrcstructsourcedtrustedLectem (phrozen_code Discord)
D2Client

The per-view render struct that controls the play-area viewport: dwFlags, ViewRadius (RECT), CheckRadius (RECT), nOffsetX/nOffsetY screen offsets, three Gouraud lighting tables, and embedded render data. This is the struct passed to every tile-draw call as pView. | struct D2GameViewStrc { int dwFlags; RECT ViewRadius; RECT CheckRadius; int nOffsetX; int nOffsetY; ...

struct.D2DrawSlotStrcstructsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

sizeof 0x24 render batching struct: nStart(+08), nEnd(+0C), then five linked lists of D2UnitDrawStrc -- pShadows(+10), pUpperWallTiles(+14), pUnits(+18), pRoofTiles(+1C), pLowerWallTiles(+20). This is the per-slot draw list that orders shadows/walls/units/roofs in the play area. | struct D2DrawSlotStrc { ... D2UnitDrawStrc* pShadows; //+10 ... pLowerWallTiles; //+20 };

struct.eD2TileDataFlags_draw_control_flagsstructsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Tile dwFlags bit meanings: 0x4000 DRAW_GROUND (layers 1-4), 0x8000 DRAW_FLOOR (trans tiles above ground e.g. fog), 0x10000 DRAW_ROOF, 0x20000 DRAWN, 0x40000 REVEALED, 0x8 HIDDEN, 0x400 FADED. DRAWCTRL mask = ROOF|FLOOR|GROUND; remaining layers are shadow. Governs which layer a tile draws in. | TILEFLAG_DRAW_GROUND = 0x00004000, //these are tile layers 1,2,3,4 ... TILEFLAG_DRAW_ROOF = 0x00010000

struct.ParticleDatastructsourcedtrustedLectem (phrozen_code Discord)
D2Client

Weather/particle entry: dataArrayVersion, nPosX, nPosY, dwSomeThresholdForSin (destroys particle randomly by Y position), nSizeIdx, a boolean, colorIdx, alpha. align(4). | struct ParticleData { ... DWORD dwSomeThresholdForSin; // Used to destroy the particle randomly based on the Y position ...

struct.RainParticleDatastructsourcedtrustedLectem (phrozen_code Discord)
D2Client

Splash/bubble particle: dataArrayVersion, nXpos, nYpos, nWhichRain, nCurFrame, rainGfxIdx. Used for rain ripples/bubbles on water tiles. | struct RainParticleData { ... DWORD nWhichRain; DWORD nCurFrame; DWORD rainGfxIdx; };

struct.D2AutoMapCacheStrc_D2AutoMapCellStrcstructsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Automap cache linked-list node: array of 512 automap cells plus a pPrev link. The automap-overlay data structure sitting over the play area. | struct D2AutoMapCacheStrc //sizeof 0x2804 { D2AutoMapCellStrc pCells[512]; //+0000 D2AutoMapCacheStrc* pPrev; //+2800 };

struct.Act_RoomCoordsstructsourcedsourcedThaison (phrozen_code Discord)
D2Common

Act: pEnviroment+0x04, nTownLevel+0x08, dwMapSeed+0x0C, pRoom1+0x10, TileData+0x18, ActMisc*+0x48, pMemoryPool+0x5C. RoomCoords (Room1+0x4C, 0x20 bytes): dwXStart/YStart/XSize/YSize, wTileX+0x10, wTileY+0x14, dwTilesWidth+0x18, dwTilesHeight+0x1C -- tile origin/extent of a room. | struct RoomCoords // sizeof(0x20) 32 bytes of Room1+4C

struct.ActEnvironmentstructsourcedsourcedThaison (phrozen_code Discord)
D2Common

Day/night lighting env: nTimeIndex, nDaytime, nIntensity+0x0C, nRed/nGreen/nBlue +0x18-0x1A, fCos/fLast/fSin +0x1C-0x24, dwTimeRate+0x28, bEclipse+0x30. Drives the outdoor floor/ambient light color. | struct ActEnvironment // sizeof(0x38)

struct.TileDatastructsourcedsourcedThaison (phrozen_code Discord)
D2Common

Tile placement record: nWidth/nXposEx+00, nHeight/nYposEx+04, nXpos+08, nYpos+0C, fFlags+14, D2TileLibraryEntryStrc* pTile+18, nRed/nGreen/nBlue/nIntensity +0x28-0x2B. Per-tile screen position + tint for floor rendering. | struct TileData //sizeof 0x30

struct.D2TileLibraryEntryStrcstructsourcedsourcedNightshades (phrozen_code Discord)
D2Common

Tile library entry; DT1 filename pointer at offset 0x58 (88 dwords in). | char* szDT1FileName; //0x058

struct.itemData_invPage_INVPAGE_INVENTORY_CUBE_STASstructsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Common

Item location is read from itemData->invPage compared against INVPAGE_INVENTORY / INVPAGE_CUBE / INVPAGE_STASH to distinguish inventory vs cube vs stash. Panel-context test. | return itemData->invPage == INVPAGE_STASH;

struct.D2RendererFunctionsStrcstructsourcedsourceddzik (phrozen_code Discord)
D2gfx/renderer

Full vtable of the swappable renderer backend (GDI/DirectDraw/Glide/D3D). D2 swaps this table on init depending on selected renderer. Includes fpBeginScene, fpEndScene, fpFlipBufferProbably, fpGetBackBuffer, fpGetScreenSize, fpUpdateScaleFactor, fpSetPerspectiveScale, fpAdjustPerspectivePosition, fpPerspectiveScalePosition, fpSetDefaultPerspectiveFactor, fpSetPalette, fpSetGlobalLight, fpDrawRect/RectEx, fpDrawSolidR | I am trying to figure out what are those undefined functions for each of renderer modes ... D2 swap table upon initialization depending on selected re

struct.DrlgLevel_LevelPreset_offsetstructsourcedsourcedThaison (phrozen_code Discord)
D2Common

DrlgLevel+0x1CC (1.13c) / +0x21C (1.10) points to the LevelPreset structure {dwDef, dwFiles, dwPreset, pNext*}. Level/DRLG generation data. | [1.13c] DrlgLevel + 0x1CC [1.10] DrlgLevel + 0x21C ... struct LevelPreset

struct.D2AutoMapRecordStrc_gpMapGfxCellsstructsourcedtrustedKingpin (phrozen_code Discord)
D2Common

Automap cell-range record {int nFirst;+00, int nLast;+04} indexed per level-type; gpMapGfxCells[MAX_LEVELTYPES] gives first/last automap gfx cell for each of the 45 level types (full name table listed). Automap/render data | struct D2AutoMapRecordStrc { int nFirst; //+00 int nLast; //+04 };

struct.D2GFXCelStrcstructsourcedtrustedNecrolis (phrozen_code Discord)
D2gfx/cel

Cel graphic header: +00 bFlip, +04 nWidth, +08 nHeight, +0C nXOffset, +10 nYOffset, +14 dwNodeLength, +18 pCelNode (also offset to next cel in file), +1C dwLength, +20 pPixels[]. Core sprite/floor-tile blit struct. | struct D2GFXCelStrc //sizeof 0x20 + x { BOOL bFlip; //+00 int nWidth; //+04 ... uint8_t pPixels[]; //+20 };

struct.DRLGLevelStruct_LevelstructsourcedsourcedThaison (phrozen_code Discord)
D2Common

Full DRLG Level struct layout: dwDrlgType@0x00 (1 maze,2 preset,3 wilderness), pRoom2First@0x10, union maze/preset/wilderness@0x14, dwPosX@0x1C, dwPosY@0x20, dwSizeX@0x24, dwSizeY@0x28, pNextLevel@0x1AC, pMisc@0x1B4, dwLevelType@0x1C0, hSeed@0x1C4, pLevelPresets@0x1CC, dwLevelNo@0x1D0, RoomCenterX/WarpX[9]@0x1E0, RoomCenterY/WarpY[9]@0x204, dwRoomEntries@0x228. Crash in D2Common #10322 DRLGInitLevel tied to sizeX/siz | DWORD dwDrlgType; //0x00 1 - maze, 2 - preset, 3 - wilderness ... DWORD dwSizeX; //0x24 DWORD dwSizeY; //0x28

struct.D2AutoMapRecordStrc_gpfMapNamesSizestructsourcedtrustedKingpin (phrozen_code Discord)
D2Common

Automap record {int nFirst;+00, int nLast;+04}; MAX_LEVELTYPES 255; level-type name table gpfMapNamesSize[] (0=None..35 '5 Lava', 36+ Dark Alliance/dummy). Patch points to repoint gpMapGfxCells and name table at D2Common +0x22C7F/22D46 (nFirst), +0x22C4E/22C9F/22D4D (nLast), +0x22AF5/22B32 (names) | struct D2AutoMapRecordStrc { int nFirst; //+00 int nLast; //+04 };

struct.D2RoomExFlags_enumstructsourcedsourcedSilvermane (phrozen_code Discord)
D2Common

RoomEx flag bits incl HAS_WARP_0..7 (0x10-0x800), SUBSHRINE_ROW1-4, HAS_WAYPOINT 0x10000, AUTOMAP_REVEAL 0x40000, NO_LOS_DRAW 0x80000 (line-of-sight draw suppression), HAS_ROOM 0x100000, HASPORTAL 0x400000, POPULATION_ZERO 0x800000 (towns/nospawn), TILELIB_LOADED 0x1000000, PRESET_UNITS_ADDED/SPAWNED, ANIMATED_FLOOR 0x8000000 (river of flame, hell act5) | ROOMEXFLAG_NO_LOS_DRAW = 0x00080000, ... ROOMEXFLAG_ANIMATED_FLOOR = 0x08000000, //animated floors (river of flame, hell act5)

struct.DLRGTileGridstructsourcedsourcedSwineFlu (phrozen_code Discord)
D2Common (DRLG)

Per-room tile container holding wall/floor/shadow tile data; sizeof 0x2C. Fields: pMapLinks+00, pAnimTiles+04, nWalls+08, nFloors+0C, nShadows+10, pTiles+14. SwineFlu asks whether it can be surgically removed and rebuilt from the room seed without client/server desync. Core of floor/tile rendering data. | struct DLRGTileGrid //sizeof 0x2C { DRLGTileLink* pMapLinks; //+00 ... int nFloors; //+0C int nShadows; //+10 DRLGRoomTiles pTiles; //+14 };

struct.D2StaticPathStrcstructsourcedsourcedSilvermane (phrozen_code Discord)
D2Common

Static path struct (used by items/objects on ground): pRoom@0x00, nTargetX@0x04, nTargetY@0x08, nXPos@0x0C, nYPos@0x10, unk[2]@0x14, nDirection(BYTE)@0x1C. (Canight variant puts dwHeight@0x50.) Ground-position data feeding unit placement. | struct D2StaticPathStrc { D2RoomStrc* pRoom; //0x00 int nTargetX; //0x04 ... int nXPos; //0x0C int nYPos; //0x10 ... BYTE nDirection; //0x1C };

struct.D2ClientFnMsg_D2WinMSGS_CLTstructsourcedsourcedConqueror (phrozen_code Discord)
D2Client

Per-panel message table entry: param, key, __stdcall callback taking D2WinMsgStrc* | struct D2ClientFnMsg { uint32_t dwPar; DWORD dwKey; D2WinMSGS_CLT pCallback; };

struct.D2AutoMapRecordStrc_gpMapGfxCells_gpfMapNamestructsourcedtrustedKingpin (phrozen_code Discord)
D2Common

Automap graphics cell first/last per level type; level-type name table (0=None..44) drives automap tile ranges; two dword patches point the engine at an enlarged name table | D2AutoMapRecordStrc gpMapGfxCells[MAX_LEVELTYPES]; ... struct { int nFirst; int nLast; }

struct.D2VendorChainStrcstructsourcedtrustedKingpin (phrozen_code Discord)
D2Game

Vendor gamble/inventory chain, size 0xC: +0x00 dwPlayerGUID, +0x04 bGamble (whether sold item appears in vendor inventory), +0x08 pPrev. | struct D2VendorChainStrc //sizeof 0xC { DWORD dwPlayerGUID; BOOL bGamble; D2VendorChainStrc* pPrev; }

struct.D2DrlgStrc_active_room_offsetsstructsourcedsourcedSwineFlu (phrozen_code Discord)
D2Common

1.13c D2DrlgStrc fields: +0x098 (=1.10 +0x3E0, DWORD added-active-rooms counter, never decremented), +0x45C (=1.10 +0x3DC, BYTE counter, can overflow), +0x008 (=1.10 +0x3DD, set to 5 or 7 per +0x24 dwFlags, can be decremented). Client-side active-room processing; see D2Common.#10715 (1.13c) / #10003 (1.10). | offsets for these fields in 1.13c are D2DrlgStrc +0x098 +0x45C +0x008 ... 0x3E0 is the amount of added active rooms

struct.D2EnvironmentCycleStrc_day_night_tint_tablesstructsourcedsourcedConqueror (phrozen_code Discord)
D2Client

Environment lighting cycle tables: {angle, phase, R,G,B, 0} keyframes driving the screen ambient color tint per act | const D2EnvironmentCycleStrc gNormalEnvironmentCycle[6] = { { 320, 3, 125, 144, 243, 0 }, ...

struct.D2TileStrcstructsourcedtrustedNecrolis (phrozen_code Discord)
D2Common/tooling

Full tile struct layout for floor/tile rendering: +00 nDirection, +04 nRoofHeight, +06 fMaterial, +08 nHeight, +0C nWidth, +10 nHeightToBottom, +14 nType, +18 nStyle, +1C nSequence, +20 nRarity/nFrame union, +24 dw24To8BitFilterMask, +28 fTileFlags[26] (first byte index, 25 subtile flags), +42 wCacheIndex, +44 dwUnk44, +48 dwBlockOffset, +4C nBlockSize, +50 nBlocks, +54 pBlock, +58 pLibraryName (runtime), +5C pLRUCac | struct D2TileStrc //sizeof 0x60 { int32_t nDirection; //+00 ... int32_t nHeightToBottom; //+10 ... D2TileBlockStrc* pBlock; //+54

struct.D2RoomCullArgsStrcstructsourcedtrustedNecrolis (phrozen_code Discord)
D2Client/D2Common

Args struct for room culling during render: +00 D2ViewStrc* pView, +04 RECT pCullingRect, +14 DRLGRoom* pClientRoom, +18 BOOL bTilesOnly. Directly about the viewport/culling rectangle that decides what tiles/rooms get drawn -- relevant to play-area vs panel clipping. | struct D2RoomCullArgsStrc { D2ViewStrc* pView; //+00 RECT pCullingRect; //+04 DRLGRoom* pClientRoom; //+14 BOOL bTilesOnly; //+18 };

struct.WaypointClientDatastructsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

Client-local simplified struct used to draw the waypoints panel: +00 int levelId, +04 byte activated. Client keeps its own copy separate from server PlayerData waypointData. | the client has it's own local copy, and also uses a separate simplified struct to draw the waypoints panel // D2Client.0xFCD8C struct WaypointClientDa

struct.CharScreenStringTable_gpCharScreenStringTablstructsourcedsourcedSilvermane (phrozen_code Discord)
D2Client

Char/stat-screen string layout table array at D2Client.6FB78010. Struct: int nXstart, int nY, int nXend, short nString, int nArg. Governs stat-panel text positioning. | //D2Client.6FB78010 CharScreenStringTable gpCharScreenStringTable[] ... struct CharScreenStringTable { int nXstart; int nY; int nXend; short nString;

struct.D2RoomStrcstructsourcedsourcedSilvermane (phrozen_code Discord)
D2Common

Full room struct layout -- the world floor/tile geometry a floor renderer walks. nSubtileX 0x00, nSubtileY 0x04, nSubtileWidth 0x08, nSubtileHeight 0x0C, nTileXPos 0x10, nTileYPos 0x14, nTileWidth 0x18, nTileHeight 0x1C; pRoomTiles 0x20; ppRoomList 0x24; nNumRooms 0x28; pUnitFirst 0x2C; pUnitUpdate 0x30; pCollisionGrid 0x34; pRoomEx 0x38; pSeed 0x3C; dwFlags 0x48; ppClients 0x4C; nTileCount 0x58; pAct 0x78; pRoomNext | int nSubtileX;//0x00 ... int nTileXPos;//0x10 ... D2RoomCollisionGridStrc* pCollisionGrid;//0x34

struct.item_bounds_check_inventory_grid_posstructsourcedsourcedEggsy (phrozen_code Discord)
D2Common

Disassembly of D2Common.0x6F68D570 charm-requirement/inventory-slot bounds check. pItem is [ESP+4]; pItem->pDynamicPath/pStaticPath union at +0x2C; grid posX at pPath+0x0C; posY at pPath+0x10. This is the inventory PANEL grid coordinate model used by CharmZone. | 6F68D57B . 8B40 2C MOV EAX,DWORD PTR DS:[EAX+2C] ; pitem->pPath ... CMP DWORD PTR DS:[EAX+0C],5 ; pPath->posX

struct.Collision_AttributesstructsourcedsourcedNeddles (phrozen_code Discord)

Full COLLISION_* bitflag table 0x0001-0x8000 (BlockWalk, BlockLOS, Wall, BlockPlayer, Missile, Player, DeadBody etc.) used in path/tile collision map | #define COLLISION_BLOCKWALK 0x0001 ... #define COLLISION_DEADBODY 0x8000

struct.AutomapCellTypeInfostructsourcedtrustedLectem (phrozen_code Discord)

{ CelFile* pCelFile; RECT tRect; DWORD nOffsetX; DWORD nOffsetY; } - describes an automap cell graphic | struct AutomapCellTypeInfo { CelFile *pCelFile; RECT tRect; DWORD nOffsetX; DWORD nOffsetY; }

struct.VendorTab_arraystructsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

Vendor/store tab struct {int x@0;int y@4;short stringId@8;BOOL isSelected@0xA;BOOL isActive@0xE}; array based at D2Client.0xE1178 | struct VendorTab { int x; int y; short stringId; BOOL isSelected; BOOL isActive; }

struct.weather_draw_update_globalsstructsourcedsourcedascar91 (phrozen_code Discord)
D2 exe (1.14d)

Weather tick decrements timer, re-rolls via FUN_00472280, and draws overlay via FUN_004f6300 with screen-relative offsets DAT_0071146c/711470 | FUN_004f6300(iVar7,0,iVar5,iVar6,0xff,5); return;

struct.StatMonitorstructsourcedsourcedBobi (phrozen_code Discord)
D2Client

{ wchar_t wszDesc[2][30]; DWORD dwStatNo; DWORD dwTimer; DWORD dwColor; BOOL fEnable; } (asks if same in 1.11b/1.13c) | struct StatMonitor { wchar_t wszDesc[2][30]; DWORD dwStatNo; DWORD dwTimer; DWORD dwColor; BOOL fEnable; }

Functions & Globals 446

Located symbols in the live binaries.

D2Client.DrawGamefunctionverified
0x6FAF4230 D2Client

main world render entry. body: PASS1(0x44302) -> tilesub(0x44307) -> PASS2(0x4436F, gated mode!=3) -> compositor(0x4437B) -> present #10040(0x443B9) -> RET(0x443BE)

D2Client.Pass1Floorfunctionverified
0x6FB3B0C0 D2Client

floor pass wrapper; esi=ctx, [esp+4]=bTilesOnly, ret 4; calls DrawRooms

D2Client.Pass2WallsUnitsfunctionverified
0x6FB3B110 D2Client

wall/roof + unit pass; eax=CullingSpec; gated cmp[ScreenOpenMode],3;je

D2Client.DrawRoomsfunctionverified
0x6FB2A9F0 D2Client

builds room-iteration RECT from ctx draw-window+scroll (band=[scrollX,scrollX+(R-L)]); per-tile cull skip if tileWorldX>RECT.R; grid helper 0x6FB2A0D0 sizes ctx+0xEAA8 (HEAP UAF trap on save/restore)

D2Client.FloorFlushfunctionverified
0x6FB4ED50 D2Client

floor tile flush; screenX=ctx+0x04-ctx+0x24+tileAbsX; NO inline clip; hands each tile to the cel blit thunks

D2Client.CelBlitThunkOpaquefunctionverified
0x6FABD0D2 D2Client FF 25 3C EC B7 6F

jmp [IAT 0x6FB7EC3C] -> D2gfx #10001. TILE-DEDICATED; the TILEBAND/GROUND-COLLECT counter hook site

D2Client.CelBlitThunkAlphafunctionverified
0x6FABD0D8 D2Client FF 25 40 EC B7 6F

jmp [IAT 0x6FB7EC40] -> D2gfx #10058 (alpha)

D2gfx.CelDrawOpaque_10001functionverified
0x6FA8BB50 D2gfx

mov eax,[0x6FA91268]; call [eax+0x9c]; ret 0x14 -- thin forwarder into the driver cel handler, no clip

D2gfx.Fill_10041functionverified
0x6FA8B080 D2gfx

solid fill; forwarder to driver vtable +0x84; 295 D2Client callers, none targets [960,1280] (overdraw ruled out)

D2Glide.DriverInit_10000functionverified
0x6F8592D0 D2Glide B8 F0 49 86 6F C3

the ONLY export: mov eax,0x6F8649F0; ret -- hands D2gfx the driver dispatch struct

D2Glide.LiveCelHandlerfunctionverified
0x6F8572F0 D2Glide

pfTileDrawLit (dispatch struct +0x9c); the live cel handler D2Client #10001 reaches; calls worker 0x7080

D2Glide.Workerfunctionverified
0x6F857080 D2Glide

gates on [0x6F865A8C]: null -> LIVE drawer 0x6840; non-null -> DEAD 0x6290

D2Glide.LiveDrawerfunctioninferred
0x6F856840 D2Glide

THE live cel->vertex drawer (confirmed fires: [GLIDECULL] edx=ScreenOpenMode). Per-subtile cull vs [esp+0x30]left,[esp+0x38]right narrowed by panel-mode (mode1 right=DispW/2=640 @0x6889, mode2 left=DispW/2-0x20=608 @0x689E). v1.9.1 NOP'd both -> NO visual change => this cull is world-space, NOT the screen clip. Submits via grDrawVertexArrayContiguous

D2Glide.DeadSoftwareDrawerfunctiondecoyverified
0x6F856290 D2Glide

DECOY -- DO NOT HOOK. Software/LFB drawer reached only via the non-taken branch of worker 0x7080; PROVEN dead (v1.9.0 hook never fired). Computes DisplayWidth-DisplayWidth/4=960 but never executes -- the 960 was mis-inferred off this dead code

D2Glide.DeadTwinfunctiondecoyverified
0x6F8561D0 D2Glide

DECOY -- DO NOT HOOK. Uncalled twin of 0x6290 (0 E8/dword refs); identical width math; register-arg convention. Dead

glide3x.grDrawVertexArrayContiguousfunctionverified
glide3x

D2DX Glide-vertex submit = the D2Glide->D2DX crossing. PROBE v1.10.0 (filtered to the tile drawer by return-addr): floor geometry crosses at max screenX 1296, ~138 verts/frame in dead-zone [960,1280). The world geometry REACHES D2DX past 960 yet renders BLACK -> the panel-world clip is IN D2DX (m2 was right).

glide3x.grSstWinOpenfunctionverified
0x1000D970 glide3x

D2DX grSstWinOpen (export _grSstWinOpen@28). OnSstWinOpen consumes _customGameSize as gameSize (D2DXContext.cpp:176-180) then creates/sizes the RenderContext. THE fix hook site: detour entry, call SetCustomResolution(1280,720) before delegating so the world target opens WIDE. Prologue 55 8B EC 6A FF (push ebp;mov ebp,esp;push -1).

glide3x.D2DXGetConfiguratorfunctionverified
0x10007850 glide3x

exported _D2DXGetConfigurator@0 (NOT @4 -- the header D2DXConfigurator.h:98 string is a bug; use @0 or ordinal 1). Returns ID2DXConfigurator*; vtable [0]QI [1]AddRef [2]Release [3]SetCustomResolution(w,h) __stdcall. SetCustomResolution sets _customGameSize (a flag consumed at the next grSstWinOpen).

ext.D2Client.DrawCenteredUnicodeTextfunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0xbc4e0 D2Client.dll

Draws centered unicode text on screen. | src: D2Client.dll DrawCenteredUnicodeText Offset 0xBC4E0

ext.D2GFX.DrawCelContextfunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2GFX.dll

Draws a CEL (sprite) via graphics context — core sprite/cell blit entry point. | src: D2GFX.dll DrawCelContext Ordinal 10041

ext.D2GFX.DrawRectanglefunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2GFX.dll

Draws a rectangle primitive. Named in FOCUS. | src: D2GFX.dll DrawRectangle Ordinal 10014

ext.D2Win.DrawUnicodeTextfunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2Win.dll

Draws unicode text. | src: D2Win.dll DrawUnicodeText Ordinal 10150

ext.D2Win.GetPopupUnicodeTextWidthAndHeightfunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2Win.dll

Measures popup text width/height (layout). | src: D2Win.dll GetPopupUnicodeTextWidthAndHeight Ordinal 10177

ext.D2Win.GetUnicodeTextDrawWidthfunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2Win.dll

Measures drawn text width (layout). | src: D2Win.dll GetUnicodeTextDrawWidth Ordinal 10028

ext.D2Win.GetUnicodeTextNDrawWidthfunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2Win.dll

Measures drawn text width for N chars (layout). | src: D2Win.dll GetUnicodeTextNDrawWidth Ordinal 10055

ext.D2Win.SetPopUpUnicodeTextfunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2Win.dll

Sets popup text content. | src: D2Win.dll SetPopUpUnicodeText Ordinal 10085

ext.D2Win.SetUnicodeTextFontfunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2Win.dll

Selects active text font for drawing. | src: D2Win.dll SetUnicodeTextFont Ordinal 10184

ext.D2Win.LoadCelFilefunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2Win.dll

Loads a CEL sprite file. | src: D2Win.dll LoadCelFile Ordinal 10111

ext.D2Win.UnloadCelFilefunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2Win.dll

Unloads a CEL sprite file. | src: D2Win.dll UnloadCelFile Ordinal 10126

ext.D2CMP.GetCelFromCelContextfunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2CMP.dll

Retrieves a CEL frame from a CEL context (sprite frame access). | src: D2CMP.dll GetCelFromCelContext Ordinal 10021

ext.D2CMP.CloseCelFilefunctionsourcedtrustedmir (Diablo-II-Address-Table, public domain)
D2CMP.dll

Closes a CEL file (sprite resource cleanup). | src: D2CMP.dll CloseCelFile Ordinal 10065

ext.D2gfx.D2gfx_dll_window_resize_width_heightfunctionsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0x7fe8 D2gfx.dll

Resizes the game window on resolution change. Sets screen width [eax] then screen height [ecx]. Left unmodified: game crunches elements, mouse cannot move past 640th x / 480th y, black lines. RENDER/window sizing. | src: ORIG: C7 00 80020000 mov [eax],00000280 (640 screen width); C7 01 E0010000 mov [ecx],000001E0 (480 screen height). PATCH: C7 00 2A040000 mov [eax],0000042A (1066); C7 01 5802000

ext.D2gdi.D2gdi_dll_Part_1_game_width_height_RENDERfunctionsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0x6d55 D2gdi.dll

Sets values determining how D2 renders the game (width render into esi, height render into edx). Unaltered: stretches 640x480 to fill window then crashes. WIDTH RENDER USES 1064 (0x428) NOT 1066 — author: non-multiple-of-4 width overflows onto LEFT side of screen. Height render 600. | src: ORIG: BE 80020000 mov esi,00000280 (640 game's width render); BE E0010000 mov edx,000001E0 (480 height render). PATCH: BE 28040000 mov esi,00000428 (1064 width render); BE 58020000

ext.D2gdi.D2gdi_dll_Part_2_x_position_render_cutofffunctionsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0x706b D2gdi.dll

Sets x-position where D2 should STOP rendering entities (walls, pillars). Unmodified: anything past x=640 not rendered (or incorrectly); no crash. Write target [D2gdi.dll+0xCA9C]. RENDER BOUNDARY. | src: ORIG: C7 05 9CCA876F 80020000 mov [D2gdi.dll+0xCA9C],00000280 (640, x-pos stop rendering). PATCH: C7 05 9CCA876F 2A040000 mov [D2gdi.dll+0xCA9C],0000042A (1066)

ext.D2client.D2Client_dll_Part_1_FOV_width_height_logicfunctionsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0x10e29 D2client.dll

THE 0x10E29 BLOCK. Sets D2 field-of-vision, character/map positioning, and UI element positions. mov eax,0x280 then stores eax to [D2client+0xDBC48] (width logic), then mov [D2client+0xDBC4C],0x1E0 (height logic). Unmodified: renders as 640x480 and places game at upper-left of window. The width store (A3 48BCB86F) is left UNMODIFIED; only the eax immediate (640->1066) and the height DWORD (480->600) change. | src: ORIG: B8 80020000 mov eax,00000280 (640); A3 48BCB86F mov [D2client.dll+0xDBC48],eax (width logic); C7 05 4CBCB86F E0010000 mov [D2client.dll+0xDBC4C],000001E0 (480 height logic).

ext.D2client.D2Client_dll_Part_2_UI_panel_offset_table_origfunctionsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0xc3a11 D2client.dll

THE 0xC3A11 UI-PANEL OFFSET SITE. Original 640x480-mode panel placement: writes ebx (=0) into panel x-offset [+0x11B9A0] and y-offset [+0x11B9A4]. Governs alignment of quest buttons, waypoint buttons, mercenary stats. reg->mem is fewer bytes than DWORD-const->mem, so 8 extra bytes needed to inject HD constants. | src: 89 1D A0B9BC6F mov [D2client.dll+0x11B9A0],ebx (x-offset of panels, ebx=0); 89 1D A4B9BC6F mov [D2client.dll+0x11B9A4],ebx (y-offset of panels, ebx=0)

ext.D2client.D2Client_dll_Part_2_wider_view_w_75_16_jne_ORIfunctionsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0xc39f9 D2client.dll

THE 75 16 PER-RESOLUTION BRANCH. Original: jne to 0xC3A11 (640x480 code) if not 800x600 mode. 800x600 block sets x-offset=0x50(80) and y-offset=0xFFFFFFC4(-60), then EB 0C jmp to 0xC3A1D past the 640x480 code. 640x480 block (at 0xC3A11) writes ebx(0) to both offsets. jne displacement = 0x16. | src: 75 16 jne D2client.dll+0xC3A11 (jump to 640x480 code if not 800x600); C7 05 A0B9BC6F 50000000 mov [+0x11B9A0],00000050 (80 x-offset); C7 05 A4B9BC6F C4FFFFFF mov [+0x11B9A4],FFFFF

ext.D2client.D2Client_dll_Part_2_SOLUTION_reordered_75_0C_jfunctionsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0xc39f9 D2client.dll

PATCHED per-resolution branch: author reorders so the shared y-offset store (-60) runs FIRST for both 800x600 and 1066x600 (both height 600), freeing 6 bytes and cutting needed bytes 8->4. jne displacement CHANGED from 0x16 to 0x0C. Now: write y-offset -60; jne 0xC3A11 (=1066x600 code) if not 800x600; 800x600 sets x=0x50(80); jmp past; the 0xC3A11 '1066x600 code' sets x-offset=0xD5(213); two trailing 90 NOPs (leftover bytes). CONCLUSION ON PANEL SHIFT: panel x-offset 213 for | src: C7 05 A4B9BC6F C4FFFFFF mov [+0x11B9A4],FFFFFFC4 (-60 y-offset); 75 0C jne D2client.dll+0xC3A11 (jump to 1066x600 code if not 800x600); C7 05 A0B9BC6F 50000000 mov [+0x11B9A0],0000

ext.D2glide.D2glide_dll_render_width_heightfunctionsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0xdcd5 D2glide.dll

Glide mode entity render dimensions. Writes rendering width [D2glide.dll+0x15A68] and height [D2glide.dll+0x15B04]. Unchanged: entities rendered at incorrect location. RENDER. | src: ORIG: C7 05 685A866F 80020000 mov [D2glide.dll+0x15A68],00000280 (width 640); C7 05 045B866F E0010000 mov [D2glide.dll+0x15B04],000001E0 (height 480). PATCH: ...2A040000 (1066); ..

ext.glide3x.glide3x_dll_window_width_heightfunctionsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0xc0d5 glide3x.dll

Sets D2 (Glide) window size. Loads ptr [glide3x.dll+0x1C9A0] (width addr) into edx then writes 640; loads ptr [glide3x.dll+0x1C82C] (height addr) into ecx then writes 480. NOTE: Cheat-Engine relative addresses differ from hex-editor addresses (offset by 0xA00). In the printed PATCH the loader/immediate opcodes C7 02 80020000 / C7 01 E0010000 are shown unchanged but the target values in comments become 1066/600 — the immediates to actually write are 0000042A and 00000258. | src: 8B 15 A0C94100 mov edx,[glide3x.dll+0x1C9A0] (Glide window width addr); C7 02 80020000 mov [edx],00000280 (640->1066 0000042A); 8B 0D 2CC84100 mov ecx,[glide3x.dll+0x1C82C] (height

ext.d2client draw_screen_background.DrawLeftScreenBackgroundfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
d2client draw_screen_background

Fills the LEFT half of the screen. First draws a solid black rectangle over the left half to block transparent-DC6 cheats, then tiles the config screen-background DC6 across it. This is the perimeter/letterbox fill on the left of the 640-wide play area. | src: ::d2::d2gfx::DrawRectangle(0, 0, resolution.width / 2, resolution.height, 0, ::d2::DrawEffect::kNone);

ext.d2client draw_screen_background.DrawRightScreenBackgroundfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
d2client draw_screen_background

Fills the RIGHT half of the screen. Draws a black rectangle from x=width/2 to x=width over full height, then tiles the background DC6 rightward. Left+Right together produce the full perimeter fill around the centered 640x480 UI region. | src: ::d2::d2gfx::DrawRectangle(resolution.width / 2, 0, resolution.width, resolution.height, 0, ::d2::DrawEffect::kNone);

ext.draw_screen_background entry.Sgd2fr_D2Client_DrawScreenBackgroundfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
draw_screen_background entry

Public interception entry (extern "C" __cdecl). Reads d2client::GetScreenOpenMode() and dispatches: kRight -> DrawRight{Background,BorderFrame,BackgroundRibbon}; kLeft -> DrawLeft*; kBoth -> both; kNone -> nothing. Then calls original d2::d2client::DrawScreens(). Declared in replacement.hpp. | src: extern "C" void __cdecl Sgd2fr_D2Client_DrawScreenBackground();

ext.patch_1_09dcpp GetPatchAddressAndSize01.DrawScreenBackground_CALL_hook_1_13cfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
rva 0xc3aa6 patch_1_09d.cpp GetPatchAddressAndSize01

1.13c call-branch patch that redirects into Sgd2fr_D2Client_DrawScreenBackground via the asm shim InterceptionFunc01. Found by tracing the 'ACT%d' string usage to the function return. Other versions: 1.09D 0x8696B, 1.10 0x81F08, 1.12A 0x90216, 1.13D 0x1D4A6, LoD1.14C 0x5264D, LoD1.14D 0x56FAF (all size 5).

ext.patch_1_09dcpp GetPatchAddressAndSize02.Disable_stock_LEFT_screen_bg_1_13cfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
rva 0x271ed patch_1_09d.cpp GetPatchAddressAndSize02

NOP patch removing the game's original left-screen 800BorderFrame draw. Located via the 'Panel\800BorderFrame' string (two usages; this is the left one). 1.09D 0x58F1B(sz0x96), 1.10 0x5F4EB(0x9B), 1.12A 0x8218D(0x9A), 1.13D 0x6D2DD(0x9A), LoD1.14C 0x94B0C(0xAA), LoD1.14D 0x9865E(0x9E).

ext.patch_1_09dcpp GetPatchAddressAndSize03.Disable_stock_RIGHT_screen_bg_1_13cfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
rva 0x270f1 patch_1_09d.cpp GetPatchAddressAndSize03

NOP patch removing the game's original right-screen 800BorderFrame draw. 1.09D 0x58FF0(0xAC), 1.10 0x5F5C0(0xAC), 1.12A 0x82094(0xB9), 1.13D 0x6D1E1(0xBD), LoD1.14C 0x94BF1(0xB3), LoD1.14D 0x98733(0xA7).

ext.set_screen_shift.Sgd2fr_D2Client_SetScreenShiftfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
set_screen_shift

THE origin of the -320 render shift. Recomputes D2's screen-shift so the 640x480 play area is centered. screen_shift_x = (general_display_width/2) - (640/2); screen_shift_y = -((general_display_height/2) - (480/2)). Note the 640/2 = 320 term and the NEGATION on the Y term. For a 1280x720 display: shift_x = 640-320 = 320, shift_y = -(360-240) = -120. The '-320'-family value is the (640/2) offset that recenters the viewport origin. Uses d2client::GetGeneralDisplayWidth/Height a | src: int screen_shift_x = (general_display_width / 2) - (640 / 2); int screen_shift_y = -((general_display_height / 2) - (480 / 2)); ::d2::d2client::SetScreenShiftX(screen_shift_x);

ext.set_screen_shift/patch_1_09dcpp.SetScreenShift_CALL_hook_1_13cfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
rva 0xc39f6 set_screen_shift/patch_1_09d.cpp

Call-branch patch feeding Sgd2fr_D2Client_SetScreenShift. Found via write-breakpoint on ScreenShiftX/Y variable. 1.09D 0x865BF, 1.10 0x81B8F, 1.12A 0x90166, 1.13D 0x1D3F6, LoD1.14C 0x5259B, LoD1.14D 0x56EFD (all size 0x27 = 0x...1D-0x...F6).

ext.set_tile_culling_bound.Sgd2fr_D2Client_SetTileCullingBoundfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
set_tile_culling_bound

Sets the map/tile draw and culling windows. draw_window_rect = SetRect(left,top,right,bottom) from args. tile_culling_window base = SetRect(-80, -80, width+80, height+40) using GetGeneralDisplayWidth/Height. If perspective mode enabled, expands top/left/right/bottom by ratios (866px up = -204.375 unit space; ratio = 1732/(1732-height/2) - 1, capped at 1.0; bottom uses ratio = 1 - 1500/(1500+height/2)). Finally sets culling_spec->flags |= 0x1. The -80/-80/+80/+40 margins are t | src: SetRect(tile_culling_rect, -80, -80, width + 80, height + 40); ... culling_spec->flags |= 0x1;

ext.set_tile_culling_bound/patch_1_11cpp.SetTileCullingBound_JMP_hook_1_13cfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
rva 0x8a4d0 set_tile_culling_bound/patch_1_11.cpp

Jump-branch patch to the tile-culling replacement (via patch_1_11_shim). Found via 'Perspective' Video-Options string -> first D2Client-space call. 1.11 0x5B560, 1.11B 0x927A0, 1.12A 0xBA990, 1.13ABeta 0x3F490, 1.13D 0xB5330, LoD1.14A 0xB5330 (all size 5). Separate patch_1_07.cpp covers 1.07-1.10 & 1.14A-D with different offsets (e.g. 1.09D 0x16000, LoD1.14C 0x71D30, LoD1.14D 0x76000).

ext.set_cel_display_left_and_right.Sgd2fr_D2GDI_SetCelDisplayLeftAndRightfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
set_cel_display_left_and_right

Sets the D2GDI cel horizontal draw window to the full width: SetCelDisplayLeft(0), SetCelDisplayRight(resolution.width). Resolution from GetIngameResolutionFromId(resolution_mode). This is the render-window widening that removes the stock 640-wide clip so the perimeter can be drawn. | src: d2::d2gdi::SetCelDisplayLeft(0); d2::d2gdi::SetCelDisplayRight(resolution.width);

ext.d2gdi set_cel_display_left_and_right/patch_1_13ccpp.SetCelDisplayLeftAndRight_JMP_hook_1_13cfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
rva 0x7049 d2gdi set_cel_display_left_and_right/patch_1_13c.cpp

Jump-branch patch in D2GDI (not D2Client) for the cel display left/right. 1.11 0x6529, 1.11B 0x7239, 1.12A 0x6599, 1.13ABeta 0x65F9, 1.13C 0x7049, 1.13D 0x7E99 (size 0x2E each). Library = kD2GDI.

ext.set_general_display_width_and_height.Sgd2fr_D2Client_SetGeneralDisplayWidthAndHeighfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
set_general_display_width_and_height

Feeds the resolution into the GeneralDisplayWidth/Height globals that everything above reads: SetGeneralDisplayWidth(resolution.width), SetGeneralDisplayHeight(resolution.height). Also cycles SetInventoryArrangeMode(inventory_arrange_mode++) as a workaround to stop inventory arrangement from transferring (overflow intentional). | src: d2::d2client::SetGeneralDisplayWidth(resolution.width); d2::d2client::SetGeneralDisplayHeight(resolution.height);

ext.position_realignment.RealignPositionFromCenterfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
position_realignment

Re-anchors a PositionalRectangle from the source (inventory-arrange) resolution center to the current resolution center, preserving width/height. dist = out.Left - source.width/2; out.Left = current.width/2 + dist; out.Right = out.Left + rectangle_width. Same for top/bottom about height/2. Uses GetIngameResolutionFromId(GetResolutionMode()) and GetSourceInventoryArrangeResolution(). | src: out_position_wrapper.SetLeft((current_resolution.width / 2) + dist_from_rect_left_to_display_center);

ext.position_realignment.RealignPositionFromBottomCenterfunctionsourcedtrustedMir Drualga (SGD2FreeRes)
position_realignment

Like RealignPositionFromCenter for X, but anchors Y to the display BOTTOM: dist_from_rect_top_to_display_bottom = source.height - out.Top; out.Top = current.height - dist; out.Bottom = out.Top + rectangle_height. Used for bottom-docked UI (belt/skill bar).

ext.DisableScrollOfInifussRectPatch_1_07_NOPfunctionsourcedtrustedMir Drualga (SGD2FreeRes)

NOPs the D2GFX.DrawRectangle call that draws the black rectangle on the Scroll-of-Inifuss (Cain quest) screen, which otherwise mis-renders at high res. Located via the '%s\UI\MENU\scroin' string then scrolling up to the DrawRectangle call. Single NOP patch per version. 1.13C at D2Client +0x7C38D (size 0x2D = 0x7C3BA-0x7C38D); 1.09D 0x8330C, 1.12A 0x70FBD, 1.13D 0x7516D, LoD1.14C 0x9C20D, LoD1.14D 0x9FA20 (all size 0x2D). Applies to essentially all versions 1.07-1.14D.

ext.d2dx.RenderContext_UpdateViewportfunctionsourcedtrustedbolrog (D2DX)
d2dx

Sets viewport + scissor from a Rect, then updates the Constants CB. Verbatim: CD3D11_VIEWPORT viewport{ (float)rect.offset.x, (float)rect.offset.y, (float)rect.size.width, (float)rect.size.height }; _deviceContext->RSSetViewports(1,&viewport); CD3D11_RECT scissorRect{ rect.offset.x, rect.offset.y, rect.size.width, rect.size.height }; _deviceContext->RSSetScissorRects(1,&scissorRect); then writes _constants.screenSize[0/1]=size.width/height, invScreenSize=1/size, flags[0]=NoAn | src: CD3D11_VIEWPORT viewport{ (float)rect.offset.x, (float)rect.offset.y, (float)rect.size.width, (float)rect.size.height };

ext.d2dx.UpdateViewport_call_sitesfunctionsourcedtrustedbolrog (D2DX)
d2dx

Called 3 places. SetSizes: UpdateViewport({0,0,_gameSize.width,_gameSize.height}) (offscreen game-size target, offset 0). Present: called with game-size dims AND with _renderRect (the centered/scaled final blit to screen). WriteToScreen: UpdateViewport({0,0,_gameSize.width,_gameSize.height}). Only the Present-with-_renderRect path carries a non-zero centering offset.

ext.d2dx.RenderContext_SetSizesfunctionsourcedtrustedbolrog (D2DX)
d2dx

Stores _gameSize/_windowSize. displaySize = (FullscreenDefault ? _desktopSize : _windowSize). _renderRect = Metrics::GetRenderRect(_gameSize, displaySize, !NoWide flag). Computes desktopCenterX=_desktopSize.width/2, desktopCenterY=(FullscreenDefault?_desktopSize.height/2:_desktopClientMaxHeight/2), reads GetWindowPosition() preferred position (used iff x>=0 && y>=0). Branches Windowed vs FullscreenDefault for placement. Then ClipCursor(); and if(_resources){ ResizeBackbuffer( | src: _renderRect = Metrics::GetRenderRect(_gameSize, displaySize, !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoWide));

ext.d2dx.RenderContext_Drawfunctionsourcedtrustedbolrog (D2DX)
d2dx

SetBlendState(batch.GetAlphaBlend()); picks atlas via GetTextureCache(batch); SetShaderState with Game vertex+pixel shaders, atlas SRV (batch.GetTextureAtlas()) and Palette Texture1D SRV; then _deviceContext->Draw(batch.GetVertexCount(), startVertexLocation + batch.GetStartVertex()). No positional transform here — geometry comes straight from the vertex buffer; the only offset is the vertex START index. | src: _deviceContext->Draw(batch.GetVertexCount(), startVertexLocation + batch.GetStartVertex());

ext.d2dx.Metrics_GetSuggestedGameSizefunctionsourcedtrustedbolrog (D2DX)
d2dx

First tries MetricsTable exact-match on desktop size (narrow vs wide variant). Fallback: loop scaleFactor=1..8 { size.width=desktop.width/scaleFactor; size.height=desktop.height/scaleFactor; if (desktop.height/(scaleFactor+1) <= 600) break; }. Height cap: if size.height>720 { aspect=desktop.width/desktop.height; size.width=(int32_t)(aspect*720); size.height=720; }. Constants: 600 (break threshold), 720 (max game height), scale range 1..8.

ext.d2dx.Metrics_GetRenderRectfunctionsourcedtrustedbolrog (D2DX)
d2dx

Integer scale: scaleFactor=1; while(gameSize.width*(scaleFactor+1)<=desktop.width && gameSize.height*(scaleFactor+1)<=desktop.height) ++scaleFactor;. Centering (integer): rect.size={gameSize.width*scaleFactor, gameSize.height*scaleFactor}; rect.offset.x=(desktop.width-gameSize.width*scaleFactor)/2; rect.offset.y=(desktop.height-gameSize.height*scaleFactor)/2. Non-integer trigger: if(rect.offset.x<0 || rect.offset.y<0 || (rect.offset.x>=16 && rect.offset.y>=16)) apply fraction

ext.d2dx.SurfaceIdTracker_UpdateBatchSurfaceId_retag_rufunctionsourcedtrustedbolrog (D2DX)
d2dx

Assigns/reuses a surfaceId per batch (tag stored on vertices via SetSurfaceId; used by AA/edge pixel shader, NOT a positional transform). Order: (1) if majorGameState!=InGame -> D2DX_SURFACE_ID_USER_INTERFACE. (2) if chroma-key disabled (text bg) -> UI. (3) gameplay-with-chroma UI reclassifications: draw-line single-pixel '(maxy - miny) == 1' -> UI; bottom band 'miny >= (gameSize.height - 48)' -> UI (48px); panels: '(_gameHelper->ScreenOpenMode() & 1)' left-panel open && 'min

ext.d2dx.OnBufferSwap_UnitMotionPrediction_per_vertex_tfunctionsourcedtrustedbolrog (D2DX)
d2dx

KEY per-batch positional translate. Gated by IsFeatureEnabled(Feature::UnitMotionPrediction) && _majorGameState==MajorGameState::InGame. offset = _unitMotionPredictor.GetOffset(_gameHelper->GetPlayerUnit()). Applied to EVERY batch EXCEPT surfaceId==D2DX_SURFACE_ID_USER_INTERFACE and TextureCategory::Player, via _vertices.items[vertexIndex++].AddOffset(-offset.x, -offset.y) (NEGATED). Floor AND wall batches are non-UI/non-player, so BOTH receive -offset; player+UI stay put. No | src: _vertices.items[vertexIndex++].AddOffset(-offset.x, -offset.y);

ext.d2dx.OnDrawLine_weather_particle_motion_offsetfunctionsourcedtrustedbolrog (D2DX)
d2dx

Weather/rain particle motion prediction: computes a directional offset and applies startPos += offset; endPos += offset; to line endpoints before vertex creation (~640-644). Separate from the world-geometry offset; affects only line primitives (weather).

ext.d2dx.Mouse_position_scale_center_mappingfunctionsourcedtrustedbolrog (D2DX)
d2dx

pos.x=(int32_t)(pos.x*scale+mouseOffsetX); pos.y=(int32_t)(pos.y*scale); maps OS cursor into scaled/centered render area (mouseOffsetX centers horizontally in fullscreen). Confirms centering offset is horizontal-only for cursor; not applied to world geometry.

ext.glide3xcpp.grDrawVertexArray_capture_seamfunctionsourcedtrustedbolrog / Necrolis (D2DX)
glide3x.cpp

Primary draw capture. Grabs _ReturnAddress() and forwards to the context factory singleton. This return address is later used to classify the draw as floor/wall/etc. | src: const auto returnAddress = (uintptr_t)_ReturnAddress(); D2DXContextFactory::GetInstance()->OnDrawVertexArray(mode, Count, (uint8_t**)pointers, returnAddress);

ext.glide3xcpp.grDrawVertexArrayContiguous_capture_seamfunctionsourcedtrustedbolrog / Necrolis (D2DX)
glide3x.cpp

Second capture path (used for 4-vertex TRIANGLE_FAN quads). Same _ReturnAddress() -> OnDrawVertexArrayContiguous(mode,Count,vertex,stride,returnAddress). | src: D2DXContextFactory::GetInstance()->OnDrawVertexArrayContiguous(mode, Count, (uint8_t*)vertex, stride, returnAddress);

ext.glide3xcpp.grViewport_is_a_no_op_asserts_origin_0_0functionsourcedtrustedbolrog / Necrolis (D2DX)
glide3x.cpp

Glide grViewport does nothing except assert x==0 && y==0. The game never sets a shifted Glide viewport; all viewport control is D2DX-internal. | src: grViewport(FxI32 x, FxI32 y, FxI32 width, FxI32 height){ assert(x == 0 && y == 0); }

ext.glide3xcpp.grClipWindow_unsupported_grCoordinateSpace_empfunctionsourcedtrustedbolrog / Necrolis (D2DX)
glide3x.cpp

grClipWindow => assert(false, Unsupported). grCoordinateSpace is an empty stub. No Glide-level scissor/clip or coordinate-space transform enters the pipeline. | src: grClipWindow(...){ assert(false && "grClipWindow: Unsupported"); } grCoordinateSpace(GrCoordinateSpaceMode_t mode){ }

ext.glide3xcpp.grSstWinOpen_OnSstWinOpenfunctionsourcedtrustedbolrog / Necrolis (D2DX)
glide3x.cpp

Resolves 640x480/800x600/1024x768 then calls OnSstWinOpen(hWnd,width,height). Asserts color_format==GR_COLORFORMAT_RGBA. | src: D2DXContextFactory::GetInstance()->OnSstWinOpen(hWnd, width, height); return 1;

ext.d2dx.D2DXContext_PrepareBatchForSubmitfunctionsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Where a Batch's category is finalized. gameAddress = IdentifyGameAddress(returnAddress); category = RefineTextureCategoryFromGameAddress(existing, gameAddress). Sets StartVertex=_vertexCount, VertexCount. NO position offset applied for any category. | src: batch.SetTextureCategory(_gameHelper->RefineTextureCategoryFromGameAddress(batch.GetTextureCategory(), gameAddress));

ext.d2dx.D2DXContext_OnBufferSwap_unit_MoP_offset_loopfunctionsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

THE ONLY post-capture positional shift of world geometry. If UnitMotionPrediction enabled and InGame: offset=_unitMotionPredictor.GetOffset(playerUnit); then for every batch with surfaceId != D2DX_SURFACE_ID_USER_INTERFACE AND category != Player, each vertex gets AddOffset(-offset.x,-offset.y). Floor AND wall both qualify -> identical uniform shift. | src: if (surfaceId != D2DX_SURFACE_ID_USER_INTERFACE && batch.GetTextureCategory() != TextureCategory::Player){ ... _vertices.items[vertexIndex++].AddOffset(-offset.x, -offset.y); }

ext.d2dx.D2DXContext_DrawBatches_mergefunctionsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

After BulkWriteVertices, merges consecutive batches when same texture-cache/atlas/alphaBlend and combined vertexCount<=65535, else flushes via _renderContext->Draw(mergedBatch,startVertexLocation). Category is NOT part of the merge key and NOT used to offset. | src: _renderContext->GetTextureCache(batch) != _renderContext->GetTextureCache(mergedBatch) || batch.GetTextureAtlas() != mergedBatch.GetTextureAtlas() || batch.GetAlphaBlend() != merge

ext.d2dx.RenderContext_UpdateViewport_single_uniform_vifunctionsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Sets ONE viewport and ONE scissor rect, both == the passed Rect (offset+size). Also writes Constants.screenSize/invScreenSize used by the vertex shader to map int16 screen coords to NDC. No per-batch or per-category viewport/scissor; set per render-pass only. | src: CD3D11_VIEWPORT viewport{ (float)rect.offset.x, (float)rect.offset.y, (float)rect.size.width, (float)rect.size.height }; ... CD3D11_RECT scissorRect{ rect.offset.x, rect.offset.y,

ext.d2dx.RenderContext_Present_viewport_sequencefunctionsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Game batches render into the Game framebuffer under UpdateViewport({0,0,_gameSize.width,_gameSize.height}) (set at end of prior Present, line 491). Gamma/AA passes also use {0,0,gameSize}. Final display blit to backbuffer uses UpdateViewport(_renderRect) (centered, letterboxed dest). gameSize=internal render res; renderRect=on-screen placement. | src: UpdateViewport({ 0,0,_gameSize.width, _gameSize.height }); ... UpdateViewport(_renderRect);

ext.d2dx.RenderContext_Draw_per_batch_no_transformfunctionsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Per merged batch: SetBlendState, pick atlas SRV, SetShaderState(Game VS/PS), then Draw(vertexCount, startVertexLocation + batch.GetStartVertex()). No matrix/offset/scissor per batch or category. | src: _deviceContext->Draw(batch.GetVertexCount(), startVertexLocation + batch.GetStartVertex());

ext.d2dx.UnitMotionPredictor_UnitMotion_GetOffset_iso_pfunctionsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Converts predicted vs last world pos (16.16 fixed) into a SCREEN-space isometric pixel offset. scaleFactors={32/sqrt2,16/sqrt2}; screenOffset=scaleFactors*{dx-dy,dx+dy}+0.5. Magnitude of the uniform world shift; a few px, never hundreds. | src: const OffsetF scaleFactors{ 32.0f / sqrtf(2.0f), 16.0f / sqrtf(2.0f) }; const OffsetF screenOffset = scaleFactors * OffsetF{ offset.x - offset.y, offset.x + offset.y } + 0.5f;

ext.d2dx.GameHelper_RefineTextureCategoryFromGameAddresfunctionsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Maps captured return address's GameAddress to a category ONLY when previous category is Unknown: DrawFloor->Floor, DrawWall1/DrawWall2->Wall, else unchanged. Category never triggers coordinate change. | src: case GameAddress::DrawFloor: return TextureCategory::Floor; case GameAddress::DrawWall1: case GameAddress::DrawWall2: return TextureCategory::Wall;

ext.d2dx.OnSstWinOpen_gameSize_windowSize_logicfunctionsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Computes gameSize from width/height (or _customGameSize override); if NoResMod off, windowSize forced 800x600; if gameSize != 640x480 then windowSize=gameSize. Creates/resizes RenderContext with (gameSize, windowSize*windowScale). Establishes internal render res (gameSize) that becomes the {0,0,gameSize} viewport. | src: Size gameSize{ width, height }; if (_customGameSize.width > 0){ gameSize = _customGameSize; } if (gameSize.width != 640 || gameSize.height != 480){ windowSize = gameSize; }

ext.d2dx.Metrics_GetRenderRect_letterbox_placementfunctionsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Computes on-screen _renderRect: integer-scales gameSize into desktop/window, centers it (offset=(dst-game*scale)/2); if margins exceed 16px falls back to non-integer scale filling width or height. This centered offset shifts only the final display blit, NOT world batches. | src: Rect rect{ (desktopSize.width - gameSize.width * scaleFactor) / 2, (desktopSize.height - gameSize.height * scaleFactor) / 2, gameSize.width * scaleFactor, gameSize.height * scaleFa

ext.D2GFX.D2GFX_DrawLinefunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
D2GFX

Draws a line between two screen points with color. | src: FUNCPTR(D2GFX, DrawLine, void __stdcall, (int X1, int Y1, int X2, int Y2, DWORD dwColor, DWORD dwUnk), -10010)

ext.D2GFX.D2GFX_DrawRectanglefunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
D2GFX

Fills/draws a screen rectangle with color and transparency mode. | src: FUNCPTR(D2GFX, DrawRectangle, void __stdcall, (int X1, int Y1, int X2, int Y2, DWORD dwColor, DWORD dwTrans), -10014)

ext.D2GFX.D2GFX_DrawAutomapCell2functionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
D2GFX

Renders a single automap cell/tile at screen xpos/ypos with two brightness values and a color table. Core automap-draw primitive. | src: FUNCPTR(D2GFX, DrawAutomapCell2, void __stdcall, (CellContext* context, DWORD xpos, DWORD ypos, DWORD bright2, DWORD bright, BYTE *coltab), -10041)

ext.D2GFX.D2GFX_GetScreenSizefunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
D2GFX

Returns encoded screen resolution (packed WxH). Render viewport sizing. | src: FUNCPTR(D2GFX, GetScreenSize, DWORD __stdcall, (void), -10031)

ext.D2GFX.D2GFX_GetHwndfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
D2GFX

Returns the game render window HWND. | src: FUNCPTR(D2GFX, GetHwnd, HWND __stdcall, (void), -10048)

ext.D2WIN.D2WIN_DrawSpritesfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x18750 D2WIN

Draws sprite batch (fastcall). Under 'D2Win Functions' header. | src: FUNCPTR(D2WIN, DrawSprites, void __fastcall, (void), 0x18750)

ext.D2WIN.D2WIN_TakeScreenshotfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x17eb0 D2WIN

Captures the current frame to a screenshot. | src: FUNCPTR(D2WIN, TakeScreenshot, void __fastcall, (), 0x17EB0)

ext.D2WIN.D2WIN_DrawTextfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
D2WIN

Draws a wide-char string at screen xPos/yPos with color. Primary on-screen text primitive. | src: FUNCPTR(D2WIN, DrawText, void __fastcall, (wchar_t *wStr, int xPos, int yPos, DWORD dwColor, DWORD dwUnk), -10150)

ext.D2WIN.D2WIN_GetTextSizefunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
D2WIN

Measures pixel width (and font file no) of a string for layout/centering. | src: FUNCPTR(D2WIN, GetTextSize, DWORD __fastcall, (wchar_t *wStr, DWORD* dwWidth, DWORD* dwFileNo), -10177)

ext.D2WIN.D2WIN_SetTextSizefunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
D2WIN

Selects the active font/text size for subsequent DrawText calls. | src: FUNCPTR(D2WIN, SetTextSize, DWORD __fastcall, (DWORD dwSize), -10184)

ext.D2WIN.D2WIN_SplitTextfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
D2WIN

Word-wraps text to a pixel width, returning line array + count (text-layout helper). | src: FUNCPTR(D2WIN, SplitText, SplitText* __fastcall, (wchar_t* text, int* lineCount, int width), -10059)

ext.D2WIN.D2WIN_FreeSplitTextfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
D2WIN

Frees a SplitText layout result. | src: FUNCPTR(D2WIN, FreeSplitText, void __fastcall, (SplitText* text), -10008)

ext.D2WIN.D2WIN_SetControlTextfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
D2WIN

Sets the text of a UI Control (edit box/button). UI, borderline render. | src: FUNCPTR(D2WIN, SetControlText, void* __fastcall, (Control* box, wchar_t* txt), -10042)

ext.D2CLIENT.D2CLIENT_GetUnitXfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x1630 D2CLIENT

Returns a unit's world X coordinate. Feeds world->screen projection. | src: FUNCPTR(D2CLIENT, GetUnitX, int __fastcall, (UnitAny* pUnit), 0x1630)

ext.D2CLIENT.D2CLIENT_GetUnitYfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x1660 D2CLIENT

Returns a unit's world Y coordinate. | src: FUNCPTR(D2CLIENT, GetUnitY, int __fastcall, (UnitAny* pUnit), 0x1660)

ext.D2CLIENT.D2CLIENT_ClearScreenfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x48350 D2CLIENT

Clears the screen/framebuffer. | src: FUNCPTR(D2CLIENT, ClearScreen, void __fastcall, (void), 0x48350)

ext.D2CLIENT.D2CLIENT_GetAutomapSizefunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x5f080 D2CLIENT

Returns automap size metric used for automap draw scaling. | src: FUNCPTR(D2CLIENT, GetAutomapSize, DWORD __stdcall, (void), 0x5F080)

ext.D2CLIENT.D2CLIENT_NewAutomapCellfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x5f6b0 D2CLIENT

Allocates a new AutomapCell node. | src: FUNCPTR(D2CLIENT, NewAutomapCell, AutomapCell * __fastcall, (void), 0x5F6B0)

ext.D2CLIENT.D2CLIENT_AddAutomapCellfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x61320 D2CLIENT

Links an AutomapCell into an automap layer node list. | src: FUNCPTR(D2CLIENT, AddAutomapCell, void __fastcall, (AutomapCell *aCell, AutomapCell **node), 0x61320)

ext.D2CLIENT.D2CLIENT_RevealAutomapRoomfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x62580 D2CLIENT

Reveals a Room1's automap cells onto an AutomapLayer (with clip flag). Core map-reveal. | src: FUNCPTR(D2CLIENT, RevealAutomapRoom, void __stdcall, (Room1 *pRoom1, DWORD dwClipFlag, AutomapLayer *aLayer), 0x62580)

ext.D2CLIENT.D2CLIENT_InitAutomapLayer_Ifunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x62710 D2CLIENT

Initializes/returns the AutomapLayer for a given layer number. | src: FUNCPTR(D2CLIENT, InitAutomapLayer_I, AutomapLayer* __fastcall, (DWORD nLayerNo), 0x62710)

ext.D2CLIENT.D2CLIENT_ClickMapfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x1bf20 D2CLIENT

Handles a click at screen x,y (translates screen->world action). Coordinate-relevant. | src: FUNCPTR(D2CLIENT, ClickMap, void __stdcall, (DWORD MouseFlag, DWORD x, DWORD y, DWORD Type), 0x1BF20)

ext.D2CLIENT.D2CLIENT_GetMouseXOffsetfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x3f6c0 D2CLIENT

Returns the mouse X offset used in screen<->world coordinate math. | src: FUNCPTR(D2CLIENT, GetMouseXOffset, DWORD __fastcall, (void), 0x3F6C0)

ext.D2CLIENT.D2CLIENT_GetMouseYOffsetfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x3f6d0 D2CLIENT

Returns the mouse Y offset used in screen<->world coordinate math. | src: FUNCPTR(D2CLIENT, GetMouseYOffset, DWORD __fastcall, (void), 0x3F6D0)

ext.D2CLIENT.D2CLIENT_PrintGameStringfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x7d850 D2CLIENT

Prints a message to the on-screen game message area. | src: FUNCPTR(D2CLIENT, PrintGameString, void __stdcall, (wchar_t *wMessage, int nColor), 0x7D850)

ext.D2CLIENT.D2CLIENT_PrintPartyStringfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x7d610 D2CLIENT

Prints a party message to screen. | src: FUNCPTR(D2CLIENT, PrintPartyString, void __stdcall, (wchar_t *wMessage, int nColor), 0x7D610)

ext.D2CLIENT.D2CLIENT_DrawRectFramefunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0xbe4c0 D2CLIENT

Draws a rectangular frame on screen. | src: FUNCPTR(D2CLIENT, DrawRectFrame, void __fastcall, (DWORD Rect), 0xBE4C0)

ext.D2CLIENT.D2CLIENT_BodyClickTable_ASMfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0xe0ec4 D2CLIENT

ASM patch anchor near the cursor/hover click-target table (adjacent to CursorHoverX/Y). | src: ASMPTR(D2CLIENT, BodyClickTable, 0xE0EC4)

ext.D2CLIENT.D2CLIENT_ClickMap_I_ASMfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0xfada4 D2CLIENT

ASM patch anchor for click-to-move handling (adjacent to AutomapOn 0xFADA8). | src: ASMPTR(D2CLIENT, ClickMap_I, 0xFADA4)

ext.D2CLIENT.D2CLIENT_ClickMap_II_ASMfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x11c4d8 D2CLIENT

ASM patch anchor for click-map coordinate handling. | src: ASMPTR(D2CLIENT, ClickMap_II, 0x11C4D8)

ext.D2CLIENT.D2CLIENT_ClickMap_III_ASMfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x3f5f0 D2CLIENT

ASM patch anchor for click-map (near GetMouseX/YOffset 0x3F6C0/0x3F6D0). | src: ASMPTR(D2CLIENT, ClickMap_III, 0x3F5F0)

ext.D2CLIENT.D2CLIENT_ClickMap_IV_ASMfunctionsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x1bf99 D2CLIENT

ASM patch anchor inside ClickMap (0x1BF20 + 0x79). | src: ASMPTR(D2CLIENT, ClickMap_IV, 0x1BF99)

ext.D2GFX.DrawLinefunctionsourcedsourcedSlashDiablo BH / planqi
D2GFX

Draws a line. Args (int X1,int Y1,int X2,int Y2,DWORD dwColor,DWORD dwUnk), __stdcall. Dual = export ordinals. | src: FUNCPTR(D2GFX, DrawLine, void __stdcall, (int X1, int Y1, int X2, int Y2, DWORD dwColor, DWORD dwUnk), -10010, -10013)

ext.D2GFX.DrawRectangle_2functionsourcedsourcedSlashDiablo BH / planqi
D2GFX

Draws a filled/trans rectangle. Args (int X1,int Y1,int X2,int Y2,DWORD dwColor,DWORD dwTrans), __stdcall. | src: FUNCPTR(D2GFX, DrawRectangle, void __stdcall, (int X1, int Y1, int X2, int Y2, DWORD dwColor, DWORD dwTrans), -10014, -10028)

ext.D2GFX.DrawAutomapCellfunctionsourcedsourcedSlashDiablo BH / planqi
D2GFX

Core automap-cell blitter. ASMPTR. Args (CellContext *context,DWORD xpos,DWORD ypos,RECT *cliprect,DWORD bright), __stdcall. | src: ASMPTR(D2GFX, DrawAutomapCell, void __stdcall, (CellContext *context, DWORD xpos, DWORD ypos, RECT *cliprect, DWORD bright), -10079, -10060)

ext.D2GFX.DrawAutomapCell2functionsourcedsourcedSlashDiablo BH / planqi
D2GFX

Second automap-cell draw variant. Args (CellContext* context,DWORD xpos,DWORD ypos,DWORD bright2,DWORD bright,BYTE *coltab), __stdcall. | src: FUNCPTR(D2GFX, DrawAutomapCell2, void __stdcall, (CellContext* context, DWORD xpos, DWORD ypos, DWORD bright2, DWORD bright, BYTE *coltab), -10041, -10042)

ext.D2GFX.DrawCellContextExfunctionsourcedsourcedSlashDiablo BH / planqi
D2GFX

Draws a cell context (sprite) with transparency/color. Args (CellContext *context,int Xpos,int Ypos,int dwl,int nTransLvl,BYTE Color), __stdcall. | src: FUNCPTR(D2GFX, DrawCellContextEx, void __stdcall, (CellContext *context, int Xpos, int Ypos, int dwl, int nTransLvl, BYTE Color), -10019, -10067)

ext.D2GFX.GetScreenSizefunctionsourcedsourcedSlashDiablo BH / planqi
D2GFX

Returns the current screen size. __stdcall (void). Render/viewport query. | src: FUNCPTR(D2GFX, GetScreenSize, DWORD __stdcall, (void), -10031, -10012)

ext.D2WIN.DrawSpritesfunctionsourcedsourcedSlashDiablo BH / planqi
rva 0x18750 D2WIN

Batched sprite draw. __fastcall (void). NOTE: dual values are DIRECT OFFSETS (positive hex), not ordinals — key rebasing distinction. | src: FUNCPTR(D2WIN, DrawSprites, void __fastcall, (void), 0x18750, 0xEAA0)

ext.D2WIN.DrawCellFilefunctionsourcedsourcedSlashDiablo BH / planqi
D2WIN

Draws a CellFile sprite. Args (CellFile * pCellFile,int xPos,int yPos,int div,int trans,int Color), __fastcall. | src: FUNCPTR(D2WIN, DrawCellFile, void __fastcall, (CellFile * pCellFile, int xPos, int yPos, int div, int trans, int Color), -10138, -10172)

ext.D2WIN.DrawTextfunctionsourcedsourcedSlashDiablo BH / planqi
D2WIN

Draws a unicode text string. Args (const wchar_t *wStr,int xPos,int yPos,DWORD dwColor,DWORD dwUnk), __fastcall. | src: FUNCPTR(D2WIN, DrawText, void __fastcall, (const wchar_t *wStr, int xPos, int yPos, DWORD dwColor, DWORD dwUnk), -10150, -10076)

ext.D2WIN.DrawTextBufferfunctionsourcedsourcedSlashDiablo BH / planqi
rva 0x12940 D2WIN

Text-buffer draw routine. ASMPTR with DIRECT OFFSETS (positive hex), not ordinals. | src: ASMPTR(D2WIN, DrawTextBuffer, 0x12940, 0x134D0)

ext.D2WIN.GetTextSizefunctionsourcedsourcedSlashDiablo BH / planqi
D2WIN

Measures text width/height. Args (wchar_t *wStr,DWORD* dwWidth,DWORD* dwFileNo), __fastcall. | src: FUNCPTR(D2WIN, GetTextSize, DWORD __fastcall, (wchar_t *wStr, DWORD* dwWidth, DWORD* dwFileNo), -10177, -10179)

ext.D2WIN.SetTextSizefunctionsourcedsourcedSlashDiablo BH / planqi
D2WIN

Sets the active font/text size. Args (DWORD dwSize), __fastcall. | src: FUNCPTR(D2WIN, SetTextSize, DWORD __fastcall, (DWORD dwSize), -10184, -10047)

ext.D2WIN.GetTextWidthFileNofunctionsourcedsourcedSlashDiablo BH / planqi
D2WIN

Text width + file-number query. Same ordinals as GetTextSize (-10177/-10179) — aliased export. Args (wchar_t *wStr,DWORD* dwWidth,DWORD* dwFileNo), __fastcall. | src: FUNCPTR(D2WIN, GetTextWidthFileNo, DWORD __fastcall, (wchar_t *wStr, DWORD* dwWidth, DWORD* dwFileNo), -10177, -10179)

ext.D2Client (uses D2Common exports).UNITS_GetDrawingPosition_non_perspective_brancfunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Client (uses D2Common exports)

Non-perspective (isometric) screen position of a unit. Two-line math: X offset minus (camera-X minus secondary-X), Y offset minus camera-Y. This is the core render-position formula for the FOCUS constants. | src: pPos->nX = D2COMMON_GetUnitOffsetX(pUnit) - (*D2CLIENT_6FBC9960 - *D2CLIENT_6FBCC418); pPos->nY = D2COMMON_GetUnitOffsetY(pUnit) - *D2CLIENT_6FBC995C;

ext.D2Client (uses D2Gfx + D2Common exports).UNITS_GetDrawingPosition_perspective_branchfunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Client (uses D2Gfx + D2Common exports)

Perspective-mode screen position. Reads unit world position (not offset), guards with D2GFX_CheckPerspectivePosition, transforms via D2GFX_AdjustPerspectivePosition, then applies +/-256 X shift based on UI panel state. | src: int nX = D2COMMON_GetUnitPositionX(pUnit); int nY = D2COMMON_GetUnitPositionY(pUnit); int nAdjustX = 0; int nAdjustY = 0; if (!D2GFX_CheckPerspectivePosition(nX, nY)) return FALSE;

ext.D2Client.UNITS_GetDrawingPosition_full_signaturefunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Client

Signature and null-guard of the reconstructed C function that kidpaddle94 describes as a slightly modified version of what the unit-hover handler uses. Returns FALSE on null args or failed perspective-position check. | src: BOOL __fastcall UNITS_GetDrawingPosition(D2UnitStrc* pUnit, D2CoordStrc* pPos) { if (!pUnit || !pPos) return FALSE;

ext.D2Client.UNITHOVER_Mainfunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
0x6FB70D30 D2Client

Unit-hover main handler. Comment notes it 'originally had pUnit arg'; reconstructed as void __fastcall pulling the unit from D2CLIENT_GetSelectedUnit(). Writes gnHoverX/gnHoverY then dispatches by pUnit->dwType. | src: /* Function: UNITHOVER_Main Address: D2Client.0xC0D30 Notes: originally had pUnit arg */ void __fastcall UNITHOVER_Main()

ext.D2Client.UNITHOVER_Main_non_perspective_hover_mathfunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
0x6FB70D30 D2Client

Non-perspective hover coordinate math. IDENTICAL X formula to UNITS_GetDrawingPosition, but Y subtracts an extra 8: (*D2CLIENT_6FBC995C - 8). This is the key difference from the drawing-position formula. | src: *D2CLIENT_gnHoverX = D2COMMON_GetUnitOffsetX(pUnit) - (*D2CLIENT_6FBC9960 - *D2CLIENT_6FBCC418); *D2CLIENT_gnHoverY = D2COMMON_GetUnitOffsetY(pUnit) - (*D2CLIENT_6FBC995C - 8);

ext.D2Client.UNITHOVER_Main_perspective_hover_mathfunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
0x6FB70D30 D2Client

Perspective hover math. Same CheckPerspectivePosition guard + AdjustPerspectivePosition + UISTATE +/-256 X shift as the drawing-position function, but the final Y adds 16: gnHoverY = nAdjustY + 16. | src: if (!D2GFX_CheckPerspectivePosition(nX, nY)) return; D2GFX_AdjustPerspectivePosition(nX, nY, 0, &nAdjustX, &nAdjustY); switch (*D2CLIENT_gnGlobalUiState) { case UISTATE_RIGHT:

ext.D2Gfx.D2GFX_CheckPerspectivefunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Gfx

Boolean gate selecting perspective vs isometric branch. Returns nonzero when D3D perspective (depth distortion) mode is active. No numeric address given in the thread. | src: if (!D2GFX_CheckPerspective())

ext.D2Gfx.D2GFX_CheckPerspectivePositionfunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Gfx

Validates a world (nX,nY) coordinate for perspective transform; failure aborts (return FALSE / return). Called before D2GFX_AdjustPerspectivePosition. | src: if (!D2GFX_CheckPerspectivePosition(nX, nY)) return FALSE;

ext.D2Gfx.D2GFX_AdjustPerspectivePositionfunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Gfx

The perspective-adjust entry point. Signature as used: (int nX, int nY, int 0, int* pAdjustX, int* pAdjustY) — takes world X/Y and a literal 0 third arg, writes screen-adjusted X/Y through the two out-pointers. Output then gets the UISTATE +/-256 X correction. | src: D2GFX_AdjustPerspectivePosition(nX, nY, 0, &nAdjustX, &nAdjustY);

ext.D2Common.D2COMMON_GetUnitOffsetX_D2COMMON_GetUnitOffsetfunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Common

Supply the isometric screen-offset X/Y of a unit; consumed by the NON-perspective branch of both functions. | src: D2COMMON_GetUnitOffsetX(pUnit) ... D2COMMON_GetUnitOffsetY(pUnit)

ext.D2Common.D2COMMON_GetUnitPositionX_D2COMMON_GetUnitPosifunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Common

Supply the world-space X/Y of a unit; consumed by the PERSPECTIVE branch (fed into D2GFX_AdjustPerspectivePosition). | src: int nX = D2COMMON_GetUnitPositionX(pUnit); int nY = D2COMMON_GetUnitPositionY(pUnit);

ext.D2Client.UNITHOVER_Main_dwType_dispatchfunctionsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
0x6FB70D30 D2Client

After computing hover coords, dispatches to per-type handlers: UNIT_PLAYER->UNITHOVER_PlayerUnits, UNIT_MONSTER->UNITHOVER_MonsterUnits, UNIT_OBJECT->UNITHOVER_ObjectUnits, UNIT_ITEM->UNITHOVER_ItemUnits, UNIT_TILE->UNITHOVER_TileUnits; default returns. | src: switch (pUnit->dwType) { case UNIT_PLAYER: UNITHOVER_PlayerUnits(pUnit); case UNIT_MONSTER: UNITHOVER_MonsterUnits(pUnit); case UNIT_OBJECT: UNITHOVER_ObjectUnits(pUnit); case UNIT

ext.GET_VERSION_D2ClientfunctionsourcedtrustedYohann Nicolas / PlugY (Speakus mirror)
rva 0x17f

D2Client.dll version probe: DWORD at base+0x17F. Render-critical module (client-side draw/UI); its detected version selects the offset table used for all client render hacks. | src: GET_VERSION(D2Client, 17F, 00000000, 14500000, 12500000, 0D814800, 0D812800, 0DA01000, 0DA03000);

ext.GET_VERSION_D2gfxfunctionsourcedtrustedYohann Nicolas / PlugY (Speakus mirror)
rva 0x10d

D2gfx.dll (D2Gfx) version probe: DWORD at base+0x10D. Graphics/present module; version selects render offsets. Note col7 (V113c) sig is 00000000 (same literal as D2Client col1) — value copied verbatim as read. | src: GET_VERSION(D2gfx, 10D, EB000000, 006FA700, 00000010, 2A000401, 19000401, 0B210E00, 00000000);

ext.GET_VERSION_D2WinfunctionsourcedtrustedYohann Nicolas / PlugY (Speakus mirror)
rva 0x1699

D2Win.dll version probe: DWORD at base+0x1699. UI/window/panel module; version selects offsets used by the panel (new-interfaces / stash) render hacks. | src: GET_VERSION(D2Win, 1699, 88686F8C, 84686F8C, D094686F, F0030000, 001435E8, 8B088F44, 0013F5E8);

ext.Install_NewInterfaces_entry_point_call_sitefunctionsourcedtrustedYohann Nicolas / PlugY (Speakus mirror)

Panel installer entry point: the new-interfaces (large stash / shared-stash panels + reworked inventory UI) hooks are installed only when the active_newInterfaces config flag is set. The Install_NewInterfaces() DEFINITION is NOT in D2wrapper.cpp (it lives in the interface/panel source, referenced via #include); D2wrapper.cpp holds only the gated call site. | src: if (active_newInterfaces) Install_NewInterfaces();

ext.Install_MultiPageStash_entry_point_call_sitefunctionsourcedtrustedYohann Nicolas / PlugY (Speakus mirror)

Panel installer entry point: the multi-page (paged) stash hooks are installed only when active_multiPageStash is set. Definition not in D2wrapper.cpp (external source via #include); D2wrapper.cpp holds only the gated call site. | src: if (active_multiPageStash) Install_MultiPageStash();

ext.113c ADDRESSES.apply_pos_changefunctionsourcedtrustedNecrolis (d2-rs)
rva 0xda40 1.13c ADDRESSES

Applies a DyPos position change. Signature per comment: __stdcall(DyPos* @ esi, FixedU16, FixedU16, Room*). DyPos pointer passed in ESI (register/thiscall-ish stdcall). | src: // Signature: `__stdcall(DyPos* @ esi, FixedU16, FixedU16, Room*)` apply_pos_change: 0xda40,

ext.113c ADDRESSES.in_perspectivefunctionsourcedtrustedNecrolis (d2-rs)
1.13c ADDRESSES

perspective-mode check (draw/render path)

ext.113c ADDRESSES.hwndfunctionsourcedtrustedNecrolis (d2-rs)
1.13c ADDRESSES

main window handle accessor

ext.113c ADDRESSES.draw_menufunctionsourcedtrustedNecrolis (d2-rs)
1.13c ADDRESSES

draw menu function

ext.113c ADDRESSES.draw_linefunctionsourcedtrustedNecrolis (d2-rs)
1.13c ADDRESSES

draw line primitive

ext.113c ADDRESSES.find_closest_colorfunctionsourcedtrustedNecrolis (d2-rs)
1.13c ADDRESSES

palette find-closest-color

ext.113c ADDRESSES.gen_weather_particlefunctionsourcedtrustedNecrolis (d2-rs)
rva 0x80700 1.13c ADDRESSES

generate one weather particle. Signature: stdcall(&mut Rng). | src: // Signature: stdcall(&mut Rng) gen_weather_particle: 0x80700,

ext.113c ADDRESSES.env_array_removefunctionsourcedtrustedNecrolis (d2-rs)
1.13c ADDRESSES

remove entry from environment array

corpus.DrawFramedTextExtfunctionsourcedsourcedMnw1995 (phrozen_code Discord)
D2Win

Draws framed text: (wchar_t* text, int nX, int nY, int nTextColor, int nAlign, int nRectColor). UI text-render primitive. | D2FUNC(D2WIN, DrawFramedTextExt, void, __fastcall, (wchar_t *wszText, int nX, int nY, int nTextColor, int nAlign, int nRectColor), 0xACD0) //D2Win.#10

corpus.D2CreatePL2functionsourcedtrustedNecrolis (phrozen_code Discord)
D2CMP

Builds a PL2 (palette+shift-tables) from a 256-color palette; entry point for changing D2's color scheme. D2PL2Strc* __stdcall D2CreatePL2(BYTE pPalette[256]). | D2CMP.#10000 D2PL2Strc* __stdcall D2CreatePL2(BYTE pPalette[256])

corpus.UNITDRAW_DrawOutlinefunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Draws the selection/aura outline around a unit. BOOL __fastcall UNITDRAW_DrawOutline(D2UnitStrc* pUnit). Part of the client unit-render path. | Function: UNITDRAW_DrawOutline Address: D2Client.0x6FB59F20

corpus.UNITDRAW_TickOutlinefunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Decrements a unit's outline tick and returns whether to draw it (unless STATE_ATTACHED). Reads pUnit->dwOutlineTick. Part of unit-render tick. | Function: UNITDRAW_TickOutline Address: D2Client.0x6FB29A40 ... DWORD dwTick = pUnit->dwOutlineTick;

corpus.Unit_visibility_check_is_unit_shownfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Under 1.10 determines whether a unit is drawn/shown; forcing its dead-check to return false keeps dead units visible. A gate in the client draw path. | D2Client.0x6FACE8F0 under 1.10 determines if a unit is shown

corpus.Life_orb_drawfunctionsourcedsourcedMnw1995 (phrozen_code Discord)
D2Client

Draws the (life/mana) orb on the HUD panel. Direct panel/HUD render entry. | D2Client.0x6FAFF9D0 draws the orb

corpus.D2AddRoomToDrawList_D2IterateRoomsfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client / D2Common

The room draw-list build step: D2Client passes its D2AddRoomToDrawList callback to D2IterateRooms (D2Common #10049) to walk visible rooms and enqueue them for drawing. Core of the world/floor render pipeline. | PUSH <D2Client.D2AddRoomToDrawList> ... CALL <D2Client.D2IterateRooms> ; JMP to D2Common.#10049

corpus.COLLISION_CheckMaskfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Common

Reads the per-subtile collision mask for a room coord: indexes pRoom->pCollisionGrid->pCollisionMask via ((y - subtilesTop)*subtilesWidth - subtilesLeft + x) & fMask. Reveals D2RoomCollisionStrc layout (pCollisionMask, pCoords with dwSubtilesTop/Left/Width). Governs floor/tile walkability that render+pathing share. | WORD __fastcall COLLISION_CheckMask(DRLGRoom* pRoom, int nXpos, int nYpos, WORD fMask) ... pMask[(((nYpos - pCoords->dwSubtilesTop) * pCoords->dwSubti

corpus.Gouraud_view_render_setup_D2CreateView_D2BuifunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Client-side view/render init trio: D2CreateView (called at 6FAA9501), D2BuildGouraudTables (6FAAB3C7), D2FreeGuaraudTables (6FAABB40). Gouraud tables drive the lighting/shading of the play-area render. | CALL D2Client.6FAB6FF0 ; D2CreateView() ... CALL D2Client.6FAB6AC0 ; D2BuildGouraudTables() ... CALL D2Client.6FAB6EB0 ; D2FreeGuaraudTables()

corpus.PATH_NormalizeCoordsfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Common

Normalizes an X/Y coord pair (in place). __forceinline void __fastcall PATH_NormalizeCoords(int* pXpos, int* pYpos). Coordinate transform used in pathing/positioning. | Function: PATH_NormalizeCoords Address: D2Common.#10111

corpus.UNITDRAW_DrawOverlaysfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Draws a unit's overlays; repeats the same world-offset/panel-adjust/perspective transform (subtract WorldX/YOffset, else-branch; or D2AdjustPerspectiveCoords + GetPanelAdjust + 16 in perspective). Second confirmed site of the panel-adjust screen-shift logic. void __fastcall(pUnit, dwColorTint, bBack, nXpos, nYpos, bPerspectiveDisabled). | Function: UNITDRAW_DrawOverlays Address: D2Client.0x6FB5A490 ... nXpos += UNITDRAW_GetPanelAdjust(); nXpos += 16; ... nXpos = pCoord.nXpos - UNITDRAW

corpus.D2ReInitRoomfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Game

void __fastcall(D2GameStrc* pGame, DRLGRoom* pRoom) - re-initialises a room | D2FUNC(D2GAME,0x85A0,void,__fastcall,D2ReInitRoom,(D2GameStrc* pGame, DRLGRoom* pRoom));

corpus.D2DrawImageToWindow_D2Gfx_10072functionsourcedsourcedDeleted User (phrozen_code Discord)
D2Gfx

D2Gfx image-blit-to-window function (ordinal 10072) at GfxOffset+0x42A0 | D2DrawImageToWindow = (GfxOffset + 0x42A0); // D2Gfx.10072

corpus.GetItemGfxData_Ifunctionsourcedsourcedmisiek1294 (phrozen_code Discord)
D2Client

Internal item gfx data fetch: (D2GfxDataStrc*, UnitAny*, CellFile*, int nDirection, int nFrame, BYTE fFlags, DWORD dwColorTint, int* pIndex). 1.13c offset 0x6E2C0 confirmed. Various naked __fastcall/__stdcall wrappers posted | D2PTR(D2CLIENT, GetItemGfxData_I,0x6E2C0) //0x6FB1E2C0

corpus.ITEMS_GetCompDatafunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Common

Builds the composite comp/color arrays (nComp[16] +088, nColor[16] +098). eD2CompositeItem enum: HEAD0,TORSO1,LEGS2,RARM3,LARM4,RHAND5,LHAND6,SHIELD7,SPECIAL1-8=8..15,COUNT16,INVALID0xFF | Function: ITEMS_GetCompData Address: D2Common.#10298

corpus.Inventory_panel_draw_functionfunctionsourcedsourcedMnw1995 (phrozen_code Discord)
D2Client

The function that handles inventory drawing. IMPORTANT for panel work: it ALSO draws the Stash, Cube, and Trade panels, so it's non-trivial to isolate a single panel. Advice: search for the .dc6 names to find the globals that store the panel images. | The function that handles the inventory drawing is located at D2Client.0x3EAC0 ... it also draws the Stash, Cube, Trade Panels

corpus.UNITDRAW_GetComponentsfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Necrolis-labeled function returning the component/draw-priority bytes for a unit's COF given rotation+frame. Indexes into the COF trailing arrays: nPos = ((nRotation*nFrames + nFrame + sizeof(D2COFLayerInfoStrc)) * nLayers) + nFrames, then adds sizeof(D2COFStrc). Part of the character/unit sprite render path. | Function: UNITDRAW_GetComponents Address: D2Client.0x6FB5EFC0

corpus.D2SetFont_D2Win_10127functionsourcedsourcedDeleted User (phrozen_code Discord)
D2Win

D2Win ordinal 10127 = D2SetFont, at WinOffset+0x09FF0 (6F8A9FF0). Text-render setup. | D2SetFont = (D2Win10127)(WinOffset + 0x09FF0); // D2Win.6F8A9FF0 #10127

corpus.D2CMP_10025_ReadDC6HeaderfunctionsourcedtrustedLectem (phrozen_code Discord)
D2CMP

signed int __stdcall ReadDC6Header(CelFile* pFile, DC6Header** a2, int a3, int a4). Asserts pFile->dwFlags & CELFILE_24BIT; allocates 4*framesPerDir*directions + sizeof(DC6Header); sets version=6, subVersion = dwFlags & 0xF8, copies directions/framesPerDir; dispatches via off_6FE091C0 table indexed by pFile->gap8. Related build-DC6 code sets subVersion 5 if 24bit else 1. | signed int __stdcall D2CMP_10025_ReadDC6Header(CelFile *pFile, DC6Header **a2, int a3, int a4)

corpus.TileGetRarity_exportfunctionsourcedsourcedeezstreet (cla$$ics) (phrozen_code Discord)
D2Common

D2Common export at 6FC711D6 named TileGetRarity (tile rarity accessor). | 6FC711D6 .text Export TileGetRarity

corpus.TileProjectSerialize_exportfunctionsourcedsourcedeezstreet (cla$$ics) (phrozen_code Discord)
D2Common

D2Common export at 6FC71668 named TileProjectSerialize (tile projection serialization). | 6FC71668 .text Export TileProjectSerialize

corpus.Composite_draw_loop_GetItemGfxData_DrawImagefunctionsourcedtrustedLectem (phrozen_code Discord)
D2Client/D2gfx

Layer draw loop over COF layers: for each selectable component, memset gfx, D2CLIENT_GetItemGfxData_6FB5EC80 fills a CelContext (dir, frame, component), then D2GFX_10076_DrawImageFast(&gfx, pXAdjust, pYAdjust, palette). Iterates layerIdx < cofFile->header.layers. Core unit/item composite rendering. | D2CLIENT_GetItemGfxData_6FB5EC80(...) ... D2GFX_10076_DrawImageFast(&tmpGfxInfo, pXAdjust, pYAdjust, nPaletteIndex[0]);

corpus.GetUnitOutlineWhenHit_D2Client_sub_7C39DD0functionsourcedtrustedLectem (phrozen_code Discord)
D2Client

Computes the outline color index when a unit is hit: lazily inits color indices via D2WIN_10034_GetClosestColorIndex (green 0,C0,0 / yellow E0,E0,0 / red E0,0,0 / blue 0,0,C0); for monsters checks MonStatsFlags bit1 (->blue), else colors by life% (<33 red, <66 yellow, else green). Uses ConvertMode #11013, GetAnimOrSeqMode #10369, QuickGetTotalMaxLife #10564, GetUnitStat #10519. | gGreenColorIndex = D2WIN_10034_GetClosestColorIndex(0, 0xC0u, 0); ... nLifePercentage < 33) return gRedColorIndex

corpus.ITEMS_GetItemPalette_D2Common_10893functionsourcedtrustedwhist / Deleted User (phrozen_code Discord)
D2Common

Returns the color/transform palette for drawing an item: __stdcall(pOwner, pItem, pColor, nTransType). Key gotcha: passing the player as pOwner tints inventory items by the player's palette; must pass NULL as pOwner so inventory items draw with correct colors. | BYTE* __stdcall ITEMS_GetItemPalette(Unit* pOwner, Unit* pItem, BYTE* pColor, int nTransType)

corpus.INVENTORYSCREEN_DrawItem_D2Client_0x6B5A0functionsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

Draws an item into the inventory/character panel: validates UNIT_ITEM, gets client player, GetItemCacheGfxData + D2CMP_InitGfxData, animates via GfxData.nFrame=((GetTickCount()>>7)+item LoSeed)%nMaxFrames, applies DRAWMODE_TRANS50 for ethereal, then D2GFX_DrawImage(&gfx, nPosX, nPosY+cell->nHeight, -1, drawMode, ITEMS_GetItemPalette(NULL,pItem,&nColor,1)). The pOwner=NULL fix lives here. | INVENTORYSCREEN_DrawItem Address: D2Client.0x6B5A0 ... D2GFX_DrawImage(&GfxData, nPosX, (nPosY + pCell->nHeight), -1, nDrawMode, ITEMS_GetItemPalette(

corpus.D2CheckUnitSelectable_D2Client_6FB5F7F0functionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

__fastcall(D2UnitStrc* pUnit, D2COFStrc* pCOF) BOOL — determines whether a unit/COF is selectable (mouse-over hit eligibility). | D2Client.6FB5F7F0 BOOL __fastcall D2CheckUnitSelectable(D2UnitStrc* pUnit, D2COFStrc* pCOF)

corpus.UI_INVENTORY_DrawItemBackgroundfunctionsourcedsourcedMnw1995 (phrozen_code Discord)
D2Client

Draws the colored cell background behind an inventory item, given the grid-info struct, X/Y, item width/height in cells, and a color index. Side-panel (inventory) rendering primitive. | void __fastcall UI_INVENTORY_DrawItemBackground(D2InventoryGridInfoStrc* pInventoryGridInfo, int nX, int nY, BYTE nItemWidth, BYTE nItemHeight, DWORD

corpus.UI_INVENTORY_LoadItemBackgroundColorsfunctionsourcedsourcedMnw1995 (phrozen_code Discord)
D2Client

Loads the inventory item-background colors in advance (paired with UI_INVENTORY_DrawItemBackground). | void __fastcall UI_INVENTORY_LoadItemBackgroundColors()

corpus.sub_6FAE0DC0_DrawInventoryItemSocketsfunctionsourcedsourcedmisiek1294 (phrozen_code Discord)
D2Client

Draws socket overlays in the inventory grid. Signature sub_6FAE0DC0(UnitAny* ptItem, InventoryGrid* pGrid, int nLeft, int nBottom). Uses a socket-position lookup array (first 2 dwords = where socket should be drawn, then 10 zero dwords per entry). 1.13c client base is 0x6FAA0000. | void __fastcall sub_6FAE0DC0(UnitAny *ptItem, InventoryGrid *pGrid, int nLeft, int nBottom)

corpus.UI_INVENTORY_socket_item_draw_internalsfunctionsourcedtrustedLectem (phrozen_code Discord)
D2Client / D2Common / D2gfx

Decompiled socket-drawing loop: indexes gSocketPositions by [height + 4*(width + 2*nbSockets) - 13], computes cell-centered X/Y from nGridBoxWidth/Height, sets up a D2GfxData with pCelFile = GEMSOCKET cellfile global (0x6FBB5E34) and calls D2GFX ordinal #10072 DrawImage, then draws each socketed item via DrawInventoryItems at 0x6FB5A320. Panel item/socket render pipeline. | pData.pCelFile = (CelFile *)gpCellfile_GEMSOCKET_6FBB5E34; D2GFX_10072_DrawImage(&pData, v10 - 1, v12, -1, 0, 0);

corpus.UNITDRAW_CheckSelectionfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Checks whether a unit is the current selection target during unit drawing; called from the unit-draw routine at 0x6FB5A720. Part of the play-area unit render path. | BOOL __fastcall UNITDRAW_CheckSelection(D2UnitStrc* pUnit) // D2Client.0x6FB286B0

corpus.D2Parse24BitTileLibrary_D2Load24BitTileLibrafunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2CMP

Ordinal exports that parse/load a 24-bit tile library, taking a per-pixel RGB mapping callback. Part of the DT1 tile-loading pipeline for the floor renderer. | 10074 void __stdcall D2Parse24BitTileLibrary(D2TileLibraryStrc* pLibrary, void (__stdcall* pfMap)(DWORD dwRGB))

corpus.D2CLIENT_GetItemGfxDatafunctionsourcedsourcedMnw1995 (phrozen_code Discord)
D2Client

Fills a D2GfxDataStrc for an item given cell file, direction, frame, component and mode. Item graphic-data resolver used in inventory/ground rendering. | BOOL __fastcall D2CLIENT_GetItemGfxData_6FB5EC80(D2GfxDataStrc* pGfxData, D2UnitStrc* pItem, D2CellFileStrc* pCellFile, int nDirection, int nFrame, in

corpus.tile_drawing_setup_fn_D2Client_6FAA3330functionsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

Whist identifies D2Client 6FAA3330 (1.10f) as where the tile-draw params struct is set up; Lectem notes one member is ptRect at +0x24 (matches pView slot in the 1.13c struct). Confirms the floor-render param block is built here before the d2gfx tile ordinals are called. | only def I have is the struct that gets passed to the tile drawing d2gfx ordinals and it's really incomplete

corpus.PATH_ScreenToWorldfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Common

Screen->world isometric coordinate transform (D2Common ordinal #10108). nX*=2; outX=(nX+nY)>>5; outY=(nX-nY)>>5. Core camera/iso projection math used when converting cursor/screen to tile world coords. | PATH_ScreenToWorld ... nXpos *= 2; *pXpos = (nXpos + nYpos) >> 5; *pYpos = (nXpos - nYpos) >> 5;

corpus.PATH_WorldToScreenfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Common

World->screen isometric transform (D2Common #10111): outX=(nX-nY)<<4; outY=(nX+nY)<<3. The inverse projection that places world tiles onto the play-area viewport. | PATH_WorldToScreen ... *pXpos = (nXpos - nYpos) << 4; *pYpos = (nXpos + nYpos) << 3;

corpus.PATH_FlattenCoordsfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Common

D2Common #10112 coordinate flatten: outX=(nX-nY)>>1; outY=(nX+nY)>>2. Another iso coordinate transform in the path/render coordinate family. | PATH_FlattenCoords ... *pXpos = (nXpos - nYpos) >> 1; *pYpos = (nXpos + nYpos) >> 2;

corpus.DUNGEON_TILES_ExpandCoords_DUNGEON_ExpandCoofunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Common

Tile coordinate expansion helpers: DUNGEON_TILES_ExpandCoords = D2Common #10110, DUNGEON_ExpandCoords = D2Common #10113. Part of the dungeon-tile coordinate transform set. | DUNGEON_TILES_ExpandCoords ... Address: D2Common.#10110 ... DUNGEON_ExpandCoords ... Address: D2Common.#10113

corpus.Waypoint_panel_draw_WindowStartX_NegWindowStfunctionsourcedsourcedThaison (phrozen_code Discord)
D2Client

Waypoint-panel drawing loop: WaypointTable at D2CLIENT 0xFCD8C, WaypointCoordTable at 0xDE6D8. Every cell/text is drawn at (*D2CLIENT_WindowStartX + coordTable->nX, coordTable->nY - *D2CLIENT_NegWindowStartY) via D2GFX_DrawCellContext / D2WIN_DrawText. Shows how side-panel UI is offset by the WindowStartX / NegWindowStartY globals -- directly relevant to panel-vs-playarea coordinate origin. | D2GFX_DrawCellContext(&pData, *D2CLIENT_WindowStartX + ptCoordTable->nX, ptCoordTable->nY - *D2CLIENT_NegWindowStartY, ...)

corpus.MULTIRES_DrawRoofTilesfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2Client.0x6FAA3B90 -- draws roof tile layer for the play area, takes D2ViewStrc* pView. | //D2Client.0x6FAA3B90 void __fastcall MULTIRES_DrawRoofTiles(D2ViewStrc* pView)

corpus.MULTIRES_DrawUpperWallTilesfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2Client.0x6FAA63E0 -- draws upper wall tiles; takes a D2DrawSlotStrc, a bOnlyShadows flag, and pView. | void __fastcall MULTIRES_DrawUpperWallTiles(D2DrawSlotStrc* pDrawSlot, BOOL bOnlyShadows, D2ViewStrc* pView)

corpus.MULTIRES_DrawLowerWallTilesfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2Client.0x6FAA6670 -- draws lower wall tiles; takes pView. | void __fastcall MULTIRES_DrawLowerWallTiles(D2ViewStrc* pView)

corpus.MULTIRES_DrawGroundTilesfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2Client.0x6FAA2E60 & D2Client.0x6FAA3330 -- draws the ground tile layers; takes pView. This is the core floor/ground draw pair that Lectem's dFloor snippets belong to. | //D2Client.0x6FAA2E60 & D2Client.0x6FAA3330 void __fastcall MULTIRES_DrawGroundTiles(D2ViewStrc* pView)

corpus.MULTIRES_SetResolutionfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2Client.0x6FAA23B0. Sets gdwScreenWidth/Height (640x480 or 800x600), sets gpExpansionUI, then gdwViewWidth = gdwScreenWidth and gdwViewHeight = gdwScreenHeight - kMultiResolutionUIHeightAdjustment. Calls MULTIRES_ResolutionChanged, MULTIRES_UpdateView(*gdwScreenPanels), D2ResizeAutomap, D2ResizeDialogs. THE function that derives the play-area view size from screen size minus UI height -- core to panel/viewport layou | *gdwViewWidth = *gdwScreenWidth; *gdwViewHeight = *gdwScreenHeight - kMultiResolutionUIHeightAdjustment; ... MULTIRES_UpdateView(*gdwScreenPanels);

corpus.D2WIN_ResizeWindow_10176functionsourcedsourcedNightshades (phrozen_code Discord)
D2Win

D2WIN.0x17B60 (ordinal #10176), __stdcall(nResolutionMode). Passes the resolution mode down to D2GFX #10025 to resize the window rect. | D2FUNC(D2WIN, ResizeWindow, void, __stdcall, (int nResolutionMode), 0x17B60);//D2WIN.#10176 - Pass resolution mode to D2GFX.#10025

corpus.D2GFX_SelectResolutionMode_10025functionsourcedsourcedNightshades (phrozen_code Discord)
D2gfx

D2GFX.0x7FD0 (ordinal #10025). Maps resolution mode -> out RECT right/bottom: 0=640x480, 1/2=800x600, 3=1344x700, default=0x0. Selects resolution mode and resizes the window rect. | D2FUNC(D2GFX,SelectResolutionMode,void,__stdcall,(int nResolutionMode,int *Out_RECT_right,int *Out_RECT_Bottom),0x7FD0)

corpus.PAL_Update_PAL_SetUpdatefunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Win

D2Win #10179 = PAL_Update() (applies palette); D2Win #10176 = PAL_SetUpdate(BOOL) sets the palette globals to activate/deactivate the update. (Note ordinal #10176 also cited for ResizeWindow -- version ambiguity to watch.) | //D2Win.#10176 void __fastcall PAL_SetUpdate(BOOL bActive) ... activate or deactivate D2Win.#10179

corpus.CLIENT_RunGame_client_app_mode_state_machinefunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Full client entry point: loads LNG, UI_Init, tables, then runs the app-mode function table (nMode loop) until APPMODE_CONTINUE. APPMODE_RENDER=6 is one of the client app modes. gszInterfaces[] maps D2Client/D2Server/D2Multi/D2Launch/D2EClient. | appmode_t* pfModes = VDEF(appmode_t*,D2CLIENT,0x44EC); ... *VDEF(DWORD*,D2CLIENT,0xD44D8) = nMode;

corpus.CLIENT_LoadDataTablesfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Common

Client-side data-table loader; note bPrecacheTileLibraries param governs tile-library precache (floor/tile relevance). | CLIENT_LoadDataTables(D2PoolManagerStrc* pMemPool, BOOL bPrecacheTileLibraries, BOOL bCreateLvlSubList)

corpus.D2Gfx_DrawLine_D2Gfx_DrawRectanglefunctionsourcedtrustedFirehawk (phrozen_code Discord)
D2Client/D2Gfx

Line/rect draw helpers positioned via ScreenWidth/2 - 0x90 (144) etc. DrawRectangle callable at D2Client.0xD0BA on 1.13c; D2gfx_DrawRectangle used with ScreenWidth/ScreenHeight to place a centered rect. | call D2Gfx_DrawLine -> D2Gfx_DrawRectangle

corpus.D2gfx_DrawRectangle_centered_box_callfunctionsourcedtrustedFirehawk (phrozen_code Discord)
D2Client

Draws a rectangle at (ScreenWidth/2 - 0x7F, ScreenHeight-0x1B) sized region -- another centered-on-play-area placement using ScreenWidth/2. | sub eax, 7Fh ... call D2gfx_DrawRectangle

corpus.UI_DrawHealthGlobe_UI_DrawManaGlobe_UI_DrawEfunctionsourcedtrustedFirehawk (phrozen_code Discord)
D2Client

Bottom-panel HUD draw functions (health globe, mana globe, exp bar). | //D2Client.0x6DDE0 - UI_DrawHealthGlobe

corpus.UI_DrawActiveSkillIcon_left_right_skill_panefunctionsourcedtrustedFirehawk (phrozen_code Discord)
D2Client

Draws the active skill icons in the bottom control panel. Left uses nX=0x75; right uses ScreenWidth + 0xFFFFFF5B (i.e. ScreenWidth-0xA5). bLeftSide flag distinguishes the two panels. | push 75h ; nX ... call UI_DrawActiveSkillIcon

corpus.UI_DrawSkillIcons_UI_DrawSkillSelectfunctionsourcedtrustedFirehawk (phrozen_code Discord)
D2Client

Skill-select menu draw. Right column at nX=0x50; left column at ScreenWidth + 0xFFFFFF80 (ScreenWidth-0x80). Input coords for the buttons live at 0x6F710; shifting the listed X offsets moves the popup menu too. | That's in UI_DrawSkillSelect, which is D2Client.0xA77D0

corpus.GAMEUI_DrawInterfacefunctionsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

whist: THE entry point for in-game UI drawing -- 'the best place to start looking for ui drawing code is always there'. Takes a GameView*. Top of the panel/HUD draw tree; prime hook target for panel/play-area work. | the best place to start looking for ui drawing code is always there ... GAMEUI_DrawInterface

corpus.Mini_Panel_handlers_drawfunctionsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

The mini-panel (the small button strip that opens inventory/character/skill panels). Input handler table + the drawing routine at 0x64990. | The drawing code is at D2Client.0x64990

corpus.DRLG_grid_cell_lookup_sub_6FD75CA0_DRLGGrid_functionsourcedtrustedMnw1995 / Lectem (phrozen_code Discord)
D2Common

Cell lookup: pCellPos[nCellPosId + pCellFlags[nCellFlagId]]. DRLGGrid = {int* pCellPos, int* pCellFlags, nWidth(nCells), nHeight, nullOnInit}; Lectem's D2DrlgCoordIndexStrc variant has pIndexList checked via bitmasks. drlg tile flags at D2DrlgTileDataStrc+0x14. | return pDrlgGrid->pCellPos[nCellPosId + pDrlgGrid->pCellFlags[nCellFlagId]];

corpus.UI_DrawSkillSelect_skill_button_input_coordsfunctionsourcedtrustedFirehawk (phrozen_code Discord)
D2Client

Where the majority of input coordinates for the skill-button inputs live; shifting the X offsets also moves the skill-select popup that appears. | D2Client.0x6F710 is where the majority of the input coordinates for the skill button inputs are on 1.13d

corpus.D2PreLoadImagesfunctionsourcedsourcedDeleted User (phrozen_code Discord)
D2Client

Client image preload routine at 0x5E370 + ClientOffset. | D2PreLoadImages = (0x5E370 + ClientOffset); // D2Client.6FAFE370

corpus.UNITDRAW_DrawUnitfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

CORE RENDER: __fastcall(pUnit,dwColorTint,nXpos,nYpos,bFade,bDrawOverlays) - draws a unit's sprite at screen X/Y with tint/fade/overlays. This is the per-unit draw entry in the play-area render path. | Function: UNITDRAW_DrawUnit Address: D2Client.0x6FB5A720

corpus.UNITDRAW_CacheGFXDatafunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

RENDER: __forceinline __fastcall(pGFXData,pUnit,pCellFile,nDirection,nFrame,fFlags,dwColorTint,pIndex) - caches the cell/DCC gfx data for a unit before drawing; called from UNITDRAW_DrawUnit. | Function: UNITDRAW_CacheGFXData Address: D2Client.0x6FB5EC80

corpus.inventory_input_handlerfunctionsourcedsourcedThaison (phrozen_code Discord)
D2Game

Inventory action handler sig (Game* pGame, Inventory* pInv, int nInput, int* a5, int* a6); a5/a6 last two args undetermined. | (Game* pGame, Inventory* pInv, int nInput, int* a5, int* a6)

corpus.UI_GetState_D2CLIENT_gnUiTogglefunctionsourcedsourcedSzumigajowy (phrozen_code Discord)
D2Client

UI panel open/close state reader: BOOL __fastcall UI_GetState(int nUiIndex) at D2Client.0x6FB23230 (1.10f); reads global array D2CLIENT_gnUiToggle[nUiIndex]; UITOGGLE_ESCMENU=9, bounded by NUM_UI. Core of knowing which side panels are open. | Address: D2Client.0x6FB23230 ... return D2CLIENT_gnUiToggle[nUiIndex];

corpus.D2GetItemName_D2Client_6FAD7210functionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2Client.6FAD7210 = D2GetItemName(pItem, wszBuffer, nBufferSize); called from 5 sites (6FADBBD6/6FADC43D/6FADC9B5/6FADD488/6FADD90D) — the item-description name builders, hookable to inject extra tooltip lines. | CALL D2Client.6FAD7210 ; D2GetItemName(D2UnitStrc* pItem, wchar_t* wszBuffer, size_t nBufferSize)

corpus.D2CLIENT_ItemLevel_tooltip_hook_offsetsfunctionsourcedsourcedKieran/Harvest (phrozen_code Discord)
D2Client

Item-Level-on-tooltip hook: patch site file offset 0x438A1 (1.9-1.13 variants 3D47C/438A1/ADD0A/789DA/AE0AA/941C0); 1.13 live 0x6FB341C0-... ; Set-item variant patch at 0x427BE. Uses D2CLIENT_SetColourPopUp (D2SetColorPopup, D2Client 85A60 in 1.11). | {D2DLL_D2CLIENT, 0x438A1, PATCH_CALL, FALSE, 1}, //6FAE38A1

corpus.SPEEDBOOK_DrawMenufunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Skill speed-book (radial skill selector) draw routine: void __fastcall SPEEDBOOK_DrawMenu(int nStart, BOOL bLeft, BOOL bDesc) at D2Client.0x6FB19720. On-screen HUD panel render. | Function: SPEEDBOOK_DrawMenu Address: D2Client.0x6FB19720

corpus.SPEEDBOOK_ClickDownfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Speed-book click handler: void __stdcall SPEEDBOOK_ClickDown(D2WinMsgStrc* pMsg) at D2Client.0x6FB19BB0. | Function: SPEEDBOOK_ClickDown Address: D2Client.0x6FB19BB0

corpus.SPEEDBOOK_DrawIconExfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Speed-book skill-icon draw: void __fastcall SPEEDBOOK_DrawIconEx(D2CellFileStrc* pCells, D2SkillStrc* pSkill, SkillsTXT* pRecord, DWORD dwGUID, int nValue, int nColor, int nXpos, int nYpos, BOOL bLeft) at D2Client.0x6FB18AC0. Takes explicit X/Y screen coords. | Function: SPEEDBOOK_DrawIconEx Address: D2Client.0x6FB18AC0

corpus.SKILLTREE_DrawTabIconsfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Skill-tree panel tab-icon draw: void __fastcall SKILLTREE_DrawTabIcons(D2UnitStrc* pPlayer, int nTab, int nClass) at D2Client.0x6FB163B0 and 0x6FB16A50 (two entry points). Left-side skill panel render. | Function: SKILLTREE_DrawTabIcons Address: D2Client.0x6FB163B0 & D2Client.0x6FB16A50

corpus.SKILLTREE_ClickDown_SKILLTREE_ClickUpfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Skill-tree panel mouse handlers: SKILLTREE_ClickDown at D2Client.0x6FB17A80 and SKILLTREE_ClickUp at D2Client.0x6FB18100, both __stdcall(D2WinMsgStrc*). | Function: SKILLTREE_ClickDown Address: D2Client.0x6FB17A80

corpus.Display_Text_D2Win_10171functionsourcedsourcedSilvermane (phrozen_code Discord)
D2Win/D2Client

Draws a Unicode string: ECX=ptr to unicode string, EDX=x, Arg1=y, Arg2=colour, Arg3=always 0. Called from D2Client to print stats on character screen. | Function: Display Text Address: 6F8AAD40 in v1.10 ECX - pointer to Unicode String EDX - x coord Arg1 - y coord Arg2 - colour

corpus.Get_the_Node_Page_item_container_pagefunctionsourcedsourcedSilvermane (phrozen_code Discord)
D2Common

Returns which container/page an item is in: 00 Inventory, 01 BodyLoc, 02 Belt, 03 Ground, 04 Cursor. Core mapping for inventory/equip side-panel item location. | Get the Node Page Offset: 00052100 (6FD92100) ... 00: Inventory 01: BodyLoc 02: Belt 03: Ground 04: Cursor

corpus.D2FillArea_underlying_renderer_callfunctionsourcedsourcedDeleted User (phrozen_code Discord)
D2Gdi/Game

D2FillArea passes 6 params; in 1.13d the CALL EAX lands in D2Gdi 0x6f877370 (takes all 6), in 1.14d lands at 0x006c8a60 windowed renderer (uses only params 3-6). Area-fill (solid rect) render path. | in 13d the CALL goes to this address 0x6f877370 in D2Gdi.dll that function takes all 6 paramters from D2FillArea

corpus.D2GFX_DrawSolidRectExfunctionsourcedsourceddzik (phrozen_code Discord)
Game (1.14)

void(nXStart,nYStart,nXEnd,nYEnd,dwColor,nDrawMode) - wrapper to every renderer. Guards x<=xEnd && y<=yEnd then calls [DAT_007c8cc0+0xB8](xEnd,yEnd,color,mode). This is the solid-rectangle fill primitive (relevant to dead-zone/panel-area fills). | void D2GFX_DrawSolidRectEx(int nXStart,int nYStart,int nXEnd,int nYEnd,DWORD dwColor,int nDrawMode)

corpus.RENDERER_WINDOWED_DrawSolidRectExfunctionsourcedsourceddzik (phrozen_code Discord)
Game (1.14)

Windowed-mode renderer solid-rect fill; __fastcall(x1,y1,x2,y2,byte color,int mode). The concrete windowed backend behind D2GFX_DrawSolidRectEx wrapper (0x4f6300). | //0x6c8a60 void __fastcall RENDERER_WINDOWED_DrawSolidRectEx (uint param_1,uint param_2,...)

corpus.D2isLODGame_D2GetDifficultyLevel_D2GetMouseXfunctionsourcedsourceddzik (phrozen_code Discord)
D2Client (1.14 merged Game.exe)

1.14 (post-DLL-merge) addresses; note subtract 0x400000 to get RVA. Mouse X/Y getters useful for cursor-vs-viewport mapping. | D2GetMouseX ... -> 0x468730 ... D2GetMouseY ... -> 0x468740

corpus.D2Win_D2DrawText_10117functionsourcedsourcedSilvermane (phrozen_code Discord)
D2Win

void __fastcall(wchar_t* pString,int nXpos,int nYpos,int nColour,int nTransTbl). Main HUD unicode draw call. | D2FUNC(D2WIN, D2DrawText, void, __fastcall, (wchar_t* pString, int nXpos, int nYpos, int nColour, int nTransTbl), 0xAD40)

corpus.D2SetScrollEndFunc_sets_two_scroll_end_callbfunctionsourcedtrustedKingpin (phrozen_code Discord)
D2Client

void __fastcall(func1,func2) that initializes two function-pointer globals. Kingpin named it D2SetScrollEndFunc. Named for scroll-end handling (viewport scroll callbacks). | static D2Client6FB1CBD0 D2SetScrollEndFunc = (D2Client6FB1CBD0)(0x7CBD0 + ClientOffset); // D2Client.6FB1CBD0

corpus.Skill_icon_drawing_functionfunctionsourcedtrustedkain_abel_666 (quoting Necrolis) (phrozen_code Discord)
D2Client

Function responsible for drawing the skill icons in 1.10 | In 1.10 have a look at D2Client.0x6FB18AC0, its responsible for drawing the icons.

corpus.CDraw_Game_UIfunctionsourcedsourcedSilvermane (phrozen_code Discord)
D2Client

Core game-UI draw function; hook target for drawing UI (calls #10072). Called from within larger UI routine; also builds a 0x13C stack frame at 6FB21B70 | //D2Client.6FB20A30 D2FUNC(D2CLIENT, CDraw_Game_UI, void, __fastcall, (DWORD Arg01), 0x80A30)

corpus.GUI_stat_skill_button_draw_callsfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Inside the GUI draw routine: CALL 6FAEF360 draws stat-points buttons, CALL 6FAEF730 draws skill-points buttons (at 6FB22030/6FB22044) | CALL d2client.6FAEF360 ; GUI Buttons -> stat points ... CALL d2client.6FAEF730 ; GUI Buttons -> skillpts

corpus.Text_background_width_height_D2GFX_DrawRectafunctionsourcedtrustedMir Drualga (phrozen_code Discord)
D2Win/D2GFX

D2Win ordinal 10110 draws a text popup background; helpers 6f8f1fb0 (text->background width, param in EAX) and 6f8f1e80 (text->background height, param in ESI); width/height feed D2GFX_DrawRectangle | 6f8f1fb0, takes text as param1 ... returns background width ... passed into the D2GFX_DrawRectangle function

corpus.background_dialog_draw_via_D2gfx_10072functionsourcedsourcedkain_abel_666 (phrozen_code Discord)
D2 (custom mod DLL, runtime 0x04D7xxxx)

kain_abel_666 located where a background is drawn: a D2gfx.#10072 draw call at 04D72AE5 that takes an X (EAX from [EBP-18C]) and a Y value (EDX from [EBP-190]); he identifies the Y push at 04D72AD0 as the value that is 'messing up' his positioning. Directly a render-pipeline / draw-position problem analogous to ours. | so i figured out where it draws the background ... CALL DWORD PTR DS:[4D76AA4] ; D2gfx.#10072 ... is the Y Value that is messing up

corpus.ITEMS_InventoryDrawfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Necrolis' reversed inventory item-draw function (draws an item cel inside the inventory/side panel, with overlay support). Shows the full draw path: UNITDRAW_CacheGFXData -> D2LoadCelFile -> GFXUTIL_CellWithinScreen -> D2DrawImage -> UNITDRAW_DrawOverlays. Directly the side-panel render pipeline. | Function: ITEMS_InventoryDraw Address: D2Client.0x6FB5A320 ... BOOL __fastcall ITEMS_InventoryDraw(D2UnitStrc* pItem, int nXpos, int nYpos)

corpus.GetMouseYCoordinatefunctionsourcedsourcedHarvest (phrozen_code Discord)
D2Client

D2Client.6FB57BD0 reads global at 6FBC1AE8; Harvest identifies it as GetMouseYCoordinate (RVA 0xB7BD0, __stdcall, no args). Screen-space coordinate accessor. | D2FUNC(D2CLIENT, GetMouseYCoordinate, DWORD, __stdcall, (), 0xB7BD0) //D2Client6FB57BD0

corpus.D2DrawText_D2Win_10117_item_gfx_GFX_10073functionsourcedsourcedkain_abel_666 (phrozen_code Discord)
D2Win / D2gfx

kain_abel_666 repositioning D2BuffIcons: text via D2Win.#10117 D2DrawText(pString,nX,nY,nColour,nTransTbl); icon images via GFX.#10073. Asks what -1 as the transtbl arg means. UI-panel overlay positioning. | D2DrawText(wchar_t* pString, int nXpos, int nYpos, int nColour, int nTransTbl); ... the only called to GFX.#10073 for the images

corpus.Item_ClickfunctionsourcedsourcedConqueror (phrozen_code Discord)
D2Client

Client-side handler run when clicking an item; takes inventory pointer, mouse X/Y and inventory page. Relevant to side-panel/inventory hit-testing. | //D2Client.0x475C0 D2FUNC(D2CLIENT, Item_Click, int32_t, __fastcall, (D2UnitStrc* pPlayer, D2InventoryStrc* pInventory, int pMouse_X, int pMouse_Y, ui

corpus.UNITS_GetInventoryRecordIdfunctionsourcedsourcedConqueror (phrozen_code Discord)
D2Common

Gets the inventory record id for a unit/inv page (Inventory.txt). Used for inventory/panel geometry. | //D2Common.0x6FDBEBE0 (#10409) int __stdcall UNITS_GetInventoryRecordId(D2UnitStrc* pUnit, int nInvPage, BOOL bLoD)

corpus.STATES_GetGfxStateFlags_STATES_GetStatFlagsfunctionsourcedsourcedConqueror (phrozen_code Discord)
D2Common

Two state-flag accessors returning DWORD* into a unit's state bitfield: #10492 GetGfxStateFlags @0x6FDB4760, #10494 GetStatFlags @0x6FDB48F0. GfxStateFlags governs which overlay/graphic states are active on a unit. | //D2Common.0x6FDB4760 (#10492) DWORD* __stdcall STATES_GetGfxStateFlags(D2UnitStrc* pUnit) ... //D2Common.0x6FDB48F0 (#10494)

corpus.PLAYERMODE_ChangefunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Game

Necrolis: void __fastcall PLAYERMODE_Change(pGame,pPlayer,pSkill,BYTE nMode,int nUnitType,DWORD dwGUID,BOOL bAllowReEnter) at D2Game.0x6FC81A00 (RVA 0x51A00). Changes a unit's animation/mode. | Function: PLAYERMODE_Change Address: D2Game.0x6FC81A00 ... void __fastcall PLAYERMODE_Change(D2GameStrc* pGame, D2UnitStrc* pPlayer, D2SkillStrc* pSki

corpus.main_UI_loop_ALT_item_box_drawfunctionsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

whist: 0xC39E0 is the main ui loop where all panels are rendered; it calls 0x58FB0 which draws the boxes shown when ALT is pressed to show ground items; hookable via d2template | at D2Client.0xC39E0 is the main ui loop where all the different panels are rendered ... a call to D2Client.0x58FB0, which is the function that handles

corpus.INTERFACE_CORE_InputAndOutputMsgWindowfunctionsourcedsourcedConqueror (orig Araksson) (phrozen_code Discord)
D2Client

Master click/message dispatcher for all menu panels; found by locating the 4 adjacent Storm#414-417 Input* calls, the function just before them handles all menu clicks; called ~40 times, one callback per panel type; callbacks share globals with the drawing function | the function immediately before is the one that controls all the Clicks of the Menus ... called about 40 times ... one for each type of Panel

corpus.DRLGROOM_AllocRoomNears_ReAllocRoomNears_roofunctionsourcedsourcedConqueror (Araksson/D2Moo) (phrozen_code Discord)
D2Common

Builds each room's ppRoomsNear list by scanning level rooms within tile distance <10 in X and Y (nTileXPos/YPos/Width/Height), sorts by position; D2Moo raises near cap 6->10 (ppNearRooms[1024]) | Function: DRLGROOM_AllocRoomNears Adress: D2Common.0x6FD77BB0 ... Improvement 6 --> 10

corpus.ITEMDESC_DrawGroundMultiHoverfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Draws multi-item ground hover boxes; second half is the relevant part; uses FONTS_GetTextDimensions then nWidth += 8 for box sizing | Function: ITEMDESC_DrawGroundMultiHover Address: D2Client.0x6FB09F60

corpus.D2Client_top_level_UI_control_panel_exp_bar_functionsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

Single top-level handler that draws the entire bottom control panel and the exp bar. NOPing its no-arg calls reveals what each sub-call draws. | that's the function I mentioned ... D2Client.0xC39E0 (top level handler necrolis mentioned)

corpus.ControlPanel_OrbTextDisplayfunctionsourcedsourcedSzumigajowy (phrozen_code Discord)
D2Client

Renders the orb text (life/mana orb display) on the control panel. | Function: ControlPanel::OrbTextDisplay Address: D2Client.0x6FAFF0B0

corpus.RoomTile_flag_setter_tile_dwFlags_bit_8functionsourcedsourcedSzumigajowy (phrozen_code Discord)
D2Common

Sets bit 8 of pTileData->dwFlags from the sign bit (0x80000000) of nTileFlags; floor/room tile flag handling. | 0x6FD88AC0 ... if (nTileFlags & 0x80000000) pTileData->dwFlags |= 8

corpus.UNITDESC_DrawInformationfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

void __fastcall UNITDESC_DrawInformation(D2UnitStrc* pUnit) at D2Client.0x6FB20A30 (1.10). Draws the unit info/nameplate; contains a large per-unit-type switch that looks up the display name. Render-relevant (unit nameplate drawing). | Function: UNITDESC_DrawInformation Address: D2Client.0x6FB20A30 ... large switch inside it for each unit type

corpus.Stamina_bar_draw_color_palettefunctionsourcedsourcedDannyIsGreat (phrozen_code Discord)
D2Client

Function responsible for drawing the stamina bar; 0x7BEFBA is an array of palette indexes picking bar color by remaining stamina | 0x4975B0. that is the func responsible for drawing the stamina bar ... 0x7BEFBA is an array of pallette indexes

corpus.XP_bar_drawfunctionsourcedsourcedDannyIsGreat (phrozen_code Discord)
D2Client

Function that draws the experience bar | try looking at 0x498EA0 for xp bar

corpus.DUNGEON_StreamRoomAtCoordsfunctionsourcedtrustedLectem (phrozen_code Discord)
D2Common

D2RoomStrc* __stdcall DUNGEON_StreamRoomAtCoords(D2DrlgActStrc* pAct, int nX, int nY) — resolves the room at world coords | D2RoomStrc* __stdcall DUNGEON_StreamRoomAtCoords(D2DrlgActStrc* pAct, int nX, int nY)

corpus.DRLG_AllocLevel_D2DrlgLevelStrc_fieldsfunctionsourcedsourcedSilvermane (phrozen_code Discord)
D2Common

Allocates a DRLG level from mempool; sets pDrlg, nLevelId, nLevelType, nDrlgType from DATATBLS_GetLevelDefRecord | D2DrlgLevelStrc* __fastcall DRLG_AllocLevel(D2DrlgStrc* pDrlg, int nLevelId)

corpus.INTERFACE_FillRenderfunctionsourcedsourcedConqueror (phrozen_code Discord)
D2Client

The callback that fills the Units renderer structure to later draw; invoked from D2Common.#10049 DUNGEON_CallRoomCallback. Central to the play-area unit render pipeline. Signature: BOOL __stdcall INTERFACE_FillRender(D2RoomStrc* pRoom, D2GameViewRenderDataCallBackStrc* pArgs). Hazel hit a crash here throwing many-CtC items into a crowd. | This is a callback from D2Common.#10049 (DUNGEON_CallRoomCallback). It is the function that is responsible for filling the Units renderer structure to

corpus.FONTS_DrawHoverText_D2WinfunctionsourcedsourcedSilvermane (phrozen_code Discord)
D2Win

Hover/tooltip text draw. Clamps X/Y against screen size via D2GFX_GetScreenSize(&nGameWidth,&nGameHeight); positions box so it stays on screen (nXpos > nGameWidth-nWidth -> nGameWidth-nWidth-5). Draws background via D2GFX_DrawSolidRectEx. Shows how the game reads viewport dimensions for UI placement -- the nGameWidth/nGameHeight globals bound the drawable area. | D2GFX_GetScreenSize(&nGameWidth, &nGameHeight); ... if ((uint32_t)nXpos > nGameWidth - nWidth) nXpos = nGameWidth - nWidth - 5;

corpus.Waypoint_panel_drawfunctionsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

Function that draws the waypoint panel in 1.13c. Active tab is a D2Client global variable (no server round-trip to switch tabs). Good entry point for locating panel-tab state globals. | the function that draws the waypoint panel (D2Client.0xAAEC0 on 1.13c)

corpus.CDraw_Game_UI_INTERFACE_DrawUnitSelectedNamefunctionsourcedsourcedConqueror (phrozen_code Discord)
D2Client

Draws the selected-unit name over the play area. Two names for same addr: D2FUNC CDraw_Game_UI (0x80A30) and Araksson's INTERFACE_DrawUnitSelectedName (void __fastcall(D2UnitStrc*)). Silvermane long wanted to rewrite this hover-name function. | Function: INTERFACE_DrawUnitSelectedName Adress: D2Client.0x80A30 void __fastcall INTERFACE_DrawUnitSelectedName(D2UnitStrc* pUnit)

corpus.UI_DrawGroundHoverItemfunctionsourcedsourcedSilvermane (phrozen_code Discord)
D2Client

Draws the ground item hover name in the play area: void __fastcall UI_DrawGroundHoverItem(D2UnitStrc* pItem, wchar_t* pBuffer, int nSize). | //D2Client.6FB20740 void __fastcall UI_DrawGroundHoverItem(D2UnitStrc* pItem, wchar_t* pBuffer, int nSize)

corpus.Ground_unit_name_lookup_funcsfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2Client.0x6FADD360 handles item names only; D2Client.0x6FB297F0 works for everything but players and vistiles (items always return base name). Used to resolve hovered-unit display names. | D2Client.0x6FADD360 is purely for item names, D2Client.0x6FB297F0 works for everything but players and vistiles

corpus.MENUS_DrawRegisteredControls_menu_input_handfunctionsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Win

OOG (out-of-game) menu draw/input entry points (1.13c): MENUS_DrawRegisteredControls (D2Win.#10024), INPUT_HandleMenuMouseInput (0x180F0), INPUT_HandleMenuKeyDownInput (0x182A0), INPUT_HandleMenuKeyCharInput (0x18370). | Function: MENUS_DrawRegisteredControls Address: D2Win.#10024 ... INPUT_HandleMenuMouseInput D2Win.0x180F0

corpus.Stat_screen_click_handlersfunctionsourcedsourcedConqueror (phrozen_code Discord)
D2Client

INTERFACE_StatScreenClickDown (D2Client.0x31290) and INTERFACE_StatScreenClickUp (0x31470), both void __stdcall(D2WinMsgStrc*). Handle input on the character/stat side panel. | Function: INTERFACE_StatScreenClickDown Adress: D2Client.0x31290 ... INTERFACE_StatScreenClickUp Adress: D2Client.0x31470

corpus.INTERFACE_InventoryLButtonDown_UP_1_13cfunctionsourcedsourcedConqueror (phrozen_code Discord)
D2Client

1.13c inventory-panel left-button handlers, __stdcall taking D2WinMsgStrc*. (1.10 equivalents: down 0x46E60, up 0x48140, InteractClickItem 0x475C0.) | D2FUNC(D2CLIENT, INTERFACE_InventoryLButtonDown, ... 0x98DC0) ... INTERFACE_InventoryLButtonUP ... 0x9C630)

corpus.DUNGEON_GetLevelIdFromRoomfunctionsourcedsourcedRayveen (phrozen_code Discord)
D2Common

BYTE __stdcall taking D2RoomStrc*; returns the level id for a room. Pair with UNITS_GetRoom to get the level a unit is standing in. | //D2Common.0x6FD8C7A0 (#10057) BYTE __stdcall DUNGEON_GetLevelIdFromRoom(D2RoomStrc* pRoom)

corpus.UNITS_GetRoomfunctionsourcedsourcedSilvermane (phrozen_code Discord)
D2Common

D2RoomStrc* __stdcall taking D2UnitStrc*; returns the room a unit occupies. | //D2Common.0x6FDBE270 (#10342) D2RoomStrc* __stdcall UNITS_GetRoom(D2UnitStrc* pUnit)

corpus.D2GAME_GetRoom_6FC52070_DUNGEON_IsRoomInTownfunctionsourcedsourcedRayveen (phrozen_code Discord)
D2Game

D2ActiveRoomStrc* getter at a target X/Y within a unit's room; DUNGEON_IsRoomInTown tests town membership. | D2ActiveRoomStrc* pRoom = D2GAME_GetRoom_6FC52070(UNITS_GetRoom(pUnit), nTargetX, nTargetY);

corpus.D2Common_CharmRequirements_10840_rewritefunctionsourcedsourcedSilvermane (phrozen_code Discord)
D2Common

CharmZone technique: rewrite of D2Common.#10840 requirement check restricting charms to an inventory sub-rectangle. Returns TRUE only if item is a charm, unit-type UNIT_ITEM, node page NODEPAGE_STORAGE, and pStaticPath->nYPos >= 6 and reqs met. Hooked from both D2Common (0x5FE70) and D2Client (0x4118B). | { D2DLL_D2COMMON, 0x5FE70, (DWORD)PATCH_JMP, FALSE, 0x01 }, { D2DLL_D2COMMON, 0x5FE71, (DWORD)D2Common_CharmRequirements, TRUE, 0x00 }, { D2DLL_D2CLIE

corpus.NewAutomapCell_automap_cell_drawfunctionsourcedsourcedOwnasaurus (phrozen_code Discord)
D2Client

NewAutomapCell returns pCell; +0x04 nCellNo determines the graphic drawn in the automap cell (asks re lookup table) | pCell(returned by NewAutomapCell)->nCellNo(offset +0x04) is what determines what is drawn in the cell

corpus.AUTOMAP_DrawCellsfunctionsourcedtrustedLectem (phrozen_code Discord)
D2Client

Automap cell drawing routine | See AUTOMAP_DrawCells 1.10f:0x6FACE230

corpus.weather_storm_phase_setter_rainfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Sets phase of a storm: phase 0 no rain, 1 wind-up, 2 rain, 3 wind-down; contains act5 level-id switch feeding snow/weather globals | D2Client.0x6FAA6D40 ... sets the phase of a storm (with phase 2 being the rain phase

corpus.PAL_LoadActData_per_frame_palette_render_ordfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Win / D2Client

Act palette load: D2Client sets act palette via D2Win.#10177 PAL_LoadActData(nActPalette) which calls D2Win.0x6F8AE5E0 with pl2/dat pair; to swap palettes load tile palette each frame, then reload units palette AFTER all tiles rendered (optional 3rd call for UI) | load the tile palette each frame ... make a second call ... just after all tiles have been rendered to load the units palette (and UI

corpus.spirit_vine_golem_hp_bar_drawfunctionsourcedsourcedoli (phrozen_code Discord)
D2Client

Draws HP bar for spirits, vines and golems (shared); monID check inside failed for user | its offset is 6FADB5F0. But it draws hp bar for spirits, vines and golems

corpus.experience_bar_hover_WIN_text_drawfunctionsourcedsourcedConqueror (phrozen_code Discord)
D2Client

Exp-bar popup init: hit-test uses gnScreenWidth/2-0x92..-0x17 and *D2CLIENT_ScreenHeight-0x2b..-0x22, calls WIN_HoverText; waypoint text via WIN_DrawNormalText(text,x,y,color,transTbl) at 0x6fb2610a | Init here D2Client.0x6faffd58 ... gnScreenWidth / 2 + -0x92 <= nMouseXPos ... WIN_DrawNormalText

corpus.unit_palette_assignment_monstersfunctionsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Unit palette assignment: 0x6FB27A23 begins it, 0x6FB5BFC0 writes palette index into unit draw data; bosses/champions roll random PL2 shift from unit GUID (source of random color shifts) | D2Client.6FB27A23 is the start of the unit palette assignment (D2Client.0x6FB5BFC0 sets the palette index in the units draw data) for monsters

D2Client.ScreenOpenModeglobalverified
0x6FBCC414 D2Client

which side panel(s) open: 0=none,1/2/3=side/both; drives the L/R world-view shift; == nScreenPanels passed to the driver tile-draw fns

D2Client.GeneralPlayAreaCameraShiftXglobalverified
0x6FBCC418 D2Client

offX: 0 with no panel, +/-DispW/4 (=320) with one; the native left-shift the mod KEEPS

D2Client.RenderContextPtrglobalverified
0x6FBCC3C4 D2Client

*[this] = the render ctx (ONE struct): draw-window +0x04(L)/+0x08(T)/+0x0C(R)/+0x10(B), scroll +0x24/+0x28, dirty +0x00, visited-room grid +0xEA9C

D2Client.GeneralDisplayWidthglobalverified
0x6FB8BC48 D2Client

DispW=1280 (SGD2FreeRes). +4=height, +8=w-copy, +0xC=centerX, +0x10=centerY

D2Client.PanelOpenArrayglobalverified
0x6FBAAD80 D2Client

per-panel open flags: inventory +4, char +8, skill +0x10, waypoint +0x50

D2gfx.DriverInterfacePtrglobalverified
0x6FA91268 D2gfx

*[this] = the active video-driver dispatch struct (= D2Glide's 0x6F8649F0); slot +0x9c = the cel handler D2Client #10001 invokes

D2Glide.DisplayWidthglobalverified
0x6F865A68 D2Glide

driver DisplayWidth; SGD2FreeRes rewrites 800->1280 at RVA 0xDCF1 at runtime; the -/4 math survives -> 960

D2Glide.DisplayHeightglobalverified
0x6F865B04 D2Glide

driver DisplayHeight; stock 600, SGD2->720

D2Glide.LiveDrawerGateglobalverified
0x6F865A8C D2Glide

[this]+4 / [this] gate the worker 0x7080: both-nonzero -> DEAD software drawer 0x6290; else -> LIVE drawer 0x6840

D2Glide.DriverDispatchStructglobalverified
0x6F8649F0 D2Glide

the driver vtable D2gfx installs at [0x6FA91268]; +0x9c=0x6F8572F0 (cel handler)

D2Glide.grDrawVertexArrayContiguous_IATglobalverified
0x6F8611D0 D2Glide

D2Glide IAT slot for glide3x!grDrawVertexArrayContiguous@16 (into D2DX). 0x6F856032 = jmp [this]. THE D2Glide->D2DX crossing; hook target for the pipeline-trace probe

ext.D2Client.ScreenShiftXglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x11b9a0 D2Client.dll

Horizontal screen shift (viewport pan offset). Named exactly in FOCUS. | src: D2Client.dll ScreenShiftX Offset 0x11B9A0

ext.D2Client.ScreenShiftYglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x11b9a4 D2Client.dll

Vertical screen shift (viewport pan offset). Named exactly in FOCUS. | src: D2Client.dll ScreenShiftY Offset 0x11B9A4

ext.D2Client.GeneralDisplayHeightglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0xdbc4c D2Client.dll

General display height. Named exactly in FOCUS. | src: D2Client.dll GeneralDisplayHeight Offset 0xDBC4C

ext.D2Client.IsAutomapOpenglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0xfada8 D2Client.dll

Automap open flag. | src: D2Client.dll IsAutomapOpen Offset 0xFADA8

ext.D2Client.IsGameMenuOpenglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0xfada4 D2Client.dll

Game menu open flag. | src: D2Client.dll IsGameMenuOpen Offset 0xFADA4

ext.D2Client.IsHelpScreenOpenglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0xfae04 D2Client.dll

Help screen open flag. | src: D2Client.dll IsHelpScreenOpen Offset 0xFAE04

ext.D2Client.InventoryArrangeModeglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x11b99c D2Client.dll

Inventory arrange mode (UI panel layout state). | src: D2Client.dll InventoryArrangeMode Offset 0x11B99C

ext.D2Client.IsNewSkillButtonPressedglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x11c30c D2Client.dll

New-skill button pressed flag (UI element state). | src: D2Client.dll IsNewSkillButtonPressed Offset 0x11C30C

ext.D2Client.IsNewStatsButtonPressedglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x11c308 D2Client.dll

New-stats button pressed flag (UI element state). | src: D2Client.dll IsNewStatsButtonPressed Offset 0x11C308

ext.D2Client.IngameMousePositionXglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x11b828 D2Client.dll

In-game mouse cursor X (screen coordinate). | src: D2Client.dll IngameMousePositionX Offset 0x11B828

ext.D2Client.IngameMousePositionYglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x11b824 D2Client.dll

In-game mouse cursor Y (screen coordinate). | src: D2Client.dll IngameMousePositionY Offset 0x11B824

ext.D2GFX.ResolutionModeglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x11260 D2GFX.dll

Render resolution mode selector. Comment gives enum values. | src: D2GFX.dll ResolutionMode Offset 0x11260 0 = 640x480, 1 = Main Menu, 2 = 800x600, 3 = 1344x700

ext.D2GFX.VideoModeglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x11258 D2GFX.dll

Active video/render backend selector. Comment gives enum values. | src: D2GFX.dll VideoMode Offset 0x11258 1 = GDI, 2 = Software, 3 = DirectDraw, 4 = Glide, 5 = OpenGL, 6 = Direct3D, 7 = Rave

ext.D2GFX.IsWindowedModeglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x1125c D2GFX.dll

Windowed vs fullscreen flag. | src: D2GFX.dll IsWindowedMode Offset 0x1125C

ext.D2GFX.WindowHandleglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x11264 D2GFX.dll

HWND of the render window. | src: D2GFX.dll WindowHandle Offset 0x11264 gtGfxSystem.hWnd

ext.D2DDraw.D2DDraw_DisplayWidthglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x101d4 D2DDraw.dll

DirectDraw backend display width. | src: D2DDraw.dll DisplayWidth Offset 0x101D4

ext.D2DDraw.D2DDraw_DisplayHeightglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x101cc D2DDraw.dll

DirectDraw backend display height. | src: D2DDraw.dll DisplayHeight Offset 0x101CC

ext.D2DDraw.D2DDraw_BitBlockWidthglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x101c8 D2DDraw.dll

DirectDraw bit-block (blit) width. | src: D2DDraw.dll BitBlockWidth Offset 0x101C8

ext.D2DDraw.D2DDraw_BitBlockHeightglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x101d8 D2DDraw.dll

DirectDraw bit-block (blit) height. | src: D2DDraw.dll BitBlockHeight Offset 0x101D8

ext.D2DDraw.D2DDraw_CelDisplayLeftglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x101e8 D2DDraw.dll

DirectDraw CEL display left bound. | src: D2DDraw.dll CelDisplayLeft Offset 0x101E8

ext.D2DDraw.D2DDraw_CelDisplayRightglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x101ec D2DDraw.dll

DirectDraw CEL display right bound. | src: D2DDraw.dll CelDisplayRight Offset 0x101EC

ext.D2GDI.D2GDI_BitBlockWidthglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0xca80 D2GDI.dll

GDI backend bit-block (blit) width. | src: D2GDI.dll BitBlockWidth Offset 0xCA80

ext.D2GDI.D2GDI_BitBlockHeightglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0xca90 D2GDI.dll

GDI backend bit-block (blit) height. | src: D2GDI.dll BitBlockHeight Offset 0xCA90

ext.D2GDI.D2GDI_CelDisplayLeftglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0xca98 D2GDI.dll

GDI CEL display left bound. | src: D2GDI.dll CelDisplayLeft Offset 0xCA98

ext.D2GDI.D2GDI_CelDisplayRightglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0xca9c D2GDI.dll

GDI CEL display right bound. | src: D2GDI.dll CelDisplayRight Offset 0xCA9C

ext.D2Direct3D.D2Direct3D_DisplayWidthglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x1ab44 D2Direct3D.dll

Direct3D backend display width. | src: D2Direct3D.dll DisplayWidth Offset 0x1AB44

ext.D2Direct3D.D2Direct3D_DisplayHeightglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x1afd4 D2Direct3D.dll

Direct3D backend display height. | src: D2Direct3D.dll DisplayHeight Offset 0x1AFD4

ext.D2Win.MainMenuMousePositionXglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x21488 D2Win.dll

Main-menu mouse cursor X (screen coordinate). | src: D2Win.dll MainMenuMousePositionX Offset 0x21488

ext.D2Win.MainMenuMousePositionYglobalsourcedtrustedmir (Diablo-II-Address-Table, public domain)
rva 0x2148c D2Win.dll

Main-menu mouse cursor Y (screen coordinate). | src: D2Win.dll MainMenuMousePositionY Offset 0x2148C

ext.D2client.D2client_dll_0xDBC4C_game_height_logicglobalsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0xdbc4c D2client.dll

Memory location storing the game's logic height. Written directly as DWORD constant in the 0x10E29 block. | src: C7 05 4CBCB86F 58020000 mov [D2client.dll+0xDBC4C], 00000258 ; 600 height logic

ext.D2client.D2client_dll_0x11B9A0_panel_X_offsetglobalsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0x11b9a0 D2client.dll

Memory storing X-offset positioning of UI panels. 640x480 -> 0; 800x600 -> 0x50 (80); 1066x600 -> 0xD5 (213). Written via 89 1D A0B9BC6F (reg) or C7 05 A0B9BC6F (const). | src: stores the x-offset positioning of panels; 1066x600 value 000000D5 (213)

ext.D2client.D2client_dll_0x11B9A4_panel_Y_offsetglobalsourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0x11b9a4 D2client.dll

Memory storing Y-offset positioning of UI panels. 640x480 -> 0; 800x600 AND 1066x600 -> 0xFFFFFFC4 (-60). Because 1066x600 shares 800x600's height (600), the y-offset code for 800x600 is reused. | src: stores the y-offset positioning of panels; value FFFFFFC4 (-60)

ext.draw_screen_background.kOriginalScreenBorderFrameImagePathglobalsourcedtrustedMir Drualga (SGD2FreeRes)
draw_screen_background

Original 800x600 border-frame DC6 path used when IsUseOriginalScreenBorderFrame() is true. | src: constexpr std::string_view kOriginalScreenBorderFrameImagePath = "data\\global\\UI\\Panel\\800BorderFrame";

ext.d2dx.gameAddresses_tables_DrawFloor_DrawWall_returnglobalsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Per-version glide3x.dll return-address constants identifying floor/wall draws. Index order: [1]=DrawWall1,[2]=DrawWall2,[3]=DrawFloor. | src: 113c: 0x6f8567ab DrawWall1, 0x6f8567b9 DrawWall2, 0x6f85befc DrawFloor | 114d: 0x50d39f DrawWall1, 0x50d3ae DrawWall2, 0x50db03 DrawFloor | 109d: 0x6f818468/0x6f818476/0x6f813e2f

ext.d2dx.D2DX_SURFACE_ID_USER_INTERFACEglobalsourcedtrustedbolrog / Necrolis (D2DX)
d2dx

=16383. UI surfaceId; MoP loop skips batches whose start vertex has this surfaceId (UI never shifts, world geometry incl. floor/wall does).

ext.D2WIN.D2WIN_FirstControlglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x214a0 D2WIN

Head of the UI Control linked list. UI, borderline render. | src: VARPTR(D2WIN, FirstControl, Control*, 0x214A0)

ext.D2WIN.D2WIN_FocusedControlglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x214b0 D2WIN

Currently focused UI Control. | src: VARPTR(D2WIN, FocusedControl, Control*, 0x214B0)

ext.D2CLIENT.D2CLIENT_ScreenSizeYglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0xdbc4c D2CLIENT

Current viewport height in pixels. | src: VARPTR(D2CLIENT, ScreenSizeY, DWORD, 0xDBC4C)

ext.D2CLIENT.D2CLIENT_CursorHoverXglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0xe0eb8 D2CLIENT

Screen X of the hovered cell/cursor target. | src: VARPTR(D2CLIENT, CursorHoverX, DWORD, 0xE0EB8)

ext.D2CLIENT.D2CLIENT_CursorHoverYglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0xe0ebc D2CLIENT

Screen Y of the hovered cell/cursor target. | src: VARPTR(D2CLIENT, CursorHoverY, DWORD, 0xE0EBC)

ext.D2CLIENT.D2CLIENT_MouseXglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x11b828 D2CLIENT

Current mouse cursor X in screen pixels. | src: VARPTR(D2CLIENT, MouseX, DWORD, 0x11B828)

ext.D2CLIENT.D2CLIENT_MouseYglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x11b824 D2CLIENT

Current mouse cursor Y in screen pixels. | src: VARPTR(D2CLIENT, MouseY, DWORD, 0x11B824)

ext.D2CLIENT.D2CLIENT_ViewportXglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x119960 D2CLIENT

Viewport/gamewindow X extent (int). Used with ViewportY for world->screen shift. | src: VARPTR(D2CLIENT, ViewportX, int, 0x119960)

ext.D2CLIENT.D2CLIENT_ViewportYglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x11995c D2CLIENT

Viewport/gamewindow Y extent (int). | src: VARPTR(D2CLIENT, ViewportY, int, 0x11995C)

ext.D2CLIENT.D2CLIENT_AutomapOnglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0xfada8 D2CLIENT

Nonzero when the automap is displayed. | src: VARPTR(D2CLIENT, AutomapOn, DWORD, 0xFADA8)

ext.D2CLIENT.D2CLIENT_AutomapModeglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0xf16b0 D2CLIENT

Automap display mode (full/mini/etc). | src: VARPTR(D2CLIENT, AutomapMode, int, 0xF16B0)

ext.D2CLIENT.D2CLIENT_Offsetglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x11c1f8 D2CLIENT

POINT screen offset applied when drawing (render/scroll origin). Coordinate-shift global. | src: VARPTR(D2CLIENT, Offset, POINT, 0x11C1F8)

ext.D2CLIENT.D2CLIENT_AutomapLayerglobalsourcedsourcedd2bs community (ds-hwang/noah- patch-113)
rva 0x11c1c4 D2CLIENT

Pointer to the active AutomapLayer being drawn. | src: VARPTR(D2CLIENT, AutomapLayer, AutomapLayer*, 0x11C1C4)

ext.D2CLIENT.ScreenSizeYglobalsourcedsourcedSlashDiablo BH / planqi
rva 0xdbc4c D2CLIENT

Screen height global (DWORD). | src: VARPTR(D2CLIENT, ScreenSizeY, DWORD, 0xDBC4C, 0xF7038)

ext.D2CLIENT.CursorHoverXglobalsourcedsourcedSlashDiablo BH / planqi
rva 0xe0eb8 D2CLIENT

Cursor hover X coordinate (DWORD). | src: VARPTR(D2CLIENT, CursorHoverX, DWORD, 0xE0EB8, 0xEE4AC)

ext.D2CLIENT.CursorHoverYglobalsourcedsourcedSlashDiablo BH / planqi
rva 0xe0ebc D2CLIENT

Cursor hover Y coordinate (DWORD). | src: VARPTR(D2CLIENT, CursorHoverY, DWORD, 0xE0EBC, 0xEE4B0)

ext.D2CLIENT.MouseXglobalsourcedsourcedSlashDiablo BH / planqi
rva 0x11b828 D2CLIENT

Mouse X screen position (DWORD). | src: VARPTR(D2CLIENT, MouseX, DWORD, 0x11B828, 0x11C950)

ext.D2CLIENT.MouseYglobalsourcedsourcedSlashDiablo BH / planqi
rva 0x11b824 D2CLIENT

Mouse Y screen position (DWORD). | src: VARPTR(D2CLIENT, MouseY, DWORD, 0x11B824, 0x11C94C)

ext.D2CLIENT.MouseOffsetXglobalsourcedsourcedSlashDiablo BH / planqi
rva 0x119960 D2CLIENT

Mouse offset X (int) — screen-to-world/viewport offset. | src: VARPTR(D2CLIENT, MouseOffsetX, int, 0x119960, 0x106844)

ext.D2CLIENT.MouseOffsetYglobalsourcedsourcedSlashDiablo BH / planqi
rva 0x11995c D2CLIENT

Mouse offset Y (int) — screen-to-world/viewport offset. | src: VARPTR(D2CLIENT, MouseOffsetY, int, 0x11995C, 0x106840)

ext.D2CLIENT.OffsetglobalsourcedsourcedSlashDiablo BH / planqi
rva 0x11c1f8 D2CLIENT

Screen render Offset (POINT struct) — the world-to-screen scroll/camera offset used when drawing units and automap. | src: VARPTR(D2CLIENT, Offset, POINT, 0x11C1F8, 0x11CF5C)

ext.D2CLIENT.PanelOffsetXglobalsourcedsourcedSlashDiablo BH / planqi
rva 0x11b9a0 D2CLIENT

Panel offset X (int) — horizontal shift applied when side panels (inventory/skill) are open. Directly relevant to viewport shift. | src: VARPTR(D2CLIENT, PanelOffsetX, int, 0x11B9A0, 0x11D354)

ext.D2CLIENT.xShakeglobalsourcedsourcedSlashDiablo BH / planqi
rva 0x11bf00 D2CLIENT

Screen-shake X offset (int) applied to render position. | src: VARPTR(D2CLIENT, xShake, int, 0x11BF00, 0x11CA6C)

ext.D2CLIENT.yShakeglobalsourcedsourcedSlashDiablo BH / planqi
rva 0x10b9dc D2CLIENT

Screen-shake Y offset (int) applied to render position. | src: VARPTR(D2CLIENT, yShake, int, 0x10B9DC, 0xFC3DC)

ext.D2CLIENT.AutomapOnglobalsourcedsourcedSlashDiablo BH / planqi
rva 0xfada8 D2CLIENT

Automap-visible flag (DWORD). | src: VARPTR(D2CLIENT, AutomapOn, DWORD, 0xFADA8, 0x11C8B8)

ext.D2CLIENT.AutomapModeglobalsourcedsourcedSlashDiablo BH / planqi
rva 0xf16b0 D2CLIENT

Automap display mode (int). | src: VARPTR(D2CLIENT, AutomapMode, int, 0xF16B0, 0xF34F8)

ext.D2CLIENT.AutomapLayerglobalsourcedsourcedSlashDiablo BH / planqi
rva 0x11c1c4 D2CLIENT

Pointer to current AutomapLayer* — root of the automap cell list that gets drawn. | src: VARPTR(D2CLIENT, AutomapLayer, AutomapLayer*, 0x11C1C4, 0x11CF28)

ext.D2GFX.VideoMode_2globalsourcedsourcedSlashDiablo BH / planqi
rva 0x11258 D2GFX

Current video/render mode (WORD) — DDraw/D3D/Glide selector. | src: VARPTR(D2GFX, VideoMode, WORD, 0x11258, 0x14A38)

ext.D2Client.D2CLIENT_6FBC9960globalsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Client

Camera/viewport X reference. Used as the minuend inside the X screen-offset term (*D2CLIENT_6FBC9960 - *D2CLIENT_6FBCC418) in BOTH UNITS_GetDrawingPosition and UNITHOVER_Main non-perspective branches. Dereferenced pointer to an int. | src: (*D2CLIENT_6FBC9960 - *D2CLIENT_6FBCC418)

ext.D2Client.D2CLIENT_6FBCC418globalsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Client

Secondary X offset. Subtrahend in the X term (*D2CLIENT_6FBC9960 - *D2CLIENT_6FBCC418). Present in both non-perspective X formulas (drawing-position and hover). | src: (*D2CLIENT_6FBC9960 - *D2CLIENT_6FBCC418)

ext.D2Client.D2CLIENT_6FBC995Cglobalsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Client

Camera/viewport Y reference. In drawing-position: nY = OffsetY - *D2CLIENT_6FBC995C. In hover: gnHoverY = OffsetY - (*D2CLIENT_6FBC995C - 8) (extra -8). | src: pPos->nY = D2COMMON_GetUnitOffsetY(pUnit) - *D2CLIENT_6FBC995C;

ext.D2Client.D2CLIENT_gnGlobalUiStateglobalsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Client

Current UI panel state, switched on in the perspective branch. UISTATE_RIGHT => nAdjustX -= 256; UISTATE_LEFT => nAdjustX += 256. The 256 is the side-panel width shift applied when a left/right panel is open. | src: switch (*D2CLIENT_gnGlobalUiState) { case UISTATE_RIGHT: nAdjustX -= 256; case UISTATE_LEFT: nAdjustX += 256; }

ext.D2Client.D2CLIENT_gnHoverX_D2CLIENT_gnHoverYglobalsourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
0x6FB70D30 D2Client

Destination globals for the hover cursor/box position. Non-perspective: Y carries an extra -8 vs the draw formula. Perspective: Y carries an extra +16. | src: *D2CLIENT_gnHoverX = ...; *D2CLIENT_gnHoverY = nAdjustY + 16;

ext.113c ADDRESSES.env_effectsglobalsourcedtrustedNecrolis (d2-rs)
rva 0x11bf60 1.13c ADDRESSES

environment effects global (weather/env array)

ext.113c ADDRESSES.entity_tableglobalsourcedtrustedNecrolis (d2-rs)
rva 0x10a608 1.13c ADDRESSES

primary entity table

ext.113c ADDRESSES.entity_table2globalsourcedtrustedNecrolis (d2-rs)
rva 0x109a08 1.13c ADDRESSES

secondary entity table

ext.113c ADDRESSES.client_loop_globalsglobalsourcedtrustedNecrolis (d2-rs)
rva 0x1197e0 1.13c ADDRESSES

client loop globals block

ext.113c ADDRESSES.cursor_tableglobalsourcedtrustedNecrolis (d2-rs)
rva 0xd8558 1.13c ADDRESSES

cursor table

ext.113c ADDRESSES.game_cursorglobalsourcedtrustedNecrolis (d2-rs)
rva 0xfb834 1.13c ADDRESSES

game cursor global

ext.113c ADDRESSES.summit_cloud_x_posglobalsourcedtrustedNecrolis (d2-rs)
1.13c ADDRESSES

summit cloud X position (0 = not present/unused in 1.13c)

ext.113c ADDRESSES.viewport_widthglobalsourcedtrustedNecrolis (d2-rs)
rva 0xf9e14 1.13c ADDRESSES

viewport width global

ext.113c ADDRESSES.viewport_heightglobalsourcedtrustedNecrolis (d2-rs)
rva 0xf9e18 1.13c ADDRESSES

viewport height global (immediately after width, +4)

ext.113c ADDRESSES.viewport_shiftglobalsourcedtrustedNecrolis (d2-rs)
rva 0x11c418 1.13c ADDRESSES

viewport shift global (the render offset knob)

ext.113c ADDRESSES.max_weather_particlesglobalsourcedtrustedNecrolis (d2-rs)
rva 0x113934 1.13c ADDRESSES

max weather particles

ext.113c ADDRESSES.weather_angleglobalsourcedtrustedNecrolis (d2-rs)
rva 0x113918 1.13c ADDRESSES

weather angle

ext.113c ADDRESSES.rain_speedglobalsourcedtrustedNecrolis (d2-rs)
rva 0x1138f4 1.13c ADDRESSES

rain speed

ext.113c ADDRESSES.is_snowingglobalsourcedtrustedNecrolis (d2-rs)
rva 0x11bf78 1.13c ADDRESSES

is-snowing flag

ext.113c ADDRESSES.sine_tableglobalsourcedtrustedNecrolis (d2-rs)
rva 0x2f470 1.13c ADDRESSES

sine lookup table

ext.BaseAddresses (from v111a, re-exported through v112 into v113c).BASE_ADDRESSES_clientglobalsourcedtrustedNecrolis (d2-rs)
rva 0x6fab0000 BaseAddresses (from v111a, re-exported through v112 into v113c)

D2Client.dll default image base

ext.BaseAddresses.BASE_ADDRESSES_gameglobalsourcedtrustedNecrolis (d2-rs)
rva 0x6fc20000 BaseAddresses

D2Game.dll default image base

ext.BaseAddresses.BASE_ADDRESSES_gfxglobalsourcedtrustedNecrolis (d2-rs)
rva 0x6fa80000 BaseAddresses

D2gfx.dll default image base (the module whose ordinals draw_menu/draw_line/in_perspective/find_closest_color/hwnd/env_array_remove are exports of)

ext.BaseAddresses.BASE_ADDRESSES_winglobalsourcedtrustedNecrolis (d2-rs)
rva 0x6f8e0000 BaseAddresses

D2Win.dll default image base

corpus.Unit_Display_globalsglobalsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Two D2Client globals referenced in the Unit_Display function; 6FBBA4A0 is believed to be the monster description string (resists, montype, etc.) built for the hover panel. | I have a feeling its used to build 6FBBA4A0, which is the monster desc string (resists, montype etc)

corpus.Charm_inventory_grid_function_1_13cglobalsourcedsourcedThaison (phrozen_code Discord)
D2Common

Function responsible for the charm inventory in 1.13c. Thaison supplies a CharmZone hook checking item grid position: pInventory at [EAX+0x2C], leftBorder/rightBorder at [+0x0C], top/bottomBorder at [+0x10] (top>=4, bottom<=8) to define the charm grid zone. Panel/inventory-grid render logic. | D2Common.dll - 0x28846 ... CMP DWORD PTR DS:[EAX+0x0C], 0 //leftBorder ... CMP DWORD PTR DS:[EAX+0x10], 4 //topBorder

corpus.D2CMP_crash_addressglobalsourcedsourcedAlphA - The Real One (phrozen_code Discord)
D2CMP

Access-violation fault address inside D2CMP.dll (cross-referenced to d2mods thread) | Fault address: 6FE21614 01:00010614 D:\Spiele\Diablo II\D2CMP.dll

corpus.Trade_panel_draw_function_1_13cglobalsourcedsourcedmisiek1294 (phrozen_code Discord)
D2Client

Function where the trade panel is drawn in 1.13c | function where trade panel are drawn 0x99440 (1.13c)

corpus.Item_quality_color_jumptable_1_13c_clientglobalsourcedtrustedFirehawk (phrozen_code Discord)
D2Client

Item gfx setup writes 0xFFFFFFFF to pnUnused via jumptable cases 0-2 (lea esi,[esp+pGfxData]) | jumptable 6FB5CFC7 cases 0-2 ... mov dword ptr [eax], 0FFFFFFFFh

corpus.Item_quality_color_select_1_10_clientglobalsourcedsourcedOgodei (phrozen_code Discord)
D2Client

Item name color selection: calls D2Common.#10695 to get quality, jumptable at 6FB20A14 maps quality-4 to color: rare=9,crafted=8,tempered=0A,magic=3,set=2,unique=4. Also 1.10 variant at 6FAE365B (item-list colors) | CALL <JMP.&D2Common.#10695> ; get item quality ... MOV EBX,9 ; D2_Yellow_Light (rare)

corpus.Menu_char_draw_loop_startglobalsourcedtrustedNecrolis (phrozen_code Discord)
D2Win

Start of the character drawing loop in menus | look at D2Win.0x6F8AD818 for the start of the char drawin loop in menus

corpus.Expanded_item_display_drop_filter_patch_offsglobalsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

1.13c D2Client offsets for the on-floor item-name display (the labels drawn over the play area). 0x58E59/0x59089/0xCDBB9 are the max-count caps (patched to 0x7F). The 0x58Exx/0x59xxx set repoint reads at the rect draw into gptAltDrawArray fields. Drop-filter hook site at 0x5907E (call) / 0x5907F (stub). | {D2DLL_D2CLIENT, 0x58E59, ... Max Count Patch ... {D2DLL_D2CLIENT, 0x58E83, ... Expanded Data Array

corpus.Unit_palette_getter_assigner_incl_Rand_PaletglobalsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2Client.0x6FAAE3C0 gets a unit's palette including Rand Palettes. D2Client.0x6FB28410 is the palette assigner; the three funcs below it compute the secondary index (source is a packet). Necrolis points a second user (Ogodei) to focus mainly on D2Client.0x6FB27A1F. Render/palette-shift relevant. | have a look at D2Client.0x6FAAE3C0 for what gets a units palette (including Rand Palettes). Look at D2Client.0x6FB28410 for the assigner

corpus.Health_orb_hover_hit_box_D2ClientglobalsourcedtrustedFirehawk (phrozen_code Discord)
D2Client

Bounding-box compares for the health orb hover region: X-left CMP EBX,1E ; X-right CMP EBX,6E ; Y-top LEA ECX,[EAX-4B] ; Y-bottom LEA EDX,[EAX-F]. JL/JG jump to 6FAF8C0E to skip. Screen-UI coordinate math for the bottom-left orb panel. | CMP EBX,1E ; Health Hover X Co-Ordinate Left ... CMP EBX,6E ; Health Hover X Co-Ordinate Right

corpus.Mana_orb_hover_hit_box_D2ClientglobalsourcedtrustedFirehawk (phrozen_code Discord)
D2Client

Mana orb hover bounding box: MOV ECX,[6FB740DC] base; X-left LEA EDX,[ECX-6F]; X-right ADD ECX,-1F; Y-top LEA ECX,[EAX-4B]; Y-bottom ADD EAX,-0F; branches to 6FAF8CD9. 6FB740DC is a global used as the mana orb X anchor. | MOV ECX,DWORD PTR DS:[6FB740DC] ... LEA EDX,DWORD PTR DS:[ECX-6F] ; Mana Hover X Co-Ordinate Left

corpus.Frame_timing_globals_D2Client_render_loopglobalsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2 does not hardcode client frame time: 6FB75460 holds ms-per-frame; gdwSkipFramesPerSecond compared to 0x16 (=22? note says D2 uses 25fps) at 6FAAA167; gdwFrameRenderSkip read at 6FAAA2C4. Directly governs the client render/skip cadence. | 6FB75460 contains the numbe of ms per frame ... CMP DWORD PTR DS:[<gdwSkipFramesPerSecond>],16

corpus.Damage_display_stat_pushes_D2Clientglobalsourcedsourcedleekun (phrozen_code Discord)
D2Client

Damage-display code in D2Client pushes (0,2) as stat args at 6FB0CF20 (left-skill damage display?), 6FB0D688 (right-skill damage display?), and 6FB0EE91. D2Game damage-calc stat table at 6FCBE420. Client-side damage panel rendering needs editing alongside D2Game. | 6FB0CF20 . 6A 00 PUSH 0 ... Damage display for left skill?

corpus.Level_id_change_setter_unused_now_entering_gglobalsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2Client.0x6FB00C10 updates the player's level id and sets a global on change (presumably for 'now entering' text), but the globals it sets are unused. | D2Client.0x6FB00C10 updates the level id of the player, and then sets a global when it changes ... the globals it sets are unused

corpus.gdwClientGameFrame_D2DrawUIglobalsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2Client global holding current client frame counter (drives animrate advancement, runs at 25fps and slows with the client); D2DrawUI is the UI render routine. Necrolis advises hooking the UI drawing to draw text then draw UI, rather than spawning threads. Core render-loop anchors. | 6FBA7768; gdwClientGameFrame 6FB21B70; D2DrawUI

corpus.rain_storm_wind_factor_floatglobalsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Float global at 6FBA763C is the storm wind factor (governs rain slant); part of the weather/particle system tied to the ground-tile draw params. | its the storm wind factor IIRC

corpus.floor_tile_draw_prologue_6FAA2E95globalsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Disassembly inside the ground-tile drawing routine (reads [EBX+24], adds 0x14 = the -20/0xFFFFFFEC pView sentinel check). Part of MULTIRES_DrawGroundTiles at 6FAA2E60/2E70. | 6FAA2E95 |. 8B43 24 MOV EAX,DWORD PTR DS:[EBX+24]

corpus.RainDrawAndPlaySounds_thunder_boolglobalsourcedtrustedLectem (phrozen_code Discord)
D2Client

6FAA8240 is RainDrawAndPlaySounds; 6FBA76AC is a boolean that triggers the thunder1 sound. Weather effects driven off the tile-draw frame counters. | 6FAA8240 is actually RainDrawAndPlaySounds

corpus.D2DDraw_screen_clear_blit_REP_STOSglobalsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2DDraw

The screen-clear routine at D2DDraw ~6F8C637A: loads front-buffer ptr from 6F8CF8A8, height/pitch from 6F8D01D8, width factor from 6F8D01C8; when a flag (ESI) is set it does ADD ECX,-2F (the 47px bottom-panel adjust) before IMUL. Zeroes the buffer with REP STOS at 6F8C639B. On failure pushes 'Blit (screen clear) failed!' string at 6F8CCE64. This is the exact HOM/dead-zone clear path. | 6F8C638C 83C1 D1 ADD ECX,-2F ... 6F8C639B F3:AB REP STOS DWORD PTR ES:[EDI] ... Blit (screen clear) failed!

corpus.DDRAW_surface_dims_800x600_0x320_0x258globalsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
ddraw

The buffer descriptor built in ddraw (505BEA43+): stores 0x320 (=800) and 0x258 (=600) as surface width/height, sourced from the DDSURFACEDESC at [ESI+1C]/[ESI+1E]. Confirms the front-buffer geometry the 47px clear operates on. | MOV DWORD PTR DS:[EDX+C],EAX ; 0x320 ... MOV DWORD PTR DS:[EDX+8],EAX ; 0x258

corpus.D2CLIENT_ScreenXShiftglobalsourcedsourcedmisiek1294 (phrozen_code Discord)
D2Client

D2CLIENT INT global at 0x11C418 (1.13c) named ScreenXShift -- the horizontal screen/camera shift value. Directly the 'screen SHIFT' global for the play-area viewport. | D2VAR(D2CLIENT, ScreenXShift, INT, 0x11C418)//1.13c

corpus.gpAutoMapCache_gnAutoMapCachesglobalsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Global pointer to the automap cache and the automap-cache count; writes at D2Client 6FACBAC6/6FACBA76 (MOV [6FBB1998],ESI / MOV [6FBB199C],ECX). | MOV DWORD PTR DS:[6FBB1998],ESI ; gpAutoMapCache / MOV DWORD PTR DS:[6FBB199C],ECX ; gnAutoMapCaches

corpus.Charm_background_jump_targets_blue_redglobalsourcedtrustedFirehawk (phrozen_code Discord)
D2Client

Firehawk's fixed 1.13c targets for the charm-cell background colour hack: 6FB45803 colours the inventory cell blue (reqs met), 6FB457BF colours it red (reqs not met). | D2Client.dll+0x95803 is the jump to color them blue / D2Client.dll+0x957BF is the jump to color them red

corpus.UI_background_color_handlerglobalsourcedsourcedSzumigajowy (phrozen_code Discord)
D2Client

Szumigajowy: this address 'handle background color' -- candidate for the panel/dead-zone background fill. | D2Client.0x6FAE1710 handle background color

corpus.ALT_item_popup_draw_routinesglobalsourcedsourcedSzumigajowy (phrozen_code Discord)
D2Client

Ground-item name popups (shown while ALT held) are driven from d2client.6FB21B70; the actual popup draw is at 0x6FB09F60. | How the hell game does draw item popups when pressing alt? ... 0x6FB09F60

corpus.HUD_close_menu_click_coordinate_1_10globalsourcedsourcedConqueror (phrozen_code Discord)
D2Client

1.10 address near 6FB0124D controls X,Y of the click on the HUD rectangle; modifying byte 75 to 0FF with a JMP changes menu-close coords. Render/HUD-layout relevant. | for 1.10 it is near to 6FB0124D because modifying those values slightly modifies me ... to make the HUD more elegant

corpus.D2Client_0x521C0_string_desc_builderglobalsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

D2Client.0x521C0 builds the item/stat string descriptions; entry point to find the desc-generation function. | D2Client.0x521C0 builds the string descs, you can find the func from there

corpus.gpMapGfxCells_map_name_table_patchesglobalsourcedtrustedKingpin (phrozen_code Discord)
D2Common / D2Game

Patch sites that repoint D2Common map graphics cell lookups (nFirst/nLast) and map-name-size table into a custom gpMapGfxCells / gpfMapNamesSize array. Involves floor/level tile graphics cell indexing: base formula (ID*4)+(GameOffset+0xFF018), original ref 6FC6F5E6. | (DWORD)&gpMapGfxCells[0].nFirst; // 6FC6F5E6 (ID * 4) + (GameOffset + 0xFF018)

corpus.GetUnitX_GetUnitY_subtile_positionglobalsourcedsourcedArolds (phrozen_code Discord)
D2Client

D2Client exports giving a unit's subtile X/Y position; also discussion of obtaining the 'world view subtile' (where the character stands / camera anchor). Relevant to screen-to-world mapping and viewport offset. | the ones I can get with 1.13c d2client.dll +0x1630 and +0x1660 ... how do I get the world view subtile? Is that where my character stands?

corpus.automap_gfx_cell_mapname_table_relocation_ofglobalsourcedtrustedKingpin (phrozen_code Discord)
D2Common

D2Common code sites patched to repoint automap cell lookup to a custom gpMapGfxCells array: CommonOffset +0x22C7F/0x22D46 (nFirst), +0x22C4E/0x22C9F/0x22D4D (nLast); +0x22AF5/0x22B32 for map-name strings. Original formula (ID*4)+(GameOffset+0xFF018), asm 6FC6F5E6 | 6FC6F5E6 (ID * 4) + (GameOffset + 0xFF018)

corpus.gold_drop_dialog_UI_functions_1_13cglobalsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

whist's UI notes: input handlers (14) at D2Client.0xDE858; draw the gold drop dialog at D2Client.0xA3840; open-dialog function D2Client.0xC30E0; in-memory dialog struct D2Client.0xFAD60. Panel drawing/UI | draw the gold drop dialog: D2Client.0xA3840

corpus.D2gfx_flush_window_function_18F46_flagglobalsourcedsourcedKieran (phrozen_code Discord)
D2gfx

D2gfx 6FA748C0 calls #10004 then 6FA7BCEE; 6FA7BCEE reads [esp+8], on TEST EAX zeroes ecx, writes result to [eax+0x18F46] (a D2gfx window/state flag). Kieran patch subject, Necrolis gave clean naked stub | 6FA748C5 E8 24740000 CALL D2gfx.6FA7BCEE

corpus.resolution_mode_global_1_14dglobalsourcedsourceddzik (phrozen_code Discord)
Game (1.14)

Resolution mode stored at 0x7c8cb8; the renderer object base DAT_007c8cc0 has draw funcs at +0xB8. Directly governs play-area sizing / resolution. | resolution mode in 1.14d is stored here 0x7c8cb8

corpus.D2Client_viewport_camera_globals_1_13c_relatglobalsourcedsourcedDeleted User (phrozen_code Discord)
D2Client

Deleted User's D2Client global-offset table: resolution X/Y and the WINDOW START X / negative window-start Y offsets = the play-area origin / screen-shift/camera-offset globals. Highly relevant to viewport positioning beside panels. | ptNegWindowStartY = (DWORD*)(offset_D2Client + 0x3A285C); ptWindowStartX = (DWORD*)(offset_D2Client + 0x3A2858);

corpus.gScreenWidth_gScreenHeight_globalsglobalsourcedsourcedkain_abel_666 (phrozen_code Discord)
D2Client

kain_abel_666 has labelled the client screen-dimension globals: gScreenWidth @ 6FB740EC and gScreenHeight @ 6FB740F0. Relevant to viewport/play-area sizing. | i have the screen width and height labeled as D2Client.gScreenWidth @ 6FB740EC and .gScreenHeight @ 6FB740F0

corpus.Set_item_color_inventory_vs_groundglobalsourcedsourcedoli (phrozen_code Discord)
D2Client

oli: changing set-item color in 1.13c. Ground color patch at 6FB6FF1D works; the inventory (side-panel) copy is a separate address 6FB4408F (changed 02000000 -> 08000000). Concrete evidence that inventory-panel drawing uses a distinct code path from world/ground drawing. | this is the adress for inv 6FB4408F changed 02000000 to 08000000 ... 6FB6FF1D is for the ground and it works

corpus.gpCurrentMouseOverItemglobalsourcedsourcederevos (phrozen_code Discord)
D2Client

Global holding the item the cursor is hovering (inventory/ground); compared at D2Client 6FAE10A1 as CMP ESI,[gpCurrentMouseOverItem] (ptr at 0x1732BC4 in the BlackSSE build). Directly tied to inventory/panel item interaction. | 6FAE10A1 |> 3B35 C42B1703 |CMP ESI,DWORD PTR DS:[BlackSSE.gpCurrentMouseOverItem

corpus.UIPanelDrawYOffset_UIPanelDrawXOffsetglobalsourcedsourcedKieran (phrozen_code Discord)
D2Client

Client panel draw offset globals used to compute inventory/stash/cube item positions; X offset scales with resolution width (simple), Y offset value (e.g. 196) source is unclear and larger than expected — used for resolution-height support of an item mover | D2VAR(D2CLIENT, UIPanelDrawYOffset, signed int, 0x124958)

corpus.palshift_Translvl_column_patchglobalsourcedsourcedeezstreet / LAZ (phrozen_code Discord)
D2Client

eezstreet: first two palshifts are ignored; changing 0x6FB27A23 to 'mov edi,0' shifts back the Translvl column so must add 2. Item draw uses the color index (one of 21) as offset into the transform/invtransform shift | Even if I change 0x6FB27A23 to be mov edi, 0 it will just shift back the Translvl column

corpus.Inventory_charm_req_code_regionglobalsourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

whist's old notes point here for higher-level inventory/charm-requirement handling. | old notes pointing to D2Client.0x95AB8

corpus.Item_charm_cell_background_color_draw_blockglobalsourcedsourcedDeleted User (phrozen_code Discord)
D2Client

Pushes RGB values and calls draw (0000D390) to set inventory item cell background colors: red-requirement bg, hover bg, default blue bg. Bytes stored at 6FBBB330-334. | PUSH 80 -> red color (red requirement background) ... default blue background

corpus.Frameless_loop_sleep_flagglobalsourcedtrustedLectem (phrozen_code Discord)
D2Client

Boolean set TRUE in most cases (solo) that governs the game's sleep; main loop otherwise spins with no vsync, which is why removing the frame limit is easy | it does have a sleep but D2Client.0x6FB747C0 is set to TRUE in most cases

corpus.Hot_boot_profiling_functionglobalsourcedtrustedLectem (phrozen_code Discord)
D2Client

Function so hot that profiling it makes the game slow to boot (likely main-loop / per-frame) | why profiling 0x6FAA9AF0 made the game so slow to boot

corpus.Panel_always_display_patch_button_visibilityglobalsourcedsourcedDannyIsGreat (phrozen_code Discord)
D2Client

NOP-ing CMP DWORD PTR DS:[6FBBA6FC],EBX / JE at 6FB22062-6FB22068 makes a UI panel always displayed, but its buttons stop working when 'closed'. 6FBBA6FC is the panel-open global flag. Relevant to forcing side panels on. | nooping the following will make it always displayed, but the buttons won't work if u "close" the panel. 6FB22062 |. 391D FCA6BB6F CMP DWORD PTR DS:[6F

corpus.Panel_button_click_handlerglobalsourcedsourcedDannyIsGreat (phrozen_code Discord)
D2Client

6FB00CD0 is the handler for when you click that (panel) button; NOP everything up to the retn 4 to disable the click action. | 6FB00CD0 i think is the handler for when u click that button. if u basically noop everything up to the retn 4 it'll disable what happens when u click

corpus.Mana_Health_orb_DC6_sizeglobalsourcedsourcedEggsy (phrozen_code Discord)
D2Client

MOV EBX,50 instructions that set the DC6 sprite draw size for the mana and health globes; patch target to enlarge the orb bubbles. | 6FAFFB8B BB 50000000 MOV EBX,50 ; Mana DC6 size ... 6FAFF9F9 ... ; Health DC6 size

corpus.light_radius_clamp_sitesglobalsourcedsourcedEggsy (phrozen_code Discord)
D2Client

Four+ CMP/MOV sites that clamp the light radius (CMP EBP,imm / MOV EBP,imm). Patching the immediate (e.g. to 0x1F) forces a large on-screen lit radius regardless of torch/quest state. Verified working in olly. | 6FAA4293 83FD 1F CMP EBP,1F ... 6FAA4298 BD 1F000000 MOV EBP,1F ... This works perfectly

corpus.text_draw_mode_color_siteglobalsourcedsourcedInhuman (phrozen_code Discord)
D2Win

PUSH 4 that supplies the draw-mode/color index for first-line text; color index 9 = white, another index = gold. | 6F8EFA43 6A 04 PUSH 4 ; draw mode first line texts 9 is for white which number is for gold

corpus.prevent_minimize_stay_running_unfocusedglobalsourcedsourcedSilvermane (phrozen_code Discord)
D2GFX

NOP these (11 bytes at each D2GFX site, 24 bytes at D2Win site) to stop the game minimizing on focus loss and keep it running while unfocused. | //D2GFX.6FA74932/6FA74D11 prevent minimizing ... //D2Win.6F8ADC9E Stays running while unfocused

corpus.gbCursorItem_SelectedInvItemglobalsourcedsourcedNeddles (phrozen_code Discord)
D2Client

Global UnitAny* for the item currently held on the cursor / selected in inventory (1.13c). | VARPTR(D2CLIENT, SelectedInvItem, UnitAny*, 0x11BC38) // Updated 1.13c

corpus.hovered_item_GUID_accessorglobalsourcedsourcedX_Dan_X (phrozen_code Discord)
D2Client

Used to read the hovered inventory item's GUID (only works for inventory items; user seeking equivalent for chest/stash object units). | I use d2client 0x1158F4 to get the hovered item's guid, but that is for inventory items

corpus.ShiftClickStats_character_panelglobalsourcedsourcedSilvermane (phrozen_code Discord)
D2Client

Hook site for the character-screen shift-click stat allocation; patched to cap shift-click stat input at 5. | //Shift clicking only imputs 5 { D2DLL_D2CLIENT, 0x315D3, (DWORD)PATCH_JMP, FALSE, 0x01 }

corpus.belt_itemtype_row_checkglobalsourcedsourcedRayveen (phrozen_code Discord)
D2Client

Belt UI row count: CALL D2Common.#10751/#11088 get item type, CMP EAX,13 (row 19 belt), JNZ hides extra rows | CMP EAX,13 ; Check for row 19 (belt itemtype) ... no additional rows is show

corpus.item_durability_desc_ethereal_checkglobalsourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Nop JMP at 6FAE2F0E; calls to D2Client.0x6FAE4060 set up the durability desc line | look for the calls to D2Client.0x6FAE4060 which sets up the durability desc line

corpus.D2GFX_DrawLineglobalsourcedsourcedOwnasaurus (phrozen_code Discord)
D2 exe / D2GFX

D2GFX_DrawLine at exe base + offset 0xF6380; pointer-arith cast (uintptr_t/DWORD) needed, hooks confirmed working | D2GFX_DrawLine = ...(hBaseExe) + 0xF6380 ... confirmed my hooks work now

corpus.NPC_tab_index_top_3_bottom_1globalsourcedsourcedArolds (phrozen_code Discord)
D2Client

Controls NPC/store tab selection; topmost tab 3, bottom 1 | It's in D2Client.0xE24F8. top most tab is 3. bottom most is 1

corpus.stash_close_button_Y_posglobalsourcedsourcedNetMech (phrozen_code Discord)
D2Client

Patch at D2Client+0x9963C adjusts stash-panel close button Y position | {D2DLL_D2CLIENT, 0x9963C, {0xEB}, 0x01}, // Stash close button Y pos

Hooks 4

Installed detours / patches.

hook.PanelWorldWidenhookactive
0x6FAF4302 D2Client E8 B9 6D 04 00

detour the base `call PASS1` to widen ctx+0x0C->DispW before PASS1 (issues floor tiles to screenX ~1236). NECESSARY but not sufficient (strip still black -- downstream clip)

hook.PanelWorldGlideCull1hookfailed
0x6F856889 D2Glide 89 4C 24 38

NOP the mode-1 right-narrow store in the live drawer. v1.9.1: applied cleanly, NO visual change -> the cull isn't the screen clip. FAILED attempt

hook.PanelWorldGlideCull2hookfailed
0x6F85689E D2Glide 89 5C 24 30

NOP the mode-2 left-narrow store. Same v1.9.1 FAILED result

hook.D2DXWidenhookexperimental
0x1000D970 glide3x 55 8B EC 6A FF

v1.11.0 THE FIX: detour grSstWinOpen; before each open call ID2DXConfigurator::SetCustomResolution(1280,720) so D2DX opens its WIDE game target (1280 vs the narrow 960 that clipped the world). Installed by a thread that waits for glide3x (late video driver). glide3x relocates -> resolve by export/ordinal. Keeps the native left-shift (it's in the submitted vertices). Awaiting Doug's test.

Notes & Sources 107

Imported notes and scraped references, with attribution.

glide3x.gameSizenoteverified
glide3x

D2DX Metrics.cpp: desktop 1280x720 -> gameSize={960,720} (narrow), gameSizeWide={1280,720}. 960 = the narrow render target. Chosen by GetSuggestedGameSize(desktop, wide=!NoWide) (D2DXContext.cpp:1170); d2dx.cfg nowide=false + [game]size=[1280,720] should force 1280 -- CONFIRM at runtime.

glide3x.SurfaceIdTracker_retagnoteunconfirmed
glide3x

SurfaceIdTracker.cpp:88: ScreenOpenMode&1 (panel open) && minx>=gameSize.width/2 (=640) RETAGS a world batch surfaceId=USER_INTERFACE. Prior audit said 'only tags, no cull' -- but the visible world clips ~640 with a panel open, so re-examine whether the UI-tag changes world compositing/clipping. Prime suspect.

note.esoteric_synthesisnoteverifiedThe Esoteric Path

The Esoteric Synthesis — D2 is a graph projected through one lens

*Our own synthesis, fusing the ingested canon with what we verified against the binary. This is the "make it ours" layer: the basic facts assembled into the single architecture that explains our whole hunt.*

1. The graph (what exists) — screen-independent, seed-deterministic

Game owns everything. The spine is Game -> Act -> Level -> Room2 -> Room1 -> UnitAny.

  • Room2 is the static, seed-deterministic *description* of a room; Room1 is the live *instance*,
  • hydrated only near players. World state is data, not pixels.

  • UnitAny is the universal entity: a tagged union with three arms -- A pUnitData (*what it is*:
  • Player/Monster/Object/Item), B pPath -> Room1 -> ... -> Act (*where it is*), C pInfo/skills (*what it can do*). Stats (pStats -> StatListEx -> Stat[]) and inventory (pInventory -> item UnitAny list) are a parallel graph off the same root -- same UnitAny, disjoint bodies, meeting only at the screen.

  • Nothing in this graph stores a screen coordinate. Ever.
2. The lens (how it reaches the screen) — one render context per frame

Every tile and unit is projected through ONE struct we verified, struct.D2Client_RenderContext (*0x6FBCC3C4): screenX = draw_left(+0x04) - scroll_x(+0x24) + worldX. The renderer walks the Arm-B spine (Act -> Level -> Room2 -> Room1 -> unit list), turns each entity's room-space Path position into a CellContext.dwPosX, and blits its GfxCell through D2Client -> D2gfx -> D2Glide -> D2DX. The lens is the *only* place world->screen happens.

3. The phenomenon (the theory that explains everything we've chased)

Opening a panel does not touch the graph -- it mutates the lens:

So the shift, the clip, and the world/units desync are one cause: a view-state mutation (reframe.one_cause_view_state). The graph is innocent; the lens moved. Units take an *extra* -320 in PASS2 (a second lens-read) -- that is the 2x desync. And the residual black is a second lens downstream: even when we widen draw_right and D2Glide submits the far tiles (338 verts into [960,1280)), D2DX drops them (finding.clip_is_d2dx_drop_confirmed). Two lenses, one graph.

4. Why this makes the fix tractable

Because the graph is screen-independent (the canon *proves* it), we can render the whole Arm-B walk in a no-panel lens (offX=0, draw_right=DispW) and composite the panel on top -- safely, without touching the 173 offX readers or any world struct. The canon tells us it's safe; our RE tells us the exact two knobs (the D2Client lens + the D2DX drop). That is the entire path to ZERO black with the world behind the panel.

One line: *D2 is a screen-independent object graph drawn through a single per-frame lens; every symptom we've chased is the lens being rewritten when a panel opens -- never the graph.*

note.units_synthesisnotesourcedThe Esoteric Path — canon synthesis (grounded + cited)

The units struct web (D2 1.13c) and how it reaches our render path

Grounding: UnitAny field offsets are from the in-repo 1.11b Structure Reference (research/kb/corpus/forum/highvalue/t47809.json); the render-context side is from grimoire/symbols.jsonl (struct.D2Client_RenderContext, D2Client.DrawRooms, D2Client.FloorFlush). Offsets are stable UnitAny-relative through 1.13c. Room1/Room2/Level/Act are named by their canonical fields; the repo confirms the *roots* of that chain (see the lifecycle section) but not every byte offset inside it, so those inner offsets are named, not asserted numerically.

1. UnitAny is the hub — a tagged union with three "arms"

Every player, monster, object, item, missile and tile is one UnitAny. It carries a type/id header and then hands off to per-type payloads through three pointer arms:

  • Identity header: +0x00 dwType, +0x04 dwTxtFileNo, +0x0C GUID (links a server unit to its client twin), +0x10 eMode (anim/action mode), +0x18 actNo, +0x28 dwInitSeed.
  • **Arm A — what it *is*** (+0x14 pUnitData, a union of 5 classes per the repo): pPlayerData / pMonsterData / pObjectData / pMissileData / pItemData. Which member is live is selected by dwType. "Note, there is no pTileData."
  • **Arm B — where it *is* (+0x2C pPath, a union of 2 classes per the repo): pStaticPath (Objects, VisTiles, Items) vs pDynamicPath = Path** (Players, Monsters, Missiles).
  • **Arm C — what it can *do* (+0xA8 pSkills): the skill controller = Info**, "a list of all skills the unit has (pointers to pSkill)."

Other hub fields that matter for lifecycle/draw: +0x5C pStatListEx, +0x60 pInventory, +0xE4 pPrevUnit (prev in the unit-type list), +0xE8 pPrevUnitInRoom ("previous unit in the current room").

2. Arm A — the payload structs
  • PlayerData: name + quest/waypoint blocks; the human-facing identity. It's the union member picked when dwType == player.
  • MonsterData (sizeof 0x60): +0x00 pMonStats = the monstats.txt record; +0x04 Components[16] = the component ids (HD/TR/LG/...) that drive which cels get drawn.
  • ObjectData: points to its objects.txt/ObjectMode record — the static-world furniture arm.
  • ItemData (sizeof 0x74): quality/flags/fingerprint. Items also thread through pInventory (Inventory: +0x08 pOwnerUnit, +0x0C pFirstItem, +0x14 pInvInfo equipped-gear list) — i.e. an item's UnitAny is reachable both as a standalone unit and as a node inside another unit's inventory.

These are pure data payloads: they never point back up to rooms. Position lives entirely in Arm B.

3. Arm B — Path → Room1 → Room2 → Level → Act (the spatial spine)

This is the chain that matters for rendering, because a unit's *screen* position is derived from its *Path* position projected through the room it sits in.

  • Path (dynamic, for moving units) carries the fine sub-tile position (dwPosX/dwPosY plus target/offset fields) and a back-pointer to its Room1. StaticPath (objects/tiles/items) is the lean version: a room pointer + a fixed tile position (no velocity, no targets), because static things never move.
  • Room1 = the collision/active room: its unit list is what the draw walks (units chained via UnitAny+0xE8 pPrevUnitInRoom), it holds the CollMap, near-room pointers, and a link up to Room2.
  • Room2 = the DRLG/preset room: geometry, presets, near-room graph, and the link up to Level.
  • Level = the area (its levelno indexes Levels.txt), owning its Room2 list and linking up to Act.
  • Act = pDrlgAct, reachable directly off the unit at UnitAny+0x1C and rooted in the game at pGame->pDrlgAct[5] / pGame->pDrlgRoomList[5].

So UnitAny → pPath → (Room1 → Room2 → Level → Act) is how any unit answers "which act/level am I in, and where," and the reverse (Act → Level → Room2 → Room1 → unit list) is exactly the order the world renderer iterates.

4. Arm C — Info → Skill → SkillInfo

UnitAny+0xA8 pSkillsInfo (the per-unit skill controller) → a linked list of Skill nodes (level, charges, left/right assignment) → each Skill's SkillInfo = the Skills.txt/skilldesc record. This arm is orthogonal to rendering (it drives casting/sequence via pSeqMode at UnitAny+0x30), included here to complete the hub.

5. Ownership & lifecycle
  • Owner: pGame owns everything — the per-act DRLG (pDrlgAct[5]), the room lists (pDrlgRoomList[5]), and the unit lists. Units do not own their Path/Data payloads' *world*; they borrow into the game-owned room/level/act graph via Arm B. A unit's own pUnitData, pPath, pInventory, pStatListEx, and Info are allocated with it.
  • Membership: a unit lives in two intrusive lists simultaneously — the global type/GUID bucket (pPrevUnit, hashed by GUID&127) and its current room (pPrevUnitInRoom). When a unit crosses a room boundary its Path's Room1 pointer is re-linked and it moves room lists; that re-link is the seam the renderer sees.
  • Determinism: dwInitSeed (unit) + GameSeed (game) make spawns reproducible.
  • Client/server twins: GUID ties a client UnitAny to its server one; several hub fields (+0x64, +0x80, +0xD8) are overloaded client-vs-server.
6. How this all lands in OUR render work

The world draw is a walk of the Arm-B spine, and each unit it reaches is projected through the render context — the one struct we've been fighting.

  • The projection: for any tile/unit, screenX = ctx+0x04 − ctx+0x24 + tileAbsX and screenY = tileAbsY − ctx+0x28 + ctx+0x08 (struct.D2Client_RenderContext; matches D2Client.FloorFlush). tileAbsX comes from the unit's Path/StaticPath room-space position; the two ctx terms are the draw-window origin (+0x04 draw_left) and the camera scroll (+0x24 scroll_x).
  • The room iteration is clipped by the ctx draw-window: D2Client.DrawRooms (0x7A9F0) builds its room-iteration RECT from ctx draw-window + scroll (band = [scrollX, scrollX + (R−L)]) and culls any tile with tileWorldX > RECT.R. So ctx+0x0C (draw_right) is literally the bound on how far along the Room1/Room2 walk we draw. That is the tile/room draw path the units hang off of.
  • The shift = the projection's origin term (finding.terrain_shift_is_offx_ctxL): opening a side panel writes offX (0x6FBCC418) = −320 into ctx+0x04, so every tile *and* unit derived from the same Path→Room projection slides −320 (1×). scroll_x (ctx+0x24) is untouched — the pan is entirely the draw-window origin, not the camera.
  • Why units desync 2×: floor tiles and units read the *same* 1× projection, yet units land at −640. The extra −320 is applied in the unit pass (PASS2), not in the shared ctx term — consistent with offX being read at 173 sites while the units path re-reads it. The Arm-A/Arm-B split is why: terrain tiles are projected once off the room; unit sprites are re-projected on top with a second offX application.
  • reframe.one_cause_view_state: the shift, the play-area narrowing, and the world-vs-units desync are one thing — opening a panel mutates the world view-state held in this render context (offX + draw_right). The clean fix is to render the whole Arm-B walk in the no-panel view-state (offX=0, draw_right=DispW) and composite the panel on top, so terrain and units share one coherent projection again.
  • The remaining black is downstream of all of this (finding.clip_is_d2dx_drop_confirmed): even when we widen ctx+0x0C so DrawRooms iterates the full room band and D2Glide submits the far tiles (verts reach x≈1264, 338 in [960,1280)), D2DX drops the dead-zone geometry. So the units-struct → render-context chain correctly *produces* the far-right world geometry; the loss is past the D2Glide→D2DX seam, not in the unit/room walk.

One-line takeaway: UnitAny is a tagged union — Arm A says *what*, Arm B (pPath → Room1 → Room2 → Level → Act) says *where*, Arm C says *what it can do*. The renderer walks Arm B (Act→Level→Room2→Room1→unit list) and turns each unit's room-space Path position into pixels via the render-context origin/scroll/draw-window; the panel-open "shift" and "clip" are just that render context's origin (+0x04=offX) and right-bound (+0x0C) being rewritten, with the residual black living downstream in D2DX.

note.world_synthesisnotesourcedThe Esoteric Path — canon synthesis (grounded + cited)

The "world" struct graph — synthesis note

Source of truth for every field/offset below: D:\_SERVER\esoteric-path\tools\dll-work\plugy-src\chaos\Commons\D2UnitStruct.h. That header uses Yohann's older names; our own patch logs use the canonical names (reference/EP-assembly-notes/townCheck.asm: CheckIfRoomIsTown(Room1* pRoom), GetTownFromAct(Act* pDrlgAct), GetActFromLevel). Name map: Room1 = Room, Room2 = RoomEx, Act = ActMap, ActMisc = ActData; Level, PresetUnit unchanged; RoomTile is the list hung at Room2's warp-tile pointer.

The core insight: two graphs, not one

The world is a static description graph (Room2 → Level → ActMisc, seed-deterministic, screen-independent) with a live instance graph (Room1) lazily allocated on top of it. UnitAny rides the live graph; the renderer is a third, per-frame layer that never touches either — which is the whole reason the terrain/click bugs are view-state, not world-state.

The pointer chain (UnitAny → … → Act)

Grounded in real fields:

  • **Unit.path (+2C, Path*) and Unit.ptPos (+38). Path.ptRoom (+1C) → the Room1** the unit occupies. Path also caches the unit's x/y (+0C/+10) and mapx/mapy (+02/+06). Unit also caches ptAct (+1C) and ptGame (+80) directly.
  • Room1 (Room) ptRoomEx (+10, RoomEx*) → its Room2. (Reverse of Room2→Room1.)
  • Room2 (RoomEx) ptLevel (+58, Level*) → its Level.
  • Level ptActData (+1B4, ActData*) → ActMisc. The canonical "→ Act" edge closes via ActData.ptActMap (+46C) → the small Act (ActMap); ActMisc↔Act is mutual (ActMap.ptActData +48).

So the full walk is: Unit.path → Path.ptRoom(Room1) → Room1.ptRoomEx(Room2) → Room2.ptLevel(Level) → Level.ptActData(ActMisc) → ActMisc.ptActMap(Act).

Ownership & lifecycle
  • Roots live on Game. Game.mapAct[5] (+BC, ActMap*) — one Act per act. ActData.ptGame (+9C) points back up; ActMap.ptFirstRoom (+10) and ActMap.ptActData (+48) fan down. ActData owns the DRLG seeds (seed1/seed2 +0/+4), difficulty (+450), room count (nbRooms +8), and the special tomb-level fields (nTalRashaTombLevel +94, nBossMummyTombLevel +484).
  • Level is the per-area container, chained by ptNextLevel (+1AC) into the act's level list; it carries levelNo (+1D0, the levels.txt Id), levelType (+1C0), its own seed1/2 (+1C4/+1C8), and bounds posX/Y/sizeX/Y (+1C…+28). Level.ptFirstRoomEx (+10) heads the level's Room2 list.
  • Room2 = persistent description. It exists for the level's whole lifetime, chained by ptNextRoomEx (+24); near-graph in roomExNear (+08) / roomsNearCount (+2C); layout in posX/Y/sizeX/Y (+34…+40, room/tile units); presetType (+48); roomFlags (+28).
  • Room1 = transient instance. Room2.ptRoom (+30) is NULL until the room is activated; when a room comes into play a Room1 is allocated and back-linked (Room1.ptRoomEx). Room1 holds the runtime-only state: coll (+20, collision map), the room's unit list head ptFirstUnit (+74, walked via Unit.ptNextUnitInRoom +E8), the active near-set ptNearRooms/nbNearRooms (+00/+24), collision-space bounds startX/Y/sizeX/Y (+4C…+58), and ptNextRoom (+7C) chaining the currently-active Room1s. Room1s are created/destroyed as units move; Room2s are not. That Room2-is-data / Room1-is-live split is the lifecycle in one sentence.
What hangs off Room2: PresetUnit and RoomTile
  • PresetUnit — the fixed seeded contents of a room (chests, waypoints, superuniques, preset monsters). Each carries unitType (+0), txtFileNo (+4), room-local xPos/yPos (+C/+10), and chains via pNext (+1C). The list hangs off Room2 (canonical Room2.pPreset; note this repo's RoomEx header truncates at ptLevel +58 and does not expand the preset field — the PresetUnit struct itself is defined and verified).
  • RoomTile — the per-Room2 warp-tile list at RoomEx.ptWarpTiles (+4C) (typed void* in this header). Each RoomTile links a spot in the room to a level transition; this is the structural basis of the warp/adjacency graph that Vis/Warp in levels.txt feeds (see kb/08-levels-maps.md).
Connection to our render work

The renderer consumes the live graph only and lays a view-state on top:

  • Tile/room draw path. D2Client walks the active Room1 list and, per room, emits its terrain tiles plus its unit list (ptFirstUnit). World positions are Room1 subtile coords (startX/Y, sizeX/Y); nothing screen-relative is stored on any world struct.
  • The render context (struct.D2Client_RenderContext) is where world→screen happens: it holds offX and the draw-window/clip rect. The concrete knobs we've located are the resolution globals in kb/15-EP-assembly-notes-index.md (GeneralDisplayWidth [6FB8BC48], GeneralDisplayHeight [6FB8BC4C]) and the tile-extent edits (!Definite.asm ADD ECX,-2F → 50, 6F865B04 = screen height; TileRendering.asm, viewmatrix110.asm).
  • Terrain shift = offX, not world. Because Room1 tile coords are fixed and seed-deterministic, any apparent terrain movement is the render context's offX/ctxL draw-window left edge changing (finding.terrain_shift_is_offx_ctxL) — i.e. the view-state, not the room graph.
  • One cause = view-state (reframe.one_cause_view_state). The terrain-shift and the bottom-bar click-miss are the same defect: the world struct graph is intact and screen-independent, while the per-frame view-state (render context draw-window + D2Client's panel/resolution geometry) is what moved after the SGD2FreeRes provider swap. The click side is the input twin of the draw side (clickThrough.txt: 6FAD81F5 JNZ/JMP gate).
  • Clip is a d2dx drop, not engine culling (finding.clip_is_d2dx_drop_confirmed). The engine's room enumeration (Room1 near-list) correctly yields the edge rooms/tiles; the missing band is dropped downstream by the d2dx/SGD2FreeRes tile culling at the wrapper, confirmed in kb/14-rendering-stack.md (the 47px void region + motion-prediction tile culling). So edge loss is renderer-side clip, cleanly separable from the world structs above it.
Punchline

Room2/Level/ActMisc/Act are a seed-deterministic, screen-agnostic description; Room1 is the live overlay the renderer and units actually touch; and every "the world moved" symptom we've chased lives one layer higher still — in the render context's view-state — never in these structs.

note.stats_inv_synthesisnotesourcedThe Esoteric Path — canon synthesis (grounded + cited)

stats_inv — synthesis note (1.13c)

Everything here hangs off one root: the player UnitAny. Two independent pointer graphs grow from it, and they only reconverge at the screen:

  • WHERE the unit ispPath → Room1 → Room2 → Level → Act (feeds the world tile/room draw).
  • WHAT the unit ispStats → StatListEx → Stat[] and pInventory → Inventory → item UnitAny list (feeds the inventory/character *panel* draw).

The six stats_inv structs are the second graph. InventoryLayout is the odd one out — it is not in the unit graph at all; it is the static screen-space stencil the panel draw borrows.

---

1. The stat chain
UnitAny
  +0x5C  pStats  ── StatListEx  (the unit's ONE aggregated/"full" list)
                       Stats(StatVector) : pStat @+0x24 → Stat[ ]  ,  wCount @+0x28
                       fpStatExpires @+0x38   (contributor-removed callback)
                       pNext / pMyLastList ── chain of contributor StatList nodes
  • Stat — one record, 8 bytes: wSubIndex(+0x00, WORD) · wStatIndex(+0x02, WORD = the ItemStatCost id) · dwStatValue(+0x04, DWORD). The corpus lists this verbatim: iStatIndex[0]/iStatID[0]/iStatValue[0] at 0/2/4, array packed 8-byte stride, kept sorted by id. wSubIndex is why one id can appear twice (e.g. per-skill / per-class stats).
  • StatEx — the same {wSubIndex, wStatIndex, dwStatValue} triple as it lives in the *extended* vector, carrying the extra bookkeeping the "Ex" (full) list needs to know which contributor list a value came from so it can be re-folded or expired. It is the element type of the aggregated list; Stat is the element type of a plain modifier list.
  • StatList — the base node/header: its Stats StatVector (pStat@+0x24, wCount@+0x28 — corpus-grounded) points at the Stat[] this node contributes, plus a pNext link. A single state (aura, curse, buff) or a single equipped item owns exactly one of these.
  • StatListEx — the unit-owned superset that UnitAny.pStats actually points at. It embeds the base StatList and adds the multi-layer value vectors, the pUnit back-pointer, and fpStatExpires(+0x38, corpus-grounded) — the callback invoked when a contributor list is torn off. The +0x58 field the corpus flags "inside StatListEx" belongs to this extra region.

Fold/expire lifecycle (the ownership story): each equipped item and each active state produces its *own* StatList of raw modifiers, links it into the owner's StatListEx chain via pNext, and its values get summed into the aggregated StatEx[]. When the item is removed or the state ends, the node is unlinked and fpStatExpires recomputes the full list. ITEMS_UpdateTransferredProperties (corpus, D2Game.0x6FC424E0, args bUpdateStatList) is the server-side driver of exactly this. So: the unit owns one StatListEx; every contributor is owned by its source and has list-scoped lifetime, not unit-scoped.

---

2. The inventory chain
UnitAny
  +0x60  pInventory ── Inventory
                          dwSignature @+0x00   (0x1020304 sanity tag)
                          pOwner       ── back to this UnitAny  (cycle)
                          pFirstItem ─► item UnitAny ─► ... ─► pLastItem   (equipped/stored items)
                          pCursorItem  (item on the mouse)
  • Inventory — the runtime container. pOwner closes the cycle back to the unit; pFirstItem…pLastItem is the linked list of item UnitAnys. Each item is a full UnitAny with its own pStats (StatListEx) — that is the physical link between the two structs: an item's stat list is a contributor that folds into the wearer's aggregated list (section 1). Corpus D2Common sigs (D2Common_FindStackTarget(Inventory*, ...), InventoryHasRoomForItem(D2InventoryStrc*, ...)) operate on exactly this node.
  • InventoryLayoutnot reached by any pointer from Inventory. It is the static per-panel/per-class grid descriptor: pixel bounds Left/Right/Top/Bottom + SlotWidth/SlotHeight (cells) + per-cell pixel size. Lifetime = process (a data table), not the unit. The inventory-panel draw (D2Client.0x3EAC0 in the ref; UI_INVENTORY_DrawItemBackground(D2InventoryGridInfoStrc*, nX, nY, nItemWidth, nItemHeight, …) in the corpus) *reads* it to place each Inventory item's cell. So Inventory says *which items and where in grid-cells*; InventoryLayout says *where those cells land in screen pixels*.

---

3. Why this sits parallel to the position chain

The map chain UnitAny.pPath → Path.pRoom1 → Room1.pRoom2 → Room2.pLevel → Level → Act is the *other* graph. It is what DrawRooms (0x7A9F0) walks: it iterates the Room1 linked list (Room1.pNext @+0x74, per-room units via UnitAny room-next @+0xE8 — both corpus-grounded) and projects each tile through the render context. The stat/inv graph never touches tiles; it drives the panel. Same root, disjoint bodies, they only collide in screen space.

---

4. Where it meets our render work

The collision point is the render context — struct.D2Client_RenderContext (*0x6FBCC3C4), which *is* the world view-state. Tile projection is screenX = ctxL(+0x04) − scrollX(+0x24) + tileAbsX.

  • Opening the inventory is the trigger for both graphs at once. The panel it opens is drawn from graph #2 (pInventory items placed via InventoryLayout.Left/Right/Top/Bottom). That panel occupies the right band — and to make room, the panel-open handler rewrites graph-#1's view-state: offX → ctx+0x04 = −320 and narrows ctx+0x0C toward 960.
  • The −320 world shift is finding.terrain_shift_is_offx_ctxL: the *whole* floor pan is offX(0x6FBCC418) → ctx+0x04, scrollX untouched. So InventoryLayout's Left edge (where the panel starts, screen-space) is conceptually the same X-coordinate the world view is being shoved away from — the panel's static geometry and the world's dynamic ctxL/draw_right are fighting over one screen column.
  • This is the whole point of reframe.one_cause_view_state: the dead zone, the camera shift, and the world/units desync are one cause — opening the panel changes the world view-state. The clean target is to render graph #1 in the *no-panel* view-state (offX = 0, full ctx+0x0C) and composite the graph-#2 panel (its InventoryLayout region) on top, so the two graphs stop colliding.
  • Caveat we proved: neutralizing the ctx window is necessary but not sufficient for the black band — finding.clip_is_d2dx_drop_confirmed. Once the world is un-shifted, valid floor geometry reaches x≥640/960 at the D2Glide→D2DX seam (submit max 1264–1296, 338 verts in the dead zone) and is still dropped downstream in D2DX, not by ctx+0x0C. So the stats_inv/panel side dictates *where* the world must not draw; the D2DX target extent dictates *whether* it can.

One-line takeaway: pStats/pInventory and pPath are twin graphs off UnitAny; the inventory panel is drawn from the former via the static InventoryLayout stencil, and the act of opening it is precisely what rewrites the render-context view-state that our terrain-shift and dead-zone work is chasing.

---

Offsets: sourcing
  • Corpus/repo-grounded: UnitAny.pStats +0x5C; StatList.Stats pStat +0x24 / wCount +0x28; StatListEx.fpStatExpires +0x38; Room1.pNext +0x74; unit room-next +0xE8; Inventory sigs via D2InventoryStrc.
  • Canonical D2Structs.h (not re-measured vs the 1.13c binary here): UnitAny.pInventory +0x60; Inventory.dwSignature/pOwner/pFirstItem/pLastItem/pCursorItem; InventoryLayout.Left/Right/Top/Bottom + SlotWidth/SlotHeight. Verify against the live binary before hooking.

note.gfx_ui_synthesisnotesourcedThe Esoteric Path — canon synthesis (grounded + cited)

gfx_ui structs — how they connect (1.13c RE reference)

One-line thesis: the eight gfx_ui structs are *content* (what a sprite/tile/overlay looks like and where it wants to sit in world space). None of them owns the world→screen transform — that lives entirely in the render context. So a panel-open never changes a single field in these structs; it changes D2Client_RenderContext, and every one of these structs re-projects through the new view-state. That's why the shift/clip fix belongs at the view-state, not here.

Confidence tiers (hostile-reader honesty)
  • Project-verified (exact offsets, from our grimoire/1.13c binary): D2Client_RenderContext layout, the FloorFlush/DrawRooms projection, the automap fns (NewAutomapCell 0x703C0, AddAutomapCell 0x71EA0, InitAutomapLayer_I 0x733D0, DrawAutomapCell D2GFX 0x7C80, AutomapLayer* var 0x11CF28), and the "Lit" cel path.
  • Canonical D2Structs.h field names (community-standard, and our own d2-re-reference §3 states "layouts are stable across versions"): the sprite triad, automap pair, Control, Waypoint, Light. Field *names* below are dependable; where a byte offset for these isn't in our verified set I name the field and flag it, rather than invent an offset.

---

1. The sprite triad — GfxInfo ⊃ GfxCell, driven by CellContext

Ownership is a strict containment + a transient pointer-in:

GfxInfo (loaded DC6/DCC file)              CellContext (per-draw state)
  dwVersion / eFormat                        nCellNo   ── frame index ──┐
  numdirs, numcells                          pCellFile ─────────────────┼──► GfxInfo
  cells[] ──► GfxCell*  (per dir × frame)     dwPosX, dwPosY            │
                    │                                                    │
                    ▼                              GfxInfo.cells[nCellNo]│
              GfxCell (one frame)  ◄───────────────────────────────────┘
                flags, width, height
                xoffs, yoffs   (sprite-local anchor, NOT screen)
                lpParent  ──► back to owning GfxInfo
                length, cols[]  (RLE pixel data)
  • GfxInfo owns GfxCell. GfxInfo.numcells sizes the cells[] table; each GfxCell.lpParent points back to its GfxInfo. GfxCells have no independent lifecycle — they live and die with the file. Lifetime = file load → cache eviction.
  • **CellContext owns nothing; it *selects*.** pCellFile → a GfxInfo; nCellNo indexes GfxInfo.cells[] to pick the GfxCell; dwPosX/dwPosY say where to blit it. It is the per-draw cursor, rebuilt every frame.
  • Key point for us: GfxCell.xoffs/yoffs are *sprite-local* anchors, and CellContext.dwPosX/dwPosY are already *screen* coords. The world→screen step that produced those screen coords happened earlier, in the render context (§4). The sprite structs are position-agnostic — hand the same GfxCell a different dwPosX and it draws shifted, unchanged.
2. Where the tile/floor GfxCells come from — the world hierarchy

The classic chain feeds the tile draw path. Named by field:

UnitAny.pPath ─► Path.pRoom1 ─► Room1.pRoom2 ─► Room2.pLevel ─► Level.pMisc ─► ActMisc.pAct ─► Act
                                   │                 │
              Room1.pRoomNext (active-room list)     Level.pRoom2First (all rooms in level)
              Room1.pUnitFirst (units in room)       Room2.pRoomTiles (tile/gfx data)
              Room1.Coll (collision)
  • DrawRooms seeds off the player's room (GetRoomFromUnit #10331 → the Room1 the player's Path.pRoom1 points at), then iterates the Room1 list. Per room it runs the per-tile dispatch (tile struct 0x30 bytes; flags [t+0x14] bit-test 0x1C000 = DRAW_GROUND|FLOOR|ROOF; orientation [t+0x1c]). Each drawn tile resolves to a GfxCell and is handed to the cel-blit thunks (§4).
  • Room1 vs Room2 = lifecycle boundary. Room2 is the persistent level-geometry node (always present via Level.pRoom2First); Room1 is the *hydrated/active* instance (Room2.pRoom1 non-null only near players) that actually carries collision + live units + the tiles that get drawn. Panel-open changes *which* Room1s survive culling, never the Room1/Room2 structs themselves.
3. The three overlay structs — parallel projections, NOT the world path

These are drawn *outside* the render-context tile path, which is exactly why they don't shift/clip with the world:

  • AutoMapLayer / AutoMapCell — one AutoMapLayer per Level (AutoMapLayer.nLayerNo == level no; global head at var 0x11CF28). Cells hang off the layer's four buckets pFloors / pWalls / pObjects / pExtras, each a sorted tree via AutoMapCell.pLess / pMore. Each AutoMapCell carries nCellNo (frame in the automap sprite) + xPixel/yPixel. Built by RevealAutomapRoom (0x73160) walking a Room's tiles → NewAutomapCell (0x703C0) → AddAutomapCell (0x71EA0); drawn by **DrawAutomapCell (D2GFX 0x7C80, takes a RECT\* cliprect)**. So the automap is a *second, independent* projection of the same Room geometry — its own coord space, its own clip. It stays whole with a panel open because it never touches D2Client_RenderContext.
  • Light — dynamic light entries associated with the active Room1/Act environment. The connection worth grounding: our live cel handler is pfTileDrawLit (D2Glide dispatch +0x9c, LiveCelHandler 0x72F0) — the *lit* tile blit. Lights modulate the same GfxCell pixels as they cross that handler. (Light's finer field layout is the thinnest of the eight in our verified set — treat its role, not specific offsets, as load-bearing until read off the binary.)
  • Control — the UI-widget chain (Control.pNext linked list; dwType, dwPosX/dwPosY, dwSizeX/dwSizeY, plus text/edit fields). Controls are the panel's buttons/boxes, drawn by the compositor/present stage of DrawGame (body: PASS1 0x44302 → tiles → PASS2 0x4436F → compositor 0x4437B → present #10040), i.e. *after and over* the world pass — they sit on top of the dead zone, not in it.
  • WaypointWaypoint.flags (one byte per waypoint, arrayed). Pure UI/save state surfaced by the waypoint side panel, which is one of the panels whose *opening* is the trigger event: PanelOpenArray waypoint slot +0x50. Opening it fires the exact view-state write that shifts/clips the world (§4). So Waypoint is upstream cause-by-association, not a rendered-world participant.

---

4. Tie-in to our render work — the whole point

Everything above pours through one transform, the render context.

D2Client_RenderContext (struct.D2Client_RenderContext, instance *0x6FBCC3C4) IS the world view-state:

  • +0x04 draw_left == offX (0 no-panel, −320 panel-open) — the shift
  • +0x0C draw_right — iteration bound (1280 no-panel, 960 panel-open) — the issue-side clip
  • +0x24/+0x28 scroll_x/scroll_y — camera origin (not touched by panels)

The tile projection (FloorFlush / DrawRooms band) is literally:

screenX = ctx.draw_left(+0x04) − ctx.scroll_x(+0x24) + tileAbsX

That screenX is what becomes a tile's CellContext.dwPosX. So:

  • finding.terrain_shift_is_offx_ctxL — panel-open writes offX 0→−320ctx+0x04 0→−320; scroll_x untouched. The floor/terrain GfxCells didn't change; their CellContext.dwPosX moved −320 because the shared ctx+0x04 term moved. Confirmed exactly −320 (=−DispW/4), standing still.
  • reframe.one_cause_view_state — shift (+0x04), clip (+0x0C narrow), and the world/units desync are ONE cause: the panel rewrites the view-state. The gfx_ui structs are all downstream of it, so the clean fix authors a coherent no-panel view-state for the whole world draw — you do not patch per-struct or per-sprite, and you keep ScreenOpenMode live for UI/culling. (Units pick up an *extra* −320 beyond the shared 1x tile projection — a separate PASS2/unit-layer source, not the ctx.)
  • finding.clip_is_d2dx_drop_confirmed — this exonerates the gfx-struct layer as the blackout cause. With the view-state widened, the tile GfxCells *do* reach the D2Glide→D2DX seam in the dead zone (submit max 1264–1296, 338 verts in [960,1280)) and still render black. The geometry — valid GfxCell frames, same path as the [640,960] tiles that paint fine — is dropped at/after D2DX. So: ctx+0x0C governs which GfxCells get issued/projected; D2DX governs whether the issued verts get painted. Two distinct clip layers; neither is a property of the eight structs.
The stack, one line
GfxInfo.cells[nCellNo] ─► GfxCell  (content, position-agnostic)
        │  positioned by
        ▼
CellContext.dwPosX/Y  =  D2Client_RenderContext (+0x04 offX, −0x24 scroll, +tileAbsX)   ← the shift & issue-clip live HERE
        │  blitted via
        ▼
CelBlitThunk → D2gfx #10001 → D2Glide pfTileDrawLit (Light modulates) → grDrawVertexArrayContiguous → D2DX  ← the terminal paint-clip lives HERE

Automap (AutoMapLayer/CellDrawAutomapCell), Control (compositor), and Waypoint (panel state) all sit *off* this spine — which is exactly why they never shift or clip when the world does.

Bottom line for the fix: don't touch the content structs. Author the no-panel view-state in D2Client_RenderContext (neutralize offX+0x04, restore +0x0C→DispW) AND defeat the D2DX dead-zone drop. Both are view/pipeline layers; the eight gfx_ui structs are innocent bystanders that simply render wherever the view-state tells them to.

Source files: E:\Diablo 2 - Esoteric Path\esoteric-dll\grimoire\symbols.jsonl (render ctx, cel path, D2DX findings), ...\research\kb\reference\d2-re-reference-1.13c.md (automap fns, struct-stability note, tile params), ...\research\kb\reference\panelworld-mods-and-hooks.md (room/tile draw chain).

ext.(global).Target_resolutionnotesourcedtrustedIAmTrial (SlashWork d2widescreenhack)
(global)

Hack replaces 640x480 mode with 1066x600 true 16:9. 0x280=640, 0x1E0=480, 0x42A=1066, 0x258=600. Note width 1066=0x42A but render uses 1064=0x428 in one spot (see D2gdi Part1). | src: replacing 640x480 mode; height wouldn't change, width would, true 16:9

ext.D2client.Author_conclusion_panel_byte_budget_600_heightnotesourcedtrustedIAmTrial (SlashWork d2widescreenhack)
rva 0xc39f9 D2client.dll

Author's reasoning verbatim-ish: reg->mem needs fewer bytes than DWORD-const->mem, so HD mode needs 8 extra bytes; instead of call+NOP he reorders. Because height is unchanged 800x600<->1066x600, the 800x600 y-offset setup code is reused, freeing 6 bytes and reducing writes from 8 to 4 bytes. | src: 'the height remains the same when switching from 800x600 mode to 1066x600 mode... By reordering the code, I can reuse the code that sets up the y-offset positioning for 800x600 mod

ext.glide3x.glide3x_CE_vs_hex_editor_address_divergencenotesourcedtrustedIAmTrial (SlashWork d2widescreenhack)
glide3x.dll

Author flags glide3x as hardest because Cheat Engine relative instruction addresses differ from hex-editor addresses: 0xC0D5-0xC0EC (hex editor) == 0xCAD5-0xCAEE (Cheat Engine). | src: 'relative instruction addresses that show up on Cheat Engine are not the same relative addresses that are shown in a hex editor'

ext.draw_screen_background.background_tiling_loopnotesourcedtrustedMir Drualga (SGD2FreeRes)
draw_screen_background

Tiling loop: half_num_frames = GetNumFrames()/2; rows alternate row_start_frame = (row%2)*half_num_frames; columns drawn until width_covered reaches (width/2)-1. Left screen anchors DrawPositionXBehavior::kRight and draws from (width/2 - width_covered); right screen anchors kLeft and draws from (width/2 + width_covered). Both use position_y_behavior kTop. Row loop condition compares height_covered against resolution.width (note: width, not height — as written in source).

ext.DrawOriginalLeftScreenBorderFrame.OriginalLeftBorderFrame_centeringnotesourcedtrustedMir Drualga (SGD2FreeRes)
DrawOriginalLeftScreenBorderFrame

Centering math for the original 800-wide border frame around the 640-wide play area. left = (width-640-(800-640))/2 = (width-800)/2; top = ((height-480-(600-480))/2)-3 = ((height-600)/2)-3. This is the (width-640)/2 family of centering offsets the task references, adjusted for the 800x600 frame art. | src: const int left = (resolution.width - 640 - (800 - 640)) / 2; const int top = ((resolution.height - 480 - (600 - 480)) / 2) - 3;

ext.DrawOriginalRightScreenBorderFrame.OriginalRightBorderFrame_centeringnotesourcedtrustedMir Drualga (SGD2FreeRes)
DrawOriginalRightScreenBorderFrame

Right-side original border frame anchor. right = (width+640+(800-640))/2 = (width+800)/2; top same as left ((height-600)/2)-3. | src: const int right = (resolution.width + 640 + (800 - 640)) / 2; const int top = ((resolution.height - 480 - (600 - 480)) / 2) - 3;

ext.DrawCustomLeft/RightScreenBorderFrame.CustomBorder_screen_left_topnotesourcedtrustedMir Drualga (SGD2FreeRes)
DrawCustomLeft/RightScreenBorderFrame

Custom border frames center the 640x480 UI: screen_left = (width-640)/2, screen_top = (height-480)/2, screen_right = (width+640)/2. Vertical centering of the side borders uses (256+176)=432 as the reference inner-panel height: border_top = screen_top + ((432 - border_height)/2). | src: const int screen_left = (resolution.width - 640) / 2; const int screen_top = (resolution.height - 480) / 2;

ext.draw_screen_background.draw_screen_background_border_ribbon_fnsnotesourcedtrustedMir Drualga (SGD2FreeRes)
draw_screen_background

DrawLeftScreenBackgroundRibbon / DrawRightScreenBackgroundRibbon tile horizontal and vertical 'ribbon' DC6s across the perimeter beyond the border frame, using inner-panel reference height (256+176)=432 for vertical centering; border_left = screen_left - firstFrameWidth; loops fill until width_covered/height_covered exceed borders. Config-gated by IsScreenBorderFrameEnabled(); original-vs-custom chosen by IsUseOriginalScreenBorderFrame().

ext.d2dx.Scissor_rect_construction_quirk_right_bottom_snotesourcedtrustedbolrog (D2DX)
d2dx

CD3D11_RECT ctor is (left,top,right,bottom) but D2DX passes { offset.x, offset.y, size.width, size.height } -> right=size.width, bottom=size.height rather than offset.x+width / offset.y+height. Harmless when offset==0 (offscreen render at {0,0,gameSize}) but with a centered _renderRect (non-zero offset in Present) the scissor's right/bottom fall short by offset.x/offset.y. Viewport still places content correctly; only the scissor is mismatched. Load-bearing if chasing edge-cl | src: CD3D11_RECT scissorRect{ rect.offset.x, rect.offset.y, rect.size.width, rect.size.height };

ext.d2dx.NO_per_batch_floor_vs_wall_transform_existsnotesourcedtrustedbolrog / Necrolis (D2DX)
d2dx

Grep + full read of D2DXContext.cpp, RenderContext.cpp, GameHelper.cpp: TextureCategory::Floor(4) and Wall(6) are never used to apply any positional offset/transform/scissor/viewport difference. They drive only texture categorization/cache selection and motion-prediction exclusion. Floor and wall are treated identically for geometry.

ext.d2dx.Text_Weather_MoP_also_uniform_category_gatednotesourcedtrustedbolrog / Necrolis (D2DX)
d2dx

TextMotionPredictor.GetOffset(textId,pos) offsets text; WeatherMotionPredictor.GetOffset offsets weather lines. Both additive screen offsets, not floor/wall specific. Feature flags: Unit+Weather MoP for Lod109d/112/113c/113d/114d; Text MoP only 113c/113d/114d.

ext.(all).Offset_convention_base_relativenotesourcedsourcedd2bs community (ds-hwang/noah- patch-113)
(all)

All FUNCPTR/VARPTR/ASMPTR offsets in this file are RELATIVE to the containing DLL's load base, not absolute VAs. d2bs adds the runtime module base. For 1.13c D2Client.dll the canonical base is 0x6FAB0000, so e.g. GetUnitX 0x1630 => ~0x6FAB1630. Negative numbers (e.g. -10010) are export ORDINALS resolved via GetProcAddress(MAKEINTRESOURCE(10010)), used for D2GFX/D2WIN/D2COMMON. | src: FUNCPTR/VARPTR/ASMPTR(MODULE, name, type, args, offset-or-ordinal)

ext.macro_formatnotesourcedsourcedSlashDiablo BH / planqi

The file uses three macros, each ending with a DUAL offset pair: the 1.13c value FIRST, then the 1.13d value. VARPTR(DLL, Name, Type, off_113c, off_113d) for globals; FUNCPTR(DLL, Name, RetType/callconv, (args), val_113c, val_113d) and ASMPTR(DLL, Name, ..., val_113c, val_113d) for functions. NEGATIVE values are export ORDINALS (e.g. -10079); POSITIVE hex values are direct in-DLL OFFSETS (e.g. 0x18750). This c-then-d ordering is the rebasing key. | src: VARPTR(D2CLIENT, ScreenSizeX, DWORD, 0xDBC48, 0xF7034)

ext.pragma_pack_context_for_world_structsnotesourcedsourcedd2bs community (noah-)

CRITICAL: Level, Room2, CollMap, PresetUnit, RoomTile, RosterUnit, BnetData, PlayerData, Waypoint, QuestInfo are inside a #pragma pack(push)/pack(1) region — their true in-memory byte spacing is packed (no natural alignment padding); trust the explicit hex offset comments, not sizeof-based math. GfxCell, CellFile, CellContext, AutomapCell/Layer, UnitAny, Room1, Act, ActMisc, Control are OUTSIDE pack(1) (default alignment). | src: #pragma pack(push) #pragma pack(1) ... #pragma pack(pop)

ext.D2Client.256px_panel_offsetnotesourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Client

The 256-pixel horizontal correction: subtract 256 from adjusted X when the RIGHT panel is up, add 256 when the LEFT panel is up. Applied only in perspective mode (isometric offsets already bake in the shift via 6FBC9960/6FBCC418).

ext.D2Client/D2Common.Necrolis_isometric_not_perspective_special_offnotesourcedtrustedkidpaddle94 / Necrolis (Phrozen Keep t=62562)
D2Client/D2Common

Correction/caveat on the render math: D2 default is ISOMETRIC projection; 'perspective' is a depth distortion applied only in D3D mode. Objects and missiles have special (file-derived, NOT image) coordinate offsets you must adjust for depending on where you read coords from; supporting perspective mode requires the 'order aware adjustment' for objects. | src: this is isometric projection, not perspective projection (which is a depth distortion you can apply in D3D mode)... objects and missiles have special offsets you need to adjust for

corpus.World_offset_panel_adjust_perspective_coord_notesourcedtrustedNecrolis (phrozen_code Discord)
D2Client

THE core screen-space transform for drawing a unit into the play area. Takes unit world X/Y, adds GFX position (Z folded into Y), then SUBTRACTS UNITDRAW_GetWorldXOffset()/YOffset() (the camera/scroll offset), and IF perspective on, ADDS UNITDRAW_GetPanelAdjust() and runs D2AdjustPerspectiveCoords. This is exactly the screen-shift / panel-offset math behind the play-area vs side-panel positioning. | nXpos -= UNITDRAW_GetWorldXOffset(); nYpos -= UNITDRAW_GetWorldYOffset(); if(GFXUTIL_CheckPerspective()){ nXpos += UNITDRAW_GetPanelAdjust(); D2Adjust

corpus.Collision_override_for_pass_through_unitsnotesourcedtrustedNecrolis (phrozen_code Discord)
D2Common

To let a unit/missile pass where it normally collides, find the flag the collision fns test and override it with an unused tile flag; likely a D2Common ordinal (points to COLLISION_CheckMask and the COLLIDE_* mask set). | you basically need to look what the fn's use to determine if they collide and override it with an unused tile flag; its likely a D2Common ordinal

corpus.Merchant_store_panel_draw_D2ClientnotesourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

Store panel draw path: when sgnInventoryMode in [INVMODE_TRADE..INVMODE_09], checks globals D2CLIENT_6FBCBC4C (tick gate), 6FBCBC54, 6FBCBC30, gdwActiveNpcGUID, gnInvPage, then calls STOREINVENTORYSCREEN_Main. 6FB3F710 is the key sub (likely click-spam/latency guard) | It's the part that draws the merchant store panel ... STOREINVENTORYSCREEN_Main();

corpus.Store_panel_draw_1_10_equivnotesourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

1.10 equivalent of store-panel draw: iterates D2CLIENT_dword_6FBB37AC..37FC stepping 20 bytes, sets gnInventoryMode=VENDORMODE_TRADE, calls 6FB575B0 with cursor item | *D2CLIENT_gnInventoryMode = VENDORMODE_TRADE;

corpus.WriteCall_hook_helper_D2Client_ingame_loop_hnotesourcedtrustedFirehawk (phrozen_code Discord)
D2Client

WriteCall(target,source,length): writes 0xE8 call + NOP pad with VirtualProtect. Example hooks the in-game loop at D2Client.dll+0x45EC4 (24-byte patch) to run per-frame code | WriteCall(DWORD(GetModuleHandle("D2Client.dll"))+0x45EC4, DWORD(&InGameLoop), 24);

corpus.Window_border_blank_pixel_fixnotesourcedsourcedmisiek1294 (phrozen_code Discord)
D2Gfx

SetWindowLongPtr(D2GFX_GetHwnd(), GWL_STYLE, WS_POPUP|WS_VISIBLE) removes blank/border pixels around the render window | SetWindowLongPtr(D2GFX_GetHwnd(), GWL_STYLE, WS_POPUP | WS_VISIBLE); removes blank pixels

corpus.Custom_GFX_driver_render_TODO_known_bugsnotesourcedtrustedNecrolis (phrozen_code Discord)
D2Glide

Necrolis' worklist for a custom OpenGL D2 driver. KEY render-pipeline facts: (1) the driver internally switches EACH draw function between perspective and flat mode - see D2Glide.0x6F814480 - render coords must be fixed and #10068's impact investigated; (2) an 'underlay shader' causes the screen to darken watching fights outside Act V town and breaks GetHit-mode transparent backgrounds on some Act 5 monsters; (3) Mod | internally the driver switchs each draw function between perspective and flat, see D2Glide.0x6F814480, we need to fix the render coords

corpus.DC6_frame_RLE_decodenotesourcedtrustedNecrolis (phrozen_code Discord)
D2CMP

DC6 frame decoder. Pixel byte semantics: 0x80 = end of row (x=0, y--); (pix & 0x80) with other bits = skip/advance x by (pix & 0x7F) transparent pixels; else pix = run length of literal pixels to copy. Necrolis also gives a DWORD-vectorized variant (copy pix/4 dwords then switch on pix&3). Writes into gpDecodeBuffer at p[(y*pw)+x]. | if(pix == 0x80){ x = 0; y--; } else if(pix & 0x80) x += pix & 0x7F; else { while(pix--){ p[(y * pw) + x++] = pFrame->pPixels[++i]; } }

corpus.Smacker_R5G5B5_pixel_pack_bugnotesourcedtrustedNecrolis (phrozen_code Discord)
D2Glide

Disasm that packs an R5G5B5 pixel for the Smacker (video) manual-decode path. Necrolis notes the last shift (SHL EDX,2 before final OR) is WRONG - a math error in the original code producing a slightly-off blue channel. | this code is meant to create an R5G5B5 pixel for the smacker (in manual decode), the last shift is wrong because someone can't math good

corpus.Force_render_life_mana_text_always_show_hovenotesourcedtrustedFirehawk / Kieran (phrozen_code Discord)
D2Client

NOP the JL/JG hover-bounds branches (6FAF8B3A/43/4E/59 and 6FAF8C19/24/2F/3A) so orb hover text always renders. To reach the mana-text block, also NOP the epilogue at 6FAFF1E6-6FAFF1F0 (pop edi/esi/ebp/ebx; add esp,25C; retn) or JMP to 6FAFF1F1, so execution falls through from life-text to mana-text rendering. | From 6FAFF1E6 to 6FAFF1F0, you should nop those instructions as well, it'll allow it to continue to the mana text rendering

corpus.D2Win_10117_text_draw_call_mana_render_pathnotesourcedsourcedKieran (phrozen_code Discord)
D2Client/D2Win

1.10 mana-render block calls D2Win.#10117 (LEA ECX,[ESP+20]; SUB EDX,EAX; CALL JMP.&D2Win.#10117) then the standard epilogue; this call is absent in the 1.09 layout. | 6FAFF1E1 |. E8 D2CD0600 CALL <JMP.&D2Win.#10117>

corpus.Uncap_FPS_by_nopping_frame_skip_check_1_13notesourcedsourcedDeleted User (phrozen_code Discord)
D2Client

NOPping CMP DWORD PTR DS:[6FBD3490],EBX / JNE at 6FAF4E51 raises SP framerate to 64 FPS; 6FBD3490 is the frame-skip gate global in 1.13 D2Client. Also gdwFrameRenderSkip test at 6FAAA2C9 can be NOPped. | nopping this out, increases the framerate to 64 FPS in SP. 6FAF4E51 CMP DWORD PTR DS:[6FBD3490],EBX

corpus.Frame_rate_limiter_pattern_render_gatenotesourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Recommended render throttle: hook the render fn with a counter and skip every X render; canonical form 'if(current-last>=target) render();' rather than Sleep-based CPU fix. | void RenderFN() { if(current - last >= target) render(); }

corpus.Component_hit_test_loop_unit_selection_AABBnotesourcedtrustedLectem (phrozen_code Discord)
D2Client

Unit-selection scans up to 16 (0x10) components: sub_6FB28D90 fetches component AABB, IsComponentSelectable_sub_860F7F0 filters, then point-in-box test (pt.x vs xMin/xMax, y vs yMax/yMin). Note inverted Y (yMax is top). Used for clicking units on the play field. | || pt.x < pAABB.xMin || pt.x > pAABB.xMax || a3 < pAABB.yMax || a3 > pAABB.yMin

corpus.Composite_palette_shift_green_blood_S7_S8_lanotesourcedtrustedLectem (phrozen_code Discord)
D2Client

Unit composite draw special-cases COF component letters: S8 layer applies green-blood palshift (GetGreenBloodPalshift) when monStatsEx->nLocalBlood and a client flag (sub_7B8C020); S7 layer resolves a linked unit (sub_7C06AD0) and if missile or in state 143 recurses into UNITDRAW_ProcessUnit_6FB4DB40. Component enum check NUM_COMPONENTS asserts in Composit.cpp:613. | else if ( compByte == S7 ) ... D2CLIENT_UNITDRAW_ProcessUnit_6FB4DB40(v45, dwGamma, 1);

corpus.Palette_shift_texture_rendering_monster_palsnotesourcedtrustedNecrolis (phrozen_code Discord)
D2gfx/D2CMP

Necrolis's custom renderer notes: D2 makes 8 palette copies for any unit with a palshift.dat; PL2/randshift are created ad hoc; DCC/DC6 must stay resident for reshifting. His approach decodes the DCC/DC6, builds an L8 or HW-compressed GPU texture, frees the source, and on palshift just changes the index (offset) into the palette LUT (or a shader switch for shadow/HW-compressed textures). Notes D2 poorly caches D2GetN | the way D2 handles monsters with a palshift.dat means that if its present, "8 copies" are made

corpus.Tile_SubTile_GFX_material_flag_enumsnotesourcedtrustedNecrolis (phrozen_code Discord)
D2CMP

Full flag/enum sets for tile libraries, sub-tile walkability/LoS/light collision, tile materials, and tile-component encoding (run-length, gamesquare). Governs floor tile behavior and decoding. | TILE_NOWALK=0x01, TILE_NOLOSLIGHT=0x02, TILE_NOJUMPTELEPORT=0x04, TILE_NOPLAYERWALK=0x08, TILE_NOLIGHT=0x20

corpus.1_07_resolution_string_s1280x800notesourcedsourcedDeleted User (phrozen_code Discord)
D2Game

1.07 references a resolution-config string 's1280x800(1).left' at 6FD447C8 - suggests table-driven screen-region layout keys. | MOV ECX,DWORD PTR DS:[6FD447C8] ; ASCII "s1280x800(1).left"

corpus.Perspective_scale_SetPerspectiveScale_usagenotesourcedtrustedLectem (phrozen_code Discord)
D2gfx/D2Client

Decompiled render code showing D2GFX_10010_CheckPerspective gating a block that reads player X/Y (D2COMMON #10327 GetXPos / #10330 GetYPos), calls D2Common_10108 (ScreenToWorld) on shifted coords, then D2GFX_10064_SetPerspectiveScale(playerX,playerY). Names the perspective-mode camera-scale ordinals: D2GFX #10010 CheckPerspective, #10064 SetPerspectiveScale; GetPlayerUnit at D2Client.6FB283D0. | if ( D2GFX_10010_CheckPerspective() ) { ... D2GFX_10064_SetPerspectiveScale(nPlayerPosX, nPlayerPosY); }

corpus.CtrlPnl7_800CtrlPnl7_control_panel_gfx_loadnotesourcedsourcedBurznazg (phrozen_code Discord)
D2Client

Client UI setup reads mpq file '800CtrlPnl7' if a GFX function returns 2, otherwise 'CtrlPnl7' (the actual control-panel UI DC6). The gating GFX function is ordinal for 0x6FAD7290 in 1.13c. 800-prefix appears to be a legacy/alt-resolution variant. | code tries to read file from mpq called 800CtrlPnl7 if some GFX function will return 2. If it returns something else then CtrlPnl7 is readed ... Ordin

corpus.Draw_both_weapons_UI_patch_menu_panel_draw_fnotesourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Necrolis patch around D2Client 6FAF8AE0..6FAF8C40 that redirects control flow (JMP/CALL Ordinal10117, then MOV [ESP+20],0) so both items draw in a menu control. Ordinal10117 is a D2Client draw helper; region is a UI/menu-control draw routine. | 6FAF8B38 | CALL <d2client.Ordinal10117> 6FAF8B3D | JMP d2client.6FAF8C40 ... MOV DWORD PTR SS:[ESP+20],0

corpus.Unit_flags_0xC4_0xC8_semanticsnotesourcedtrustedBlighthorn / Necrolis (phrozen_code Discord)
D2Client

Unit flags at +0xC4: 0x01 = tells game to update the unit, 0x20 = whether to draw a shadow (client-only). FlagsEx +0xC8: 0x02000000 = expansion unit, 0x00000004 = spawned in vendor shop, 0x10 (server) = seed initialized (signals sync packet). Necrolis confirms 0x20 is a client-only draw flag. | 0x00000020 - whenever to draw a shadow or not ( client only )

corpus.D2Common_dll_export_ordinal_table_base_6fd40notesourcedtrustedLectem (phrozen_code Discord)
D2Common

Lectem posts the start of a full D2Common 1.13c ordinal->address->name table: #10000 GetLevelType @6fd75260, #10001 GetActNoFromLevelId @6fd75300, #10002 GetLOSDraw @6fd75350, #10005 GetLevel @6fd749a0, #10006 InitLevel @6fd74c10, #10007 GetRoomExFromRoom @6fd8d0c0, #10010 GetLevelDefsRecord @6fd60d90, #10011 GetAutomapCell @6fd62d30, #10012 FreeDrlgAndLevels @6fd743b0, etc. | // List of D2Common.dll exports (base address 6fd40000) ... D2COMMON_10000_GetLevelType ... @address 6fd75260

corpus.D2GetRoomList_D2PlaySound_offsetsnotesourcedsourcedDeleted User (phrozen_code Discord)
D2Common/D2Client

D2GetRoomList = D2Common.10035 @0x4BC10; D2PlaySound = D2Client.6FB55820 @0xB5820. Room-list retrieval feeds the render room iteration. | static D2Common2733 D2GetRoomList = (D2Common2733)(0x4BC10 + CommonOffset); // D2Common.10035

corpus.floor_tile_draw_order_steppingnotesourcedtrustedLectem (phrozen_code Discord)
D2Client

Ground tiles are drawn in draw-order steps 1..2 selected by ((dwFlags>>14)&7); DrawOrderMask = 0x7<<14 (0x4000|0x8000|0x10000). Each tile drawn via D2Client_Draw_dFloor_6FAA2E70 with position = ptTileDrawParams->nViewXOffset + tile width and nnViewYOffset + tile height, plus pos*5. Lectem also notes 0x10000 can mean 'do not draw'. Shows the exact viewport-offset math applied to floor tiles. | D2Client_Draw_dFloor_6FAA2E70(ptTileDrawParams, pFloorTileData, v18, ptTileDrawParams->nViewXOffset + pFloorTileData->nWidth, ptTileDrawParams->nnView

corpus.BOTTOM_PANEL_47px_clear_adjustment_0x2FnotesourcedtrustedNecrolis (phrozen_code Discord)
D2DDraw

CRITICAL PANEL FACT: In the D2DDraw screen-clear/blit, the constant 0x2F (47) is the adjustment for the bottom panel -- the game deliberately never clears the last 47px of the screen. 6F8D01C8 is the screen front buffer being cleared; if your custom back buffer is smaller than the sizes passed here you get a buffer overflow / HOM. Directly explains uncleared dead-zones tied to panel geometry. | if you see 0x2F you now its the adjustment for the bottom panel, as the game never clears the last 47px... 6F8D01C8 is the screen front buffer thats b

corpus.Charm_inventory_cell_red_blue_background_hacnotesourcedsourcedSwoosh (phrozen_code Discord)
D2Client

Patch to colour an inventory item cell's background red when a charm is not in a valid charm-zone, blue when its requirements are met. Hooks the item-cell draw path in the inventory panel; original code calls D2Common.#10244 then TEST/JE at 6FB45790. This is inventory-side-panel item rendering. | I'm trying to get charms to have a red background when not in a charm zone ... 6FB45790 E9 CB860300 JMP D2Client.6FB7DE60

corpus.Charm_check_D2Common_ordinals_resolved_to_adnotesourcedtrustedFirehawk (phrozen_code Discord)
D2Common/D2Client

Ordinals used by the charm inventory-cell colouring code in 1.13c; Firehawk gives the resolved D2Client jump-stub addresses for #10744 and #11057. | #10744 - 6FABC1CC / #11057 - 6FABCA30

corpus.Inventory_cell_hook_stack_slot_player_ptr_shnotesourcedtrustedFirehawk (phrozen_code Discord)
D2Client

In the charm inventory-cell draw hook the player unit pointer is at [esp+0x28] on 1.13c rather than [esp+0x20] as in 1.10 — a porting gotcha for the panel item-render stack frame. | it also seems that [esp+0x28] holds the player on 1.13c rather than [esp+0x20]

corpus.S_C_0x58_UnitInteract_opens_UI_panelnotesourcedsourcedDeleted User (phrozen_code Discord)
D2Client

Server->client packet 0x58 opens a user-interface panel (UnitInteract); nUIType selects which UI/panel. | S>C 0x58 - Opens User Interface on Client - UnitInteract

corpus.2D_projection_matrix_setup_OpenGL_glrenderernotesourcedtrustedNecrolis (phrozen_code Discord)
D2Glide/gl

Necrolis' recipe for D2 2D rendering: glViewport(0,0,w,h); glOrtho(0,w,h,0,-1,1) with identity modelview -- top-left origin ortho matching D2 screen space. | glOrtho(0,nWidth,nHeight,0,-1,1);

corpus.Screen_center_text_bounds_mathnotesourcedtrustedFirehawk (phrozen_code Discord)
D2Client

Belt/mouse text draw gate: computes (ScreenWidth/2)-146 and (ScreenWidth/2)-23 as x-bounds, ScreenHeight-43/-34 as y-bounds. Shows the play-area is centered via ScreenWidth/2 -- key idiom for panel/viewport offset work. | lea edx, [eax-146] ; edx = (screenwidth/2) - 146

corpus.Item_slot_color_rect_draw_sites_D2DrawRectnotesourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Necrolis: to color item background rects you must patch many D2DrawRect call sites -- belt, inv slots, equipment slots, merc, vendors -- because coloring wasn't done in the item draw function. Enumerates the 1.10 panel item-slot draw calls. | belt drawing, inv slots, equipment slots, merc, vendors etc, this amounts to quite a few calls to D2DrawRect

corpus.D2Win_widgets_vs_bank_dialognotesourcedtrustedNecrolis (phrozen_code Discord)
D2Win

UI widget structs (editbox etc.) come from D2Win and are cleanly done; the arg-1 object at D2Client 6FAD7540 (1.10f) is the ad-hoc bank dialog code which is messy and reused by other adhoc menus - advise side-stepping. | The widgets come from D2Win, these are actually properly done... What you are refering to in that code is the incredible piece of shit that is the ban

corpus.world_screen_coordinate_conversionnotesourcedtrustedNecrolis (phrozen_code Discord)
D2Common/D2Client

KEY RENDER FACT: world<->screen conversion done by D2Common ordinals in the #1011x range (choose tile vs subtile variant), result then offset by the CURRENT SCREEN WORLD CENTER (camera). This is the camera/scroll offset applied to drawn units/tiles. | Conversion is done via a few funcs in the D2Common.#1011x range depending on if you want tile or subtile coords, and then offset by the current screen

corpus.merc_items_draw_global_dword_6FBCBE5Cnotesourcedsourcedmisiek1294 (phrozen_code Discord)
D2Client

Global dword_6FBCBE5C (1.13c) / dword_6FBB5B14 (1.10) used as a loop counter inside the mercenary-items draw function; observed always 0. | I found that inside merc items draw functions (compare loop counter with that but this is always 0 )

corpus.HelloWorld_RevealMinimap_hook_interaction_bunotesourcedsourcedOwnasaurus (phrozen_code Discord)
D2Client

Practical note: a draw-text hook and a reveal-minimap hook each work alone in DLL_PROCESS_ATTACH, but running back-to-back the minimap reveal fails — timing/ordering caveat when hooking D2 draw functions at attach. | when executed back-to-back like here, the minimap is not revealed

corpus.Barkscroll_UI_custom_draw_hooknotesourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Mini-guide to inject a custom UI: hook client-side item right-click handler (find barkscroll item code in D2Client), set a global, open barkscroll UI; hook the barkscroll draw function to load/draw a replacement DC6 when the global is set. A concrete example of hooking a D2Client panel draw routine. | Hook barkscroll draw function, if that global is set, load (and unload the old DC6) and then draw the new DC6

corpus.D2Cmp_ordinal_10044_1_10_10067_1_13cnotesourcedsourcedmisiek1294 (phrozen_code Discord)
D2Cmp

Cross-version ordinal mapping in D2Cmp (graphics/cell decompression module): #10044 in 1.10 == #10067 in 1.13c | D2Cmp.10044 (1.10) D2Cmp.10067(1.13c)

corpus.Area_Level_print_asm_level_name_overlaynotesourcedsourcedSilvermane (phrozen_code Discord)
D2Client

Silvermane's on-screen text overlay: calls level-name printout at D2Client.6FACF0D0, fetches Y coord from [6FBB19FC] and X coord from [6FB9A704], SUB EDX,30 to shift X, then D2Win #10171 display text. Shows the screen coord globals used for HUD text placement. | MOV EAX,DWORD PTR DS:[6FBB19FC] ; retrieve Y coordinate ... MOV EDX,DWORD PTR DS:[6FB9A704] ; retrieve X coordinate SUB EDX,30

corpus.Charm_inventory_location_check_1_13cnotesourcedsourcedCanight (phrozen_code Discord)
D2Common

Patch point in inventory placement: CheckCharmInvLocation(pItem) validates a charm's inventory location (ItemLocation, IsMatchingType 0x0D, pItemPath->dwPosY>3). Directly touches inventory-panel item placement logic. | // Charm Inventory check {D2DLL_D2COMMON, 0x27064, PATCH_JMP...} ... if (pItemPath->dwPosY > 3) return TRUE;

corpus.ExpDrawLine_via_D2GFX_10057notesourcedtrustedNecrolis (phrozen_code Discord)
D2GFX

Hookable draw-line export; params (nXStart,nYStart,nXEnd,nYEnd,nPaletteIndex,nAlpha); width = end-start. Direct hook point for line rendering | void __stdcall ExpDrawLine(int nXStart, int nYStart, int nXEnd, int nYEnd, uint8_t nPaletteIndex, uint8_t nAlpha)

corpus.Panel_Level_DC6_side_panel_graphicsnotesourcedtrustedkain_abel_666 / Necrolis (phrozen_code Discord)
D2Client

The 'Panel\Level' DC6 (character/level side panel gfx) string is at D2Client.6FB79AB0; loaded via DC6 load function D2Client.6FB20100 and the loaded handle stored at global [6FBB5BB4]; corresponding unload function at D2Client.6FB20140. Load MOV ECX,6FB79AB0 at 6FAEEBA0; store MOV [6FBB5BB4],EAX at 6FAEEBAF | 6FAEEBA0 MOV ECX,D2Client.6FB79AB0 ; ASCII "Panel\Level" ... so DC6/DCC loading function is D2Client.6FB20100?

corpus.Ctrl_click_drop_item_refresh_chainnotesourcedsourcedSilvermane (phrozen_code Discord)
D2Common/D2Game

Correct sequence to drop from inventory without leaving blank slot: ChangeAnimMode(IMODE_ONGROUND), RemoveItemFromInventory, GetRoom, D2DropItem(pGame,NULL,pItem,pRoom,x,y), then server UpdateInventoryItems + RefreshInventory | D2COMMON_INVENTORY_RemoveItemFromInventory(pUnit->pInventory, pItem); D2GAME_D2DropItem(pGame, NULL, pItem, pRoom, pCoords.nX, pCoords.nY);

corpus.Automap_level_type_table_extension_1_13cnotesourcedsourcedMubbsy (phrozen_code Discord)
D2Common

Automap uses hardcoded ASCII level-type table in D2Common .rdata; table start at 6FDDB9E0 (offset 0xB9E0), sole reference/call at offset 0x6B30D (live 6FDBC30D) which loads table into EDI. To extend past lvltype 35 relocate table and repoint the call | copies the table from 6FDDB9E0 into the EDI register ... the call to that address at 0x6B30D

corpus.Screen_window_WIDTH_accessor_per_versionnotesourcedtrustedMir Drualga (phrozen_code Discord)
D2Win

To get game render width: D2Win ordinal 10121 (1.09D/1.10), 10132 (1.12A), 10028 (1.13C), 10150 (1.13D); Game.exe+0xFF010 (1.14C), Game.exe+0x101820 (1.14D) | ordinal 10028 for 1.13C ... offset Game.exe+0xFF010 for LoD 1.14C

corpus.Draw_label_above_player_head_world_to_screennotesourcedsourcedDeleted User (phrozen_code Discord)
D2Client

Player is always at center of screen; getting window rect and halving mis-places by ~15-20%. Correct approach converts unit world coords to screen via a world_to_screen routine (diablo2::utils::screen::world_to_screen); unit x/y from get_unit_x/y(player->path) | diablo2::utils::screen::world_to_screen(label->x, label->y, mx, my); Does the trick.

corpus.Bottom_UI_panel_button_hooknotesourcedtrustedKingpin (phrozen_code Discord)
D2Client

Kingpin: the easy way to add new buttons is to hook the code that draws the bottom UI panel. Points at the panel-draw routine as the injection site. | add new buttons is simple, easiest way is hook part where it draw the bottom ui panel

corpus.gfx_frame_vs_game_frame_dwGfxFrame_dwGfxGamenotesourcedsourcedElmegaard (phrozen_code Discord)
D2Common / D2Game

Elmegaard working through a broken gfx-frame bug: proposal to split pUnit->dwGfxFrame from a new dwGfxGameFrame = pGame->dwGameFrame << 8, and where the frame bonus (D2Common_10031_GetFrameBonus << 8) is applied. Animation-frame timing for unit rendering. | pUnit->dwGfxGameFrame = D2Common_10031_GetFrameBonus(pUnit) << 8; pUnit->dwGfxFrame = pGame->dwGameFrame << 8;

corpus.D2Client_SkillDesc_formatting_hooksnotesourcedsourcedConqueror (phrozen_code Discord)
D2Client

Skill-description decimal precision fix; hook points in D2Client at 0x73438 (capture value), 0x73495 (fix), 0x73458 (exit-if-zero), with returns 0x7343D/0x734A1/0x73472. This is on the skill-description UI text render path. | { D2DLL_D2CLIENT, 0x73438, (DWORD)PATCH_JMP, FALSE, 0x1 }, { D2DLL_D2CLIENT, 0x73439, (DWORD)SkillDescCaptureValue, TRUE, 0x0 }

corpus.Automap_level_name_hardcoded_table_8E2D0notesourcedsourcedDeleted User (phrozen_code Discord)
D2Common

v1.10 automap.txt loader compares level names against a hardcoded 36-entry table at D2Common offset 0x8E2D0 (from lvltypes.txt), which breaks level types past 36; fix substitutes a lvltypes record-index field. Relevant to floor/tile/automap level handling. | The existing loader function in v1.10 for automap.txt compares the level names against a hardcoded table at d2common offset 8E2D0 ... has only 36 entr

corpus.Rain_render_tied_to_GetRainFromLevelsTxtnotesourcedtrustedLectem (phrozen_code Discord)
D2Client

D2Client.0x6FAA8870 was thought to control rain but actually just reflects the result of D2COMMON_10632_GetRainFromLevelsTxt (weather from levels.txt) - a client display value. | D2Client.0x6FAA8870 ... it's actually just updated to reflect the results of D2COMMON_10632_GetRainFromLevelsTxt

corpus.D2Common_10177_usage_D2PathPointStrc_X_unsignotesourcedtrustedLectem (phrozen_code Discord)
D2Client

D2Common_10177 (returns 32-bit) is consumed in D2Client.0x6FB49D70; lack of sign-extension when moving from D2PathPointStrc implies D2PathPointStrc::X is unsigned. Path point coords feed positional/camera math. | looking at the usage of D2Common_10177 in D2Client.0x6FB49D70 ... D2PathPointStrc::X is unsigned

corpus.HD_PanelPosition_InterceptionnotesourcedsourcedStephen Lynx (phrozen_code Discord)
D2Client

A patch that intercepts panel positioning at 0xC39F7 (one past the 0xC39E0 UI loop region) | { D2DLL_D2CLIENT, 0xC39F7, 38, (int)HD::PanelPosition_Interception, TRUE }

corpus.Resolution_adaptive_panel_coordinate_mathnotesourcedsourcedConqueror (Araksson code) (phrozen_code Discord)
D2Client

Panel button hit-test computes coords from screen width/height minus WindowStartX/Y plus fixed offsets (e.g. -0x12e..-0x10e X, -0x60..-0x40 Y); Conqueror advises deriving positions from screen-width/window-width constants and half-screen center + cursor offset for dragging | (BLSS_CLIENT_GetScreenWidth() - BLSS_CLIENT_WindowStartX()) + -0x12e <= mouse_x

corpus.Multires_additional_room_floor_drawing_SluggnotesourcedsourcedSwineFlu (phrozen_code Discord)
D2Client

To fill missing tiles at higher resolution: iterate nearby rooms excluding those the client already drew, and do additional drawing for rooms visible from client's perspective. FLOOR tiles are drawn by a SEPARATE function from wall/roof tiles and units — two distinct functions, both called recursively. Directly relevant to filling the black play-area/dead-zone with world tiles | Floor tiles are handled separately from wall/roof tiles and units - hence why there are two distinct functions ... These two functions are also called

corpus.Screen_shift_struct_must_be_extendednotesourcedsourcedLAZ (phrozen_code Discord)
D2Client

LAZ: to add a NEW screen/UI shift without disturbing existing behavior, you must extend the shift struct | if you want a new shift that doesn't affect other stuff, you might have to extend that struct

corpus.Bottom_panel_input_handler_NOP_sitesnotesourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

Patch sites (NOPBLOCK, 0x15 bytes) that disable the bottom-panel input handlers; input handlers are separate from the draw code. | Disable bottom panel input handlers

corpus.Mini_panel_input_handler_NOP_sitesnotesourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

Patch sites (NOPBLOCK 0x15) that disable the mini-panel input handlers. | Disable mini panel input handlers

corpus.Find_panel_draw_vs_input_codenotesourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Control panel + exp bar draw from one top-level handler; NOP the no-arg calls to see what draws what. Click/input handlers are NOT part of draw code and can be found via storm ordinal #511. | You can NOP out the calls with no args to quickly see what draws what ... find any input handler via that ordinal

corpus.Find_what_draws_a_loaded_assetnotesourcedtrustedNecrolis (phrozen_code Discord)
D2Client

To locate draw code for an asset, find READS of the asset's data pointer, not the load code. Example pointer 6FBCBC80. | to find what draws it you need to look for reads of 6FBCBC80

corpus.RoomExFlag_0x80000000notesourcedtrustedLectem (phrozen_code Discord)
D2Common

Unnamed RoomEx flag bit; room-tile relevant, asked but unnamed. | anybody has a name for RoomExFlag 0x80000000 ?

corpus.Charm_blue_background_jumpnotesourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

When charm reqs are met, jump gives the charm a blue background (Firehawk 2019 note). Cited by our own Doug. | reqs met, give charm a blue background ... JMP D2Client.6FB45803

corpus.New_high_res_stat_page_panel_decompile_d2expnotesourcedsourcedSophia Philonous (phrozen_code Discord)
D2Client

Plan to decompile d2expres in Ghidra to load a high-res DC6 for a new stat page and make its button work. | decompile d2expres in ghidra, and see if anything there seems like it may let us load a high res dc6 for new stat page and make the button work

corpus.NewInterface_Stats_DC6_panel_drawnotesourcedsourcedSophia Philonous (phrozen_code Discord)
D2Client

Custom stats interface: loads a DC6 and blits frames via D2PrintImage at panel coordinates (0,256),(256,256),(0,512),(256,512),(0,552) then fillRect(0,0,400,552). Extra frames 6/7 don't render. Uses setImage/setFrame/D2PrintImage/fillRect helpers; palette index 5. | NewInterface_Stats.cpp is containing this code, where it loads the dc6 ... D2PrintImage(&data, 0, 256, -1, 5, 0);

corpus.Porting_1_10_hooks_to_1_13c_requires_offset_notesourcedsourcedSophia Philonous (phrozen_code Discord)
D2Client

Doug (Esoteric Path/Sophia) notes porting functions/hooks from 1.10 to 1.13c requires shifting all offsets and re-hooking. | It would require too much work to shift offsets from 1.10, hook those functions. Etc.

corpus.Alt_item_ground_item_name_drawing_palette_drnotesourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Item-on-ground nameplate rendering. When Alt held, MOV DWORD PTR [EBP+118] sets draw mode: 6FB0933F sets 1 (regular items), 6FB09306 sets 5 (hovered item). Can hook 6FB0946E CALL D2Win.#10013 to override palette/draw mode. Single nameplate (alt not held) at 6F8F356C PUSH 2. | else you can just hook: 6FB0946E CALL <JMP.&D2Win.#10013> and replace the values for palette and draw mode yourself

corpus.Remove_skill_tab_hover_text_black_backgroundnotesourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

1.13c: forced a conditional jump to unconditional JMP at D2Client 6FB28092 -> 6FB281BF to eliminate the 'Skill Tab 1' hover text plus its black background box. | 6FB28092 E9 28010000 JMP D2Client.6FB281BF ... eliminated it

corpus.Level_entry_level_name_DC6_display_routinenotesourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

Whilst's edit controlling the level-name DC6 shown on level change. At D2Client BF33F (6FB6F337): reads [6FB8BC4C], CDQ/SUB/SAR, PUSH 1 (palshift), PUSH 5 (draw mode), SUB EAX,8C (vertical position), PUSH 1 (horizontal position). Routine begins at 6FB6F0D0 (reads [6FBCBBFC]); called from 6FB73AA1. | 6FB6F345 2D 8C000000 SUB EAX,8C ;vertical position 6FB6F34A 6A 01 PUSH 1 ;horizontal position

corpus.Level_entry_DC6_caller_sitenotesourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

CALL D2Client.6FB6F0D0 at 6FB73AA1 is the sole call site of the level-entry display routine; nearby CMP [6FBCC414],3 and CALL 6FB12680 / 6FB0C5C0 / CMP [6FBAADCC]. | 6FB73AA1 |> E8 2AB6FFFF CALL D2Client.6FB6F0D0 <--- here

corpus.Monster_desc_immunities_drawing_editsnotesourcedtrustedNecrolis (phrozen_code Discord)
D2Client

Client monster-description block (immunities/minion text) around 6FB2100D. NOP the TEST EAX/JNZ at 6FB2100D to append monstats to minions/uniques; also adjust MOV EAX,13 at 6FB22167 and ADD EAX,31 at 6FB221D0. Underlying draw call at 6FB20600. D2Client based at 6FAA0000. | 6FB2100D ... 90 NOP ... 6FB22167 B8 13000000 MOV EAX,13 ... 6FB221D0 83C0 31 ADD EAX,31

corpus.Skill_speed_bars_attack_speed_positioningnotesourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Client

1.13c D2Client attack/cast speed bars for left/right click. Draw routine at 6FAEE7F0 (SUB ESP,124; MOV EAX,[6FB8BC4C]; ADD EAX,-56 vertical position). Horizontal position set by PUSH 50 (Arg2) at 6FAEEC24 — that section holds left bar, right bar, and mouseover icon values. | 6FAEEC24 |. 6A 50 PUSH 50 ; |Arg2 = 00000050 ... values for the icons for the left skill bar, right skill bar

corpus.Health_mana_globe_15_16_bit_wideningnotesourcedtrustedThe Esoteric Path (Doug, phrozen_code Discord)
D2Game/D2Client

HP/mana/stamina sent as 15-bit fields in one packet; change the 15 to 16 at three server + three client sites to raise the ~65k limit (1.13c) | just gotta change the 15 to 16 at these three locations (1.13c) D2Game.0x8B81B D2Game.0x8B82D D2Game.0x8B83F

corpus.HoverObjectPatch_custom_monster_bar_drawnotesourcedsourcedRayveen (phrozen_code Discord)
D2Client

Hover-object hook draws custom DC6 frames (hp/res frames) at hardcoded screen coords via D2GFX_DrawShiftedImage when hovered unit is an enemy monster matching a txt id | D2GFX_DrawShiftedImage(&ChesRes, 90, 17, -1, 5, 0);// hp frame

corpus.Automap_custom_icon_via_Miniicons_RevealRoomnotesourcedsourcedRayveen (phrozen_code Discord)
D2Client

Walks Room2->pPreset PresetUnit chain; sets cellNo into Maximap.dc6 to draw custom automap icons for preset units/shrines (shrine list by dwTxtFileNo, Type 0x0F) | void Miniicons::RevealRoom(Room2* room) { for (PresetUnit* preset = room->pPreset; ...) ... cellNo = 1499;

corpus.Loot_filter_dropped_item_render_handler_hooknotesourcedsourcedEggsy (phrozen_code Discord)
D2Client

D2Tweaks hooks two D2Client call sites: +0x1641B = handle dropped items, +0x81BF5 = draw dropped item names (the ground-label render call). Useful anchors for the ground-item name draw in the play area. | m_draw_dropped_items_names_original = ... get_call(diablo2::d2_client::get_base() + 0x81BF5)); ... set_call(...get_base() + 0x1641B, handle_dropped_it

corpus.D2GFX_prevent_minimize_stay_running_unfocusenotesourcedsourcedSilvermane (phrozen_code Discord)
D2GFX/D2Win

For windowed/background running: NOP D2GFX 0x4D11 and 0x4932 (VA 6FA74932/6FA74D11) to prevent minimizing; NOP D2Win 0xDC9E (6F8ADC9E) to keep the game running while unfocused. D2Gfx multiclient fix at d2Gfx+0x446A (write 0xEB). | //D2GFX.6FA74932/6FA74D11 prevent minimizing { D2DLL_D2GFX, 0x4D11, 0x90, FALSE, 11 }, { D2DLL_D2GFX, 0x4932, 0x90, FALSE, 11 }, //D2Win.6F8ADC9E Stay

corpus.crash_c0000005_at_6F622D97_on_level_entrynotesourcedsourcedMike (phrozen_code Discord)
D2Common

Reported intermittent access-violation in D2Common when entering one particular level -- unresolved in this chunk; possibly level/room load related. No fix given. | c0000005 at 6F622D97 01:00021D97 in D2Common.dll ... when entering one level

corpus.D2GFX_multibox_nop_patchnotesourcedsourcedZephyr (phrozen_code Discord)
D2GFX

Multibox patch nops D2GFX window-focus check; 1.13c range 0x21-0x66 | { D2Dll_kD2GFX, 0x21, kPatchNop, 0, 0x66 - 0x21 }

corpus.Lighting_Interception_patchnotesourcedsourcedtimer (phrozen_code Discord)
D2Client

Lighting interception patch; user reports it fails in Act 5 for 1.13c (may need separate act5 patch) | Patch* lightingPatch = new Patch(Call, D2CLIENT, { 0xA9A37, 0x233A7 }, (int)Lighting_Interception, 6)

corpus.snow_weather_render_globals_act5notesourcedsourcedascar31 (phrozen_code Discord)
D2 exe (1.14d)

Act5 snow/rain: per-level switch sets weather DAT_007a89d8/dc/9c/c0 params, RNG FUN_00472280, draw via FUN_004f6300(x,0,y,...); seeds from pPlayer->pSeed | i assume the snow is here ... switch(DVar3) { case LEVEL_HARROGATH ... _DAT_007a89dc = 0x2a

corpus.vendor_tab_access_asmnotesourcedsourcedThaison (phrozen_code Discord)
D2Client

Store-tab loop MOV ECX,D2Client.6FB91186 then indexed access [ESP+EAX*4+10] | MOV ECX,D2Client.6FB91186 ... MOV EBP,DWORD PTR SS:[ESP+EAX*4+10]

corpus.unit_palette_PL2_shiftnotesourcedtrustedNecrolis (phrozen_code Discord)
D2Win / D2Client

D2Win.#10175 uses pUnit->nGlobalPaletteShift(+0x6C) for the unit's current PL2 shift, taking precedence over D2Client.0x6FAAE3C0; overwrite those calls for custom PL2 shift | D2Win.#10175 which uses pUnit->nGlobalPaletteShift (+6C) ... takes precedence over D2Client.0x6FAAE3C0

Other 9

ref.discord_corpusreferencenotedtrustedThe Esoteric Path

THE DISCORD CORPUS: research/kb/corpus/discord/phrozen_code_channel.json = 52,604 messages from PhrozenKeep's #phrozen_code. Top authors (all TRUSTED-tier candidates): Necrolis(6737), Lectem(2883), whist(1930), Kingpin(1495), Firehawk, Silvermane, Ogodei, eezstreet, Mir Drualga(SGD2FreeRes author). DOUG IS IN IT as 'Esoteric Path' (47 msgs, 2022-06 to 2025-06). His 2022-11 thread IS this exact problem: he asked how to fill the 47px bottom void; Necrolis: 'd2dx might be doing it via a clipping rect' (= the RenderContext viewport we re-found); Mir: 'those black rects are hardcoded in SGD2FreeRes to hide the HOM from not adding assets'; EP: 'a 4 tile buffer... also appears on the left if you open the character stats pane'. The bottom void + the side-panel void are the same phenomenon, documented by Doug in 2022. whist = Doug's 'whilst'.

protocol.evidence_rulereferencenotedverifiedThe Esoteric Path (Doug directive 2026-07-13)

DOCUMENTATION RIGOR RULE: every recorded observation MUST carry a valid way to confirm it -- the DLL VERSION it was seen in + the exact LOG line (or disasm) that proves it. Mark PROVEN (log/disasm) vs INFERRED (visual). No narrative claims without a verification handle. Applies to all esoteric.dll findings henceforth.

finding.novelty_audit_2026_07_18referencenotedverifiedThe Esoteric Path (novelty audit 2026-07-18)

NOVELTY vs public prior art (10 findings audited): 8 NOVEL (no public source states the technique/conclusion): the DrawGame 1.13c pipeline map; offX single-write@0x6FB5AF7C read-173x; the no-panel view-state lens; the floor-un-shifted-at-seam experiment; the worldCtx(0x11C3C4) menu gate; playAreaW-governs-floor-AND-hittest; the whole dead-zone-fill endeavor; the render-context struct at *(0x11C3C4). 2 CORROBORATED (attribute, do not claim as discovery): (a) D2DXWiden = bolrog's SANCTIONED public Configurator API SetCustomResolution -- attribute bolrog; (b) GlideUnclamp is adjacent to IAmTrial's documented hardcoded-640 D2Glide clamp @+0x15A68 (a DIFFERENT site than our 0x6F856840 cull -- ours is distinct). SUBSTRATE (named globals ScreenShiftX 0x11B9A0, GeneralPlayAreaCameraShiftX 0x11C418, GeneralDisplayWidth 0xDBC48) is PUBLIC (mir) -- attribute; the behavioral CONCLUSIONS + techniques on top are ours. Attribution: mir, bolrog, IAmTrial, Necrolis, kidpaddle94/JayTee.

finding.crossverify_2026_07_19referenceconfirmedverifiedThe Esoteric Path (cross-verify 2026-07-19)

CROSS-VERIFY of all Grimoire addresses vs the on-disk 1.13c DLLs (D2Client 0x6FAB0000-0x6FBE5000, D2gfx ..0x6FAA1000, D2Win 0x6F8E0000, D2Glide 0x6F850000, D2Common 0x6FD50000, D2Game 0x6FC20000, D2CMP 0x6FE10000). 476 addresses / 698 records: 419 (88%) fall in a valid 1.13c module range (222 D2Client code + 117 D2Client data + D2Glide/D2Common/D2gfx/D2Win/D2Game); 57 FLAGGED as outside any 1.13c module = 1.10f-era/wrong. Flagged cluster = corpus.* 0x6FAAxxxx addresses sitting in the GAP between D2gfx (ends 0x6FAA1000) and D2Client (0x6FAB0000) -> they are 1.10f D2Client (lower base). NOTABLY the floor-drawer MULTIRES_DrawGroundTiles 0x6FAA2E60 and tile-setup 0x6FAA3330 ARE FLAGGED 1.10f -> do NOT use for 1.13c; rebase via struct layout + a 1.13c xref/disasm. RULE: any corpus 6FAAxxxx (below 0x6FAB0000) or cross-module-gap address is 1.10f, rebase before use. Assimilation-verification baseline.

finding.live_patch_map_methodreferenceconfirmedverifiedThe Esoteric Path (Doug insight + method 2026-07-19)

METHOD (Doug's key correction 2026-07-19): the ON-DISK DLL is NOT what runs. PlugY / SGD2FreeRes / d2fps patch D2Client in memory at load; at least 3 mods control frame/UI. Analyzing on-disk VANILLA code produced FOUR dead-end floor-shift hypotheses (see finding.floor_shift_ledger). Correct approach: (1) LIVE-vs-DISK byte diff of the running process reveals every mod patch; (2) disassemble the LIVE (patched) code, following mod redirects; (3) NOP-test via WriteProcessMemory (reversible, elevated) to isolate which mod's patch drives a symptom. D2Client live map: 117 patched regions / 1419 bytes; core floor draw all vanilla; render-controlling mods = SGD2FreeRes (@0x739C0000: SetScreenShift/SetTileCullingBound/DrawScreenBg/disable-stock-bg), PlugY (many), esoteric (ours). Redirect target bases seen: 0x739Cxxxx=SGD2FreeRes; 0x72Exxxx/0x73Dxxxx = mod code; 0x032xxxxx=codecaves(ours); 0x10000000=another module. TOOLKIT built tonight (scratchpad, all elevated RPM/WPM, no x32dbg needed for read/write/NOP): rpm_poll.py, freeze_poke.py, panel_delta.py, floor_scan.py, live_patch_map.py, disasm_sgd2.py, patch_toggle.py.

no records match.