feat(webui): unify npc/monster spawner with categorized catalog table, realtime search, and instant Add button

This commit is contained in:
CAtlas0784 committed 2026-09-17 22:58:11 +07:00
1 parent b5fc15e4ec
commit 29b2f480d5
4 files changed
+318 -64

No files matched your search

@@ -12,7 +12,9 @@ internal sealed record NpcCatalogEntry4229938(
uint BehaviorActionConfigId,
uint ActionGroupId,
uint AiSettingId,
uint InteractSettingId);
uint InteractSettingId,
uint Camp = 0,
string Category = "citizen");
internal static class NpcCatalog4229938
{
@@ -21,6 +23,43 @@ internal static class NpcCatalog4229938
internal static bool TryGet(uint id, out NpcCatalogEntry4229938 entry)
=> Cache.Value.TryGetValue(id, out entry!);
internal static IReadOnlyCollection<NpcCatalogEntry4229938> All => Cache.Value.Values;
internal static List<NpcCatalogEntry4229938> Search(string? query, string? category, int limit = 150)
{
var q = query?.Trim();
var cat = category?.Trim().ToLowerInvariant();
var hasQuery = !string.IsNullOrWhiteSpace(q);
var hasCat = !string.IsNullOrWhiteSpace(cat) && cat != "all";
uint queryId = 0;
if (hasQuery) uint.TryParse(q, out queryId);
var list = new List<NpcCatalogEntry4229938>();
foreach (var entry in Cache.Value.Values)
{
if (hasCat && !string.Equals(entry.Category, cat, StringComparison.OrdinalIgnoreCase))
continue;
if (hasQuery)
{
if (queryId != 0 && entry.Id == queryId)
{
list.Add(entry);
if (list.Count >= limit) break;
continue;
}
if (!entry.Name.Contains(q!, StringComparison.OrdinalIgnoreCase) &&
!entry.Id.ToString().Contains(q!))
continue;
}
list.Add(entry);
if (list.Count >= limit) break;
}
return list;
}
private static Dictionary<uint, NpcCatalogEntry4229938> Load()
{
var root = PrivateServerConfigStore.ResolveProjectPath(PrivateServerConfigStore.Current.Paths.ClientConfigs);
@@ -59,9 +98,27 @@ internal static class NpcCatalog4229938
var animSetTag = UInt32(row, "AnimStereotype");
if (animSetTag == 0)
animSetTag = UInt32(row, "BattleAnimType");
var camp = UInt32(row, "Camp");
var enemyClass = UInt32(row, "EnemyClassType");
var isAnimal = (row.TryGetProperty("AnimalGamePlay", out var ag) && ag.ValueKind == JsonValueKind.True) ||
name.Contains("鸭") || name.Contains("电台") || name.Contains("猫") || name.Contains("鸽") || name.Contains("鸟") || name.Contains("犬");
string category;
if (isAnimal || camp == 11)
category = "animal";
else if (camp is 0 or 26 || enemyClass > 0 || name.Contains("帮") || name.Contains("怪") || name.Contains("Boss") || name.Contains("哨兵") || name.Contains("斗士") || name.Contains("无赖") || name.Contains("狂飙") || name.Contains("狂战士") || name.Contains("史莱姆"))
category = "monster";
else if (camp is 16 or 17 or 18 or 19 or 24 || name.Contains("警") || name.Contains("守卫") || name.Contains("保镖") || name.Contains("巡卫") || name.Contains("安保"))
category = "police";
else if (camp == 1 || name.Contains("Seymour") || name.Contains("Taffy") || name.Contains("Bansy") || name.Contains("Garm") || name.Contains("Rin") || name.Contains("HotDog"))
category = "ally";
else
category = "citizen";
result[id] = new(id, name, model, persona, animSetTag,
UInt32(row, "Action"), UInt32(row, "ActionGroupId"),
UInt32(row, "AI"), UInt32(row, "InteractSetting"));
UInt32(row, "AI"), UInt32(row, "InteractSetting"),
camp, category);
}
return result;
}