areatemplate added

This commit is contained in:
Boki 2026-03-04 17:12:32 -05:00
parent 0c14d78d8a
commit 94b460bbc8
8 changed files with 125 additions and 20 deletions

View file

@ -5520,4 +5520,64 @@ public sealed class MemoryDiagnostics
BfsSearchUiElements(mem, offsets, childPtr, childPath, currentDepth + 1, maxDepth, visited, results, predicate, selfOffset);
}
}
/// <summary>
/// Diagnostic: dump AreaInstance → AreaTemplate pointer chain.
/// </summary>
public string ScanAreaTemplate()
{
var sb = new StringBuilder();
var mem = _ctx.Memory;
var offsets = _ctx.Offsets;
// Resolve InGameState
var controller = mem.ReadPointer(_ctx.GameStateBase);
if (controller == 0) { sb.AppendLine("ERROR: controller is null"); return sb.ToString(); }
nint inGameState;
if (offsets.InGameStateDirectOffset > 0)
inGameState = mem.ReadPointer(controller + offsets.InGameStateDirectOffset);
else
inGameState = 0;
if (inGameState == 0) { sb.AppendLine("ERROR: InGameState is null"); return sb.ToString(); }
sb.AppendLine($"InGameState: 0x{inGameState:X}");
// AreaInstance ptr
var aiPtr = mem.ReadPointer(inGameState + offsets.IngameDataFromStateOffset);
sb.AppendLine($"AreaInstance (+0x{offsets.IngameDataFromStateOffset:X}): 0x{aiPtr:X}");
if (aiPtr == 0) { sb.AppendLine("ERROR: AreaInstance is null"); return sb.ToString(); }
// AreaTemplate ptr
var atPtr = mem.ReadPointer(aiPtr + offsets.AreaTemplateOffset);
sb.AppendLine($"AreaTemplate (+0x{offsets.AreaTemplateOffset:X}): 0x{atPtr:X}");
if (atPtr == 0) { sb.AppendLine("ERROR: AreaTemplate pointer is null"); return sb.ToString(); }
sb.AppendLine();
DumpAreaTemplateAt(sb, mem, atPtr, offsets);
return sb.ToString();
}
private void DumpAreaTemplateAt(StringBuilder sb, ProcessMemory mem, nint addr, GameOffsets offsets)
{
var rawNamePtr = mem.ReadPointer(addr + offsets.AreaTemplateRawNameOffset);
var namePtr = mem.ReadPointer(addr + offsets.AreaTemplateNameOffset);
var act = mem.Read<int>(addr + offsets.AreaTemplateActOffset);
var isTown = mem.Read<byte>(addr + offsets.AreaTemplateIsTownOffset);
var hasWaypoint = mem.Read<byte>(addr + offsets.AreaTemplateHasWaypointOffset);
var monsterLevel = mem.Read<int>(addr + offsets.AreaTemplateMonsterLevelOffset);
var worldAreaId = mem.Read<int>(addr + offsets.AreaTemplateWorldAreaIdOffset);
var rawName = _strings.ReadNullTermWString(rawNamePtr);
var name = _strings.ReadNullTermWString(namePtr);
sb.AppendLine($" RawNamePtr (+0x{offsets.AreaTemplateRawNameOffset:X2}): 0x{rawNamePtr:X} → \"{rawName}\"");
sb.AppendLine($" NamePtr (+0x{offsets.AreaTemplateNameOffset:X2}): 0x{namePtr:X} → \"{name}\"");
sb.AppendLine($" Act (+0x{offsets.AreaTemplateActOffset:X2}): {act}");
sb.AppendLine($" IsTown (+0x{offsets.AreaTemplateIsTownOffset:X2}): {isTown}");
sb.AppendLine($" HasWaypoint(+0x{offsets.AreaTemplateHasWaypointOffset:X2}): {hasWaypoint}");
sb.AppendLine($" MonsterLvl (+0x{offsets.AreaTemplateMonsterLevelOffset:X2}): {monsterLevel}");
sb.AppendLine($" WorldAreaId(+0x{offsets.AreaTemplateWorldAreaIdOffset:X2}): {worldAreaId}");
}
}

View file

