This commit is contained in:
2026-02-27 17:32:08 +08:00
parent 305c7b122f
commit 5d2f824140
11 changed files with 1090 additions and 94 deletions

View File

@@ -69,6 +69,18 @@
<suspendable>false</suspendable>
</JobDef>
<JobDef>
<defName>WULA_TransformPawn</defName>
<driverClass>WulaFallenEmpire.JobDriver_TransformPawn</driverClass>
<reportString>变更装备中。</reportString>
<abilityCasting>true</abilityCasting>
<alwaysShowWeapon>true</alwaysShowWeapon>
<playerInterruptible>true</playerInterruptible>
<casualInterruptible>false</casualInterruptible>
<checkOverrideOnDamage>Always</checkOverrideOnDamage>
<suspendable>false</suspendable>
</JobDef>
<JobDef>
<defName>WULA_EnterMech</defName>

View File

@@ -265,7 +265,7 @@
<ThingDef ParentName="BuildingBase">
<defName>WULA_Cat_Bunker_Cleanzone</defName>
<label>乌拉猫猫地堡</label>
<description>清理出一块场地并准备好资源,使得乌拉帝国可以向此处投放建筑。\n\n乌拉猫猫地堡是集生产和防御为一体的坚实建筑3只乌拉猫猫自律机械体会和地堡一起空降。地堡可供乌拉猫猫更换工作类型允许乌拉猫猫进驻使用内部武器击败来犯敌军。</description>
<description>清理出一块场地并准备好资源,使得乌拉帝国可以向此处投放建筑。\n\n乌拉猫猫地堡是集生产和防御为一体的坚实建筑3只乌拉猫猫自律机械体会和地堡一起空降。地堡可供乌拉猫猫更换工作类型拥有内部武器用于击败来犯敌军。</description>
<uiIconPath>Wula/Building/WULA_Cat_Bunker_south</uiIconPath>
<minifiedDef>MinifiedThing</minifiedDef>
<tickerType>Normal</tickerType>
@@ -385,9 +385,9 @@
<ThingDef ParentName="BuildingBase">
<defName>WULA_Cat_Bunker</defName>
<label>乌拉猫猫地堡</label>
<description>这是一个从乌拉帝国母舰上空投下来的地堡,它拥有一些预制件,可以供乌拉猫猫更换其工作类型。此外,当敌人袭击时,可将乌拉猫猫召回至地堡内,它们将在地堡里对外射击以击退来犯者。</description>
<thingClass>WulaFallenEmpire.Building_MechanoidRecycler</thingClass>
<description>这是一个从乌拉帝国母舰上空投下来的地堡,它拥有一些预制件,可以供乌拉猫猫更换其工作类型。此外,当敌人袭击时,地堡的自动机枪会对外射击以击退来犯者。</description>
<tickerType>Normal</tickerType>
<thingClass>Building_TurretGun</thingClass>
<descriptionHyperlinks>
<ThingDef>WULA_Cat_Bunker_TurretGun</ThingDef>
<ThingDef>Mech_WULA_Cat</ThingDef>
@@ -407,10 +407,8 @@
</shadowData>
</graphicData>
<altitudeLayer>Building</altitudeLayer>
<passability>Impassable</passability>
<blockWind>true</blockWind>
<fillPercent>1</fillPercent>
<blockLight>false</blockLight>
<passability>PassThroughOnly</passability>
<fillPercent>0.9</fillPercent>
<castEdgeShadows>false</castEdgeShadows>
<canOverlapZones>false</canOverlapZones>
<hasInteractionCell>false</hasInteractionCell>
@@ -444,101 +442,28 @@
<usePlayerFactionIfNull>true</usePlayerFactionIfNull>
<overrideExistingFaction>false</overrideExistingFaction>
</li>
<li Class="WulaFallenEmpire.CompProperties_MechanoidRecycler">
<maxStorageCapacity>6</maxStorageCapacity>
<!-- 可回收的机械族种族 -->
<recyclableRaces>
<li>Mech_WULA_Cat</li>
<li>Mech_WULA_Cat_Constructor</li>
<li>Mech_WULA_Cat_Assault</li>
</recyclableRaces>
<recycleJobDef>WULA_RecycleMechanoid</recycleJobDef>
<recycleRange>5</recycleRange>
<!-- 可生成的机械族种类 -->
<spawnablePawnKinds>
<li>Mech_WULA_Cat</li>
<li>Mech_WULA_Cat_Constructor</li>
<li>Mech_WULA_Cat_Assault</li>
</spawnablePawnKinds>
<!-- 新增初始单位配置 -->
<initialUnits>
<li>
<pawnKindDef>Mech_WULA_Cat</pawnKindDef>
<count>1</count>
</li>
<li>
<pawnKindDef>Mech_WULA_Cat_Constructor</pawnKindDef>
<count>1</count>
</li>
<li>
<pawnKindDef>Mech_WULA_Cat_Assault</pawnKindDef>
<count>1</count>
</li>
</initialUnits>
<!-- <ownershipFaction>Player</ownershipFaction> -->
<li Class="WulaFallenEmpire.CompProperties_BuildToPawn">
<pawnKindDef>Mech_WULA_Cat_Assault</pawnKindDef>
<spawnCount>3</spawnCount>
<inheritFaction>true</inheritFaction>
<destroyBuilding>false</destroyBuilding>
<initDrafted>true</initDrafted>
</li>
<li Class="WulaFallenEmpire.CompProperties_Gather">
<gatherRange>500</gatherRange>
<!-- <gatherSound></gatherSound> -->
<cooldownTicks>60</cooldownTicks>
</li>
<li Class="WulaFallenEmpire.CompProperties_PathCostUpdater">
<adaptiveExpansion>true</adaptiveExpansion>
</li>
<li Class="WulaFallenEmpire.CompProperties_StorageMultiTurretGun">
<ID>1</ID>
<turretDef>WULA_Cat_Bunker_TurretGun</turretDef>
<angleOffset>0</angleOffset>
<autoAttack>true</autoAttack>
<requiredMechanoids>1</requiredMechanoids>
<autoActivate>true</autoActivate>
</li>
<li Class="WulaFallenEmpire.CompProperties_StorageMultiTurretGun">
<ID>2</ID>
<turretDef>WULA_Cat_Bunker_TurretGun</turretDef>
<angleOffset>0</angleOffset>
<autoAttack>true</autoAttack>
<requiredMechanoids>2</requiredMechanoids>
<autoActivate>true</autoActivate>
</li>
<li Class="WulaFallenEmpire.CompProperties_StorageMultiTurretGun">
<ID>3</ID>
<turretDef>WULA_Cat_Bunker_TurretGun</turretDef>
<angleOffset>0</angleOffset>
<autoAttack>true</autoAttack>
<requiredMechanoids>3</requiredMechanoids>
<autoActivate>true</autoActivate>
</li>
<li Class="WulaFallenEmpire.CompProperties_StorageMultiTurretGun">
<ID>4</ID>
<turretDef>WULA_Cat_Bunker_TurretGun</turretDef>
<angleOffset>0</angleOffset>
<autoAttack>true</autoAttack>
<requiredMechanoids>4</requiredMechanoids>
<autoActivate>true</autoActivate>
</li>
<li Class="WulaFallenEmpire.CompProperties_StorageMultiTurretGun">
<ID>5</ID>
<turretDef>WULA_Cat_Bunker_TurretGun</turretDef>
<angleOffset>0</angleOffset>
<autoAttack>true</autoAttack>
<requiredMechanoids>5</requiredMechanoids>
<autoActivate>true</autoActivate>
</li>
<li Class="WulaFallenEmpire.CompProperties_StorageMultiTurretGun">
<ID>6</ID>
<turretDef>WULA_Cat_Bunker_TurretGun</turretDef>
<angleOffset>0</angleOffset>
<autoAttack>true</autoAttack>
<requiredMechanoids>6</requiredMechanoids>
<autoActivate>true</autoActivate>
</li>
</comps>
</ThingDef>
<ThingDef ParentName="BaseWeaponTurret">
<defName>WULA_Cat_Bunker_TurretGun</defName>
<label>地堡炮塔</label>
<description>进入地堡的乌拉猫猫操纵的炮塔——说那么多干什么,扣扳机就完了,殖民地会报销弹药的。</description>
<description>驻守地堡的乌拉猫猫操纵的炮塔——说那么多干什么,扣扳机就完了,殖民地会报销弹药的。</description>
<graphicData>
<texPath>Wula/Weapon/WULA_Weapon_Empty</texPath>
<graphicClass>Graphic_Single</graphicClass>
@@ -558,7 +483,7 @@
<defaultProjectile>Bullet_WULA_RW_Base_AR</defaultProjectile>
<range>28</range>
<ticksBetweenBurstShots>5</ticksBetweenBurstShots>
<burstShotCount>12</burstShotCount>
<burstShotCount>24</burstShotCount>
<soundCast>Shot_AssaultRifle</soundCast>
<soundCastTail>GunTail_Medium</soundCastTail>
<muzzleFlashScale>9</muzzleFlashScale>

