90 lines
3.6 KiB
C#
90 lines
3.6 KiB
C#
using Nexus.Core;
|
|
|
|
namespace Nexus.Data;
|
|
|
|
/// <summary>
|
|
/// Classifies entities from path + component data into EntityCategory.
|
|
/// Single source of truth for entity classification in the Data layer.
|
|
/// </summary>
|
|
public static class EntityClassifier
|
|
{
|
|
public static EntityCategory Classify(string? path, HashSet<string>? components)
|
|
{
|
|
var baseCategory = ClassifyFromPath(path);
|
|
if (components is { Count: > 0 })
|
|
return ReclassifyFromComponents(baseCategory, components);
|
|
return baseCategory;
|
|
}
|
|
|
|
private static EntityCategory ClassifyFromPath(string? path)
|
|
{
|
|
if (path is null) return EntityCategory.Unknown;
|
|
|
|
var firstSlash = path.IndexOf('/');
|
|
if (firstSlash < 0) return EntityCategory.Unknown;
|
|
|
|
var secondSlash = path.IndexOf('/', firstSlash + 1);
|
|
var segment = secondSlash > 0
|
|
? path[(firstSlash + 1)..secondSlash]
|
|
: path[(firstSlash + 1)..];
|
|
|
|
switch (segment)
|
|
{
|
|
case "Characters":
|
|
return EntityCategory.Player;
|
|
|
|
case "Monsters":
|
|
if (path.Contains("/Critters/", StringComparison.OrdinalIgnoreCase))
|
|
return EntityCategory.Critter;
|
|
if (path.Contains("/NPC/", StringComparison.OrdinalIgnoreCase) ||
|
|
path.Contains("/TownNPC/", StringComparison.OrdinalIgnoreCase))
|
|
return EntityCategory.Npc;
|
|
return EntityCategory.Monster;
|
|
|
|
case "NPC":
|
|
return EntityCategory.Npc;
|
|
|
|
case "Effects":
|
|
return EntityCategory.Effect;
|
|
|
|
case "MiscellaneousObjects":
|
|
if (path.Contains("Doodad", StringComparison.OrdinalIgnoreCase))
|
|
return EntityCategory.Doodad;
|
|
if (path.Contains("/Chest", StringComparison.OrdinalIgnoreCase) ||
|
|
path.Contains("/Stash", StringComparison.OrdinalIgnoreCase))
|
|
return EntityCategory.Chest;
|
|
if (path.Contains("/Shrine", StringComparison.OrdinalIgnoreCase))
|
|
return EntityCategory.Shrine;
|
|
if (path.Contains("/Portal", StringComparison.OrdinalIgnoreCase))
|
|
return EntityCategory.Portal;
|
|
return EntityCategory.MiscObject;
|
|
|
|
case "Terrain":
|
|
if (path.Contains("/Doodad", StringComparison.OrdinalIgnoreCase))
|
|
return EntityCategory.Doodad;
|
|
return EntityCategory.Terrain;
|
|
|
|
case "Items":
|
|
return EntityCategory.WorldItem;
|
|
|
|
default:
|
|
return EntityCategory.Unknown;
|
|
}
|
|
}
|
|
|
|
private static EntityCategory ReclassifyFromComponents(EntityCategory baseCategory, HashSet<string> components)
|
|
{
|
|
// Priority order matching ExileCore's ParseType logic
|
|
if (components.Contains("Monster")) return EntityCategory.Monster;
|
|
if (components.Contains("Chest")) return EntityCategory.Chest;
|
|
if (components.Contains("Shrine")) return EntityCategory.Shrine;
|
|
if (components.Contains("Waypoint")) return EntityCategory.Waypoint;
|
|
if (components.Contains("AreaTransition")) return EntityCategory.AreaTransition;
|
|
if (components.Contains("Portal")) return EntityCategory.Portal;
|
|
if (components.Contains("TownPortal")) return EntityCategory.TownPortal;
|
|
if (components.Contains("NPC")) return EntityCategory.Npc;
|
|
if (components.Contains("Player")) return EntityCategory.Player;
|
|
// Don't override path-based classification for Effects/Terrain/etc.
|
|
return baseCategory;
|
|
}
|
|
}
|