@ -192,8 +192,8 @@ public class GameMemoryReader : IDisposable
snap.AreaLevel = areaLevel;
snap.AreaHash = ai.AreaHash;
// Area template from WorldData
var at = gs.InGame.WorldData.AreaTemplate;
// Area template from AreaInstance
var at = ai.AreaTemplate;
if (at.IsValid)
{
snap.AreaRawName = at.RawName;

View file

@ -235,9 +235,9 @@ public sealed class GameOffsets
/// <summary>Offset within Camera struct to the Matrix4x4 (64 bytes). 0 = disabled.</summary>
public int CameraMatrixOffset { get; set; } = 0x1A0;
// ── AreaTemplate (WorldData → WorldAreaDetailsPtr → AreaTemplate) ──
/// <summary>WorldData struct → WorldAreaDetailsPtr offset. Already in struct at 0x98.</summary>
public int WorldAreaDetailsOffset { get; set; } = 0x98;
// ── AreaTemplate (AreaInstance +0xA0 → AreaTemplate) ──
/// <summary>AreaInstance → AreaTemplate pointer. Confirmed: 0xA0.</summary>
public int AreaTemplateOffset { get; set; } = 0xA0;
/// <summary>AreaTemplate → RawName wchar_t* pointer.</summary>
public int AreaTemplateRawNameOffset { get; set; } = 0x00;
/// <summary>AreaTemplate → Name wchar_t* pointer (display name).</summary>

View file

@ -20,6 +20,7 @@ public sealed class AreaInstance : RemoteObject
public PlayerSkills PlayerSkills { get; }
public QuestFlags QuestFlags { get; }
public Terrain Terrain { get; }
public AreaTemplate AreaTemplate { get; }
public AreaInstance(MemoryContext ctx, ComponentReader components, MsvcStringReader strings, QuestNameLookup? questNames)
: base(ctx)
@ -28,6 +29,7 @@ public sealed class AreaInstance : RemoteObject
PlayerSkills = new PlayerSkills(ctx, components, strings);
QuestFlags = new QuestFlags(ctx, strings, questNames);
Terrain = new Terrain(ctx);
AreaTemplate = new AreaTemplate(ctx, strings);
}
protected override bool ReadData()
@ -99,6 +101,13 @@ public sealed class AreaInstance : RemoteObject
else
QuestFlags.Reset();
// AreaTemplate — pointer at AreaInstance + AreaTemplateOffset
var areaTemplatePtr = mem.ReadPointer(Address + offsets.AreaTemplateOffset);
if (areaTemplatePtr != 0)
AreaTemplate.Update(areaTemplatePtr);
else
AreaTemplate.Reset();
// Terrain — pass loading/area state before update
Terrain.AreaHash = AreaHash;
Terrain.Update(Address);
@ -133,5 +142,6 @@ public sealed class AreaInstance : RemoteObject
PlayerSkills.Reset();
QuestFlags.Reset();
Terrain.Reset();
AreaTemplate.Reset();
}
}

View file

@ -19,7 +19,7 @@ public sealed class InGameState : RemoteObject
: base(ctx)
{
AreaInstance = new AreaInstance(ctx, components, strings, questNames);
WorldData = new WorldData(ctx, strings);
WorldData = new WorldData(ctx);
}
protected override bool ReadData()

View file

@ -7,7 +7,6 @@ namespace Roboto.Memory.Objects;
/// <summary>
/// Reads WorldData struct (168B, 1 RPM) and resolves the camera matrix.
/// Primary camera source: WorldData.CameraPtr. Fallback: InGameState.CameraPtr (set via FallbackCameraPtr).
/// Owns AreaTemplate child for area metadata.
/// </summary>
public sealed class WorldData : RemoteObject
{
@ -21,12 +20,7 @@ public sealed class WorldData : RemoteObject
/// <summary>Resolved address of the camera matrix for hot-path caching.</summary>
public nint CameraMatrixAddress { get; private set; }
public AreaTemplate AreaTemplate { get; }
public WorldData(MemoryContext ctx, MsvcStringReader strings) : base(ctx)
{
AreaTemplate = new AreaTemplate(ctx, strings);
}
public WorldData(MemoryContext ctx) : base(ctx) { }
protected override bool ReadData()
{
@ -36,12 +30,6 @@ public sealed class WorldData : RemoteObject
// Read the full WorldData struct (0xA8 = 168 bytes, 1 RPM)
_data = mem.Read<WdStruct>(Address);
// Cascade to AreaTemplate
if (_data.WorldAreaDetailsPtr != 0)
AreaTemplate.Update(_data.WorldAreaDetailsPtr);
else
AreaTemplate.Reset();
// Resolve camera: primary from WorldData, fallback from InGameState
if (offsets.CameraMatrixOffset <= 0)
return true;
@ -69,6 +57,5 @@ public sealed class WorldData : RemoteObject
FallbackCameraPtr = 0;
CameraMatrix = null;
CameraMatrixAddress = 0;
AreaTemplate.Reset();
}
}