huge refactor
This commit is contained in:
parent
e5ebe05571
commit
a8341e8232
29 changed files with 3184 additions and 340 deletions
|
|
@ -18,16 +18,13 @@ public enum EntityType
|
|||
Waypoint,
|
||||
AreaTransition,
|
||||
Door,
|
||||
Doodad,
|
||||
}
|
||||
|
||||
public enum MonsterRarity
|
||||
{
|
||||
White,
|
||||
Magic,
|
||||
Rare,
|
||||
Unique,
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
|
|
@ -58,14 +55,25 @@ public class Entity
|
|||
public bool IsTargetable { get; internal set; }
|
||||
public bool IsOpened { get; internal set; }
|
||||
public bool IsAvailable { get; internal set; }
|
||||
public MonsterRarity Rarity { 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; }
|
||||
|
||||
// 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 IsHostile => Type == EntityType.Monster;
|
||||
public bool IsNpc => Type == EntityType.Npc;
|
||||
public bool IsPlayer => Type == EntityType.Player;
|
||||
public bool HasComponent(string name) => Components?.Contains(name) == true;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -86,7 +94,7 @@ public class Entity
|
|||
/// <summary>
|
||||
/// Short category string derived from path (e.g. "Monsters", "Effects", "NPC").
|
||||
/// </summary>
|
||||
public string Category
|
||||
public string PathCategory
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
@ -96,36 +104,12 @@ public class Entity
|
|||
}
|
||||
}
|
||||
|
||||
public EntityType Type { get; internal set; }
|
||||
|
||||
internal Entity(nint address, uint id, string? path)
|
||||
{
|
||||
Address = address;
|
||||
Id = id;
|
||||
Path = path;
|
||||
Metadata = ExtractMetadata(path);
|
||||
Type = ClassifyType(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reclassify entity type using component names (called after components are read).
|
||||
/// Component-based classification is more reliable than path-based.
|
||||
/// </summary>
|
||||
internal void ReclassifyFromComponents()
|
||||
{
|
||||
if (Components is null || Components.Count == 0) return;
|
||||
|
||||
// Priority order matching ExileCore's ParseType logic
|
||||
if (Components.Contains("Monster")) { Type = EntityType.Monster; return; }
|
||||
if (Components.Contains("Chest")) { Type = EntityType.Chest; return; }
|
||||
if (Components.Contains("Shrine")) { Type = EntityType.Shrine; return; }
|
||||
if (Components.Contains("Waypoint")) { Type = EntityType.Waypoint; return; }
|
||||
if (Components.Contains("AreaTransition")) { Type = EntityType.AreaTransition; return; }
|
||||
if (Components.Contains("Portal")) { Type = EntityType.Portal; return; }
|
||||
if (Components.Contains("TownPortal")) { Type = EntityType.TownPortal; return; }
|
||||
if (Components.Contains("NPC")) { Type = EntityType.Npc; return; }
|
||||
if (Components.Contains("Player")) { Type = EntityType.Player; return; }
|
||||
// Don't override path-based classification for Effects/Terrain/etc.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -139,60 +123,6 @@ public class Entity
|
|||
return atIndex > 0 ? path[..atIndex] : path;
|
||||
}
|
||||
|
||||
private static EntityType ClassifyType(string? path)
|
||||
{
|
||||
if (path is null) return EntityType.Unknown;
|
||||
|
||||
// Check second path segment: "Metadata/<Category>/..."
|
||||
var firstSlash = path.IndexOf('/');
|
||||
if (firstSlash < 0) return EntityType.Unknown;
|
||||
|
||||
var secondSlash = path.IndexOf('/', firstSlash + 1);
|
||||
var category = secondSlash > 0
|
||||
? path[(firstSlash + 1)..secondSlash]
|
||||
: path[(firstSlash + 1)..];
|
||||
|
||||
switch (category)
|
||||
{
|
||||
case "Characters":
|
||||
return EntityType.Player;
|
||||
|
||||
case "Monsters":
|
||||
// Sub-classify: some "monsters" are actually NPCs or critters
|
||||
if (path.Contains("/Critters/", StringComparison.OrdinalIgnoreCase))
|
||||
return EntityType.Critter;
|
||||
if (path.Contains("/NPC/", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/TownNPC/", StringComparison.OrdinalIgnoreCase))
|
||||
return EntityType.Npc;
|
||||
return EntityType.Monster;
|
||||
|
||||
case "NPC":
|
||||
return EntityType.Npc;
|
||||
|
||||
case "Effects":
|
||||
return EntityType.Effect;
|
||||
|
||||
case "MiscellaneousObjects":
|
||||
if (path.Contains("/Chest", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/Stash", StringComparison.OrdinalIgnoreCase))
|
||||
return EntityType.Chest;
|
||||
if (path.Contains("/Shrine", StringComparison.OrdinalIgnoreCase))
|
||||
return EntityType.Shrine;
|
||||
if (path.Contains("/Portal", StringComparison.OrdinalIgnoreCase))
|
||||
return EntityType.Portal;
|
||||
return EntityType.MiscellaneousObject;
|
||||
|
||||
case "Terrain":
|
||||
return EntityType.Terrain;
|
||||
|
||||
case "Items":
|
||||
return EntityType.WorldItem;
|
||||
|
||||
default:
|
||||
return EntityType.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var pos = HasPosition ? $"({X:F0},{Y:F0})" : "no pos";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue