875 lines
35 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @Author: 白哉
* @Description:
* @Date: 2026年03月31日 星期二 20:03:43
* @Modify:
*/
using System;
using System.Collections.Generic;
using Logic.Action;
using Logic.Multilingual;
using RuntimeData;
using UI.HintUI;
using UnityEngine;
// 全局命名空间下的 PlayerInfo 与 TH1_Logic.MatchConfig.PlayerInfo 同名,用别名消歧
using GlobalPlayerInfo = global::PlayerInfo;
namespace TH1_Logic.MatchConfig
{
public enum WikiType
{
BigTypeForce = 0,
BigTypeUnit = 1,
BigTypeHero = 2,
BigTypeBase = 3,
BigTypeAction = 4,
BigTypeTech = 5,
BigTypeSkill = 6,
BigTypeResource = 7,
BigTypeBuilding = 8,
SmallTypeSkillNormal = 100,
SmallTypeSkillSpecial = 101,
SmallTypeSkillUnique = 102,
SmallTypeSkillPositive = 103,
SmallTypeSkillNegative = 104,
SmallTypeSkillBase = 105,
SmallTypeTechGroup = 110,
SmallTypeTechAtom= 111,
SmallTypeActionActive = 120,
SmallTypeActionBuild = 121,
SmallTypeActionTrainUnit = 122,
SmallTypeActionUnitAction = 123,
SmallTypeActionCityLevelUp = 124,
SmallTypeBaseBase = 130,
SmallTypeBaseBuild = 131,
SmallTypeBaseCoin = 132,
SmallTypeBaseUnit = 133,
SmallTypeBaseMoveAttack = 134,
SmallTypeBaseTech = 135,
SmallTypeBaseCulture = 136,
SmallTypeBaseOther = 137,
SmallTypeBaseActionPoint = 138,
SmallTypeBaseActionMechanic = 139,
SmallTypeBaseMoveMechanic = 140,
SmallTypeBaseAttackMechanic = 141,
SmallTypeBaseDamageSettlement = 142,
SmallTypeBaseUnitRule = 143,
SmallTypeHeroOther = 200,
SmallTypeHeroScarlet = 201,
SmallTypeHeroHouraisan = 202,
SmallTypeHeroMoriya = 203,
SmallTypeHeroKomeiji = 204,
SmallTypeHeroHakurei = 205,
}
public enum WikiDescType
{
Normal,
Literary,
TechLock,//Action或者Unit的科技解锁要求
Hint,
UnitInfo,
UnitSkill,
SkillBelongUnit,
PreTech,//科技的前置科技
HeroUpgrade,
PlayerTechPool,//拥有科技池
HasTechAtom,//拥有科技子条目
HasInitTechAtom,//拥有初始科技子条目
BelongTech,//科技子条目属于什么科技?
Level1Tech,//阵营科技池里拥有的1级科技
Level2Tech,//阵营科技池里拥有的2级科技
Level3Tech,//阵营科技池里拥有的3级科技
TechBelongEmpire,//一个tech属于哪些帝国
UnitAction,
}
// 虚拟 WikiItem 的来源,用于 Id 命名空间分段
public enum WikiSource : byte
{
Manual = 0,
Tech = 1,
TechAtom = 2,
Action = 3,
Skill = 4,
Unit = 5,
Player = 6,
}
// uint Id 编码:高 8 bit = WikiSource低 24 bit = 原始 raw id
public static class WikiId
{
public const uint MANUAL_MAX = 0x00FFFFFFu;
public static uint Encode(WikiSource src, uint raw)
=> ((uint)src << 24) | (raw & 0x00FFFFFFu);
public static WikiSource GetSource(uint id) => (WikiSource)(id >> 24);
public static uint GetRaw(uint id) => id & 0x00FFFFFFu;
}
[CreateAssetMenu(fileName = "WikiData", menuName = "TH1/WikiData")]
public class WikiData : ScriptableObject
{
public List<WikiItem> Items = new List<WikiItem>();
// WikiType -> 多语言 Name 的配置表,编辑器内直接填写
public List<WikiTypeInfo> WikiTypeInfoList = new List<WikiTypeInfo>();
// WikiDescType -> 多语言 Name 的配置表,编辑器内直接填写
public List<WikiDescTypeInfo> WikiDescTypeInfoList = new List<WikiDescTypeInfo>();
// 运行时由 Tech/TechAtom/Action/Skill 数据源生成的虚拟 item不参与序列化
[NonSerialized] private List<WikiItem> _virtualItems = new List<WikiItem>();
// 合并视图Items + _virtualItems
[NonSerialized] private List<WikiItem> _allItems = new List<WikiItem>();
[NonSerialized] private Dictionary<uint, WikiItem> _idCache;
[NonSerialized] private string[] _lowerNameCache;
[NonSerialized] private HashSet<WikiType>[] _itemTypeSets;
[NonSerialized] private bool _cacheBuilt;
[NonSerialized] private Dictionary<WikiType, string> _typeNameCache;
[NonSerialized] private Dictionary<WikiDescType, string> _descTypeNameCache;
public IReadOnlyList<WikiItem> AllItems
{
get { EnsureCache(); return _allItems; }
}
public void AddWikiItem(string name)
{
uint id = 1;
if (Items.Count != 0) id = Items[^1].Id + 1;
if (id > WikiId.MANUAL_MAX)
{
Debug.LogError($"[WikiData] 手填 Id {id} 越界,超过保留段 0x{WikiId.MANUAL_MAX:X}");
return;
}
var item = new WikiItem();
item.Id = id;
item.Name = name;
Items.Add(item);
InvalidateCache();
}
public void InvalidateCache()
{
_cacheBuilt = false;
_idCache = null;
_lowerNameCache = null;
_itemTypeSets = null;
_typeNameCache = null;
_descTypeNameCache = null;
}
// 根据 WikiType 查询对应的多语言 Name未配置时返回 string.Empty
public string GetTypeName(WikiType type)
{
if (_typeNameCache == null) BuildTypeNameCache();
return _typeNameCache.TryGetValue(type, out var name) ? name : string.Empty;
}
private void BuildTypeNameCache()
{
int n = WikiTypeInfoList != null ? WikiTypeInfoList.Count : 0;
_typeNameCache = new Dictionary<WikiType, string>(n);
for (int i = 0; i < n; i++)
{
var info = WikiTypeInfoList[i];
if (info == null) continue;
_typeNameCache[info.Type] = info.Name;
}
}
// 根据 WikiDescType 查询对应的多语言 Name未配置时返回 string.Empty
public string GetDescTypeName(WikiDescType type)
{
if (_descTypeNameCache == null) BuildDescTypeNameCache();
return _descTypeNameCache.TryGetValue(type, out var name) ? name : string.Empty;
}
private void BuildDescTypeNameCache()
{
int n = WikiDescTypeInfoList != null ? WikiDescTypeInfoList.Count : 0;
_descTypeNameCache = new Dictionary<WikiDescType, string>(n);
for (int i = 0; i < n; i++)
{
var info = WikiDescTypeInfoList[i];
if (info == null) continue;
_descTypeNameCache[info.Type] = info.Name;
}
}
// 由 Table 构造完成后调用;从 Tech/TechAtom/Action/Skill 数据源生成虚拟 item
public void RebuildVirtualItems()
{
if (_virtualItems == null) _virtualItems = new List<WikiItem>();
_virtualItems.Clear();
BuildFromTech(_virtualItems);
BuildFromTechAtom(_virtualItems);
BuildFromAction(_virtualItems);
BuildFromSkill(_virtualItems);
BuildFromUnit(_virtualItems);
BuildFromPlayer(_virtualItems);
InvalidateCache();
}
// 反向解码:从 wikiId 还原来源类型与原始 raw idUI 跳转用)
public bool TryGetOrigin(uint wikiId, out WikiSource src, out uint raw)
{
src = WikiId.GetSource(wikiId);
raw = WikiId.GetRaw(wikiId);
return src != WikiSource.Manual;
}
private void EnsureCache()
{
if (_cacheBuilt) return;
// 构建合并视图
if (_allItems == null) _allItems = new List<WikiItem>();
_allItems.Clear();
_allItems.AddRange(Items);
if (_virtualItems != null) _allItems.AddRange(_virtualItems);
int n = _allItems.Count;
_idCache = new Dictionary<uint, WikiItem>(n);
_lowerNameCache = new string[n];
_itemTypeSets = new HashSet<WikiType>[n];
for (int i = 0; i < n; i++)
{
var it = _allItems[i];
_idCache[it.Id] = it;
_lowerNameCache[i] = string.IsNullOrEmpty(it.Name) ? string.Empty : it.Name.ToLowerInvariant();
// WikiType 枚举值最大已超过 63无法用 ulong 位掩码C# 位移按 mod 64改用 HashSet 直接判定
var set = new HashSet<WikiType>();
if (it.Types != null)
{
foreach (var t in it.Types) set.Add(t);
}
_itemTypeSets[i] = set;
}
_cacheBuilt = true;
}
// 通过 Id 直接获取 WikiItem不存在返回 null
public WikiItem GetById(uint id)
{
EnsureCache();
_idCache.TryGetValue(id, out var item);
return item;
}
// 通过 SkillType 反查对应 Wiki 条目的 Id找不到返回 0。
// Skill 条目的 wikiId 用递增 raw 编码(见 BuildFromSkill无法直接用 SkillType 编码,
// 需要遍历查找。技能数量有限O(n) 可接受。
public uint FindWikiIdBySkillType(SkillType skillType)
{
if (skillType == SkillType.NONE) return 0;
EnsureCache();
for (int i = 0; i < _allItems.Count; i++)
{
var it = _allItems[i];
if (it == null) continue;
if (WikiId.GetSource(it.Id) != WikiSource.Skill) continue;
if (it.SkillType == skillType) return it.Id;
}
return 0;
}
// 通过 CommonActionId 反查对应 Wiki 条目的 Id找不到含未通过白名单/未生成)返回 0。
// Action 条目的 wikiId 是 ActionList 的 index 编码(见 BuildFromAction同样 O(n) 遍历。
public uint FindWikiIdByActionId(CommonActionId actionId)
{
if (actionId == null) return 0;
EnsureCache();
for (int i = 0; i < _allItems.Count; i++)
{
var it = _allItems[i];
if (it == null) continue;
if (WikiId.GetSource(it.Id) != WikiSource.Action) continue;
if (it.ActionId != null && it.ActionId.Equals(actionId)) return it.Id;
}
return 0;
}
// 通过 Name 多语言 key 的包含关系筛选大小写不敏感keyword 为空时返回空
// 调用方传入 outResult 复用 List 以避免 GC
public void FindByName(string keyword, List<WikiItem> outResult)
{
if (outResult == null) return;
outResult.Clear();
if (string.IsNullOrEmpty(keyword)) return;
EnsureCache();
string lower = keyword.ToLowerInvariant();
for (int i = 0; i < _allItems.Count; i++)
{
if (_lowerNameCache[i].IndexOf(lower, StringComparison.Ordinal) >= 0)
outResult.Add(_allItems[i]);
}
}
// 全包含筛选:返回同时拥有 required 中所有 WikiType 的 itemrequired 为空返回空
// 调用方传入 outResult 复用 List 以避免 GC
public void FindByTypes(List<WikiType> required, List<WikiItem> outResult)
{
if (outResult == null) return;
outResult.Clear();
if (required == null || required.Count == 0) return;
EnsureCache();
for (int i = 0; i < _allItems.Count; i++)
{
var set = _itemTypeSets[i];
if (set == null) continue;
bool match = true;
for (int j = 0; j < required.Count; j++)
{
if (!set.Contains(required[j])) { match = false; break; }
}
if (match) outResult.Add(_allItems[i]);
}
}
//-------------------- 虚拟 WikiItem 构建器 --------------------
private static void BuildFromTech(List<WikiItem> outList)
{
var assets = Table.Instance?.TechDataAssets;
if (assets == null) return;
foreach (var info in assets.TechList)
{
if (info == null) continue;
if (string.IsNullOrEmpty(info.TechName)) continue; // Name 为空 → 跳过
// TechGroup 默认带一个 Normal 描述;如果有前置则再追加 PreTech
// 实际前置文本由 WikiDescGroup 的 PreTech 分支按 TechType 反查生成
var techDescs = new List<DescItem>
{
new DescItem { DescType = WikiDescType.Normal, Desc = info.Description }
};
if (info.FatherTechList != null && info.FatherTechList.Count > 0)
techDescs.Add(new DescItem { DescType = WikiDescType.PreTech });
if (info.TechAtomList != null && info.TechAtomList.Count > 0)
techDescs.Add(new DescItem { DescType = WikiDescType.HasTechAtom });
if (HasAnyEmpireWithTech(info.TechType))
techDescs.Add(new DescItem { DescType = WikiDescType.TechBelongEmpire });
var item = new WikiItem
{
Id = WikiId.Encode(WikiSource.Tech, (uint)info.TechType),
Name = info.TechName,
IsAutoGenerated = true,
Types = WikiTagRules.ForTech(info),
// 按需求 TechList 一律不挂 Icon
HasIcon = false,
DescItems = techDescs,
TechType = info.TechType,
};
outList.Add(item);
}
}
private static void BuildFromTechAtom(List<WikiItem> outList)
{
var assets = Table.Instance?.TechDataAssets;
if (assets == null) return;
foreach (var info in assets.TechAtomList)
{
if (info == null) continue;
if (string.IsNullOrEmpty(info.TechAtomName)) continue;
// TechAtom 的 GetIcon 接受 civ/force/gridSpType图鉴里没有玩家上下文传 Common+无 GridSp 取默认图
Sprite icon = null;
try { icon = info.GetIcon(CivEnum.Common, ForceEnum.Common); }
catch { icon = null; }
var atomDescs = new List<DescItem>
{
new DescItem { DescType = WikiDescType.Normal, Desc = info.Desc }
};
// 仅当存在 Tech 把这个 atom 列在 TechAtomList 里时,才追加 BelongTech 段
// 具体的 Tech 名称列表由 WikiDescGroup 的 BelongTech 分支运行时拼。
if (HasOwningTech(info.TechAtom))
atomDescs.Add(new DescItem { DescType = WikiDescType.BelongTech });
var item = new WikiItem
{
Id = WikiId.Encode(WikiSource.TechAtom, (uint)info.TechAtom),
Name = info.TechAtomName,
IsAutoGenerated = true,
Types = WikiTagRules.ForTechAtom(info),
// TechAtom 复用其原本的 IconViewSizeType
Icon = icon,
IconSizeType = info.iconViewSizeType,
HasIcon = icon != null,
DescItems = atomDescs,
TechAtomKey = info.TechAtom,
};
outList.Add(item);
}
}
private static void BuildFromAction(List<WikiItem> outList)
{
var assets = Table.Instance?.ActionDataAssets;
if (assets == null) return;
// CommonActionId 是 class无法稳定编码 → 用 ActionList 的 index 作为 raw id
var list = assets.ActionList;
for (int i = 0; i < list.Count; i++)
{
var info = list[i];
if (info == null || info.ActionId == null) continue;
if (string.IsNullOrEmpty(info.ActionName)) continue;
// 只接收白名单内的 ActionType其余忽略
if (!WikiTagRules.IsActionWhitelisted(info.ActionId.ActionType)) continue;
var descs = new List<DescItem>
{
new DescItem { DescType = WikiDescType.Normal, Desc = info.Desc }
};
// 仅当存在 TechAtom 解锁此 action 时,才追加 TechLock 段(具体名称在 WikiDescGroup 运行时拼)
if (HasUnlockingTechAtom(info.ActionId))
descs.Add(new DescItem { DescType = WikiDescType.TechLock });
Sprite icon = null;
try { icon = info.GetIcon(CivEnum.Common, ForceEnum.Common, GridSpType.None); }
catch { icon = null; }
var item = new WikiItem
{
Id = WikiId.Encode(WikiSource.Action, (uint)i),
Name = info.ActionName,
IsAutoGenerated = true,
Types = WikiTagRules.ForAction(info),
// Action 复用其原本的 IconViewSizeType
Icon = icon,
IconSizeType = info.IconViewSizeType,
HasIcon = icon != null,
DescItems = descs,
ActionId = info.ActionId,
};
outList.Add(item);
}
}
// 是否存在某个 TechInfo.TechAtomList 包含此 atom即此 atom 至少归属一个 Tech
private static bool HasOwningTech(TechAtom atom)
{
var techAssets = Table.Instance?.TechDataAssets;
if (techAssets == null) return false;
var techList = techAssets.TechList;
if (techList == null) return false;
for (int i = 0; i < techList.Count; i++)
{
var t = techList[i];
if (t == null || t.TechAtomList == null) continue;
if (t.TechAtomList.Contains(atom)) return true;
}
return false;
}
// 是否存在 TechAtom 通过 EnableAction + TechActions 解锁此 actionId
private static bool HasUnlockingTechAtom(CommonActionId actionId)
{
if (actionId == null) return false;
var techAssets = Table.Instance?.TechDataAssets;
if (techAssets == null) return false;
var atomList = techAssets.TechAtomList;
if (atomList == null) return false;
for (int i = 0; i < atomList.Count; i++)
{
var atom = atomList[i];
if (atom == null) continue;
if (!atom.EnableAction) continue;
if (atom.TechActions == null) continue;
for (int j = 0; j < atom.TechActions.Count; j++)
{
if (atom.TechActions[j] != null && atom.TechActions[j].Equals(actionId))
return true;
}
}
return false;
}
private static void BuildFromSkill(List<WikiItem> outList)
{
var assets = Table.Instance?.SkillDataAssets;
if (assets == null) return;
// 每个 SkillInfo 只出一条 WikiItem。
// SkillShowList 里的 pack 是给特定 UnitFullType 的特化图标/描述,
// 不是独立技能;图鉴展示一律使用 info.SkillIcon / SkillName / SkillDesc。
uint raw = 0;
foreach (var info in assets.SkillInfoList)
{
if (info == null) continue;
if (info.NotShow) continue;
if (string.IsNullOrEmpty(info.SkillName)) continue;
var item = new WikiItem
{
Id = WikiId.Encode(WikiSource.Skill, raw++),
Name = info.SkillName,
IsAutoGenerated = true,
Types = WikiTagRules.ForSkill(info),
Icon = info.SkillIcon,
IconSizeType = IconViewSizeType._256x256,
HasIcon = info.SkillIcon != null,
DescItems = new List<DescItem>
{
new DescItem { DescType = WikiDescType.Normal, Desc = info.SkillDesc }
},
SkillType = info.SkillType,
};
outList.Add(item);
}
}
private static void BuildFromUnit(List<WikiItem> outList)
{
var assets = Table.Instance?.UnitTypeDataAssets;
if (assets == null) return;
var list = assets.UnitTypeList;
if (list == null) return;
// UnitTypeInfo 没有稳定 id沿用 ActionList 的 index 编码方式
for (int i = 0; i < list.Count; i++)
{
var info = list[i];
if (info == null) continue;
if (string.IsNullOrEmpty(info.Name)) continue;
// 白名单GiantType=None 走 BigTypeUnit取所有 Level
// GiantType!=None 走 BigTypeHero取 Level=1~4每级一条其余忽略
if (!WikiTagRules.IsUnitWhitelisted(info)) continue;
if (!ContentGate.CanShowHeroIntroduction(info)) continue;
// HeroGiantType != None走 HeroDataAssets 取 HeroAvatar普通 Unit 沿用 info.Sprite
Sprite unitIcon = info.Sprite;
if (info.GiantType != GiantType.None)
{
var heroAssets = Table.Instance?.HeroDataAssets;
if (heroAssets != null && heroAssets.GetHeroInfo(info.GiantType, out var heroInfo) && heroInfo != null)
{
if (heroInfo.HeroAvatar != null) unitIcon = heroInfo.HeroAvatar;
}
}
// 特殊模块UnitInfo6 项数值) + UnitSkill技能列表仅有技能时
// + UnitAction可用 Action仅有 EnableActions 时)+ HeroUpgrade仅 Hero
var descItems = new List<DescItem>
{
new DescItem { DescType = WikiDescType.Normal, Desc = info.Desc },
new DescItem { DescType = WikiDescType.UnitInfo },
};
if (info.Skills != null && info.Skills.Count > 0)
descItems.Add(new DescItem { DescType = WikiDescType.UnitSkill });
if (info.EnableActions != null && info.EnableActions.Count > 0)
descItems.Add(new DescItem { DescType = WikiDescType.UnitAction });
if (info.GiantType != GiantType.None)
descItems.Add(new DescItem { DescType = WikiDescType.HeroUpgrade });
var item = new WikiItem
{
Id = WikiId.Encode(WikiSource.Unit, (uint)i),
Name = info.Name,
IsAutoGenerated = true,
Types = WikiTagRules.ForUnit(info),
// Unit 全部按 IconViewSizeType.Unit 处理
Icon = unitIcon,
IconSizeType = IconViewSizeType.Unit,
HasIcon = unitIcon != null,
DescItems = descItems,
// 运行时反查字段
UnitFullType = new UnitFullType(info.UnitType, info.GiantType, info.UnitLevel),
HeroGiantType = info.GiantType,
};
outList.Add(item);
}
}
// PlayerDataList 中是否存在 TechPool 包含指定 TechType 的 Player
// 用于决定 Tech 条目是否生成 TechBelongEmpire 段
private static bool HasAnyEmpireWithTech(global::TechType techType)
{
var assets = Table.Instance?.PlayerDataAssets;
if (assets?.PlayerDataList == null) return false;
var list = assets.PlayerDataList;
for (int i = 0; i < list.Count; i++)
{
var info = list[i];
if (info == null) continue;
if (string.IsNullOrEmpty(info.ForceName)) continue;
if (info.TechPool == null) continue;
if (info.TechPool.Contains(techType)) return true;
}
return false;
}
// PlayerInfo.TechPool 中是否存在指定 CostLevel 的 Tech用于决定是否生成 LevelXTech 段
private static bool HasTechPoolAtCostLevel(GlobalPlayerInfo info, int costLevel)
{
if (info?.TechPool == null) return false;
var techAssets = Table.Instance?.TechDataAssets;
if (techAssets == null) return false;
for (int i = 0; i < info.TechPool.Count; i++)
{
if (!techAssets.GetTechInfo(info.TechPool[i], out var techInfo) || techInfo == null) continue;
if (techInfo.CostLevel == costLevel) return true;
}
return false;
}
private static void BuildFromPlayer(List<WikiItem> outList)
{
var assets = Table.Instance?.PlayerDataAssets;
if (assets == null) return;
var list = assets.PlayerDataList;
if (list == null) return;
// PlayerInfo 没有稳定唯一 id同 ForceId 可对应多个 Civ沿用 list index 作为 raw id
for (int i = 0; i < list.Count; i++)
{
var info = list[i];
if (info == null) continue;
if (string.IsNullOrEmpty(info.ForceName)) continue;
if (!WikiTagRules.IsPlayerWhitelisted(info)) continue;
var playerDescs = new List<DescItem>
{
new DescItem { DescType = WikiDescType.Normal, Desc = info.EmpireDesc }
};
// 把 TechPool 按 CostLevel 拆 3 段,每段一个 CommonGridItemDescGroup
if (HasTechPoolAtCostLevel(info, 1))
playerDescs.Add(new DescItem { DescType = WikiDescType.Level1Tech });
if (HasTechPoolAtCostLevel(info, 2))
playerDescs.Add(new DescItem { DescType = WikiDescType.Level2Tech });
if (HasTechPoolAtCostLevel(info, 3))
playerDescs.Add(new DescItem { DescType = WikiDescType.Level3Tech });
if (info.TechAtomList != null && info.TechAtomList.Count > 0)
playerDescs.Add(new DescItem { DescType = WikiDescType.HasInitTechAtom });
var item = new WikiItem
{
Id = WikiId.Encode(WikiSource.Player, (uint)i),
Name = info.ForceName,
IsAutoGenerated = true,
Types = WikiTagRules.ForPlayer(info),
// Force 使用 PlayerInfo.LeaderAvatar 作为图标
Icon = info.LeaderAvatar,
HasIcon = info.LeaderAvatar != null,
DescItems = playerDescs,
ForceId = info.ForceId,
};
outList.Add(item);
}
}
// 根据数据源 Info 字段生成 WikiType 标签的规则。当前 WikiType 仅占位,规则待补。
private static class WikiTagRules
{
public static List<WikiType> ForTech(TechInfo info)
=> new List<WikiType> { WikiType.BigTypeTech, WikiType.SmallTypeTechGroup };
public static List<WikiType> ForTechAtom(TechAtomInfo info)
=> new List<WikiType> { WikiType.BigTypeTech, WikiType.SmallTypeTechAtom };
// GiantType=None → BigTypeUnit
// GiantType!=None & Level∈[1,4] → BigTypeHero + 按 GiantEmpire 分类的 SmallTypeHero*
public static bool IsUnitWhitelisted(UnitTypeInfo info)
{
if (info == null) return false;
if (info.GiantType == GiantType.None) return true;
return info.UnitLevel >= 1 && info.UnitLevel <= 4;
}
public static List<WikiType> ForUnit(UnitTypeInfo info)
{
var list = new List<WikiType>();
if (info == null) return list;
if (info.GiantType == GiantType.None)
{
list.Add(WikiType.BigTypeUnit);
}
else
{
list.Add(WikiType.BigTypeHero);
list.Add(MapGiantEmpireToHeroSmall(info.GiantEmpire.Force));
}
return list;
}
// GiantEmpire.Force → SmallTypeHero*
// Remilia → Scarlet, Kaguya → Houraisan, Kanako → Moriya, Satori → Komeiji, Reimu → Hakurei, 其余 → Other
private static WikiType MapGiantEmpireToHeroSmall(ForceEnum force)
{
switch (force)
{
case ForceEnum.Remilia: return WikiType.SmallTypeHeroScarlet;
case ForceEnum.Kaguya: return WikiType.SmallTypeHeroHouraisan;
case ForceEnum.Kanako: return WikiType.SmallTypeHeroMoriya;
case ForceEnum.Satori: return WikiType.SmallTypeHeroKomeiji;
case ForceEnum.Reimu: return WikiType.SmallTypeHeroHakurei;
default: return WikiType.SmallTypeHeroOther;
}
}
// PlayerInfo 按当前可选阵营集合纳入,避免 Wiki 阵营列表滞后于选阵营入口。
// 全局 PlayerInfoPlayerDataAssets.cs和 TH1_Logic.MatchConfig.PlayerInfo 同名,
// 走 using 别名 GlobalPlayerInfo 显式指向前者
public static bool IsPlayerWhitelisted(GlobalPlayerInfo info)
=> info != null && ContentGate.CanSelectEmpire(info.CivId, info.ForceId);
public static List<WikiType> ForPlayer(GlobalPlayerInfo info)
=> new List<WikiType> { WikiType.BigTypeForce };
public static List<WikiType> ForAction(ActionInfo info)
{
var list = new List<WikiType>();
if (info == null || info.ActionId == null) return list;
var small = MapActionTypeToSmall(info.ActionId.ActionType);
if (small.HasValue)
{
// BigTypeAction + 对应 SmallTypeAction*;这样 SelectGroups 里把
// BigTypeAction → [SmallTypeActionBuild,...] 配好后才能筛得出来
list.Add(WikiType.BigTypeAction);
list.Add(small.Value);
}
return list;
}
// 白名单:只有这些 ActionType 才会被纳入虚拟 WikiItem
public static bool IsActionWhitelisted(CommonActionType type)
=> MapActionTypeToSmall(type).HasValue;
private static WikiType? MapActionTypeToSmall(CommonActionType type)
{
switch (type)
{
// Build 类Gain / Build / BuildWonder / GridMisc 全部归到 SmallTypeActionBuild
case CommonActionType.Gain:
case CommonActionType.Build:
case CommonActionType.BuildWonder:
case CommonActionType.GridMisc:
return WikiType.SmallTypeActionBuild;
case CommonActionType.UnitAction:
return WikiType.SmallTypeActionUnitAction;
case CommonActionType.CityLevelUpAction:
return WikiType.SmallTypeActionCityLevelUp;
case CommonActionType.TrainUnit:
return WikiType.SmallTypeActionTrainUnit;
default:
return null;
}
}
public static List<WikiType> ForSkill(SkillInfo info)
{
var list = new List<WikiType> { WikiType.BigTypeSkill };
if (info == null) return list;
switch (info.SkillViewType)
{
case SkillViewType.Normal: list.Add(WikiType.SmallTypeSkillNormal); break;
case SkillViewType.Special: list.Add(WikiType.SmallTypeSkillSpecial); break;
case SkillViewType.Unique: list.Add(WikiType.SmallTypeSkillUnique); break;
case SkillViewType.Positive: list.Add(WikiType.SmallTypeSkillPositive); break;
case SkillViewType.Negative: list.Add(WikiType.SmallTypeSkillNegative); break;
// SkillViewType.Max 是哨兵值,忽略
}
return list;
}
}
#if UNITY_EDITOR
private void OnValidate()
{
InvalidateCache();
}
#endif
}
[Serializable]
public class WikiTypeInfo
{
public WikiType Type;
[MultilingualField]
public string Name;
}
[Serializable]
public class WikiDescTypeInfo
{
public WikiDescType Type;
[MultilingualField]
public string Name;
}
[Serializable]
public class WikiItem
{
public uint Id;
[MultilingualField]
public string Name;
public List<WikiType> Types = new List<WikiType>();
public List<DescItem> DescItems = new List<DescItem>();
// Icon + 走 IconSizingUtility 的尺寸类型HasIcon=false 时表示该条目不显示图标
public Sprite Icon;
public IconViewSizeType IconSizeType;
public bool HasIcon;
[NonSerialized] public bool IsAutoGenerated;
// 运行时由 BuildFromXxx 写入,给 WikiDescGroup 的特殊模块当数据源。
// 不参与序列化Manual 条目不会带这些。
[NonSerialized] public UnitFullType UnitFullType;
[NonSerialized] public SkillType SkillType;
[NonSerialized] public GiantType HeroGiantType;
[NonSerialized] public TechType TechType;
[NonSerialized] public TechAtom TechAtomKey;
[NonSerialized] public CommonActionId ActionId;
[NonSerialized] public uint ForceId;
// 去重:保留每个枚举值的首次出现,移除后续重复项
public void Refresh()
{
var seenTypes = new HashSet<WikiType>();
Types.RemoveAll(t => !seenTypes.Add(t));
var seenDescTypes = new HashSet<WikiDescType>();
DescItems.RemoveAll(d => !seenDescTypes.Add(d.DescType));
}
}
[Serializable]
public class DescItem
{
public WikiDescType DescType;
[MultilingualField]
public string Desc;
public bool UseHint;
public HintDataProvider HintProvider;
public string GetDesc()
{
if (UseHint) return HintProvider.GetHintText();
return Desc;
}
}
}