View File

@@ -951,6 +951,16 @@
<li>Firefighter</li>
</mechEnabledWorkTypes>
</race>
<comps>
<li Class="WulaFallenEmpire.CompProperties_AutonomousCat">
<autoDraftOnGather>true</autoDraftOnGather>
<transformablePawnKinds>
<li>Mech_WULA_Cat</li>
<li>Mech_WULA_Cat_Constructor</li>
<li>Mech_WULA_Cat_Assault</li>
</transformablePawnKinds>
</li>
</comps>
</AlienRace.ThingDef_AlienRace>
<AlienRace.ThingDef_AlienRace ParentName="Mech_WULA_Cat_Base">
<defName>Mech_WULA_Cat_Constructor</defName>
@@ -975,6 +985,16 @@
<statBases>
<WorkSpeedGlobal>2</WorkSpeedGlobal>
</statBases>
<comps>
<li Class="WulaFallenEmpire.CompProperties_AutonomousCat">
<autoDraftOnGather>true</autoDraftOnGather>
<transformablePawnKinds>
<li>Mech_WULA_Cat</li>
<li>Mech_WULA_Cat_Constructor</li>
<li>Mech_WULA_Cat_Assault</li>
</transformablePawnKinds>
</li>
</comps>
</AlienRace.ThingDef_AlienRace>
<AlienRace.ThingDef_AlienRace ParentName="Mech_WULA_Cat_Base">
<defName>Mech_WULA_Cat_Assault</defName>
@@ -1003,6 +1023,16 @@
<!-- <WorkSpeedGlobal>2</WorkSpeedGlobal> -->
<MoveSpeed>4</MoveSpeed>
</statBases>
<comps>
<li Class="WulaFallenEmpire.CompProperties_AutonomousCat">
<autoDraftOnGather>true</autoDraftOnGather>
<transformablePawnKinds>
<li>Mech_WULA_Cat</li>
<li>Mech_WULA_Cat_Constructor</li>
<li>Mech_WULA_Cat_Assault</li>
</transformablePawnKinds>
</li>
</comps>
</AlienRace.ThingDef_AlienRace>
<AlienRace.ThingDef_AlienRace ParentName="Mech_WULA_Cat_Base">
<defName>Mech_WULA_Cat_Cloak_Sniper</defName>

