diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index 9f0feedc..86145388 100644 Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ diff --git a/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml b/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml index 79489a62..ea7a7900 100644 --- a/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml +++ b/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml @@ -69,6 +69,18 @@ false + + WULA_TransformPawn + WulaFallenEmpire.JobDriver_TransformPawn + 变更装备中。 + true + true + true + false + Always + false + + WULA_EnterMech diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml index cb08025e..5776549f 100644 --- a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml +++ b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml @@ -265,7 +265,7 @@ WULA_Cat_Bunker_Cleanzone - 清理出一块场地并准备好资源,使得乌拉帝国可以向此处投放建筑。\n\n乌拉猫猫地堡是集生产和防御为一体的坚实建筑,3只乌拉猫猫自律机械体会和地堡一起空降。地堡可供乌拉猫猫更换工作类型,并允许乌拉猫猫进驻使用内部武器击败来犯敌军。 + 清理出一块场地并准备好资源,使得乌拉帝国可以向此处投放建筑。\n\n乌拉猫猫地堡是集生产和防御为一体的坚实建筑,3只乌拉猫猫自律机械体会和地堡一起空降。地堡可供乌拉猫猫更换工作类型,并拥有内部武器用于击败来犯敌军。 Wula/Building/WULA_Cat_Bunker_south MinifiedThing Normal @@ -385,9 +385,9 @@ WULA_Cat_Bunker - 这是一个从乌拉帝国母舰上空投下来的地堡,它拥有一些预制件,可以供乌拉猫猫更换其工作类型。此外,当敌人袭击时,可将乌拉猫猫召回至地堡内,它们将在地堡里对外射击以击退来犯者。 - WulaFallenEmpire.Building_MechanoidRecycler + 这是一个从乌拉帝国母舰上空投下来的地堡,它拥有一些预制件,可以供乌拉猫猫更换其工作类型。此外,当敌人袭击时,地堡的自动机枪会对外射击以击退来犯者。 Normal + Building_TurretGun WULA_Cat_Bunker_TurretGun Mech_WULA_Cat @@ -407,10 +407,8 @@ Building - Impassable - true - 1 - false + PassThroughOnly + 0.9 false false false @@ -444,101 +442,28 @@ true false -
  • - 6 - - - -
  • Mech_WULA_Cat
  • -
  • Mech_WULA_Cat_Constructor
  • -
  • Mech_WULA_Cat_Assault
  • - - WULA_RecycleMechanoid - 5 - - - -
  • Mech_WULA_Cat
  • -
  • Mech_WULA_Cat_Constructor
  • -
  • Mech_WULA_Cat_Assault
  • -
    - - - -
  • - Mech_WULA_Cat - 1 -
  • -
  • - Mech_WULA_Cat_Constructor - 1 -
  • -
  • - Mech_WULA_Cat_Assault - 1 -
  • -
    - - +
  • + Mech_WULA_Cat_Assault + 3 + true + false + true
  • +
  • + 500 + + 60 +
  • +
  • true
  • - -
  • - 1 - WULA_Cat_Bunker_TurretGun - 0 - true - 1 - true -
  • -
  • - 2 - WULA_Cat_Bunker_TurretGun - 0 - true - 2 - true -
  • -
  • - 3 - WULA_Cat_Bunker_TurretGun - 0 - true - 3 - true -
  • -
  • - 4 - WULA_Cat_Bunker_TurretGun - 0 - true - 4 - true -
  • -
  • - 5 - WULA_Cat_Bunker_TurretGun - 0 - true - 5 - true -
  • -
  • - 6 - WULA_Cat_Bunker_TurretGun - 0 - true - 6 - true -
  • WULA_Cat_Bunker_TurretGun - 由进入地堡的乌拉猫猫操纵的炮塔——说那么多干什么,扣扳机就完了,殖民地会报销弹药的。 + 由驻守地堡的乌拉猫猫操纵的炮塔——说那么多干什么,扣扳机就完了,殖民地会报销弹药的。 Wula/Weapon/WULA_Weapon_Empty Graphic_Single @@ -558,7 +483,7 @@ Bullet_WULA_RW_Base_AR 28 5 - 12 + 24 Shot_AssaultRifle GunTail_Medium 9 diff --git a/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml b/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml index 610a2796..7ac3aef8 100644 --- a/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml +++ b/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml @@ -951,6 +951,16 @@
  • Firefighter
  • + +
  • + true + +
  • Mech_WULA_Cat
  • +
  • Mech_WULA_Cat_Constructor
  • +
  • Mech_WULA_Cat_Assault
  • + + +
    Mech_WULA_Cat_Constructor @@ -975,6 +985,16 @@ 2 + +
  • + true + +
  • Mech_WULA_Cat
  • +
  • Mech_WULA_Cat_Constructor
  • +
  • Mech_WULA_Cat_Assault
  • + + +
    Mech_WULA_Cat_Assault @@ -1003,6 +1023,16 @@ 4 + +
  • + true + +
  • Mech_WULA_Cat
  • +
  • Mech_WULA_Cat_Constructor
  • +
  • Mech_WULA_Cat_Assault
  • + + +
    Mech_WULA_Cat_Cloak_Sniper diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_BuildToPawn/CompBuildToPawn.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_BuildToPawn/CompBuildToPawn.cs new file mode 100644 index 00000000..008e8fa1 --- /dev/null +++ b/Source/WulaFallenEmpire/BuildingComp/WULA_BuildToPawn/CompBuildToPawn.cs @@ -0,0 +1,62 @@ +// CompBuildToPawn_SimpleDelay.cs +using RimWorld; +using System.Collections.Generic; +using Verse; + +namespace WulaFallenEmpire +{ + public class CompBuildToPawn : ThingComp + { + public CompProperties_BuildToPawn Props => (CompProperties_BuildToPawn)props; + + private bool shouldSpawn = false; + private int delayCounter = 0; + + public override void PostSpawnSetup(bool respawningAfterLoad) + { + base.PostSpawnSetup(respawningAfterLoad); + + // 跳过存档加载和蓝图/框架 + if (respawningAfterLoad || parent.def.IsBlueprint || parent.def.IsFrame) + return; + + // 延迟一帧 + shouldSpawn = true; + delayCounter = 0; + } + + public override void CompTick() + { + base.CompTick(); + + if (shouldSpawn && delayCounter >= 1) // 延迟一帧后执行 + { + if (Props.pawnKindDef != null && parent != null && !parent.Destroyed && parent.Map != null) + { + // 生成Pawn + for (int i = 0; i < Props.spawnCount; i++) + { + Pawn pawn = PawnGenerator.GeneratePawn(Props.pawnKindDef); + if (Props.inheritFaction) + pawn.SetFaction(parent.Faction, null); + + GenSpawn.Spawn(pawn, parent.Position, parent.Map, WipeMode.Vanish); + + if (Props.initDrafted && pawn.drafter!=null) + pawn.drafter.Drafted = true; + } + + if (Props.destroyBuilding) + // 摧毁建筑 + parent.Destroy(); + } + + shouldSpawn = false; + } + else if (shouldSpawn) + { + delayCounter++; + } + } + } +} diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_BuildToPawn/CompProperties_BuildToPawn.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_BuildToPawn/CompProperties_BuildToPawn.cs new file mode 100644 index 00000000..5378cc98 --- /dev/null +++ b/Source/WulaFallenEmpire/BuildingComp/WULA_BuildToPawn/CompProperties_BuildToPawn.cs @@ -0,0 +1,20 @@ +// CompProperties_BuildToPawn.cs +using RimWorld; +using Verse; + +namespace WulaFallenEmpire +{ + public class CompProperties_BuildToPawn : CompProperties + { + public PawnKindDef pawnKindDef; // 要生成的Pawn种类 + public int spawnCount = 1; // 生成数量,默认为1 + public bool inheritFaction = true; // 是否继承建筑的派系 + public bool destroyBuilding = false; // 是否销毁建筑 + public bool initDrafted = false; // 是否生成时直接设为征召 + + public CompProperties_BuildToPawn() + { + this.compClass = typeof(CompBuildToPawn); + } + } +} diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_Gather/CompGather.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_Gather/CompGather.cs new file mode 100644 index 00000000..1355c57d --- /dev/null +++ b/Source/WulaFallenEmpire/BuildingComp/WULA_Gather/CompGather.cs @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using Verse; +using Verse.AI; +using RimWorld; +using UnityEngine; + +namespace WulaFallenEmpire +{ + // 召集组件属性 + public class CompProperties_Gather : CompProperties + { + public float gatherRange = 100f; // 召集范围(单元格) + public SoundDef gatherSound; // 召集音效 + public int cooldownTicks = 1200; // 召集冷却时间(tick,默认20秒) + + // 转化相关 + public bool canTransformPawns = true; // 是否允许转化Pawn + public IntVec3 spawnOffset = IntVec3.Zero; // 新Pawn生成位置偏移 + + public CompProperties_Gather() + { + compClass = typeof(Comp_Gather); + } + } + + // 召集组件实现 + public class Comp_Gather : ThingComp + { + private int lastGatherTick = -1000; // 上一次召集的tick + private bool gatheringActive = false; // 召集是否正在进行 + private int gatherDurationLeft = 0; // 召集持续时间剩余 + + // Gizmo缓存 + private Command_Action cachedGizmo; + + // 属性 + public CompProperties_Gather Props => (CompProperties_Gather)props; + + public bool CanGatherNow + { + get + { + if (!parent.Spawned || parent.Destroyed) + return false; + + // 检查冷却时间 + int currentTick = Find.TickManager.TicksGame; + if (currentTick - lastGatherTick < Props.cooldownTicks) + return false; + + // 检查建筑是否可用 + if (parent is Building building) + { + if (building.IsBrokenDown() || building.IsForbidden(Faction.OfPlayer)) + return false; + } + + return true; + } + } + + public bool IsGatheringActive => gatheringActive; + + // 公开方法:检查是否可以进行转化 + public bool CanTransformPawn(Pawn pawn) + { + if (!Props.canTransformPawns) + return false; + + if (!parent.Spawned || parent.Destroyed) + return false; + + // 检查Pawn是否拥有AutonomousCat组件 + if (pawn.TryGetComp() == null) + return false; + + // 检查建筑是否可用 + if (parent is Building building) + { + if (building.IsBrokenDown() || building.IsForbidden(Faction.OfPlayer)) + return false; + } + + return true; + } + + // 获取生成位置 + public IntVec3 GetSpawnPosition() + { + if (parent.Spawned) + { + return parent.Position + Props.spawnOffset; + } + return parent.Position; + } + + // 公开方法:执行转化 + public void TransformPawn(Pawn originalPawn, PawnKindDef targetPawnKind) + { + if (!CanTransformPawn(originalPawn)) + return; + + try + { + // 记录原始信息 + Map map = originalPawn.Map; + IntVec3 position = GetSpawnPosition(); + Faction faction = originalPawn.Faction; + + // 检查位置是否可用 + if (!position.Walkable(map) || position.Fogged(map)) + { + // 尝试在周围寻找可用位置 + if (!CellFinder.TryFindRandomCellNear(position, map, 3, + (c) => c.Walkable(map) && !c.Fogged(map), out position)) + { + Messages.Message("Wula_NoSpawnSpace".Translate(), MessageTypeDefOf.RejectInput); + return; + } + } + + // 创建新的Pawn + Pawn newPawn = PawnGenerator.GeneratePawn(targetPawnKind, faction); + + // 复制一些重要信息 + if (originalPawn.Name != null) + { + newPawn.Name = originalPawn.Name; + } + newPawn.gender = originalPawn.gender; + + // 销毁原始Pawn + originalPawn.Destroy(DestroyMode.Vanish); + + // 生成新的Pawn + GenSpawn.Spawn(newPawn, position, map); + + // 为新Pawn添加AutonomousCat组件(如果还没有) + EnsureAutonomousCatComponent(newPawn); + + // 显示消息 + Messages.Message( + "Wula_TransformComplete".Translate(originalPawn.LabelShort, newPawn.LabelShort), + MessageTypeDefOf.PositiveEvent + ); + + // 记录日志 + if (Prefs.DevMode) + { + Log.Message($"[CompGather] Transformation complete: {originalPawn.LabelShort} -> {newPawn.LabelShort}"); + } + } + catch (Exception ex) + { + Log.Error($"Error completing transformation: {ex.Message}"); + } + } + + // 确保新Pawn有AutonomousCat组件 + private void EnsureAutonomousCatComponent(Pawn newPawn) + { + // 检查是否已经有AutonomousCat组件 + if (newPawn.TryGetComp() != null) + return; + + // 检查Pawn的定义中是否有AutonomousCat组件 + var compProps = newPawn.def.comps?.Find(c => c.compClass == typeof(Comp_AutonomousCat)) as CompProperties_AutonomousCat; + if (compProps == null) + { + // 如果没有,添加一个默认的AutonomousCat组件 + newPawn.AllComps.Add(new Comp_AutonomousCat() + { + parent = newPawn, + props = new CompProperties_AutonomousCat() + { + autoDraftOnGather = true + } + }); + + if (Prefs.DevMode) + { + Log.Message($"[CompGather] Added AutonomousCat component to {newPawn.LabelShort}"); + } + } + } + + // Gizmo显示 + public override IEnumerable CompGetGizmosExtra() + { + foreach (Gizmo gizmo in base.CompGetGizmosExtra()) + { + yield return gizmo; + } + + // 只对玩家派系显示 + if (parent.Faction == Faction.OfPlayer) + { + if (cachedGizmo == null) + { + InitializeGizmo(); + } + + yield return cachedGizmo; + } + } + + private void InitializeGizmo() + { + cachedGizmo = new Command_Action(); + cachedGizmo.defaultLabel = "Wula_GatherCats".Translate(); + cachedGizmo.defaultDesc = "Wula_GatherCatsDesc".Translate(); + cachedGizmo.icon = ContentFinder.Get("UI/Gizmos/GatherCats", false) ?? BaseContent.BadTex; + cachedGizmo.action = StartGathering; + + // 添加冷却时间显示 + cachedGizmo.disabledReason = GetDisabledReason(); + + // 热键 + cachedGizmo.hotKey = KeyBindingDefOf.Misc2; + } + + private string GetDisabledReason() + { + if (!parent.Spawned || parent.Destroyed) + return "Building destroyed or not spawned"; + + int currentTick = Find.TickManager.TicksGame; + int ticksSinceLastGather = currentTick - lastGatherTick; + + if (ticksSinceLastGather < Props.cooldownTicks) + { + int remainingTicks = Props.cooldownTicks - ticksSinceLastGather; + return "Wula_GatherCooldown".Translate(remainingTicks.ToStringTicksToPeriod()); + } + + if (parent is Building building && building.IsBrokenDown()) + return "Wula_BuildingBroken".Translate(); + + return null; + } + + // 开始召集 + private void StartGathering() + { + if (!CanGatherNow) + return; + + // 记录召集时间 + lastGatherTick = Find.TickManager.TicksGame; + + // 设置召集状态 + gatheringActive = true; + gatherDurationLeft = 60; // 持续1秒 + + // 查找并命令范围内的 Autonomous Cats + GatherAutonomousCats(); + + // 显示消息 + Messages.Message("Wula_GatheringStarted".Translate(), MessageTypeDefOf.PositiveEvent); + + // 刷新Gizmo状态 + cachedGizmo.disabledReason = GetDisabledReason(); + } + + // 查找并命令 Autonomous Cats + private void GatherAutonomousCats() + { + if (parent.Map == null) + return; + + // 查找范围内的所有 Autonomous Cats + List autonomousCats = new List(); + foreach (Pawn pawn in parent.Map.mapPawns.AllPawnsSpawned) + { + // 检查是否拥有 AutonomousCat 组件 + var comp = pawn.TryGetComp(); + if (comp != null) + { + // 检查是否有待处理的转化,如果有则不响应召集 + if (comp.PendingTransformTarget != null) + continue; + + // 检查距离 + float distance = pawn.Position.DistanceTo(parent.Position); + if (distance <= Props.gatherRange) + { + autonomousCats.Add(pawn); + } + } + } + + // 命令每个 Autonomous Cat + foreach (Pawn cat in autonomousCats) + { + CommandCatToGather(cat); + } + + // 报告召集数量 + if (autonomousCats.Count > 0) + { + Messages.Message( + "Wula_CatsGathered".Translate(autonomousCats.Count), + MessageTypeDefOf.NeutralEvent + ); + } + } + + // 命令单个 Autonomous Cat + private void CommandCatToGather(Pawn cat) + { + if (cat == null || !cat.Spawned || cat.Dead) + return; + + try + { + // 获取 AutonomousCat 组件 + var comp = cat.TryGetComp(); + if (comp != null) + { + // 使用组件的响应方法 + comp.RespondToGather(parent); + } + else + { + // 如果没有组件,使用默认行为 + if (cat.drafter != null) + { + cat.drafter.Drafted = true; + } + + Job job = new Job(JobDefOf.Goto, parent.Position); + job.expiryInterval = 30000; + job.checkOverrideOnExpire = true; + + if (cat.CurJob != null) + { + cat.jobs.EndCurrentJob(JobCondition.InterruptForced); + } + + cat.jobs.StartJob(job, JobCondition.InterruptForced); + } + + // 记录日志 + if (Prefs.DevMode) + { + Log.Message($"[CompGather] Cat {cat.LabelShort} commanded to gather at {parent.Position}"); + } + } + catch (Exception ex) + { + Log.Error($"Error commanding cat to gather: {ex.Message}"); + } + } + + // 每帧更新 + public override void CompTick() + { + base.CompTick(); + + if (gatheringActive) + { + gatherDurationLeft--; + if (gatherDurationLeft <= 0) + { + gatheringActive = false; + } + } + + // 每30tick更新一次Gizmo状态 + if (parent.IsHashIntervalTick(30) && cachedGizmo != null) + { + cachedGizmo.disabledReason = GetDisabledReason(); + } + } + + // 保存/加载数据 + public override void PostExposeData() + { + base.PostExposeData(); + Scribe_Values.Look(ref lastGatherTick, "lastGatherTick", -1000); + Scribe_Values.Look(ref gatheringActive, "gatheringActive", false); + Scribe_Values.Look(ref gatherDurationLeft, "gatherDurationLeft", 0); + } + + // 绘制效果(可选) + public override void PostDraw() + { + base.PostDraw(); + + // 如果正在召集,绘制一个光环效果 + if (gatheringActive) + { + float pulse = Mathf.Sin(Find.TickManager.TicksGame * 0.1f) * 0.5f + 0.5f; + GenDraw.DrawRadiusRing(parent.Position, Props.gatherRange, Color.Lerp(Color.cyan, Color.white, pulse), + (c) => (c.x + c.y + Find.TickManager.TicksGame) % 10 < 5); + } + } + } +} diff --git a/Source/WulaFallenEmpire/Pawn_Comps/AutonomousCat/CompAutonomousCat.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousCat/CompAutonomousCat.cs new file mode 100644 index 00000000..cf35d3ef --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousCat/CompAutonomousCat.cs @@ -0,0 +1,398 @@ +using System; +using System.Collections.Generic; +using Verse; +using Verse.AI; +using RimWorld; +using UnityEngine; + +namespace WulaFallenEmpire +{ + // AutonomousCat组件属性 + public class CompProperties_AutonomousCat : CompProperties + { + public bool autoDraftOnGather = true; // 被召集时自动征召 + + // 转化功能 + public List transformablePawnKinds; // 可转化的Pawn种类列表 + public float transformTime = 3f; // 转化所需时间(秒) + public SoundDef transformSound; // 转化音效 + public EffecterDef transformEffect; // 转化特效 + + // 外观设置 + public Color gatherLineColor = new Color(0.2f, 0.8f, 0.2f, 0.8f); // 召集线颜色 + public float gatherLineWidth = 0.2f; // 召集线宽度 + + public CompProperties_AutonomousCat() + { + compClass = typeof(Comp_AutonomousCat); + } + } + + // AutonomousCat组件实现 + public class Comp_AutonomousCat : ThingComp + { + // 状态跟踪 + private bool isRespondingToGather = false; + private Thing gatherTarget = null; + private int gatherResponseEndTick = 0; + + // 转化目标 + private PawnKindDef pendingTransformTarget = null; + + // Gizmo缓存 + private Command_Action cachedTransformGizmo; + private bool gizmoInitialized = false; + + // 属性 + public CompProperties_AutonomousCat Props => (CompProperties_AutonomousCat)props; + + // 公开属性 + public bool IsRespondingToGather => isRespondingToGather; + public Thing GatherTarget => gatherTarget; + public PawnKindDef PendingTransformTarget => pendingTransformTarget; + + public override void Initialize(CompProperties props) + { + base.Initialize(props); + } + + // 响应召集命令 + public void RespondToGather(Thing target) + { + if (parent is Pawn pawn && !pawn.Dead && pawn.Spawned) + { + try + { + // 如果有待处理的转化,不响应召集 + if (pendingTransformTarget != null) + return; + + // 设置目标 + gatherTarget = target; + isRespondingToGather = true; + gatherResponseEndTick = Find.TickManager.TicksGame + 6000; // 100秒后超时 + + // 自动征召 + if (Props.autoDraftOnGather && pawn.drafter != null) + { + pawn.drafter.Drafted = true; + } + + // 创建移动到目标的Job + if (target != null && target.Spawned) + { + CreateGatherJob(pawn, target); + } + + // 记录日志 + if (Prefs.DevMode) + { + Log.Message($"[CompAutonomousCat] {pawn.LabelShort} responding to gather call from {target?.LabelShort ?? "unknown"}"); + } + } + catch (Exception ex) + { + Log.Error($"Error in RespondToGather for {parent.LabelShort}: {ex.Message}"); + } + } + } + + // 创建召集任务 + private void CreateGatherJob(Pawn pawn, Thing target) + { + try + { + // 清除当前任务 + if (pawn.CurJob != null) + { + pawn.jobs.EndCurrentJob(JobCondition.InterruptForced); + } + + // 创建移动到目标的Job + Job job = new Job(JobDefOf.Goto, target.Position); + + // 设置任务优先级和参数 + job.expiryInterval = 30000; // 长时间有效 + job.checkOverrideOnExpire = true; + + // 开始任务 + pawn.jobs.StartJob(job, JobCondition.InterruptForced); + } + catch (Exception ex) + { + Log.Error($"Error creating gather job for {pawn.LabelShort}: {ex.Message}"); + } + } + + // 开始转化流程 + public void StartTransformation(PawnKindDef targetPawnKind) + { + if (parent is Pawn pawn && !pawn.Dead && pawn.Spawned) + { + try + { + // 查找最近的Comp_Gather建筑 + Thing closestGatherBuilding = FindClosestGatherBuilding(); + + if (closestGatherBuilding == null) + { + Messages.Message( + "Wula_NoGatherBuilding".Translate(), + MessageTypeDefOf.RejectInput + ); + return; + } + + // 存储转化目标 + pendingTransformTarget = targetPawnKind; + + // 创建转化Job + Job job = JobMaker.MakeJob(Wula_JobDefOf.WULA_TransformPawn, closestGatherBuilding); + + // 清除当前任务并开始新任务 + pawn.jobs.StopAll(); + pawn.jobs.StartJob(job, JobCondition.InterruptForced); + + // 显示消息 + Messages.Message( + "Wula_TransformStarted".Translate(pawn.LabelShort, targetPawnKind.label), + MessageTypeDefOf.NeutralEvent + ); + + // 记录日志 + if (Prefs.DevMode) + { + Log.Message($"[CompAutonomousCat] {pawn.LabelShort} starting transformation to {targetPawnKind.label}"); + } + } + catch (Exception ex) + { + Log.Error($"Error starting transformation for {parent.LabelShort}: {ex.Message}"); + } + } + } + + // 清除转化目标(由JobDriver调用) + public void ClearTransformTarget() + { + pendingTransformTarget = null; + } + + // 查找最近的Comp_Gather建筑 + private Thing FindClosestGatherBuilding() + { + if (parent.Map == null) + return null; + + Thing closestBuilding = null; + float closestDistance = float.MaxValue; + + // 查找所有Comp_Gather建筑 + foreach (Thing thing in parent.Map.listerThings.AllThings) + { + if (thing.TryGetComp() != null && + thing.Spawned && + !thing.Destroyed && + thing.Faction == parent.Faction) + { + float distance = thing.Position.DistanceTo(parent.Position); + if (distance < closestDistance) + { + closestDistance = distance; + closestBuilding = thing; + } + } + } + + return closestBuilding; + } + + // 每帧更新 + public override void CompTick() + { + base.CompTick(); + + // 处理召集响应 + if (isRespondingToGather) + { + UpdateGatherResponse(); + } + } + + // 更新召集响应 + private void UpdateGatherResponse() + { + // 检查是否超时 + if (Find.TickManager.TicksGame > gatherResponseEndTick) + { + EndGatherResponse(); + return; + } + + // 检查目标是否仍然有效 + if (gatherTarget == null || !gatherTarget.Spawned || gatherTarget.Destroyed) + { + EndGatherResponse(); + return; + } + + // 检查Pawn是否已经死亡或无法行动 + if (parent is Pawn pawn && (pawn.Dead || pawn.Downed || !pawn.Spawned)) + { + EndGatherResponse(); + return; + } + + // 检查是否已经到达目标附近 + if (parent is Pawn movingPawn && movingPawn.Position.DistanceTo(gatherTarget.Position) < 5f) + { + // 到达目标,结束召集响应 + EndGatherResponse(); + + // 可选:到达后执行其他动作 + if (movingPawn.CurJobDef == JobDefOf.Goto) + { + // 切换到等待或警戒状态 + movingPawn.jobs.EndCurrentJob(JobCondition.Succeeded); + } + } + } + + // 结束召集响应 + private void EndGatherResponse() + { + isRespondingToGather = false; + gatherTarget = null; + } + + // 绘制效果 + public override void PostDraw() + { + base.PostDraw(); + + // 如果正在响应召集,绘制连接线 + if (isRespondingToGather && gatherTarget != null && parent.Spawned) + { + DrawGatherLine(); + } + } + + // 绘制召集线 + private void DrawGatherLine() + { + Vector3 startPos = parent.DrawPos; + Vector3 endPos = gatherTarget.DrawPos; + + // 提升到覆盖层高度 + startPos.y = AltitudeLayer.MetaOverlays.AltitudeFor(); + endPos.y = AltitudeLayer.MetaOverlays.AltitudeFor(); + + // 绘制连接线 + GenDraw.DrawLineBetween( + startPos, + endPos + ); + } + + // Gizmo显示 + public override IEnumerable CompGetGizmosExtra() + { + foreach (Gizmo gizmo in base.CompGetGizmosExtra()) + { + yield return gizmo; + } + + // 只对玩家派系显示 + if (parent.Faction == Faction.OfPlayer && parent is Pawn pawn && !pawn.Dead) + { + // 延迟初始化Gizmo + if (!gizmoInitialized) + { + InitializeGizmo(); + gizmoInitialized = true; + } + + if (cachedTransformGizmo != null) + { + // 如果已有待处理的转化,显示不同图标或状态 + if (pendingTransformTarget != null) + { + cachedTransformGizmo.disabledReason = "Wula_TransformPending".Translate(pendingTransformTarget.label); + } + + yield return cachedTransformGizmo; + } + } + } + + // 初始化Gizmo + private void InitializeGizmo() + { + if (Props.transformablePawnKinds != null && Props.transformablePawnKinds.Count > 0) + { + cachedTransformGizmo = new Command_Action(); + cachedTransformGizmo.defaultLabel = "Wula_Transform".Translate(); + cachedTransformGizmo.defaultDesc = "Wula_TransformDesc".Translate(); + cachedTransformGizmo.icon = ContentFinder.Get("UI/Gizmos/Transform", false) ?? BaseContent.BadTex; + cachedTransformGizmo.action = () => ShowTransformMenu(); + + // 设置热键 + cachedTransformGizmo.hotKey = KeyBindingDefOf.Misc3; + } + } + + // 显示转化菜单 + private void ShowTransformMenu() + { + if (Props.transformablePawnKinds == null || Props.transformablePawnKinds.Count == 0) + return; + + List options = new List(); + + foreach (PawnKindDef pawnKind in Props.transformablePawnKinds) + { + options.Add(new FloatMenuOption( + pawnKind.LabelCap, + () => StartTransformation(pawnKind), + pawnKind.race.uiIcon, + Color.white + )); + } + + // 添加取消选项 + options.Add(new FloatMenuOption( + "Cancel".Translate(), + null, + MenuOptionPriority.Default + )); + + Find.WindowStack.Add(new FloatMenu(options)); + } + + // 保存/加载数据 + public override void PostExposeData() + { + base.PostExposeData(); + + Scribe_Values.Look(ref isRespondingToGather, "isRespondingToGather", false); + Scribe_References.Look(ref gatherTarget, "gatherTarget"); + Scribe_Values.Look(ref gatherResponseEndTick, "gatherResponseEndTick", 0); + + // 保存/加载转化目标 + Scribe_Defs.Look(ref pendingTransformTarget, "pendingTransformTarget"); + + if (Scribe.mode == LoadSaveMode.PostLoadInit) + { + // 重置不正确的状态 + if (isRespondingToGather && (gatherTarget == null || !gatherTarget.Spawned)) + { + isRespondingToGather = false; + } + + // 重置Gizmo缓存 + gizmoInitialized = false; + cachedTransformGizmo = null; + } + } + } +} diff --git a/Source/WulaFallenEmpire/Pawn_Comps/AutonomousCat/JobDriver_TransformPawn.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousCat/JobDriver_TransformPawn.cs new file mode 100644 index 00000000..6da93fe0 --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousCat/JobDriver_TransformPawn.cs @@ -0,0 +1,143 @@ +using System.Collections.Generic; +using Verse; +using Verse.AI; +using RimWorld; +using UnityEngine; + +namespace WulaFallenEmpire +{ + public class JobDriver_TransformPawn : JobDriver + { + private const TargetIndex GatherBuildingIndex = TargetIndex.A; + + private Comp_Gather GatherComp => job.targetA.Thing?.TryGetComp(); + private Comp_AutonomousCat PawnComp => pawn.TryGetComp(); + + public override bool TryMakePreToilReservations(bool errorOnFailed) + { + // 预留目标建筑 + if (!pawn.Reserve(job.targetA, job, 1, -1, null, errorOnFailed)) + { + return false; + } + return true; + } + + protected override IEnumerable MakeNewToils() + { + // 第1步:移动到目标建筑 + yield return Toils_Goto.GotoCell(GatherBuildingIndex, PathEndMode.InteractionCell); + + // 第2步:进行转化工作 + Toil transformToil = new Toil(); + transformToil.initAction = () => + { + // 获取目标建筑 + Thing gatherBuilding = job.targetA.Thing; + if (gatherBuilding == null || gatherBuilding.Destroyed) + { + ReadyForNextToil(); + return; + } + + // 获取Comp_Gather + var gatherComp = gatherBuilding.TryGetComp(); + if (gatherComp == null) + { + ReadyForNextToil(); + return; + } + + // 确保可以转化 + if (!gatherComp.CanTransformPawn(pawn)) + { + Messages.Message("Wula_CannotTransformHere".Translate(), MessageTypeDefOf.RejectInput); + ReadyForNextToil(); + return; + } + }; + + transformToil.tickAction = () => + { + // 确保目标建筑仍然有效 + Thing gatherBuilding = job.targetA.Thing; + if (gatherBuilding == null || gatherBuilding.Destroyed || + gatherBuilding.Map != pawn.Map || + pawn.Position.DistanceTo(gatherBuilding.Position) > 3f) + { + // 中断转化,清除转化目标 + PawnComp?.ClearTransformTarget(); + EndJobWith(JobCondition.Incompletable); + return; + } + + // 面向建筑 + pawn.rotationTracker.FaceCell(gatherBuilding.Position); + + // 播放转化效果 + if (Find.TickManager.TicksGame % 20 == 0) + { + PlayTransformEffects(); + } + }; + + // 从Comp_AutonomousCat获取转化时间 + var compProps = PawnComp?.Props as CompProperties_AutonomousCat; + int transformDuration = compProps != null ? + Mathf.RoundToInt(compProps.transformTime * 60f) : 180; // 默认3秒 + + transformToil.defaultCompleteMode = ToilCompleteMode.Delay; + transformToil.defaultDuration = transformDuration; + yield return transformToil; + + // 第3步:完成转化 + yield return new Toil + { + initAction = () => + { + // 获取目标建筑 + Thing gatherBuilding = job.targetA.Thing; + if (gatherBuilding == null || gatherBuilding.Destroyed) + { + return; + } + + // 获取Comp_Gather + var gatherComp = gatherBuilding.TryGetComp(); + if (gatherComp == null) + { + return; + } + + // 获取要转化的PawnKindDef(从Comp_AutonomousCat中) + var targetPawnKind = PawnComp?.PendingTransformTarget; + if (targetPawnKind == null) + { + Messages.Message("Wula_NoTransformTarget".Translate(), MessageTypeDefOf.RejectInput); + return; + } + + // 调用Comp_Gather的转化方法 + gatherComp.TransformPawn(pawn, targetPawnKind); + + // 清除转化目标 + PawnComp?.ClearTransformTarget(); + }, + defaultCompleteMode = ToilCompleteMode.Instant + }; + } + + // 播放转化效果 + private void PlayTransformEffects() + { + // 播放音效 + var compProps = PawnComp?.Props as CompProperties_AutonomousCat; + + // 播放特效 + if (compProps?.transformEffect != null) + { + compProps.transformEffect.Spawn(pawn.Position, pawn.Map).Cleanup(); + } + } + } +} diff --git a/Source/WulaFallenEmpire/WulaDefOf.cs b/Source/WulaFallenEmpire/WulaDefOf.cs index 627e86af..d98eeede 100644 --- a/Source/WulaFallenEmpire/WulaDefOf.cs +++ b/Source/WulaFallenEmpire/WulaDefOf.cs @@ -30,6 +30,7 @@ namespace WulaFallenEmpire public static JobDef WULA_RepairMech; public static JobDef WULA_ForceEjectPilot; public static JobDef WULA_CarryToMech; + public static JobDef WULA_TransformPawn; static Wula_JobDefOf() { diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj index d8cd77ae..681553e3 100644 --- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj +++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj @@ -85,6 +85,9 @@ + + + @@ -97,6 +100,8 @@ + +