Captured 2026-05-10 during Phase A.5 polish discussion. User asked why
the 9070 XT @ 1440p doesn't hit Unreal-level FPS for an old game like
AC. Answer: architectural — we rebuild the entire draw plan from
scratch every frame instead of caching pre-baked static-world data.
Tier 1 (entity-classification cache) lands as A.5 polish (separate
commit). Tiers 2 + 3 documented here for future scheduling:
- Tier 2 — Static/dynamic split with persistent groups
~2-week phase. Static entities (~95% of world) get permanent GPU-
resident matrix slots, populated at spawn, dirty-tracked for delta
upload. Per-frame CPU cost for static = LB-cull + dirty-flag check
only. Estimated entity dispatcher: 3.5ms → 0.5-1ms median.
400-600 FPS at standstill, radius=12.
- Tier 3 — GPU-side culling (compute pre-pass)
~1-month phase. Per-instance frustum cull moves to GPU compute
shader. Compute writes draw-indirect buffer; rasterizer reads it.
Estimated CPU dispatcher: ~0.05ms (essentially free).
600-1000+ FPS at standstill, radius=12.
Doc captures effort estimates, sub-decisions, risks, mitigations, and
scheduling triggers for each tier. Also notes the architectural
ceiling (~800-1500 FPS for a C# + GL client; reaching native engine
performance requires becoming a different engine).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Document Phase N.5b shipping (terrain on the modern rendering path via
Path C — `TerrainModernRenderer` mirrors WB's `TerrainRenderManager`
pattern but consumes acdream's `LandblockMesh.Build` so retail's
`FSplitNESW` formula stays in lockstep with physics + visual mesh).
Changes:
- `docs/plans/2026-04-11-roadmap.md` — add N.5b row to the Shipped
table; promote N.5b's "Phases ahead" entry to ✓ SHIPPED with the
Path C resolution + perf reality check; refresh N.6 scope to note
Terrain has joined the modern path (legacy `Texture2D` retirement
scope narrows to Sky + Debug); update top-of-doc Status line.
- `docs/ISSUES.md` — close issue #51 (WB terrain-split formula
divergence). Move from OPEN to "Recently closed" with the Path C
resolution: never adopted WB's formula; modern dispatcher uses
retail's via `LandblockMesh.Build`. References `da56063` (the
black-terrain fix that landed within the N.5b ship chain).
- `CLAUDE.md` — add `TerrainModernRenderer.cs` to the WB integration
cribs list with the GL_INVALID_OPERATION caveat (use uvec2 +
`sampler2DArray(handle)` constructor, NOT direct
`uniform sampler2DArray` + `glProgramUniformHandleARB`). Update
the "Currently in flight" preamble: N.6 builds on N.5 + N.5b;
add an N.5b shipped paragraph linking the perf baseline doc.
- `docs/plans/2026-05-09-phase-n5b-perf-baseline.md` — new doc
capturing the radius=5 Holtburg perf measurement (modern 6.4-7.0
µs median vs legacy 1.5 µs — modern is ~4× SLOWER on CPU at
radius=5). Documents the spec acceptance criterion #5 amendment,
the architectural wins that DO hold (zero glBindTexture/frame,
constant-cost dispatch as A.5 raises radius, per-LB frustum cull),
and the three high-value gotchas surfaced during implementation.
User-memory updates (outside repo, not in this commit):
- `memory/project_phase_n5b_state.md` — full N.5b state file with
the three gotchas captured.
- `memory/MEMORY.md` — index entry pointing at the state file.
Build: dotnet build green. No code changes in this commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Records a new Phase A sub-piece: split the single ACDREAM_STREAM_RADIUS
into separate terrain + entity radii so terrain renders to a much
further horizon (WB-style) while entities/scenery stay at the current
closer radius.
Motivated by perf at ACDREAM_STREAM_RADIUS=5 dropping from ~810 fps
to ~200-300 fps because everything stays full-detail. Both retail and
WorldBuilder render terrain way out and strip entities at distance.
Estimate: 3-5 days for the radius split + fog tuning; +1 week if
terrain LOD via mesh decimation is included. Not yet brainstormed.
N.8 (sky + particles via WB's SkyboxRenderManager + ParticleEmitterRenderer)
was already on the roadmap; user confirmed they want it tracked there.
No edit needed for N.8 — already at the right level of detail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final cross-cutting review of N.5 found that Task 15's deletion of
mesh_instanced.vert/.frag left InstancedMeshRenderer orphaned —
ACDREAM_USE_WB_FOUNDATION=0 silently rendered terrain+sky only with
no entities. The SHIP commit's "[x] ACDREAM_USE_WB_FOUNDATION=0 still
works" claim was inaccurate.
Resolution: formal retirement of the legacy renderer path within N.5
instead of deferring to N.6.
Deleted:
- src/AcDream.App/Rendering/InstancedMeshRenderer.cs
- src/AcDream.App/Rendering/StaticMeshRenderer.cs
- src/AcDream.App/Rendering/Wb/WbFoundationFlag.cs
GameWindow simplified — capability detection is unconditional, missing
bindless throws NotSupportedException with a clear message at startup.
WbDrawDispatcher + mesh_modern shader load are mandatory after init.
No escape hatch.
GpuWorldState simplified — WbFoundationFlag.IsEnabled guards on
AddLandblock/RemoveLandblock removed; adapter calls are unconditional
when the adapter is non-null.
PendingSpawnIntegrationTests updated — WbFoundationFlag.ForTestsOnly_ForceEnable
static ctor removed (flag is gone; adapter calls are unconditional).
The ApplyLoadedTerrain physics-data loop was also simplified: the
EnsureUploaded sub-loop that fed InstancedMeshRenderer is gone;
_pendingCellMeshes is now explicitly cleared to prevent unbounded
accumulation (the worker thread still populates it, but WB handles
EnvCell geometry through its own pipeline).
Spec §2 Decision 5 + §10 Out-of-Scope updated. Plan ship-amendment
section added. Roadmap updated (N.5 ships with retirement; N.6 scope
narrowed to perf-only). CLAUDE.md "WB integration cribs" updated.
Perf baseline doc updated. WbDrawDispatcher class summary docstring
corrected to describe the as-shipped SSBO + multi-draw-indirect path.
ISSUES.md #51 updated (terrain not in N.5 scope; deferred to N.7).
Bindless support is now a hard requirement. Modern desktop GPUs
universally expose GL_ARB_bindless_texture + GL_ARB_shader_draw_parameters;
if a user hits the NotSupportedException, that's a real bug report
worth investigating, not a silent fallback.
Build: 0 errors, 0 warnings. Tests: 71/71 (Wb+MatrixComposition+TextureCacheBindless filter).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Moves N.5 from in-flight to Shipped (2026-05-08). N.6 (retire
InstancedMeshRenderer + perf polish) becomes the in-flight phase.
CLAUDE.md in-flight pointer updated to match.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CPU dispatcher: 1227 µs / frame median (1303 µs p95) at Holtburg
courtyard, 1662 groups in working set. Inferred ~810 fps sustained.
CPU dispatcher acceptance gate (≤70% of N.4): PASS — N.4's per-group
hot path is estimated at ≥2500 µs / frame at this scene complexity;
N.5 is comfortably under half.
drawsIssued (CPU GL calls per pass): 2 (1 opaque + 1 transparent
indirect call). Down from N.4's ~hundreds per pass. PASS.
GPU timing: unmeasured. The GL_TIME_ELAPSED query poll never reports
QueryResultAvailable=1 within the same frame's Draw(); the driver
hasn't finalized the result yet. Fix is double-buffering (queryA
on frame N, read on N+2). Deferred to N.6 perf polish — doesn't block
N.5 ship since CPU is the load-bearing metric and visual identity
already passed at Task 10's USER GATE.
Direct N.4 baseline NOT measured. Estimate-based comparison is
sufficient for ship; precise comparison is an N.6 follow-up.
Baseline doc at docs/plans/2026-05-08-phase-n5-perf-baseline.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase N.4 (Rendering Pipeline Foundation) ships. WbFoundationFlag
flips to default-on (== "1" → != "0"). WB's ObjectMeshManager is
now acdream's production mesh pipeline; WbDrawDispatcher is the
production draw path. Legacy InstancedMeshRenderer is retained as
ACDREAM_USE_WB_FOUNDATION=0 escape hatch until N.6 retires it.
Visual verification at Holtburg passed:
- Scenery (trees / rocks / fences / buildings) renders correctly
- Characters connected with full close-detail geometry (Issue #47
preserved — GfxObjDegradeResolver path intact)
- FPS substantially improved by grouped instanced draws + per-entity
AABB cull + opaque front-to-back sort + palette-hash memoization
Three high-value WB API gotchas surfaced during Task 26 visual
verification and are now documented in CLAUDE.md "WB integration
cribs" + plan Adjustments 7-9 + memory project_phase_n4_state.md:
1. ObjectMeshManager.IncrementRefCount only bumps a counter — does
NOT trigger mesh loading. Call PrepareMeshDataAsync explicitly.
2. ObjectRenderBatch.SurfaceId is unset — read batch.Key.SurfaceId.
3. Modern rendering (GL 4.3 + bindless = every modern GPU) packs
every mesh into ONE global VAO/VBO/IBO. Use
glDrawElementsInstancedBaseVertex(BaseInstance) with FirstIndex +
BaseVertex from the batch, not naive DrawElementsInstanced.
Plan doc flipped to Final state. Roadmap N.4 → Live ✓; N.5 rebranded
from "Terrain rendering" to "Modern rendering path" (bindless +
multi-draw indirect on top of N.4's foundation; terrain rendering
moves to N.5b). CLAUDE.md "Currently in flight" pointer updated to
N.5. New memory file project_phase_n4_state.md preserves the three
WB gotchas for cross-session continuity.
n4-verify*.log added to .gitignore.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After brainstorming N.4 we recognized WB's ObjectMeshManager isn't a
static helper — it's a 2070-line stateful asset pipeline (GPU resources,
atlas system, LRU + memory budget, background staging, bindless path).
Adopting it wholesale is the foundation that N.5/N.6/N.7 build on, not
a parallel substitution.
Updates:
- N.4 expanded to capture Option A scope: ObjectMeshManager + atlas +
per-instance customization layer + animation cache strategy + streaming
adapter. Estimate 3-4 weeks.
- N.5 estimate revised down (3-4w → 2-3w) since atlas + pipeline come
from N.4. Includes N.2's deferred terrain math substitution.
- N.6 estimate revised down (2-3w → 1-2w) — most substance lands in N.4.
- N.7 estimate revised down (2-3w → 1-2w) — naturally smaller on shared
infrastructure.
- N.8 estimate revised down (1.5-2w → ~1w) — C.1 already shipped most.
- N.10 noted as likely subsumed by N.4 (OpenGLGraphicsDevice arrives
with ObjectMeshManager).
- Calendar header revised to reflect the rebalanced totals.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Audit during N.3 follow-up uncovered that WB's TerrainUtils
CalculateSplitDirection uses a different math expression than
retail's FSplitNESW (the AC2D-cited polynomial 0x0CCAC033 etc that
our visual terrain mesh and physics already share). Substituting
TerrainSurface.SampleZ with WB's GetHeight in isolation would
re-introduce the triangle-Z hover bug from earlier work — physics
and visual mesh would pick different diagonals on disputed cells.
Updates:
- ISSUE #51 documents the divergence with file references and the
research that's needed when N.5 picks this up.
- Roadmap N.2 entry flags the dependency on N.5 and the reasoning
("not low-risk after all").
N.1's conformance proved slope-filtering equivalence (boolean
walkable verdict), not formula equivalence. The lesson is captured
in memory (feedback_wb_migration_formulas.md, not in-repo).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Walked Holtburg with the user; no texture regressions on terrain
blending, mesh textures, scenery clipmap edges, or building surfaces.
The deliberate A8 non-additive change (R=G=B=255,A=val) produced no
visible delta on entity textures. Phase N.3 is shipped end-to-end.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Roadmap: N.3 row added to shipped table; sub-phase block updated from
ahead-estimate to shipped summary. Document header date bumped.
Plan: docs/superpowers/plans/2026-05-08-phase-n3-texture-decode-via-wb.md
captures the audit + per-format substitution strategy + A8 isAdditive
divergence resolution that drove this phase.
No ISSUES.md update — visual verification at Holtburg is the remaining
gate; if the A8 non-additive change produces a visible delta on entity
textures, an issue gets filed there.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Roadmap updates after Phase N.1 ship:
- Marks N.0 (submodule + project refs setup) as ✓ SHIPPED with the
c8782c9 commit reference
- Updates N.2-N.9 effort estimates with realistic post-N.1 numbers
(originals were 1-2 days / 1 week / 2 weeks; realistic numbers
factor in conformance-test discovery, ACME-vs-Chorizite delta
hunts, and the visual-verification-then-revert cycle that ate
most of N.1's calendar time)
- Adds a "Lessons from N.1" subsection so future N phases benefit
from the rotation-bug-conformance-test pattern, the ACME divergence
insight, and the "whackamole = stop" rule
- Updates total calendar estimate to 3-4 months / 10-12 engineering
weeks for N.2-N.9 (was 2-3 months / 6-8 weeks)
New handoff doc at docs/research/2026-05-08-phase-n3-handoff.md
captures everything a fresh agent picking up N.3 (texture decoding)
needs: phase context, what to read first, suggested task decomposition,
watchouts (especially the ACME-divergence and conformance-test
lessons), and where to start.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Phase N.1 to "Phases already shipped" table at top of roadmap,
updates the Phase N section to mark N.1 with checkmark SHIPPED status,
and files the known road-edge-tree cosmetic difference at landblock
0xA9B1 in ISSUES.md as issue #50 follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two design docs and a roadmap entry for the strategic shift
from "port retail rendering algorithms ourselves" to "depend on a
fork of Chorizite/WorldBuilder for rendering + dat-handling."
- docs/superpowers/specs/2026-05-08-phase-n-worldbuilder-migration-design.md
— parent design: integration model (fork + submodule), 10 sub-phases
(N.0 setup through N.10 GL consolidation), strangler-fig phasing
with per-phase feature flags, retail-decomp boundary clarified for
what WB does NOT cover (network, physics, animation, motion, UI,
plugin, audio, chat).
- docs/superpowers/specs/2026-05-08-phase-n1-scenery-via-wb-helpers-design.md
— N.1 detailed design: replace IsOnRoad / DisplaceObject /
slope-normal calc / rotation / scale inside SceneryGenerator.Generate()
with calls to WB's SceneryHelpers + TerrainUtils. Keep data flow,
ScenerySpawn shape, and renderer integration. Add small LandBlock →
TerrainEntry[] adapter. Feature flag ACDREAM_USE_WB_SCENERY=1.
- docs/plans/2026-04-11-roadmap.md — Phase N entry added between
Phase M and Phase J. Lists all 10 sub-phases with effort estimates.
Fork already created at https://github.com/eriknihlen/WorldBuilder.
N.0 setup (replace references/WorldBuilder/ snapshot with submodule,
add project references, build green) is the next implementation step.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the Phase M planning entry: replace the happy-path WorldSession
shape with a holtburger-aligned reliable network stack while keeping
acdream's stricter checksum verification + live ACE compatibility.
Lays out M.1–M.x sub-lanes (audit/parity map, layer extraction,
reliability core, etc.).
Detailed spec to land at
docs/superpowers/specs/2026-05-02-network-stack-conformance.md
before implementation starts. Holtburger is the client-behavior
oracle for this phase.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Plan doc `docs/plans/2026-04-30-sky-pes-port.md` captures the full
porting plan for #36 (sky-PES dispatch chain). Three phases:
M.1 — decomp dive (no code yet) for CallPESHook::Execute,
CPhysicsObj::CallPES, CreateParticleHook::Execute,
GameSky::CreateDeletePhysicsObjects, and the dynamic-spawn
trigger (region/weather/time-of-day handler).
M.2 — optional cdb verification with detailed args (this pointer +
pes_id + caller stack walk).
M.3 — implementation: persistent emitter creation at cell load,
dynamic spawn on transitions, PES script-timeline driver,
particle-system render wire-up.
M.4 — live side-by-side verification.
Acceptance: aurora visible at right moments, clouds dense like retail,
storm flashes during Rainy storm windows, PES dispatch rate matches
retail's ~150/min.
.gitignore extended to suppress per-session retail-debugger scratch
files (cdb scripts, launch logs, analysis ps1 helpers). The
canonical workflow lives in CLAUDE.md "Retail debugger toolchain";
session-specific traces should not pollute the repo.
Closes#36 plan stage. Implementation work begins next session.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase L.4 closes the "stuck in falling animation on a steep roof" bug
the user reported on 2026-04-30 ("I jump up, I land on it. It should not
even let me land, should just slide with a falling animation"). After
this commit the body no longer sticks to a steep roof when jumping
into it — it slides along the slope while keeping the falling animation.
Two pieces:
1. BSPQuery Path 6 steep-poly slide
When an airborne sphere hits a polygon whose world normal Z is below
FloorZ (≈ 0.6642, slope > ~49°), the previous flow was:
Path 6 SetCollide → Path 4 set_walkable → ContactPlane committed →
body "lands" on the steep poly with Contact bit + falling animation.
This left the player stuck mid-slope because OnWalkable was cleared
but Contact stayed set.
The new branch detects the steep normal in Path 6 BEFORE SetCollide
is called. Instead of entering the landing path, it removes the
into-wall component of the move (project onto the steep face), sets
CollisionNormal + SlidingNormal, and returns Slid. Same shape as
Path 5's step-up fallback and CylinderCollision. The resolver retries;
the sphere is now outside the poly; FindCollisions returns OK;
ValidateTransition commits the slid position. ContactPlane is never
set, so the body stays airborne with falling animation.
2. PlayerMovementController L.3a-bounce carve-out + Inelastic stop
Re-enables the velocity-reflection bounce when the contact normal is
upward-facing but steeper than walkable (0 < N.Z < FloorZ). The base
L.3a rule suppresses bounce on landing transitions to avoid micro-
bounce on flat terrain; that suppression also stuck the player to
too-steep roofs they shouldn't land on. This carve-out re-enables
the reflection specifically for the steep upward case.
Also lands related L.2c precipice / edge-slide work that was in flight:
- TransitionTypes EdgeSlideAfterStepDownFailed: walkable-poly-steep
cliff route + steep-ContactPlane cliff route ordering, so that
CliffSlide fires when the stored walkable polygon itself is too
steep (Path 4 had previously accepted it as a "landing" via the
permissive LandingZ threshold).
- CliffSlide reference-normal selection: prefer LastWalkable, fall back
to LastKnownContactPlane only when walkable, else use world-up. This
prevents the cross(steepN, steepN) = 0 degenerate case that left the
cliff slide as a no-op when both current and last-known were steep.
- Phase 2 / step-down branch / edge-slide branch / cliff-slide
diagnostic helpers gated on ACDREAM_DUMP_EDGE_SLIDE / ACDREAM_DUMP_STEEP_ROOF.
- Two new airborne-mover regression tests in BSPStepUpTests +
PhysicsEngineTests covering wall-slide and edge tangent motion.
DEVIATION FROM RETAIL — DOCUMENTED FOR FOLLOW-UP
The Path 6 steep slide is NOT what retail does. Retail's flow on the
same hit is:
Path 6 SetCollide (no steep check) → Path 4 find_walkable returns
nothing for steep → Phase 3 reset path: restore_check_pos +
kill_velocity → return COLLIDED → validate_transition reverts CheckPos
to CurPos and forces OK.
Net retail behavior: position reverts to pre-failed-move (typically
just below the roof in the common jump-up case), velocity zeroed,
gravity rebuilds Z next frame, body falls back down naturally with
the falling animation. The "freeze" framing I used earlier was wrong;
in the typical case retail just bounces the body off and lets gravity
take over.
Strict retail behavior would match the user's intent better in the
common case AND avoid the bounce-energy-accumulation we saw with the
slide-tangent approach (V grew to ~50 m/s in continuous-contact frames).
However, retail's behavior degenerates in the edge case of an overhead
landing onto a steep slope (body would freeze mid-air above the roof).
This commit ships the slide-tangent fix as an interim "much better"
state per user verification on 2026-04-30. Follow-up work to match
retail strictly: revert Path 6 steep-slide, audit Phase 3 reset to
ensure kill_velocity (matching OBJECTINFO::kill_velocity ->
CPhysicsObj::set_velocity({0,0,0}, 0)) actually fires, and re-test.
Refs:
- acclient_2013_pseudo_c.txt:323784-323821 (Path 6 SetCollide)
- acclient_2013_pseudo_c.txt:273191-273239 (Phase 3 reset path)
- acclient_2013_pseudo_c.txt:272563-272596 (validate_transition revert)
- acclient_2013_pseudo_c.txt:274467-274475 (kill_velocity)
- acclient_2013_pseudo_c.txt:282699-282715 (handle_all_collisions bounce)
Tests: 833/833 green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Port the first retail precipice-slide slice from named retail/ACE: terrain and BSP walkable hits now preserve polygon vertices, failed step-down edges back-probe to rediscover the walkable polygon, and edge-slide can run precipice/cliff slide instead of only hard-stopping.
Adds pseudocode anchors plus regression coverage for terrain polygon context and loaded-terrain boundary edge-slide.
Co-authored-by: Codex <codex@openai.com>
Formalize Phase L.2 as the active holistic movement/collision program, align the roadmap and architecture docs, file tactical physics follow-ups, and refresh collision memory away from rewrite-from-zero guidance.
Co-authored-by: OpenAI Codex <codex@openai.com>
21 commits porting retail's MoveToManager-equivalent client-side
behavior for server-controlled creature locomotion and combat
engagement. Shipped as MVP after live visual verification across
multiple iteration rounds with the user.
Highlights:
- 186a584 — initial Phase L.1c port: extracts Origin / target guid /
MovementParameters block from MoveTo packets (movementType 6/7),
adds RemoteMoveToDriver per-tick body-orientation steering with
±20° aux-turn-equivalent snap tolerance.
- d247aef — corrected arrival predicate semantics + 1.5 s
stale-destination timeout for entities leaving the streaming view.
- f794832 — root-caused "creature won't stop to attack" via two
research subagents converging on retail
CMotionInterp::move_to_interpreted_state's unconditional
forward_command bulk-copy. Lifted ServerMoveToActive flag clearing
+ InterpretedState bulk-copy out of substate-only branch so
Action-class swing UMs (mt=0 ForwardCommand=AttackHigh1) clear
stale MoveTo state and zero forward velocity.
- ff6d3d0 — RemoteMoveToDriver.ClampApproachVelocity caps horizontal
velocity at the final-approach tick so body lands EXACTLY at
DistanceToObject instead of overshooting through the player.
- 37de771 — bulk-copy ForwardCommand for MoveTo packets too (closed
the regression where MoveTo creatures stayed at default
ForwardCommand=Ready in InterpretedState and only translated via
UpdatePosition snaps).
- 34d7f4d + e71ed73 — AnimationSequencer.HasCycle query +
fallback chain (requested → WalkForward → Ready → no-op) at BOTH
the OnLiveMotionUpdated path AND the spawn handler. Prevents
ClearCyclicTail from wiping the body's cyclic tail when ACE
CreateObject carries CurrentMotionState.ForwardCommand pointing
to an Action-class motion (e.g. AttackHigh1 from a mid-swing
creature) which has no cyclic-table entry — was the "torso on
the ground" symptom for monsters seen in combat by a fresh
observer.
Cross-references: docs/research/named-retail/acclient_2013_pseudo_c.txt
(MoveToManager 0x00529680 + 0x0052a240 + 0x00529d80,
CMotionInterp::move_to_interpreted_state 0x00528xxx,
MovementParameters::UnPackNet 0x0052ac50), references/ACE/Source/
ACE.Server/Physics/Animation/MoveToManager.cs (port aid),
references/holtburger/ (cross-check on snapshot-only client
behavior), docs/research/2026-04-28-remote-moveto-pseudocode.md
(the Phase L.1c pseudocode doc).
Tests: 1404 → 1422 (parser type-7 path retention, type-6 target
guid retention, driver arrival semantics, retail-faithful
chase/flee branches, approach-velocity clamp scenarios,
HasCycle present/missing, AttackHigh1 wire layout).
Pending follow-ups (filed for future): target-guid live resolution
for type 6 packets (residual chase lag), StickToObject sticky-target
guid trailing field, full MoveToManager state machine port
(CheckProgressMade stall detector, Sticky/StickTo, use_final_heading,
pending_actions queue).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the Phase C.1 row to the "Phases already shipped" table and
flags the C.1 bullet in the "Phases ahead — Phase C — Polish / visuals"
section as ✓ SHIPPED. Retains C.1 entity-emitter wiring (portal swirls,
chimney smoke, fireplace flames) as a Phase C.1.5 follow-up — the data
layer is ready, only the wiring of entity-attached emitters to retail
effect IDs is deferred.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports retail's ParticleEmitterInfo / Particle::Init / Particle::Update
(0x005170d0..0x0051d400) and PhysicsScript runtime to a C# data-layer
plus a Silk.NET billboard renderer. Sky-PES path is debug-only behind
ACDREAM_ENABLE_SKY_PES because named-retail decomp confirms GameSky
copies SkyObject.pes_id but never reads it (CreateDeletePhysicsObjects
0x005073c0, MakeObject 0x00506ee0, UseTime 0x005075b0).
Post-review fixes folded into this commit:
H1: AttachLocal (is_parent_local=1) follows live parent each frame.
ParticleSystem.UpdateEmitterAnchor + ParticleHookSink.UpdateEntityAnchor
let the owning subsystem refresh AnchorPos every tick — matches
ParticleEmitter::UpdateParticles 0x0051d2d4 which re-reads the live
parent frame when is_parent_local != 0. Drops the renderer-side
cameraOffset hack that only worked when the parent was the camera.
H3: Strip the long stale comment in GfxObjMesh.cs that contradicted the
retail-faithful (1 - translucency) opacity formula. The code was
right; the comment was a leftover from an earlier hypothesis and
would have invited a wrong "fix".
M1: SkyRenderer tracks textures whose wrap mode it set to ClampToEdge
and restores them to Repeat at end-of-pass, so non-sky renderers
that share the GL handle can't silently inherit clamped wrap state.
M2: Post-scene Z-offset (-120m) only fires when the SkyObject is
weather-flagged AND bit 0x08 is clear, matching retail
GameSky::UpdatePosition 0x00506dd0. The old code applied it to
every post-scene object — a no-op today (every Dereth post-scene
entry happens to be weather-flagged) but a future post-scene-only
sun rim would have been pushed below the camera.
M4: ParticleSystem.EmitterDied event lets ParticleHookSink prune dead
handles from the per-entity tracking dictionaries, fixing a slow
leak where naturally-expired emitters' handles stayed in the
ConcurrentBag forever during long sessions.
M5: SkyPesEntityId moves the post-scene flag bit to 0x08000000 so it
can't ever overlap the object-index range. Synthetic IDs stay in
the reserved 0xFxxxxxxx space.
New tests (ParticleSystemTests + ParticleHookSinkTests):
- UpdateEmitterAnchor_AttachLocal_ParticlePositionFollowsLiveAnchor
- UpdateEmitterAnchor_AttachLocalCleared_ParticleFrozenAtSpawnOrigin
- EmitterDied_FiresOncePerHandle_AfterAllParticlesExpire
- Birthrate_PerSec_EmitsOnePerTickWhenIntervalElapsed (retail-faithful
single-emit-per-frame behavior)
- UpdateEntityAnchor_WithAttachLocal_MovesParticleToLiveAnchor
- EmitterDied_PrunesPerEntityHandleTracking
dotnet build green, dotnet test green: 695 / 393 / 243 = 1331 passed
(up from 1325).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Self-contained spec for the next session: PES (Particle Effect
Schedule) renderer that produces retail's "aurora light play",
portal swirls, chimney smoke, fireplace flames in one
implementation. Rolls up ISSUES.md #28 (root-caused this session
to PES on CelestialPosition.pes_id) and likely #29 (residual
cloud density gap).
Picks up after sky/weather session (merged at f7c9e88). Phase
E.3 already shipped the data layer (ParticleSystem,
EmitterDescLoader, ParticleHookSink, PhysicsScriptRunner,
VfxModel in src/AcDream.Core/Vfx/). C.1 is the visual half:
SkyDescLoader PesObjectId capture, SkyRenderer emitter spawn,
billboarded-quad GL renderer following WorldBuilder's
ParticleBatcher pattern.
Spec includes Step 0 grep targets, references in priority order
(decomp first, ACME/WorldBuilder second), the Dereth Rainy
DayGroup PES enumeration from tools/StarsProbe (notably
0x3300042C active 0.27-0.91 = "render this and confirm" target),
implementation outline (C.1.0 through C.1.6), pitfalls from
prior sessions, and the worktree setup commands.
To kick off the next session, point it at this file.
K shipped previously (commit f42c164) but never got a row in the
"Phases already shipped" table — only the per-sub-piece K.3 callout
in the Phase K section. Adding the K row here for completeness.
L.0 — full retail-style Settings interface — shipped this session.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase K final commit. Settings panel with click-to-rebind UX on top of
the K.1+K.2 input architecture, plus the roadmap / ISSUES / memory
updates that retire Phase K.
InputDispatcher gains BeginCapture / CancelCapture / IsCapturing /
SetBindings — modal capture suppresses normal action firing for the
next chord. Esc cancels (returns sentinel default chord); modifier-only
keys don't complete capture; non-modifier key down with current
modifier mask completes.
IPanelRenderer + ImGuiPanelRenderer + FakePanelRenderer gain
BeginMainMenuBar / EndMainMenuBar / BeginMenu / EndMenu / MenuItem
primitives.
SettingsVM owns a draft copy of KeyBindings with explicit Save /
Cancel / Reset semantics. Click-to-rebind enters dispatcher capture
mode; on chord captured, conflict-detect against draft (excluding the
action being rebound itself); surface a ConflictPrompt when the chord
collides; ResolveConflict(replace=true|false) commits or reverts.
ResetActionToDefault restores a single action to RetailDefaults();
ResetAllToDefaults rebuilds the entire draft. Save invokes the
onSave callback (which writes JSON + swaps the live dispatcher's
bindings).
SettingsPanel renders 8 retail-keymap-categorized CollapsingHeader
sections (Movement, Postures, Camera, Combat, UI panels, Chat,
Hotbar, Emotes). Per action: name + current binding(s) summary +
"Rebind"/"Reset" buttons. Conflict prompt at the top when pending.
Save / Cancel / "Reset all to retail defaults" at the top.
GameWindow registers SettingsPanel + wires F11 →
ToggleOptionsPanel → IsVisible toggle, plus a top-of-frame ImGui
MainMenuBar with View → Settings/Vitals/Chat/Debug entries (calls
ImGui directly — the abstraction methods exist for backend
portability but the host doesn't own a menu-bar surface).
Tests: +37 across InputDispatcherCaptureTests (7),
IPanelRendererMainMenuBarTests (9), SettingsVMTests (13),
SettingsPanelTests (8). Solution total 1220 green.
Roadmap (docs/plans/2026-04-11-roadmap.md) appends Phase K shipped
section after Phase J with K.1a–K.3 commit SHAs. ISSUES.md files
Phase L deferred work as #L.1–#L.8 (hotbar UI, spellbook favorites,
combat-mode dispatch, F-key panels, floating chat windows, UI layout
save/load, joystick bindings, plugin input subscription) and adds
#21–#25 to Recently closed. project_input_pipeline.md updated to
shipped state. CLAUDE.md gets an input-pipeline reference.
Closes Phase K.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wraps Phase I — UI consolidation + complete chat system. All 7 prior
commits (I.1 through I.7 + I.2) are now reflected in the canonical
sources of truth.
- docs/plans/2026-04-11-roadmap.md: new "Phase I — UI consolidation +
complete chat system" section between H and J. 8 sub-pieces all
marked SHIPPED 2026-04-25 with their actual commit SHAs:
I.1 b131514, I.2 56037a4, I.3 8e6e5a0, I.4 f14296c, I.5 ff5ed9e,
I.6 ca968fc, I.7 3d26c8e, I.8 (this commit).
Plus Phase H.1 entry annotated to credit I.4 + I.7 for chat input
+ combat translation. D.5 / D.6 entries cross-link to the new I
surface where relevant. Three Q&A rows added to "When will my
specific complaint be fixed?".
- docs/ISSUES.md: 7 issues filed and closed in the same session
(#14 IPanelRenderer widgets, #15 DebugPanel migration, #16
LiveCommandBus, #17 ChatPanel input, #18 holtburger inbound
parity, #19 TurbineChat, #20 CombatChatTranslator). All in
Recently closed with real commit SHAs.
- CLAUDE.md: surgical update to the UI strategy paragraph (~line 35).
ImGui now hosts ALL dev/debug UI (Vitals + Chat + Debug);
StbTrueTypeSharp DebugOverlay deleted in I.2; TextRenderer +
BitmapFont retained for the future HUD-in-world (D.6); custom
retail-look toolkit (D.2b) remains the long-term retail-look
path while ImGui is the pragmatic D.2a default.
- memory/project_chat_pipeline.md (auto-loaded; in user's claude
project memory tree): new evergreen crib documenting the
ChatLog -> ChatVM -> ChatPanel + LiveCommandBus -> WorldSession
pipeline with the slash-command set + opcode coverage.
- memory/MEMORY.md: indexed line for project_chat_pipeline.
Solution state at end of Phase I:
989 tests green (107 + 639 + 243), 0 warnings, 0 errors.
+124 tests across the phase.
Closes Phase I in roadmap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CLAUDE.md edits (6 surgical ranges):
- Goal section: introduce named-retail/ as primary; old chunks
remain as fallback for chunk-by-chunk address-range navigation.
- Workflow renamed to "grep named -> decompile -> verify -> port"
with a new STEP 0 GREP NAMED FIRST. Decompile demoted to a
fallback (Step 1) for the rare obfuscated/packed minority that
pseudo-C lacks.
- Function-map citation updated to point at symbols.json + the
cross-port hand-curated table.
- "Do not guess" rule strengthened: PDB has the answer for almost
everything; guessing is now negligence.
- Phase completion checklist accepts named symbols + addresses.
- Reference hierarchy table gets a new top row pointing at
docs/research/named-retail/ as the primary oracle for any
AC-specific algorithm — beats every other reference.
memory/project_named_decompilation.md (new): evergreen crib-sheet
with file inventory, grep examples, hard rules. Pattern matches
project_ui_architecture.md.
memory/project_retail_research_index.md: updated preamble to point
named-retail/ as first stop; older slices remain useful for
pseudocode + C# port sketches.
memory/project_collision_port.md: rewrote the "Decompiled ground
truth" section to put named-retail/ first, chunks second. The
"DECOMPILE FIRST" mandate becomes "GREP NAMED FIRST, then DECOMPILE
FALLBACK".
docs/architecture/acdream-architecture.md: Guiding Principle text
updated to introduce named-retail as the primary decomp source.
docs/plans/2026-04-11-roadmap.md: new Phase R block — Retail
research infrastructure. R.1 (corpus, shipped a9a01d8), R.2
(pdb-extract, shipped 69d884a), R.3 (actestclient vendored,
shipped a9a01d8). All marked SHIPPED 2026-04-25.
Auto-loaded MEMORY.md index updated with a new entry pointing at
project_named_decompilation.md so post-compaction sessions inherit
the workflow change automatically.
Acceptance verified:
- grep -c "named-retail" CLAUDE.md = 9 (>= 3 required)
- grep -c "named-retail" MEMORY.md = 1
- dotnet build green (docs-only commit, but verified)
Foundation phases A + B + C all landed. Next: Phase D files
ISSUES #8/#9/#11 + closes#10 (KillerNotification orphan parser).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes Phase D.2a. Launch with ACDREAM_DEVTOOLS=1 now shows a live
ImGui "Vitals" window whose HP bar reads CombatState.GetHealthPercent
for the local player. Without the env var the branches are dead code,
no ImGui context is created, and behaviour is identical to before.
GameWindow hunks:
- fields: _imguiBootstrap / _panelHost / _vitalsVm + DevToolsEnabled
- init (OnLoad): construct bootstrap + host, register VitalsPanel
- GUID push: _vitalsVm?.SetLocalPlayerGuid(chosen.Id) at live-connect
- frame begin: _imguiBootstrap.BeginFrame(dt) after GL clear
- frame end: _panelHost.RenderAll(ctx) + _imguiBootstrap.Render() after debug overlay
- input gating: skip WASD when ImGui.GetIO().WantCaptureKeyboard
Backend pivot: Hexa.NET.ImGui → ImGui.NET + Silk.NET.OpenGL.Extensions.ImGui.
First-light integration with the Hexa backend crashed 0xC0000005 inside
Hexa.NET.ImGui.Backends.OpenGL3.ImGuiImplOpenGL3.InitNative. Root cause:
Hexa's native OpenGL3 backend resolves GL function pointers via GLFW or
SDL internally; with Silk.NET (which uses neither) the pointers are null
and the native code crashes on first use. The mitigation path was
already planned — the design doc's Risk section called a pivot to
ImGui.NET a "one-morning operation" — and that's exactly what happened.
- Packages: Hexa.NET.ImGui 2.2.9 + Hexa.NET.ImGui.Backends 1.0.18
→ ImGui.NET 1.91.6.1 + Silk.NET.OpenGL.Extensions.ImGui 2.23.0
- ImGuiBootstrapper: was static Initialize(gl)+Shutdown() wrapping
Hexa's OpenGL3 init; now an IDisposable wrapping Silk.NET's
ImGuiController instance which handles GL backend init + input
subscription in one go.
- SilkInputBridge.cs deleted (~190 LOC): ImGuiController subscribes
IKeyboard / IMouse events itself, we don't need a bespoke bridge.
- ImGuiPanelRenderer: ImGuiNET.ImGui.* calls instead of
Hexa.NET.ImGui.ImGui.*. Widget surface unchanged.
Boundary discipline is preserved — no panel imports ImGuiNET; only
ImGuiPanelRenderer does. The D.2b custom toolkit will implement the
same IPanelRenderer contract without touching panel code.
Out of scope (tracked for follow-up):
- Stam/Mana currently return float? null (VitalsVM). Absolute values
need LocalPlayerState + PlayerDescription (0x0013) parsing to be
stored rather than discarded — filed as a post-D.2a issue.
- Mouse-capture gating (WorldMouseFallThrough-style click-through
tests) — not needed until we add clickable inventory items.
Roadmap + memory + architecture doc + UI framework plan updated in the
same commit per CLAUDE.md roadmap-discipline rules. 753 tests pass
(550 Core + 192 Core.Net + 11 new UI.Abstractions), 0 build warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Landed the UI framework design in 2026-04-24-ui-framework.md yesterday;
this commit propagates the decisions across the documents that future
sessions touch first, so the three-layer pattern is discoverable without
re-reading the full plan.
Changes:
* NEW memory/project_ui_architecture.md — evergreen crib-sheet:
three-layer diagram, AcDream.UI.Abstractions contract, D.2a/D.2b
split, module layout, hard rules, why staged not pure-custom.
* CLAUDE.md: new paragraph describing the three-layer UI split, naming
AcDream.UI.Abstractions as the plugin-facing contract, pointing at
the full plan + memory crib.
* docs/architecture/acdream-architecture.md: new "UI Architecture"
companion-stack diagram after Layer 0-5 (doesn't renumber the main
stack), plus step 6a "UI tick" in Per-Frame Update Order.
* docs/plans/2026-04-11-roadmap.md Phase D tightened:
- D.2 split explicitly into D.2a (Hexa.NET.ImGui scaffold + abstraction
layer) and D.2b (custom retail-look backend, implements same contracts).
- D.3 AcFont / D.4 dat sprites / D.7 cursor flagged as D.2b dependencies.
- D.5 core panels / D.6 HUD flagged as abstraction-layer deliverables
— ship with D.2a, reskinned by D.2b.
- D.8 Sound marked superseded (shipped as Phase E.2).
- F.5 core panels + H.1 chat-window cross-references updated to say
they target AcDream.UI.Abstractions, unblocked by D.2a.
- Shipped-phases table untouched.
* docs/research/retail-ui/00-master-synthesis.md: scope note at top
clarifies the Keystone research is the D.2b (custom backend)
foundation, NOT where D.2a starts.
* ~/.claude/.../memory/MEMORY.md: one-line index entry pointing at the
new project_ui_architecture.md (so session auto-load surfaces it).
Zero code changes; doc-only. dotnet build stays green. All verification
greps pass (see plan file for exact checks).
Two-stage rollout, one stable abstraction layer:
1. Short-term: Hexa.NET.ImGui as the backend. Wire up in days, iterate
game logic (chat, inventory, vitals) in weeks. Looks like a debugger,
acceptable while we prove the interaction logic end-to-end.
2. `AcDream.UI.Abstractions` — ViewModels + Commands + `IPanel` /
`IPanelRenderer` interfaces. Backend-agnostic. Plugin API targets
this layer; plugins never see ImGui.
3. Long-term: custom retail-look backend using dat assets. Swap panel
by panel. ImGui stays forever as the `ACDREAM_DEVTOOLS=1` overlay.
The new doc (`2026-04-24-ui-framework.md`) captures:
- Full design of the three-layer split
- Why Hexa.NET.ImGui over ImGui.NET + Silk.NET.OpenGL.Extensions.ImGui
(AOT readiness, tracks upstream ImGui faster, cleaner native-lib
bundling)
- Alternatives considered and ruled out (Myra, Avalonia, NoesisGUI,
RmlUi, pure custom from day one)
- Implementation order (Sprint 1 vitals HUD → Sprint 2 interaction
panels → Sprint 3 plugin API → Sprint 4+ more panels → later
custom retail-look)
- Risks + mitigations and open questions deferred to implementation
Roadmap Phase D updated with a pointer to the new plan so future
sessions start from the latest strategy, not the original
all-custom-from-day-one Phase D description.
No code changes yet. Ready to start Sprint 1 when approved.
Captures where we stand after Phase 4b and lays out the remaining
retail-faithful port work across four phases (5-8):
- Phase 5: PhysicsScript loader + runtime + sky lifecycle. Replaces
WeatherSystem's crude "DayGroup name contains Rainy → spawn rain"
shortcut with retail's actual PES-driven particle emission.
- Phase 6: Fog on sky meshes. The sky frag currently ignores fog
uniforms; retail's D3D fog applies to sky.
- Phase 7: Lightning flash trigger + thunder audio for storm keyframes.
- Phase 8: Weather / DayGroup crossfade (DAT_008427a9 / _DAT_008427b8
lerp) + AdminEnvirons override → fog crossfade.
User observation 2026-04-23 during Phase 4b verification: "Now it is
raining when it should not be." Root cause traced to the
SetKindFromDayGroupName string match firing rain particles on a "Rainy"
DayGroup regardless of whether that DayGroup actually has a visible
rain-emitting SkyObject. Proper fix requires porting PhysicsScript.
Also commits the earlier research from agent Q1-Q6:
`docs/research/2026-04-23-sky-material-state.md`.
Four parallel decompile agents are in flight as of this commit:
- PhysicsScript dat + runtime
- Sky↔PES wiring + emitter lifecycle
- Lightning + weather crossfade
- Fog on sky + vertex distance
Phase 5 implementation starts once those land.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update the roadmap's 'shipped' table with the G.1+ entry covering the
end-to-end visual integration (sky renderer, weather system, particle
renderer, UBO-backed shader lighting, server time sync) — not just the
data-plumbing layer that went out yesterday.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two fixes for jump physics:
- Skip ground-snap when velocity Z > 0 (prevents immediate re-landing
at high framerates where per-frame Z delta < 0.05 snap threshold)
- Guard apply_current_movement velocity write behind OnWalkable check
(prevents MotionInterpreter.DoMotion from zeroing jump velocity on
every frame while airborne)
- Guard PlayerMovementController velocity replacement behind OnWalkable
(preserves momentum during airborne flight)
Jump works locally but server packet not yet sent (BUG-002).
Facing direction mismatch logged as BUG-003.
RunRate not verified as BUG-004.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All Foundation sub-pieces now shipped:
A.1 ✓ Streaming landblock loader
A.2 ✓ Frustum culling (~160fps)
A.3 ✓ Background net receive thread
A.4 Folded into A.1 (deferred — DatCollection not thread-safe)
Phase A (Foundation) is complete. Next: Phase B (Gameplay).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
~160fps uncapped at 5×5 radius with per-landblock AABB culling.
Perf overlay in window title shows visible/total landblock ratio
so the culling impact is visible at a glance.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move A.1 from "ahead" to "shipped" per the roadmap discipline rule.
The shipped row notes that the loader currently runs synchronously
(the original async-worker design hit DatCollection's lack of thread
safety) and that the Channel-based outbox API is preserved so async
loading can return cleanly when Phase A.3 lands a thread-safe dat
wrapper. Pending-spawn list in GpuWorldState handles live spawn /
streaming races without dropping data.
Quick-lookup table updated:
- "Can't walk past the loaded 3×3 window" → A.1 FIXED ✓
- "Frame hitch crossing landblock boundary" → Phase A.3
(synchronous loader for now; async returns when DatCollection is
thread-safe)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Output of a brainstorming session after Phase 6/7.1/9.1/9.2 shipped
and the lifestone crystal bug was isolated. Two documents:
1. docs/plans/2026-04-11-roadmap.md — strategic roadmap replacing
the stale post-Phase-5 version. Reflects what's actually shipped,
reorganizes upcoming work into Phases A (Foundation), B (Gameplay),
C (Polish — includes VFX/particles, dynamic lights, palette tuning,
double-sided translucents), D (UI + Sound), and E (long-tail).
Updates the "when will my complaint be fixed" quick-lookup with
the correct phase for portals (VFX, not shader tricks as previously
claimed), smoke, fireplace fire, and everything we fixed this
session. Phase ordering: A → B → (C/D in parallel) → E.
2. docs/superpowers/specs/2026-04-11-foundation-phase-design.md —
detailed implementation spec for Phase A only. Covers the four
sub-pieces (streaming landblock loader, frustum culling, net I/O
thread, async dat decoding folded into the streaming worker),
their components, data flow, error handling, testing strategy,
and commit-point ordering. Includes non-goals to prevent scope
creep.
No code changes yet. The spec goes to user review next, then into
the writing-plans skill for a detailed implementation plan.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Captures all the "this looks wrong" findings from the Phase 5 visual
verification and assigns each to a future phase. Top-of-document is
phases done, then phases ahead in suggested order, then a quick
lookup table that maps user complaints to their owning phase.
Phases ahead:
6 Animation system (creature poses, walk/attack motions, breathe-idle)
7 Multi-floor interiors + dungeons (second floors, foundry interior,
subterranean rooms)
8 Player input → server (movement, interact, ack pump, combat)
9 Visual polish (portals, mesh-origin offsets, exact palette ranges,
lighting/shadows)
10 UI / HUD (chat, inventory, character panel, spellbook, minimap)
11 Sound (SoundTable, audio engine, 3D positional audio)
12 Streaming + perf (chunked landblock loading, frustum culling, LOD,
background net thread)
Each phase entry has: what it owns, what it requires, references in the
existing references/ tree, and rough effort estimate.
Document is intentionally a living roadmap — updated whenever a phase
lands or when a new defect is observed that doesn't fit the existing
buckets.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Blending-focused plan for Phase 3c: port WorldBuilder's per-cell
palette-code + alpha-atlas terrain texture merge scheme so terrain
type boundaries blend smoothly instead of stair-stepping.
Scope deliberately excludes chunking (deferred to a possible Phase 3d
when streaming actually matters) so the work stays focused on the
visible win.
Execution split into 4 small commits:
3c.1 palette math (pure CPU, unit tested against golden values)
3c.2 alpha atlas loading
3c.3 per-cell vertex layout refactor
3c.4 shader rewrite (the visual-win commit)
Each step is runnable on its own so if 3c.4 looks wrong we know the
earlier steps were fine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Design-only doc covering the networking subsystem needed to bring
the static Holtburg scene online: UDP packet codec, ISAAC keystream,
fragment reassembly, GameMessage dispatch, WorldSession lifecycle,
and composite IGameState so server CreateObject messages flow through
the same IEvents pipeline the dat-hydrated static entities already use.
Also documents that ACE is the authority for the protocol (neither
WorldBuilder nor ACViewer implements client networking) and captures
the license hygiene plan: read ACE's AGPL source for protocol knowledge,
reimplement from scratch, credit in NOTICE.md.
Explicit win condition for Phase 4: the foundry statue finally appears,
because it's a server weenie, not a dat decoration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Locks the four decisions from brainstorming: full scope (all 8 sketched
tasks in one Phase 2b), GL_TEXTURE_2D_ARRAY for terrain atlas with a
per-vertex flat uint layer attribute, raw cursor capture FlyCamera with
F toggle and Escape release, and WorldEvents.EntitySpawned with
replay-on-subscribe so plugin ordering doesn't matter.
Grows AcDream.Plugin.Abstractions by IGameState + IEvents +
WorldEntitySnapshot. Host-side WorldGameState + WorldEvents implementations
live in AcDream.App.Plugins. AppPluginHost constructor gains two
parameters. Program.cs wiring order keeps Phase 2a's Enable-before-Run,
relying on replay-on-subscribe to make subscription-before-world-load
produce the right observable behavior.
Task 1 expands Vertex struct and LandblockMesh signature — this affects
StaticMeshRenderer's vertex stride too, so Phase 2a's shader bindings
need a matching update.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>