ROOT CAUSE FIX for missing left-side animations.
The AC client's MotionTable has NO cycles for TurnLeft (0x000E),
SideStepLeft (0x0010), or WalkBackward (0x0006). The real client
calls adjust_motion() which remaps these to their right-side
equivalents with NEGATIVE speed before looking up the cycle. Then
multiply_framerate() swaps LowFrame↔HighFrame so the animation
plays backward.
Source: ACE MotionInterp.cs:394-428, decompiled FUN_005267E0.
Changes:
- AnimationSequencer.SetCycle: adds adjust_motion block that remaps
left→right with speed *= -1 (TurnLeft, SideStepLeft) or
speed *= -0.65 (WalkBackward = BackwardsFactor)
- LoadAnimNode: when framerate < 0, swaps Low↔High (matching the
decompiled multiply_framerate)
- GameWindow.UpdatePlayerAnimation: passes original animCommand to
SetCycle (sequencer handles remapping internally), keeps legacy
fallback for non-sequencer entities
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete decompilation of the retail Asheron's Call client using
Ghidra 12.0.4 + pyghidra headless. 22,225 of 22,226 functions
successfully decompiled in 75 seconds.
Output: docs/research/decompiled/ (54 files, 688,567 lines of C)
Key findings already identified:
- CLandBlockStruct::ConstructPolygons at chunk_00530000.c:2270
(split direction formula with 0x0CCAC033 constants)
- Motion command handlers at chunk_00510000.c (0x45000005 etc)
- Motion interpreter at chunk_00520000.c
- Portal space UI at chunk_004D0000.c and chunk_00560000.c
Next: identify CPhysicsObj, CMotionInterp, collision, and movement
functions by cross-referencing against ACE's C# port.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exhaustive analysis of two working AC clients revealing three critical
findings that reshape acdream's movement system:
1. Server-authoritative Z: neither AC2D nor holtburger computes local
terrain Z for the player. AC2D sends keys, receives position. Holtburger
dead-reckons for smoothing but the server overrides.
2. Terrain split formula mismatch: AC2D and ACViewer's render path use
0x0CCAC033-based FSplitNESW; WorldBuilder (our source) uses a different
214614067-based physics formula. Our terrain mesh triangulation doesn't
match the real AC client's, causing Z mismatches on slopes.
3. Movement deduplication: MoveToState sent once per state change, not per
frame. AutonomousPosition heartbeat every 1 second.
Also adds AC2D to CLAUDE.md reference repos section.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comprehensive spec for completing the physics/movement system:
CellPortal-based indoor/outdoor/room-to-room transitions via
sphere-plane intersection, multi-landblock boundary crossing,
momentum-preserving jump with gravity arc, portal-space state
machine for teleports, Setup.StepUpHeight hookup, and
scenery-on-road exclusion fix.
Replaces the current "outdoor heightmap sampler with disabled
indoor transitions" with the full world-navigation system AC's
client uses. 12 acceptance criteria, ~20 new unit tests planned.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tab-toggled player mode with WASD ground walking, A/D + mouse
turning, Z/X strafing, Shift for run, third-person chase camera,
local walk/run/turn/idle animations, and outbound MoveToState +
AutonomousPosition server messages. Uses PhysicsEngine from B.3
for collision-resolved positions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5-task TDD plan: TerrainSurface (outdoor heightmap Z + cell ID),
CellSurface (indoor floor polygon projection via barycentric interp),
PhysicsEngine (top-level resolver with step-height + cell transitions),
GameWindow integration (populate from streaming), and roadmap update.
~16 new unit tests with fake data. No dat files or rendering needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pure-computation collision engine for ground-based entity movement.
Three components: TerrainSurface (outdoor heightmap Z interpolation),
CellSurface (indoor EnvCell floor polygon projection), PhysicsEngine
(top-level resolver with step-height enforcement, outdoor/indoor
cell transitions via CellPortals, and gravity reporting).
Uses PhysicsPolygons from CellStruct for walkable surfaces with
brute-force polygon iteration (< 20 polys per cell). BSP tree
acceleration deferred — same collision fidelity, simpler code.
Standalone module with no rendering or networking dependencies.
~15-20 unit tests with fake data covering flat terrain, slopes,
stairs, wall rejection, and cell transitions. Integration with
the streaming system via ApplyLoadedTerrainLocked. Consumed by
Phase B.2 (player movement mode, separate spec).
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>
Review follow-up from commit 11df793. Three fixes:
1. Visible semantics: StreamingRegion.Visible now strictly describes the
current (2r+1)×(2r+1) window, not window + hysteresis retainees.
Added a parallel Resident property exposing the actual loaded set
(window + hysteresis buffer). This matters because StreamingController
(next task) reads these to decide what to render vs what to unload;
conflating them in one set would have forced awkward post-processing
downstream.
2. Doc/code disagreement: updated the RecenterTo and RegionDiff doc
comments from "radius + 1" to "radius + 2" to match the actual
implementation (which is what the tests require). Also updated the
plan doc so future readers don't hit the same contradiction.
3. Edge-clamping test coverage: added a single-axis edge test
(cx=0, cy=50 → 15 entries) and an ID-encoding test (radius=0 at
0x12,0x34 → 0x1234FFFE) so a swapped-shift bug in EncodeLandblockId
or an asymmetric off-by-one would fail a test instead of passing
silently.
9 tests green, full suite regressions-free.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9-task TDD plan for the first chunk of Phase A (Foundation): the
streaming landblock loader. Covers StreamingRegion (pure data +
hysteresis diff), LandblockStreamer (background worker + channels),
GpuWorldState (render-thread entity registry), StreamingController
(glue), TerrainRenderer.RemoveLandblock, GameWindow wiring (env var,
camera/player center switch, Dispose), scenery + interior integration,
and the roadmap-shipped-table update.
Each task is a full TDD cycle: failing test, run, minimal impl, run,
commit. ~16 new unit tests land alongside the implementation.
Phase A.2 (frustum culling) and A.3 (net I/O thread) get their own
plans once A.1 ships — they're independent subsystems per the
brainstorming skill's decomposition guidance.
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>
Tasks 1-10 are fully specified TDD-bite-sized and cover Phase 2a:
scaffold + GfxObjMesh.Build + SurfaceDecoder + LandblockLoader +
SetupMesh.Flatten + WorldView + TextureCache + StaticMeshRenderer +
debug + visual verification. End state: Holtburg terrain with buildings
rendered and at least some textures resolved.
Tasks 11-18 are sketches for Phase 2b in a future session: terrain
atlas, 3x3 neighbor rendering, ICamera/FlyCamera/CameraController, and
the IGameState/IEvents plugin API growth. These will be re-planned
with fresh context once Phase 2a is known to work.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verified the real GfxObj data shape against DatReaderWriter's generated
types and WorldBuilder's ObjectMeshManager. Three corrections:
- GfxObj.Surfaces is a list and Polygon.PosSurface indexes into it;
multiple surfaces per GfxObj are the norm. GfxObjMesh.Build now
returns IReadOnlyList<GfxObjSubMesh> — one sub-mesh per referenced
surface. StaticMeshRenderer draws entities by grouping entities by
GfxObj and then by sub-mesh within each GfxObj. Matches the approach
WorldBuilder takes.
- LandBlockInfo has both Objects (Stabs — small decorations, rocks,
trees) AND Buildings (BuildingInfo). LandblockLoader loads both
lists and maps them uniformly to WorldEntity since both types share
the Frame shape.
- Stab.Frame and BuildingInfo.Frame carry position+orientation only,
no scale component. WorldEntity.Scale dropped. WorldEntitySnapshot
loses its Scale field too. Phase 3+ can add it back if needed.
Closes the one open design question flagged in the original doc.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Locks the design decisions from brainstorming: full scope (geometry +
textures + 3x3 neighbor grid), dual cameras (orbit default + FlyCamera
toggled by F), minimal plugin API growth (IGameState.Entities snapshot
+ IEvents.EntitySpawned). BCnEncoder.Net handles DXT/BCn decoding,
matching WorldBuilder's approach.
Adds three new namespaces under AcDream.Core (World, Meshing, Textures)
and three new rendering components under AcDream.App (StaticMeshRenderer,
TextureCache, FlyCamera + ICamera). Entities are deduplicated by
GfxObj id at the GPU layer; textures dedup by Surface id. Plugins
get immutable snapshots, not live references.
Rough 18-task sequence captured. Full bite-sized plan comes next via
superpowers:writing-plans.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ten bite-sized tasks covering: solution reorg into four projects,
TDD-driven PluginManifest parsing + discovery + collectible-ALC
loader (with a real fixture DLL), LandblockMesh pure generator,
Silk.NET window smoke, dat-backed landblock mesh upload, height-
ramp shader + orbit camera, and finally the end-to-end smoke
plugin round-trip. Manual visual smoke only for the GL bits;
xUnit TDD for everything testable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Captures the day-1 plugin + scripting contract for acdream:
C# plugins via collectible AssemblyLoadContext with a stable
IPluginHost API, Lua macros via a first-party Macros plugin that
embeds MoonSharp, and a four-stage packet pipeline for raw and
parsed traffic in both directions. Extends the phased MVP so
every core system is exposed through the plugin API in the same
commit that adds it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>