diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll
index 2f4562ac..e4d77621 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/DamageDefs/Damages_Wula.xml b/1.6/1.6/Defs/DamageDefs/Damages_Wula.xml
index ab4647a2..5c041dab 100644
--- a/1.6/1.6/Defs/DamageDefs/Damages_Wula.xml
+++ b/1.6/1.6/Defs/DamageDefs/Damages_Wula.xml
@@ -419,6 +419,69 @@
15
+
+
+ WULA_Combat_Excavator_Shoke_Damage
+
+
+ WulaFallenEmpire.DamageWorker_ExplosionWithTerrain
+ 10
+ 10
+ 2
+
+ WULA_Disturber_Turret_Bomb_Shockwave
+
+
+
+ LavaShallow
+
+
+
+
+
+ WULA_Combat_Excavator_Shoke_S_Damage
+
+
+ WulaFallenEmpire.DamageWorker_ExplosionWithTerrain
+ 10
+ 10
+ 2
+
+ WULA_Disturber_Turret_Bomb_Shockwave
+
+
+
+ CooledLava
+
+
+
+
WULA_DarkMatterBomb
diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml
index 3d60d825..dafccd7a 100644
--- a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml
+++ b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml
@@ -1068,4 +1068,138 @@
+
+
+
+ WULA_Combat_Excavator
+
+ 乌拉帝国用于发起游击战的隧道挖掘机,在指定地点部署后,钻机将通过高速旋转的刀片引发若干次地震冲击波,随后将单位一股脑放出。
+ WulaFallenEmpire.Building_ExtraGraphics
+ true
+ Building
+ 50
+ true
+ PassThroughOnly
+ 1
+ (1,1)
+ true
+ None
+ (0.56, 0.62, 0.9)
+ Wula/Building/Flag/WULA_Flag_Building_A
+ WULA_Buildings
+ false
+
+ Wula/Building/Flag/WULA_Flag_Building_Mount
+ Graphic_Single
+ (1,1)
+
+
+ 4
+
+
+ 99999
+ 0
+ 1000
+ 5
+
+ Normal
+ true
+ false
+ North
+ true
+ Light
+ BulletImpact_Metal
+ true
+ RealtimeOnly
+ ConstructMetal
+ true
+
+
+ false
+ BuildingDestroyed_Metal_Big
+ true
+ true
+
+
+
+
+
+
+ Wula_Progressive_Faction
+ true
+ false
+
+
+ 3
+ (120,240,252,0)
+
+
+ 240
+ 180
+
+
+
+ WULA_Combat_Excavator_Shoke_S_Damage
+ 6
+ 2
+ 5
+
+
+ WULA_Combat_Excavator_Shoke_S_Damage
+ 9
+ 2
+ 5
+
+
+ WULA_Combat_Excavator_Shoke_Damage
+ 9
+ 2
+ 5
+
+
+ Smoke
+ 12
+ BlindSmoke
+ 2
+ 0
+
+
+
+
+ Mech_WULA_Cat_Constructor
+ Mech_WULA_Cat_Assault
+
+
+ 3
+ 180
+
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml b/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml
index 09cae253..8e90b7c8 100644
--- a/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml
+++ b/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml
@@ -1663,6 +1663,7 @@
false
+
Building
Standable
false
@@ -1862,6 +1863,7 @@
false
false
(1,1)
+
Wula/Building/WULA_Surveillance_Building
Graphic_Single
@@ -2097,6 +2099,7 @@
0
false
false
+
1
0
diff --git a/Source/WulaFallenEmpire/BuildingComp/Building_ExtraGraphics.cs b/Source/WulaFallenEmpire/BuildingComp/Building_ExtraGraphics.cs
index 6f769b2c..78421863 100644
--- a/Source/WulaFallenEmpire/BuildingComp/Building_ExtraGraphics.cs
+++ b/Source/WulaFallenEmpire/BuildingComp/Building_ExtraGraphics.cs
@@ -153,7 +153,7 @@ namespace WulaFallenEmpire
}
}
- // 绘制单个图形层 - 现在支持旋转动画
+ // 绘制单个图形层 - 现在支持三种旋转动画
private void DrawGraphicLayer(Vector3 baseDrawPos, bool flip, GraphicLayerData layer)
{
if (string.IsNullOrEmpty(layer.texturePath))
@@ -183,7 +183,9 @@ namespace WulaFallenEmpire
}
break;
- case AnimationType.Rotate:
+ case AnimationType.RotateZ:
+ case AnimationType.RotateY:
+ case AnimationType.RotateX:
if (layer.enableAnimation && layerRotationAngles.ContainsKey(layerIndex))
{
rotationAngle = layerRotationAngles[layerIndex];
@@ -194,9 +196,13 @@ namespace WulaFallenEmpire
// 最终绘制位置 = 基础位置 + 图层偏移 + 动画偏移
Vector3 drawPos = baseDrawPos + layer.offset + animationOffset;
- // 如果启用了旋转动画,使用矩阵变换绘制
- if (layer.animationType == AnimationType.Rotate && layer.enableAnimation && rotationAngle != 0f)
+ // 如果启用了旋转动画,使用特殊方法绘制
+ if ((layer.animationType == AnimationType.RotateZ ||
+ layer.animationType == AnimationType.RotateY ||
+ layer.animationType == AnimationType.RotateX) &&
+ layer.enableAnimation)
{
+ // 使用自定义旋转绘制方法
DrawWithRotation(graphic, drawPos, flip, rotationAngle, layer);
}
else
@@ -206,7 +212,7 @@ namespace WulaFallenEmpire
}
}
- // 使用矩阵变换绘制旋转图形
+ // 使用矩阵变换绘制旋转图形 - 支持三种旋转轴
private void DrawWithRotation(Graphic graphic, Vector3 drawPos, bool flip, float rotationAngle, GraphicLayerData layer)
{
try
@@ -221,26 +227,48 @@ namespace WulaFallenEmpire
return;
}
- // 创建旋转矩阵
- Quaternion rotation = Quaternion.Euler(0f, 0f, rotationAngle);
+ // 根据旋转类型创建不同的旋转矩阵
+ Quaternion rotation = Quaternion.identity;
+
+ switch (layer.animationType)
+ {
+ case AnimationType.RotateZ:
+ // 绕Z轴旋转(2D平面旋转)
+ rotation = Quaternion.Euler(0f, 0f, rotationAngle);
+ break;
+
+ case AnimationType.RotateY:
+ // 绕Y轴旋转(3D旋转,类似旋转门)
+ rotation = Quaternion.Euler(0f, rotationAngle, 0f);
+ break;
+
+ case AnimationType.RotateX:
+ // 绕X轴旋转(3D旋转,类似翻跟斗)
+ rotation = Quaternion.Euler(rotationAngle, 0f, 0f);
+ break;
+ }
// 如果图层有旋转中心偏移,需要调整位置
- Vector3 pivotOffset = new Vector3(layer.pivotOffset.x, layer.pivotOffset.y, 0f);
+ Vector3 pivotOffset = new Vector3(layer.pivotOffset.x, 0, layer.pivotOffset.y);
- // 计算最终矩阵
+ // 最终绘制位置 = 基础位置 + 图层偏移 + 旋转中心偏移
+ Vector3 finalDrawPos = drawPos + pivotOffset;
+
+ // 创建变换矩阵
+ // 注意:Graphic已经应用了缩放,所以这里使用Vector3.one
Matrix4x4 matrix = Matrix4x4.TRS(
- drawPos + pivotOffset, // 位置
- rotation, // 旋转
- new Vector3(layer.scale.x, layer.scale.y, 1f) // 缩放
+ finalDrawPos, // 位置
+ rotation, // 旋转
+ Vector3.one // 缩放(已由Graphic处理)
);
- // 绘制网格
- Graphics.DrawMesh(mesh, matrix, mat, 0);
+ // 使用RimWorld的绘制方法
+ GenDraw.DrawMeshNowOrLater(mesh, matrix, mat, false);
- // 如果需要,绘制第二面(双面渲染)
+ // 如果需要双面渲染,再绘制一次
if (layer.doubleSided)
{
- Graphics.DrawMesh(mesh, matrix, mat, 0, null, 0, null, UnityEngine.Rendering.ShadowCastingMode.Off, true);
+ GenDraw.DrawMeshNowOrLater(mesh, matrix, mat, false);
}
}
catch (Exception ex)
@@ -268,6 +296,13 @@ namespace WulaFallenEmpire
if (!layerAnimationTimes.ContainsKey(i))
{
layerAnimationTimes[i] = layer.animationStartTime;
+ // 为旋转动画初始化旋转角度
+ if (layer.animationType == AnimationType.RotateZ ||
+ layer.animationType == AnimationType.RotateY ||
+ layer.animationType == AnimationType.RotateX)
+ {
+ layerRotationAngles[i] = 0f;
+ }
}
// 更新动画时间
@@ -285,22 +320,35 @@ namespace WulaFallenEmpire
layerHoverOffsets[i] = hoverOffset;
break;
- case AnimationType.Rotate:
+ case AnimationType.RotateZ:
+ case AnimationType.RotateY:
+ case AnimationType.RotateX:
// 计算该图层的旋转角度
float rotateSpeed = layer.animationSpeed > 0 ? layer.animationSpeed : ModExtension.globalAnimationSpeed;
- float maxAngle = layer.animationIntensity > 0 ? layer.animationIntensity : ModExtension.globalAnimationIntensity;
- // 旋转角度(循环)
- float rotationAngle = (layerAnimationTimes[i] * rotateSpeed * 360f) % 360f;
+ // 旋转角度计算:动画时间 × 旋转速度(度/秒)
+ float rotationAngle = layerAnimationTimes[i] * rotateSpeed;
- // 限制旋转角度范围(如果设置了最大角度)
- if (maxAngle > 0 && maxAngle < 360f)
+ // 如果设置了动画强度且小于360,则限制旋转范围
+ if (layer.animationIntensity > 0 && layer.animationIntensity < 360f)
{
- // 使用正弦波限制旋转角度范围
- rotationAngle = Mathf.Sin(layerAnimationTimes[i] * rotateSpeed) * maxAngle;
+ // 使用正弦波创建来回旋转效果
+ rotationAngle = Mathf.Sin(layerAnimationTimes[i] * rotateSpeed * Mathf.Deg2Rad) * layer.animationIntensity;
+ }
+ else if (layer.animationIntensity >= 360f)
+ {
+ // 完整旋转:取模确保在0-360度之间
+ rotationAngle %= 360f;
}
layerRotationAngles[i] = rotationAngle;
+
+ // 调试输出
+ if (DebugSettings.godMode && i == 0 && Find.TickManager.TicksGame % 60 == 0)
+ {
+ // 只在开发模式下,每60帧输出一次第一条图层的旋转信息
+ Log.Message($"{layer.animationType} 图层 {i}: 角度={rotationAngle:F1}°, 时间={layerAnimationTimes[i]:F2}s, 速度={rotateSpeed}°/s");
+ }
break;
}
}
@@ -315,14 +363,32 @@ namespace WulaFallenEmpire
base.ExposeData();
// 保存自定义状态(如果需要)
}
+
+ // 调试方法:手动触发旋转测试
+ public void TestRotation()
+ {
+ for (int i = 0; i < ModExtension.graphicLayers.Count; i++)
+ {
+ var layer = ModExtension.graphicLayers[i];
+ if (layer.animationType == AnimationType.RotateZ ||
+ layer.animationType == AnimationType.RotateY ||
+ layer.animationType == AnimationType.RotateX)
+ {
+ layerRotationAngles[i] = 45f; // 设置为45度测试
+ break;
+ }
+ }
+ }
}
- // 动画类型枚举
+ // 动画类型枚举 - 现在有五种动画类型
public enum AnimationType
{
None, // 无动画
Hover, // 上下浮动
- Rotate // 自旋转
+ RotateZ, // 绕Z轴旋转(2D平面旋转)- 原来的Rotate
+ RotateY, // 绕Y轴旋转(3D旋转,类似旋转门)- 新增
+ RotateX // 绕X轴旋转(3D旋转,类似翻跟斗)- 新增
}
// 主要的 ModExtension 定义
@@ -404,5 +470,19 @@ namespace WulaFallenEmpire
get => animationPhase;
set => animationPhase = value;
}
+
+ // 向后兼容:Rotate应该映射到RotateZ
+ [Obsolete("Use animationType instead")]
+ public AnimationType animationTypeCompat
+ {
+ get => animationType;
+ set
+ {
+ animationType = value;
+ // 如果旧值是Rotate,映射到RotateZ
+ if (animationType == AnimationType.RotateZ)
+ animationType = AnimationType.RotateZ;
+ }
+ }
}
}
diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_PhaseCombatTower/CompPhaseCombatTower.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_PhaseCombatTower/CompPhaseCombatTower.cs
new file mode 100644
index 00000000..2b4b72b7
--- /dev/null
+++ b/Source/WulaFallenEmpire/BuildingComp/WULA_PhaseCombatTower/CompPhaseCombatTower.cs
@@ -0,0 +1,361 @@
+using System;
+using System.Collections.Generic;
+using RimWorld;
+using UnityEngine;
+using Verse;
+using Verse.AI;
+using Verse.Sound;
+using System.Linq;
+
+namespace WulaFallenEmpire
+{
+ public class CompPhaseCombatTower : ThingComp
+ {
+ // 组件状态枚举
+ public enum TowerState
+ {
+ Idle, // 空闲(未激活)
+ Warmup, // 启动期
+ Exploding, // 爆炸阶段
+ SpawningPawns, // 生成Pawn阶段
+ Finished // 完成
+ }
+
+ private CompProperties_PhaseCombatTower Props => (CompProperties_PhaseCombatTower)props;
+
+ // 状态变量
+ private TowerState currentState = TowerState.Idle;
+ private int ticksInCurrentState = 0;
+ private int currentExplosionIndex = 0;
+ private int pawnsSpawned = 0;
+ private int nextSpawnTick = 0;
+
+ // 缓存
+ private List cachedPawnKindDefs = null;
+
+ public override void PostSpawnSetup(bool respawningAfterLoad)
+ {
+ base.PostSpawnSetup(respawningAfterLoad);
+
+ if (!respawningAfterLoad)
+ {
+ // 初次生成时开始启动期
+ StartWarmup();
+ }
+ }
+
+ // 开始启动期
+ private void StartWarmup()
+ {
+ currentState = TowerState.Warmup;
+ ticksInCurrentState = 0;
+ currentExplosionIndex = 0;
+ pawnsSpawned = 0;
+ }
+
+ // 开始爆炸阶段
+ private void StartExplosionPhase()
+ {
+ currentState = TowerState.Exploding;
+ ticksInCurrentState = 0;
+ currentExplosionIndex = 0;
+
+ if (Props.explosions.Count == 0)
+ {
+ StartSpawningPhase();
+ }
+ }
+
+ // 开始生成Pawn阶段
+ private void StartSpawningPhase()
+ {
+ currentState = TowerState.SpawningPawns;
+ ticksInCurrentState = 0;
+ pawnsSpawned = 0;
+
+ // 初始化Pawn种类缓存
+ if (cachedPawnKindDefs == null)
+ {
+ cachedPawnKindDefs = new List();
+ foreach (string pawnKindName in Props.pawnKindDefs)
+ {
+ PawnKindDef pawnKindDef = DefDatabase.GetNamedSilentFail(pawnKindName);
+ if (pawnKindDef != null)
+ {
+ cachedPawnKindDefs.Add(pawnKindDef);
+ }
+ else
+ {
+ Log.Error($"PhaseCombatTower: 找不到PawnKindDef '{pawnKindName}'");
+ }
+ }
+ }
+
+ if (cachedPawnKindDefs.Count > 0 && Props.spawnCount > 0)
+ {
+ nextSpawnTick = Find.TickManager.TicksGame + Props.spawnIntervalTicks;
+ }
+ else
+ {
+ currentState = TowerState.Finished;
+ }
+ }
+
+ public override void CompTick()
+ {
+ base.CompTick();
+
+ if (parent.Map == null || parent.Destroyed)
+ return;
+
+ switch (currentState)
+ {
+ case TowerState.Warmup:
+ TickWarmup();
+ break;
+ case TowerState.Exploding:
+ TickExploding();
+ break;
+ case TowerState.SpawningPawns:
+ TickSpawningPawns();
+ break;
+ }
+ }
+
+ private void TickWarmup()
+ {
+ ticksInCurrentState++;
+
+ // 启动期结束
+ if (ticksInCurrentState >= Props.warmupTicks)
+ {
+ StartExplosionPhase();
+ }
+ }
+
+ private void TickExploding()
+ {
+ ticksInCurrentState++;
+
+ // 检查是否需要执行下一次爆炸
+ if (currentExplosionIndex < Props.explosions.Count)
+ {
+ // 第一次爆炸或冷却期已过
+ if (currentExplosionIndex == 0 ||
+ ticksInCurrentState >= Props.explosionCooldownTicks * currentExplosionIndex)
+ {
+ ExecuteExplosion(currentExplosionIndex);
+ currentExplosionIndex++;
+
+ // 如果这是最后一次爆炸,开始生成Pawn阶段
+ if (currentExplosionIndex >= Props.explosions.Count)
+ {
+ StartSpawningPhase();
+ }
+ }
+ }
+ }
+
+ private void TickSpawningPawns()
+ {
+ if (Find.TickManager.TicksGame >= nextSpawnTick && pawnsSpawned < Props.spawnCount)
+ {
+ SpawnPawn();
+ pawnsSpawned++;
+
+ if (pawnsSpawned >= Props.spawnCount)
+ {
+ currentState = TowerState.Finished;
+ }
+ else
+ {
+ nextSpawnTick = Find.TickManager.TicksGame + Props.spawnIntervalTicks;
+ }
+ }
+ }
+
+ // 执行爆炸 - 使用标准爆炸方法,支持气体释放
+ private void ExecuteExplosion(int explosionIndex)
+ {
+ if (explosionIndex < 0 || explosionIndex >= Props.explosions.Count)
+ return;
+
+ var explosionData = Props.explosions[explosionIndex];
+
+ // 使用RimWorld标准爆炸方法
+ if (parent.Map != null)
+ {
+ // 调用GenExplosion.DoExplosion方法,包含气体参数
+ GenExplosion.DoExplosion(
+ center: parent.Position, // 爆炸中心
+ map: parent.Map, // 地图
+ radius: explosionData.radius, // 爆炸半径
+ damType: explosionData.damageDef ?? DamageDefOf.Bomb, // 伤害类型
+ instigator: parent, // 爆炸者
+ damAmount: explosionData.damageAmount, // 伤害值
+ armorPenetration: explosionData.armorPenetration, // 穿甲系数
+ explosionSound: explosionData.explosionSound, // 爆炸声音
+ weapon: null, // 武器(可选)
+ projectile: null, // 抛射物(可选)
+ intendedTarget: null, // 预定目标(可选)
+
+ // 爆炸前生成物
+ preExplosionSpawnThingDef: explosionData.preExplosionSpawnThingDef,
+ preExplosionSpawnChance: explosionData.preExplosionSpawnChance,
+ preExplosionSpawnThingCount: explosionData.preExplosionSpawnThingCount,
+
+ // 爆炸后生成物
+ postExplosionSpawnThingDef: explosionData.postExplosionSpawnThingDef,
+ postExplosionSpawnChance: explosionData.postExplosionSpawnChance,
+ postExplosionSpawnThingCount: explosionData.postExplosionSpawnThingCount,
+
+ // 气体释放参数
+ postExplosionGasType: explosionData.postExplosionGasType, // 气体类型
+ postExplosionGasRadiusOverride: explosionData.postExplosionGasRadiusOverride, // 气体半径
+ postExplosionGasAmount: explosionData.postExplosionGasAmount, // 气体数量
+
+ // 其他参数
+ applyDamageToExplosionCellsNeighbors: explosionData.applyDamageToExplosionCellsNeighbors,
+ chanceToStartFire: explosionData.chanceToStartFire,
+ damageFalloff: explosionData.damageFalloff,
+ direction: explosionData.direction,
+ ignoredThings: null,
+ affectedAngle: explosionData.affectedAngle,
+ doVisualEffects: true, // 总是显示视觉效果
+ propagationSpeed: explosionData.propagationSpeed,
+ excludeRadius: explosionData.excludeRadius,
+ doSoundEffects: explosionData.explosionSound != null,
+ screenShakeFactor: explosionData.screenShakeFactor
+ );
+ }
+ }
+
+ // 生成Pawn
+ private void SpawnPawn()
+ {
+ if (cachedPawnKindDefs == null || cachedPawnKindDefs.Count == 0)
+ return;
+
+ // 随机选择Pawn种类
+ PawnKindDef pawnKindDef = cachedPawnKindDefs.RandomElement();
+
+ // 寻找合适的生成位置
+ IntVec3 spawnPosition = FindSpawnPosition();
+ if (!spawnPosition.IsValid)
+ return;
+
+ // 生成Pawn
+ PawnGenerationRequest request = new PawnGenerationRequest(
+ pawnKindDef,
+ faction: parent.Faction,
+ forceGenerateNewPawn: true,
+ canGeneratePawnRelations: false,
+ fixedBiologicalAge: 0,
+ fixedChronologicalAge: 0
+ );
+
+ Pawn pawn = PawnGenerator.GeneratePawn(request);
+
+ // 生成Pawn到地图
+ GenSpawn.Spawn(pawn, spawnPosition, parent.Map);
+
+ // 添加生成效果
+ FleckMaker.ThrowDustPuff(spawnPosition, parent.Map, 2f);
+ }
+
+ // 寻找生成位置
+ private IntVec3 FindSpawnPosition()
+ {
+ Map map = parent.Map;
+
+ // 尝试在建筑周围寻找合适的空单元格
+ for (int radius = 1; radius <= 5; radius++)
+ {
+ CellRect rect = CellRect.CenteredOn(parent.Position, radius);
+ List validCells = new List();
+
+ foreach (IntVec3 cell in rect)
+ {
+ if (cell.InBounds(map) &&
+ cell.Walkable(map) &&
+ !cell.Fogged(map) &&
+ map.thingGrid.ThingsAt(cell).Count() == 0)
+ {
+ validCells.Add(cell);
+ }
+ }
+
+ if (validCells.Count > 0)
+ {
+ return validCells.RandomElement();
+ }
+ }
+
+ // 如果找不到合适位置,使用建筑位置(可能会重叠)
+ return parent.Position;
+ }
+
+ // 获取当前状态描述(用于UI显示)
+ public string GetStatusDescription()
+ {
+ switch (currentState)
+ {
+ case TowerState.Warmup:
+ float progress = (float)ticksInCurrentState / Props.warmupTicks;
+ return $"启动中: {Mathf.RoundToInt(progress * 100)}%";
+
+ case TowerState.Exploding:
+ return $"爆炸阶段: {currentExplosionIndex + 1}/{Props.explosions.Count}";
+
+ case TowerState.SpawningPawns:
+ return $"生成单位: {pawnsSpawned}/{Props.spawnCount}";
+
+ case TowerState.Finished:
+ return "已完成";
+
+ default:
+ return "待机";
+ }
+ }
+
+ // 保存和加载状态
+ public override void PostExposeData()
+ {
+ base.PostExposeData();
+
+ Scribe_Values.Look(ref currentState, "currentState", TowerState.Idle);
+ Scribe_Values.Look(ref ticksInCurrentState, "ticksInCurrentState", 0);
+ Scribe_Values.Look(ref currentExplosionIndex, "currentExplosionIndex", 0);
+ Scribe_Values.Look(ref pawnsSpawned, "pawnsSpawned", 0);
+ Scribe_Values.Look(ref nextSpawnTick, "nextSpawnTick", 0);
+ }
+
+ public override IEnumerable CompGetGizmosExtra()
+ {
+ // 添加调试Gizmo(开发模式下)
+ if (DebugSettings.godMode)
+ {
+ yield return new Command_Action
+ {
+ defaultLabel = "强制启动",
+ action = () => StartWarmup(),
+ icon = TexCommand.ForbidOff
+ };
+
+ yield return new Command_Action
+ {
+ defaultLabel = "跳至爆炸阶段",
+ action = () => StartExplosionPhase(),
+ icon = TexCommand.Attack
+ };
+
+ yield return new Command_Action
+ {
+ defaultLabel = "跳至生成阶段",
+ action = () => StartSpawningPhase(),
+ icon = TexCommand.Attack
+ };
+ }
+ }
+ }
+}
diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_PhaseCombatTower/CompProperties_PhaseCombatTower.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_PhaseCombatTower/CompProperties_PhaseCombatTower.cs
new file mode 100644
index 00000000..11c3eb1e
--- /dev/null
+++ b/Source/WulaFallenEmpire/BuildingComp/WULA_PhaseCombatTower/CompProperties_PhaseCombatTower.cs
@@ -0,0 +1,62 @@
+using System.Collections.Generic;
+using RimWorld;
+using Verse;
+
+namespace WulaFallenEmpire
+{
+ public class CompProperties_PhaseCombatTower : CompProperties
+ {
+ // 阶段1: 启动期
+ public int warmupTicks = 180; // 启动期帧数 (3秒)
+
+ // 阶段2: 爆炸阶段
+ public List explosions = new List();
+
+ // 阶段3: 生成Pawn阶段
+ public List pawnKindDefs = new List(); // Pawn种类列表
+ public int spawnCount = 5; // 需要生成的Pawn数量 (Y)
+ public int spawnIntervalTicks = 120; // 生成间隔帧数 (Z)
+
+ // 爆炸冷却期
+ public int explosionCooldownTicks = 60; // 爆炸之间的冷却时间 (默认1秒)
+
+ public CompProperties_PhaseCombatTower()
+ {
+ compClass = typeof(CompPhaseCombatTower);
+ }
+ }
+
+ // 爆炸数据定义
+ public class ExplosionData
+ {
+ public DamageDef damageDef; // 伤害类型
+ public float radius = 3f; // 爆炸范围
+ public float armorPenetration = 0f; // 穿甲系数
+ public int damageAmount = 30; // 伤害值
+ public SoundDef explosionSound = null; // 爆炸声音
+ public float chanceToStartFire = 0f; // 起火概率
+ public bool damageFalloff = true; // 伤害是否随距离衰减
+
+ // 气体释放参数
+ public GasType? postExplosionGasType = null; // 爆炸后生成的气体类型
+ public float? postExplosionGasRadiusOverride = null; // 气体半径覆盖(如果为null,则使用爆炸半径)
+ public int postExplosionGasAmount = 255; // 气体数量(0-255)
+
+ // 爆炸前/后生成物体
+ public ThingDef preExplosionSpawnThingDef = null; // 爆炸前生成物
+ public float preExplosionSpawnChance = 0f; // 爆炸前生成几率
+ public int preExplosionSpawnThingCount = 1; // 爆炸前生成数量
+
+ public ThingDef postExplosionSpawnThingDef = null; // 爆炸后生成物
+ public float postExplosionSpawnChance = 0f; // 爆炸后生成几率
+ public int postExplosionSpawnThingCount = 1; // 爆炸后生成数量
+
+ // 高级参数
+ public bool applyDamageToExplosionCellsNeighbors = false; // 是否对爆炸单元格邻居造成伤害
+ public float? direction = null; // 爆炸方向(角度,0-360)
+ public FloatRange? affectedAngle = null; // 受影响角度范围
+ public float propagationSpeed = 1f; // 爆炸传播速度
+ public float excludeRadius = 0f; // 排除半径(中心区域不受伤害)
+ public float screenShakeFactor = 1f; // 屏幕震动因子
+ }
+}
diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
index 25862d8d..87e01a73 100644
--- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
+++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
@@ -114,6 +114,8 @@
+
+