This commit is contained in:
Boki 2026-03-04 16:49:23 -05:00
parent 8a0e4bb481
commit 0df70abad7
24 changed files with 0 additions and 1225 deletions

View file

@ -0,0 +1,132 @@
namespace Roboto.Memory;
public enum EntityType
{
Unknown,
Player,
Monster,
Npc,
Effect,
WorldItem,
MiscellaneousObject,
Terrain,
Critter,
Chest,
Shrine,
Portal,
TownPortal,
Waypoint,
AreaTransition,
Door,
Doodad,
}
/// <summary>
/// Raw entity data read from process memory. No business logic or classification —
/// type classification lives in EntityReader (Memory-internal) and EntityClassifier (Data layer).
/// </summary>
public class Entity
{
public nint Address { get; }
public uint Id { get; }
public string? Path { get; }
public string? Metadata { get; }
// Position (from Render component)
public bool HasPosition { get; internal set; }
public float X { get; internal set; }
public float Y { get; internal set; }
public float Z { get; internal set; }
// Vitals (from Life component — only populated when explicitly read)
public bool HasVitals { get; internal set; }
public int LifeCurrent { get; internal set; }
public int LifeTotal { get; internal set; }
public int ManaCurrent { get; internal set; }
public int ManaTotal { get; internal set; }
public int EsCurrent { get; internal set; }
public int EsTotal { get; internal set; }
// Component info
public int ComponentCount { get; internal set; }
public HashSet<string>? Components { get; internal set; }
// Component-based properties (populated by GameMemoryReader)
public bool IsTargetable { get; internal set; }
public bool IsOpened { get; internal set; }
public bool IsAvailable { get; internal set; }
public int Rarity { get; internal set; }
// Mods (from Mods component)
public List<string>? ModNames { get; internal set; }
// AreaTransition destination (raw area ID, e.g. "G1_4")
public string? TransitionName { get; internal set; }
public int TransitionState { get; internal set; } = -1;
// Action state (from Actor component)
public short ActionId { get; internal set; }
public bool IsAttacking { get; internal set; }
public bool IsMoving { get; internal set; }
// Classification (set by EntityReader)
public EntityType Type { get; internal set; }
// Derived properties
public bool IsAlive => HasVitals && LifeCurrent > 0;
public bool IsDead => HasVitals && LifeCurrent <= 0;
public bool HasComponent(string name) => Components?.Contains(name) == true;
/// <summary>
/// Grid-plane distance to another point (ignores Z).
/// </summary>
public float DistanceTo(float px, float py)
{
var dx = X - px;
var dy = Y - py;
return MathF.Sqrt(dx * dx + dy * dy);
}
/// <summary>
/// Grid-plane distance to another entity.
/// </summary>
public float DistanceTo(Entity other) => DistanceTo(other.X, other.Y);
/// <summary>
/// Short category string derived from path (e.g. "Monsters", "Effects", "NPC").
/// </summary>
public string PathCategory
{
get
{
if (Path is null) return "?";
var parts = Path.Split('/');
return parts.Length >= 2 ? parts[1] : "?";
}
}
internal Entity(nint address, uint id, string? path)
{
Address = address;
Id = id;
Path = path;
Metadata = ExtractMetadata(path);
}
/// <summary>
/// Strips the "@N" instance suffix from the path.
/// "Metadata/Monsters/Wolves/RottenWolf1_@2" → "Metadata/Monsters/Wolves/RottenWolf1_"
/// </summary>
private static string? ExtractMetadata(string? path)
{
if (path is null) return null;
var atIndex = path.LastIndexOf('@');
return atIndex > 0 ? path[..atIndex] : path;
}
public override string ToString()
{
var pos = HasPosition ? $"({X:F0},{Y:F0})" : "no pos";
return $"[{Id}] {Type} {Path ?? "?"} {pos}";
}
}

View file

@ -0,0 +1,80 @@
using System.Numerics;
using Roboto.Memory.States;
namespace Roboto.Memory;
public class GameStateSnapshot
{
// Process
public bool Attached;
public int ProcessId;
public nint ModuleBase;
public int ModuleSize;
public string? Error;
// GameState
public nint GameStateBase;
public bool OffsetsConfigured;
public int StatesCount;
public GameStateType CurrentGameState = GameStateType.GameNotLoaded;
// Pointers
public nint ControllerPtr;
public nint InGameStatePtr;
public nint AreaInstancePtr;
public nint ServerDataPtr;
public nint LocalPlayerPtr;
// Area
public int AreaLevel;
public uint AreaHash;
// Player
public string? CharacterName;
// Player position (Render component)
public bool HasPosition;
public float PlayerX, PlayerY, PlayerZ;
// Player vitals (Life component)
public bool HasVitals;
public int LifeCurrent, LifeTotal;
public int ManaCurrent, ManaTotal;
public int EsCurrent, EsTotal;
// Entities
public int EntityCount;
public List<Entity>? Entities;
// Loading state
public bool IsLoading;
public bool IsEscapeOpen;
// Live state flags — individual bytes from InGameState+0x200 region
public byte[]? StateFlagBytes; // raw bytes from InGameState+0x200, length 0x30
public int StateFlagBaseOffset = 0x200;
// Active game states (ExileCore state machine)
public nint[] StateSlots = Array.Empty<nint>(); // State[0]..State[N] pointer values (all 12 slots)
public int[]? StateSlotValues; // int32 at state+0x08 for each slot
public HashSet<nint> ActiveStates = new(); // which state pointers are in the active list
public nint ActiveStatesBegin, ActiveStatesEnd; // debug: raw vector pointers
public nint[] ActiveStatesRaw = Array.Empty<nint>(); // debug: all pointers in the vector
public (int Offset, nint Value)[] WatchOffsets = []; // candidate controller offsets
public (int Offset, nint Value, string? Match, bool Changed, string? DerefInfo)[] ControllerPreSlots = []; // qwords before state array
// Player skills (from Actor component)
public List<SkillSnapshot>? PlayerSkills;
// Quest flags (from ServerData → PlayerServerData)
public List<QuestSnapshot>? QuestFlags;
// Camera
public Matrix4x4? CameraMatrix;
// Terrain
public int TerrainWidth, TerrainHeight;
public int TerrainCols, TerrainRows;
public WalkabilityGrid? Terrain;
public int TerrainWalkablePercent;
}

View file

@ -0,0 +1,22 @@
namespace Roboto.Memory;
public sealed class WalkabilityGrid
{
public int Width { get; }
public int Height { get; }
public byte[] Data { get; }
public WalkabilityGrid(int width, int height, byte[] data)
{
Width = width;
Height = height;
Data = data;
}
public bool IsWalkable(int x, int y)
{
if (x < 0 || x >= Width || y < 0 || y >= Height)
return false;
return Data[y * Width + x] != 0;
}
}