View File

@@ -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++;
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<Comp_AutonomousCat>() == 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<Comp_AutonomousCat>() != 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<Gizmo> 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<Texture2D>.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<Pawn> autonomousCats = new List<Pawn>();
foreach (Pawn pawn in parent.Map.mapPawns.AllPawnsSpawned)
{
// 检查是否拥有 AutonomousCat 组件
var comp = pawn.TryGetComp<Comp_AutonomousCat>();
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<Comp_AutonomousCat>();
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);
}
}
}
}

View File

@@ -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<PawnKindDef> 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<Comp_Gather>() != 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<Gizmo> 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<Texture2D>.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<FloatMenuOption> options = new List<FloatMenuOption>();
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;
}
}
}
}

View File

@@ -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<Comp_Gather>();
private Comp_AutonomousCat PawnComp => pawn.TryGetComp<Comp_AutonomousCat>();
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
// 预留目标建筑
if (!pawn.Reserve(job.targetA, job, 1, -1, null, errorOnFailed))
{
return false;
}
return true;
}
protected override IEnumerable<Toil> 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<Comp_Gather>();
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<Comp_Gather>();
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();
}
}
}
}

View File

@@ -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()
{

View File

@@ -85,6 +85,9 @@
</ItemGroup>
<ItemGroup>
<!-- 第一部分:基础类和组件 -->
<Compile Include="BuildingComp\WULA_BuildToPawn\CompBuildToPawn.cs" />
<Compile Include="BuildingComp\WULA_BuildToPawn\CompProperties_BuildToPawn.cs" />
<Compile Include="BuildingComp\WULA_Gather\CompGather.cs" />
<Compile Include="HarmonyPatches\WULA_MechUnit\Patch_Pawn_RotationTracker_UpdateRotation.cs" />
<Compile Include="HediffComp\WULA_DamageResponse\HediffComp_DamageResponse.cs" />
<Compile Include="HediffComp\WULA_DisappearWithEffect\HediffCompProperties_DisappearWithEffect.cs" />
@@ -97,6 +100,8 @@
<Compile Include="HediffComp\WULA_TimedExplosion\HediffComp_TimedExplosion.cs" />
<Compile Include="ITab\ITab_MechSkills.cs" />
<Compile Include="MentalState\MentalState_MechNoPilot.cs" />
<Compile Include="Pawn_Comps\AutonomousCat\CompAutonomousCat.cs" />
<Compile Include="Pawn_Comps\AutonomousCat\JobDriver_TransformPawn.cs" />
<Compile Include="Pawn_Comps\PawnRenderExtra\Comp_PawnRenderExtra.cs" />
<Compile Include="Pawn_Comps\HighSpeedCollision\CompHighSpeedCollision.cs" />
<Compile Include="Work\EnterMech\FloatMenuOptionProvider_EnterMech.cs" />