Modern open-source C# .NET 10 Asheron's Call client. Faithful port of retail client behaviour to Silk.NET with a plugin API.
Walks each entity's Setup → MotionTable → Animation chain to get the
per-part frame for its default idle pose, then uses that frame in
SetupMesh.Flatten instead of the static PlacementFrames lookup. For
creatures and characters this should produce the upright "Resting"
pose (e.g. for the Nullified Statue of a Drudge) instead of the
Setup-default crouch.
This is a minimal Phase 6 cut: render the FIRST frame of the IDLE
motion as a static pose. No per-frame interpolation, no walking, no
attack motions, no transitions. Those are larger pieces tracked in
docs/plans/2026-04-11-roadmap.md under Phase 6.
Algorithm ported from references/ACViewer/.../Physics/Animation/
MotionTable.SetDefaultState:
1. Look up Setup.DefaultMotionTable (0x09XXXXXX). 0 → no motion,
fall back to PlacementFrames.
2. MotionTable.StyleDefaults[DefaultStyle] → default substate.
3. cycleKey = (DefaultStyle << 16) | (substate & 0xFFFFFF)
4. MotionTable.Cycles[cycleKey] → MotionData.
5. MotionData.Anims[0].AnimId → Animation dat.
6. Animation.PartFrames[animData.LowFrame] → AnimationFrame
containing the per-part transforms for the idle pose.
Added:
- Core/Meshing/MotionResolver.cs: pure function GetIdleFrame(setup,
dats) that walks the chain and returns an AnimationFrame or null.
null is the "no motion data" sentinel and means caller should fall
back to PlacementFrames.
- SetupMesh.Flatten now takes an optional AnimationFrame override
parameter. Pose source priority is:
override → PlacementFrames[Resting] → PlacementFrames[Default]
So existing call sites that don't pass an override get the Phase 5d
Resting-fallback behavior unchanged. Static scenery is unaffected.
- GameWindow.OnLiveEntitySpawned (live-mode hydrator) calls
MotionResolver.GetIdleFrame and passes the result to Flatten.
Other Flatten call sites (offline scenery, interior EnvCells, scenery
generator) NOT yet wired — those use static dat hydration where the
entities don't have meaningful motion tables. The user-visible win
from this commit is in the live spawn pipeline only.
Things I'm not certain about and will check via the live run:
- Whether Animation.PartFrames are in entity-root-relative space
(matching PlacementFrames) or parent-relative (would need a parent
walk we don't do). ACViewer's UpdateParts applies frames per-part
without walking parents, suggesting root-relative — same convention
as PlacementFrames.
- Whether the resolver's null fallback is hit for creatures whose
Setup.DefaultMotionTable happens to be 0 (would silently regress
to Default placement). Worth checking if drudge looks the same.
Tests: 77 core + 83 net = 160, all green. No new tests yet because
the change is data-driven and best validated end-to-end via the live
run rather than synthetic dat fixtures (which would require fabricating
a complete MotionTable + Animation chain just to test the lookup).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| docs/plans | ||
| src | ||
| tests | ||
| .gitignore | ||
| AcDream.slnx | ||
| CLAUDE.md | ||
| README.md | ||
acdream
Experimental modern open-source Asheron's Call client in C# / .NET 10.
Status: pre-alpha, not playable. Phase 0 only — dat file asset inventory.
Stack: .NET 10, Chorizite.DatReaderWriter for dat parsing. Silk.NET + Avalonia planned for rendering/UI (not yet wired up).
Requires: A retail Asheron's Call install (Turbine/Microsoft property — supply your own). Set ACDREAM_DAT_DIR environment variable to the directory containing client_portal.dat, client_cell_1.dat, client_highres.dat, and client_local_English.dat, or pass it as the first CLI argument.
Layout
src/AcDream.Cli/— console app that dumps asset counts from a dat directoryreferences/— local read-only reference material (ACE, ACViewer, WorldBuilder, DatReaderWriter, holtburger, retail AC install). Gitignored.
Run
dotnet run --project src/AcDream.Cli -- "C:\path\to\Asheron's Call"
Or set ACDREAM_DAT_DIR and run without args.