diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll
index 018b8abd..f50252aa 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/ThingDefs_Buildings/WULA_Misc_Buildings.xml b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml
index 400634de..203bea16 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
@@ -66,6 +66,56 @@
Thing
+
+ WULA_Watch_Tower
+
+
+ WulaFallenEmpire.Building_MapObserver
+ true
+ Building
+ 50
+ true
+ PassThroughOnly
+ 1
+ (3,3)
+ true
+ (0.56, 0.62, 0.9)
+
+ Wula/Building/WULA_Fake_Fighter_Drone_Building
+ Graphic_Single
+ TransparentPostLight
+ (3,3)
+ (195,195,195,255)
+
+
+ 100
+ 0.5
+ 36000
+ 125
+ 0.65
+
+ WULA_Buildings
+ Normal
+ true
+ true
+ false
+ East
+ true
+ Light
+ BulletImpact_Metal
+ true
+ RealtimeOnly
+ ConstructMetal
+ true
+
+ BuildingDestroyed_Metal_Big
+ true
+ true
+
+
+
+
+
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 0d91548c..5bf8aa46 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
@@ -877,12 +877,6 @@
-
- WULA_Firepower_Minigun_Strafe_Damage
-
- 25
- Explosion_Bomb
-
WULA_EnergyLance_Base
diff --git a/Source/WulaFallenEmpire/BuildingComp/Building_MapObserver.cs b/Source/WulaFallenEmpire/BuildingComp/Building_MapObserver.cs
new file mode 100644
index 00000000..63d3a62a
--- /dev/null
+++ b/Source/WulaFallenEmpire/BuildingComp/Building_MapObserver.cs
@@ -0,0 +1,230 @@
+using System.Collections.Generic;
+using RimWorld;
+using RimWorld.Planet;
+using UnityEngine;
+using Verse;
+
+namespace WulaFallenEmpire
+{
+ public class Building_MapObserver : Building
+ {
+ public MapParent observedMap;
+
+ private CompPowerTrader compPower;
+
+ // 静态列表跟踪所有活跃的观察者
+ public static HashSet activeObservers = new HashSet();
+
+ public override void SpawnSetup(Map map, bool respawningAfterLoad)
+ {
+ base.SpawnSetup(map, respawningAfterLoad);
+ compPower = this.TryGetComp();
+
+ // 如果正在观察地图且建筑正常,注册到活跃列表
+ if (observedMap != null && (compPower == null || compPower.PowerOn))
+ {
+ activeObservers.Add(this);
+ }
+ }
+
+ public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish)
+ {
+ // 建筑被销毁时停止监测
+ DisposeObservedMapIfEmpty();
+ activeObservers.Remove(this);
+ base.DeSpawn(mode);
+ }
+
+ public override IEnumerable GetGizmos()
+ {
+ foreach (Gizmo gizmo in base.GetGizmos())
+ {
+ yield return gizmo;
+ }
+
+ // 只有在有电力且属于玩家时才显示控制按钮
+ if (Faction == Faction.OfPlayer && (compPower == null || compPower.PowerOn))
+ {
+ // 开始监测按钮
+ yield return new Command_Action
+ {
+ defaultLabel = "开始监测地图",
+ defaultDesc = "选择一个世界位置进行监测",
+ icon = ContentFinder.Get("UI/Commands/ShowMap"),
+ action = delegate
+ {
+ CameraJumper.TryShowWorld();
+ Find.WorldTargeter.BeginTargeting(ChooseWorldTarget, canTargetTiles: true);
+ }
+ };
+
+ // 如果正在监测地图,显示停止按钮
+ if (observedMap != null)
+ {
+ if (observedMap.Destroyed)
+ {
+ observedMap = null;
+ activeObservers.Remove(this);
+ }
+ else
+ {
+ yield return new Command_Action
+ {
+ defaultLabel = "停止监测",
+ defaultDesc = $"停止监测 {observedMap.Label}",
+ icon = ContentFinder.Get("UI/Commands/DesirePower"),
+ action = delegate
+ {
+ StopObserving();
+ }
+ };
+ }
+ }
+ }
+ }
+
+ private bool ChooseWorldTarget(GlobalTargetInfo target)
+ {
+ DisposeObservedMapIfEmpty();
+
+ if (target.WorldObject != null && target.WorldObject is MapParent mapParent)
+ {
+ // 开始监测选中的地图
+ observedMap = mapParent;
+ activeObservers.Add(this);
+
+ // 确保地图被生成并取消迷雾
+ LongEventHandler.QueueLongEvent(delegate
+ {
+ Map map = GetOrGenerateMapUtility.GetOrGenerateMap(target.Tile, null);
+ if (map != null)
+ {
+ // 取消迷雾获得完整视野
+ map.fogGrid.ClearAllFog();
+
+ // 记录日志以便调试
+ Log.Message($"[MapObserver] 开始监测地图: {mapParent.Label} at tile {target.Tile}");
+ }
+ }, "GeneratingMap", doAsynchronously: false, null);
+
+ return true;
+ }
+
+ // 在空地创建新监测点
+ if (target.WorldObject == null && !Find.World.Impassable(target.Tile))
+ {
+ LongEventHandler.QueueLongEvent(delegate
+ {
+ // 创建新的玩家定居点用于监测
+ SettleUtility.AddNewHome(target.Tile, Faction.OfPlayer);
+ Map map = GetOrGenerateMapUtility.GetOrGenerateMap(target.Tile, Find.World.info.initialMapSize, null);
+ observedMap = map.Parent;
+ activeObservers.Add(this);
+
+ // 取消迷雾获得完整视野
+ map.fogGrid.ClearAllFog();
+
+ // 设置监测点名称
+ if (observedMap is Settlement settlement)
+ {
+ settlement.Name = $"监测点-{thingIDNumber}";
+ Log.Message($"[MapObserver] 创建新监测点: {settlement.Name} at tile {target.Tile}");
+ }
+ else
+ {
+ // 如果observedMap不是Settlement,使用Label属性
+ Log.Message($"[MapObserver] 创建新监测点: {observedMap.Label} at tile {target.Tile}");
+ }
+
+ }, "GeneratingMap", doAsynchronously: false, null);
+
+ return true;
+ }
+
+ Messages.Message("无法监测该位置", MessageTypeDefOf.RejectInput);
+ return false;
+ }
+
+ private void StopObserving()
+ {
+ DisposeObservedMapIfEmpty();
+ observedMap = null;
+ activeObservers.Remove(this);
+ }
+
+ private void DisposeObservedMapIfEmpty()
+ {
+ if (observedMap != null && observedMap.Map != null &&
+ !observedMap.Map.mapPawns.AnyColonistSpawned &&
+ !observedMap.Map.listerBuildings.allBuildingsColonist.Any() &&
+ observedMap.Faction == Faction.OfPlayer)
+ {
+ // 只有在没有殖民者、没有玩家建筑的情况下才销毁
+ Current.Game.DeinitAndRemoveMap(observedMap.Map, notifyPlayer: false);
+ if (!observedMap.Destroyed)
+ {
+ Find.World.worldObjects.Remove(observedMap);
+ }
+ Log.Message($"[MapObserver] 清理空置监测地图: {observedMap.Label}");
+ }
+ }
+
+ protected override void ReceiveCompSignal(string signal)
+ {
+ base.ReceiveCompSignal(signal);
+
+ // 断电或被关闭时停止监测
+ if (observedMap != null && (signal == "PowerTurnedOff" || signal == "FlickedOff"))
+ {
+ Log.Message($"[MapObserver] 电力中断,停止监测: {observedMap.Label}");
+ StopObserving();
+ }
+ // 恢复电力时重新注册
+ else if (observedMap != null && (signal == "PowerTurnedOn" || signal == "FlickedOn"))
+ {
+ activeObservers.Add(this);
+ Log.Message($"[MapObserver] 电力恢复,继续监测: {observedMap.Label}");
+ }
+ }
+
+ public override string GetInspectString()
+ {
+ string text = base.GetInspectString();
+
+ if (observedMap != null)
+ {
+ if (!text.NullOrEmpty())
+ {
+ text += "\n";
+ }
+ text += $"正在监测: {observedMap.Label}";
+
+ // 显示电力状态
+ if (compPower != null && !compPower.PowerOn)
+ {
+ text += " (电力中断)";
+ }
+ }
+
+ return text;
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_References.Look(ref observedMap, "observedMap");
+
+ // 加载后重新注册到活跃列表
+ if (Scribe.mode == LoadSaveMode.PostLoadInit && observedMap != null && (compPower == null || compPower.PowerOn))
+ {
+ activeObservers.Add(this);
+ }
+ }
+
+ // 检查这个观察者是否正在监测指定的地图
+ public bool IsObservingMap(MapParent mapParent)
+ {
+ return observedMap == mapParent && (compPower == null || compPower.PowerOn);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_FlyOverBeacon/Building_FlyOverBeacon.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_FlyOverBeacon/Building_FlyOverBeacon.cs
new file mode 100644
index 00000000..db52a936
--- /dev/null
+++ b/Source/WulaFallenEmpire/BuildingComp/WULA_FlyOverBeacon/Building_FlyOverBeacon.cs
@@ -0,0 +1,480 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using Verse;
+using Verse.AI;
+using Verse.Sound;
+using RimWorld;
+using RimWorld.Planet;
+
+namespace WulaFallenEmpire
+{
+ [StaticConstructorOnStartup]
+ public class Building_FlyOverBeacon : Building
+ {
+ public GlobalTargetInfo flyOverTarget;
+ public FlyOverConfig flyOverConfig;
+ public static readonly Texture2D CallFlyOverTex = ContentFinder.Get("UI/Commands/CallFlyOver", true);
+
+ private CompPowerTrader powerComp;
+ private CompRefuelable refuelableComp;
+ private int cooldownTicksLeft;
+
+ public bool IsReady => (powerComp == null || powerComp.PowerOn) &&
+ (refuelableComp == null || refuelableComp.HasFuel) &&
+ cooldownTicksLeft <= 0;
+
+ public override void SpawnSetup(Map map, bool respawningAfterLoad)
+ {
+ base.SpawnSetup(map, respawningAfterLoad);
+ powerComp = this.TryGetComp();
+ refuelableComp = this.TryGetComp();
+
+ if (!respawningAfterLoad)
+ {
+ flyOverTarget = GlobalTargetInfo.Invalid;
+ flyOverConfig = new FlyOverConfig();
+ }
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_TargetInfo.Look(ref flyOverTarget, "flyOverTarget");
+ Scribe_Deep.Look(ref flyOverConfig, "flyOverConfig");
+ Scribe_Values.Look(ref cooldownTicksLeft, "cooldownTicksLeft", 0);
+ }
+
+ protected override void Tick()
+ {
+ base.Tick();
+
+ // 冷却计时
+ if (cooldownTicksLeft > 0)
+ cooldownTicksLeft--;
+
+ // 自动执行已设定的飞越任务
+ if (flyOverTarget.IsValid && IsReady)
+ {
+ ExecuteFlyOverMission();
+ }
+ }
+
+ public override string GetInspectString()
+ {
+ StringBuilder sb = new StringBuilder(base.GetInspectString());
+
+ if (cooldownTicksLeft > 0)
+ {
+ if (sb.Length > 0) sb.AppendLine();
+ sb.Append("飞越信标冷却中: ".Translate() + cooldownTicksLeft.ToStringTicksToPeriod());
+ }
+
+ if (flyOverTarget.IsValid)
+ {
+ if (sb.Length > 0) sb.AppendLine();
+ sb.Append("已锁定目标: ".Translate() + flyOverTarget.Label);
+ }
+
+ if (!IsReady)
+ {
+ if (sb.Length > 0) sb.AppendLine();
+ if (powerComp != null && !powerComp.PowerOn)
+ sb.Append("需要电力".Translate());
+ else if (refuelableComp != null && !refuelableComp.HasFuel)
+ sb.Append("需要燃料".Translate());
+ else if (cooldownTicksLeft > 0)
+ sb.Append("冷却中".Translate());
+ }
+
+ return sb.ToString();
+ }
+
+ public override IEnumerable GetGizmos()
+ {
+ foreach (var g in base.GetGizmos())
+ {
+ yield return g;
+ }
+
+ // 召唤飞越命令
+ Command_Action callFlyOver = new Command_Action
+ {
+ defaultLabel = "召唤跨图飞越",
+ defaultDesc = "在世界地图上选择目标位置召唤飞越单位",
+ icon = CallFlyOverTex,
+ action = StartChoosingFlyOverTarget
+ };
+
+ if (!IsReady)
+ {
+ callFlyOver.Disable(GetDisabledReason());
+ }
+ yield return callFlyOver;
+
+ // 配置飞越参数命令
+ if (flyOverTarget.IsValid)
+ {
+ Command_Action configureFlyOver = new Command_Action
+ {
+ defaultLabel = "配置飞越参数",
+ defaultDesc = "调整飞越单位的类型和行为",
+ icon = ContentFinder.Get("UI/Commands/Configure", true),
+ action = OpenConfigurationDialog
+ };
+ yield return configureFlyOver;
+
+ // 清除目标命令
+ Command_Action clearTarget = new Command_Action
+ {
+ defaultLabel = "清除目标",
+ defaultDesc = "清除已锁定的飞越目标",
+ icon = ContentFinder.Get("UI/Designators/Cancel", true),
+ action = () => flyOverTarget = GlobalTargetInfo.Invalid
+ };
+ yield return clearTarget;
+ }
+ }
+
+ private string GetDisabledReason()
+ {
+ if (powerComp != null && !powerComp.PowerOn)
+ return "需要电力";
+ if (refuelableComp != null && !refuelableComp.HasFuel)
+ return "需要燃料";
+ if (cooldownTicksLeft > 0)
+ return "冷却中";
+ return "无法使用";
+ }
+
+ private void StartChoosingFlyOverTarget()
+ {
+ CameraJumper.TryJump(CameraJumper.GetWorldTarget(this));
+ Find.WorldSelector.ClearSelection();
+
+ Find.WorldTargeter.BeginTargeting(
+ ChooseWorldTarget,
+ canTargetTiles: true,
+ targeterMouseAttachment: CallFlyOverTex,
+ extraLabelGetter: GetExtraTargetingLabel
+ );
+ }
+
+ private string GetExtraTargetingLabel(GlobalTargetInfo target)
+ {
+ if (target.IsValid)
+ {
+ return "召唤飞越至: " + target.Label;
+ }
+ return "选择飞越目标位置";
+ }
+
+ private bool ChooseWorldTarget(GlobalTargetInfo target)
+ {
+ if (!target.IsValid)
+ {
+ Messages.Message("无效的目标位置", MessageTypeDefOf.RejectInput);
+ return false;
+ }
+
+ if (target.Tile == this.Map.Tile)
+ {
+ Messages.Message("无法在同一地图召唤飞越", MessageTypeDefOf.RejectInput);
+ return false;
+ }
+
+ // 处理不同类型的目标
+ if (target.WorldObject is MapParent mapParent && mapParent.HasMap)
+ {
+ // 切换到目标地图选择具体飞越路径
+ var originalMap = this.Map;
+ Action onFinished = () => {
+ if (Current.Game.CurrentMap != originalMap)
+ Current.Game.CurrentMap = originalMap;
+ };
+
+ Current.Game.CurrentMap = mapParent.Map;
+ Find.Targeter.BeginTargeting(
+ new TargetingParameters
+ {
+ canTargetLocations = true,
+ canTargetPawns = false,
+ canTargetBuildings = false,
+ mapObjectTargetsMustBeAutoAttackable = false
+ },
+ (LocalTargetInfo localTarget) =>
+ {
+ // 设置飞越配置
+ flyOverTarget = new GlobalTargetInfo(localTarget.Cell, mapParent.Map);
+ flyOverConfig.targetCell = localTarget.Cell;
+ flyOverConfig.targetMap = mapParent.Map;
+
+ // 自动计算飞越路径
+ CalculateFlyOverPath(mapParent.Map, localTarget.Cell);
+ },
+ null, onFinished, CallFlyOverTex, true);
+ }
+ else
+ {
+ // 空地目标,生成临时地图
+ flyOverTarget = target;
+ flyOverConfig.targetTile = target.Tile;
+ CalculateFlyOverPathForTile(target.Tile);
+ }
+
+ return true;
+ }
+
+ private void CalculateFlyOverPath(Map targetMap, IntVec3 targetCell)
+ {
+ // 计算从地图边缘到目标点的飞越路径
+ Vector3 targetPos = targetCell.ToVector3();
+
+ // 随机选择进入方向
+ Vector3[] approachDirections = {
+ new Vector3(1, 0, 0), // 从右边进入
+ new Vector3(-1, 0, 0), // 从左边进入
+ new Vector3(0, 0, 1), // 从上边进入
+ new Vector3(0, 0, -1) // 从下边进入
+ };
+
+ Vector3 approachDir = approachDirections.RandomElement();
+ Vector3 startPos = FindMapEdgePosition(targetMap, approachDir);
+ Vector3 endPos = FindMapEdgePosition(targetMap, -approachDir); // 从对面飞出
+
+ flyOverConfig.startPos = startPos.ToIntVec3();
+ flyOverConfig.endPos = endPos.ToIntVec3();
+ flyOverConfig.approachType = FlyOverApproachType.StraightLine;
+
+ Log.Message($"Calculated flyover path: {flyOverConfig.startPos} -> {targetCell} -> {flyOverConfig.endPos}");
+ }
+
+ private void CalculateFlyOverPathForTile(int tile)
+ {
+ // 为地图瓦片计算默认飞越路径(穿越地图中心)
+ Map targetMap = GetOrGenerateMapUtility.GetOrGenerateMap(tile, Find.World.info.initialMapSize, null);
+ if (targetMap != null)
+ {
+ targetMap.fogGrid.ClearAllFog(); // 获得视野
+
+ IntVec3 center = targetMap.Center;
+ CalculateFlyOverPath(targetMap, center);
+ }
+ }
+
+ private IntVec3 FindMapEdgePosition(Map map, Vector3 direction)
+ {
+ // 找到地图边缘位置
+ Vector3 center = map.Center.ToVector3();
+ Vector3 edgePos = center;
+ float maxDistance = Mathf.Max(map.Size.x, map.Size.z) * 0.6f;
+
+ for (int i = 1; i <= maxDistance; i++)
+ {
+ Vector3 testPos = center + direction.normalized * i;
+ IntVec3 testCell = new IntVec3((int)testPos.x, 0, (int)testPos.z);
+
+ if (!testCell.InBounds(map))
+ {
+ // 找到最近的边界内单元格
+ return FindClosestValidPosition(testCell, map);
+ }
+ }
+
+ return map.Center;
+ }
+
+ private IntVec3 FindClosestValidPosition(IntVec3 invalidPos, Map map)
+ {
+ for (int radius = 1; radius <= 10; radius++)
+ {
+ foreach (IntVec3 offset in GenRadial.RadialPatternInRadius(radius))
+ {
+ IntVec3 testPos = invalidPos + offset;
+ if (testPos.InBounds(map))
+ return testPos;
+ }
+ }
+ return map.Center;
+ }
+
+ private void OpenConfigurationDialog()
+ {
+ // 打开飞越配置对话框
+ List options = new List
+ {
+ new FloatMenuOption("标准侦察飞越", () => ConfigureStandardRecon()),
+ new FloatMenuOption("地面扫射飞越", () => ConfigureGroundStrafing()),
+ new FloatMenuOption("监视巡逻飞越", () => ConfigureSurveillance()),
+ new FloatMenuOption("货运投送飞越", () => ConfigureCargoDrop())
+ };
+
+ Find.WindowStack.Add(new FloatMenu(options));
+ }
+
+ private void ConfigureStandardRecon()
+ {
+ flyOverConfig.flyOverType = FlyOverType.Standard;
+ flyOverConfig.flyOverDef = DefDatabase.GetNamedSilentFail("ARA_HiveScout");
+ flyOverConfig.altitude = 20f;
+ flyOverConfig.flightSpeed = 1.2f;
+ Messages.Message("已配置为标准侦察飞越", MessageTypeDefOf.TaskCompletion);
+ }
+
+ private void ConfigureGroundStrafing()
+ {
+ flyOverConfig.flyOverType = FlyOverType.GroundStrafing;
+ flyOverConfig.flyOverDef = DefDatabase.GetNamedSilentFail("ARA_HiveGunship");
+ flyOverConfig.altitude = 10f;
+ flyOverConfig.flightSpeed = 0.8f;
+ flyOverConfig.enableStrafing = true;
+ Messages.Message("已配置为地面扫射飞越", MessageTypeDefOf.TaskCompletion);
+ }
+
+ private void ConfigureSurveillance()
+ {
+ flyOverConfig.flyOverType = FlyOverType.Surveillance;
+ flyOverConfig.flyOverDef = DefDatabase.GetNamedSilentFail("ARA_HiveObserver");
+ flyOverConfig.altitude = 25f;
+ flyOverConfig.flightSpeed = 0.6f;
+ flyOverConfig.enableSurveillance = true;
+ Messages.Message("已配置为监视巡逻飞越", MessageTypeDefOf.TaskCompletion);
+ }
+
+ private void ConfigureCargoDrop()
+ {
+ flyOverConfig.flyOverType = FlyOverType.CargoDrop;
+ flyOverConfig.flyOverDef = DefDatabase.GetNamedSilentFail("ARA_HiveTransport");
+ flyOverConfig.altitude = 15f;
+ flyOverConfig.flightSpeed = 1.0f;
+ flyOverConfig.dropContentsOnImpact = true;
+ Messages.Message("已配置为货运投送飞越", MessageTypeDefOf.TaskCompletion);
+ }
+
+ public void ExecuteFlyOverMission()
+ {
+ if (!IsReady) return;
+
+ try
+ {
+ Log.Message($"[FlyOverBeacon] Executing flyover mission to {flyOverTarget.Label}");
+
+ // 创建世界飞越载体
+ WorldObject_FlyOverCarrier carrier = (WorldObject_FlyOverCarrier)WorldObjectMaker.MakeWorldObject(
+ DefDatabase.GetNamed("FlyOverCarrier")
+ );
+
+ carrier.Tile = this.Map.Tile;
+ carrier.destinationTile = flyOverTarget.Tile;
+ carrier.flyOverConfig = flyOverConfig.Clone();
+ carrier.sourceBeacon = this;
+
+ Find.WorldObjects.Add(carrier);
+
+ // 本地视觉效果
+ CreateLocalLaunchEffects();
+
+ // 资源消耗
+ if (refuelableComp != null)
+ refuelableComp.ConsumeFuel(1);
+
+ // 进入冷却
+ cooldownTicksLeft = 6000; // 1分钟冷却
+
+ Messages.Message($"飞越单位已派遣至 {flyOverTarget.Label}", MessageTypeDefOf.PositiveEvent);
+
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"Error executing flyover mission: {ex}");
+ Messages.Message("飞越任务执行失败", MessageTypeDefOf.NegativeEvent);
+ }
+ }
+
+ private void CreateLocalLaunchEffects()
+ {
+ // 发射视觉效果
+ MoteMaker.MakeStaticMote(this.DrawPos, this.Map, ThingDefOf.Mote_ExplosionFlash, 3f);
+
+ for (int i = 0; i < 3; i++)
+ {
+ FleckMaker.ThrowSmoke(this.DrawPos, this.Map, 2f);
+ FleckMaker.ThrowLightningGlow(this.DrawPos, this.Map, 1.5f);
+ }
+
+ // 发射音效
+ SoundDefOf.PsychicPulseGlobal.PlayOneShot(new TargetInfo(this.Position, this.Map));
+ }
+ }
+
+ // 飞越配置类
+ public class FlyOverConfig : IExposable
+ {
+ public ThingDef flyOverDef;
+ public FlyOverType flyOverType = FlyOverType.Standard;
+ public IntVec3 startPos;
+ public IntVec3 endPos;
+ public IntVec3 targetCell;
+ public Map targetMap;
+ public int targetTile = -1;
+ public float flightSpeed = 1f;
+ public float altitude = 15f;
+ public bool dropContentsOnImpact = false;
+ public bool enableStrafing = false;
+ public bool enableSurveillance = false;
+ public FlyOverApproachType approachType = FlyOverApproachType.StraightLine;
+
+ public void ExposeData()
+ {
+ Scribe_Defs.Look(ref flyOverDef, "flyOverDef");
+ Scribe_Values.Look(ref flyOverType, "flyOverType", FlyOverType.Standard);
+ Scribe_Values.Look(ref startPos, "startPos");
+ Scribe_Values.Look(ref endPos, "endPos");
+ Scribe_Values.Look(ref targetCell, "targetCell");
+ Scribe_References.Look(ref targetMap, "targetMap");
+ Scribe_Values.Look(ref targetTile, "targetTile", -1);
+ Scribe_Values.Look(ref flightSpeed, "flightSpeed", 1f);
+ Scribe_Values.Look(ref altitude, "altitude", 15f);
+ Scribe_Values.Look(ref dropContentsOnImpact, "dropContentsOnImpact", false);
+ Scribe_Values.Look(ref enableStrafing, "enableStrafing", false);
+ Scribe_Values.Look(ref enableSurveillance, "enableSurveillance", false);
+ Scribe_Values.Look(ref approachType, "approachType", FlyOverApproachType.StraightLine);
+ }
+
+ public FlyOverConfig Clone()
+ {
+ return new FlyOverConfig
+ {
+ flyOverDef = this.flyOverDef,
+ flyOverType = this.flyOverType,
+ startPos = this.startPos,
+ endPos = this.endPos,
+ targetCell = this.targetCell,
+ targetMap = this.targetMap,
+ targetTile = this.targetTile,
+ flightSpeed = this.flightSpeed,
+ altitude = this.altitude,
+ dropContentsOnImpact = this.dropContentsOnImpact,
+ enableStrafing = this.enableStrafing,
+ enableSurveillance = this.enableSurveillance,
+ approachType = this.approachType
+ };
+ }
+ }
+
+ public enum FlyOverType
+ {
+ Standard,
+ GroundStrafing,
+ Surveillance,
+ CargoDrop
+ }
+
+ public enum FlyOverApproachType
+ {
+ StraightLine,
+ CirclePattern,
+ FigureEight
+ }
+}
diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_FlyOverBeacon/WorldObject_FlyOverCarrier.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_FlyOverBeacon/WorldObject_FlyOverCarrier.cs
new file mode 100644
index 00000000..92a533b6
--- /dev/null
+++ b/Source/WulaFallenEmpire/BuildingComp/WULA_FlyOverBeacon/WorldObject_FlyOverCarrier.cs
@@ -0,0 +1,241 @@
+using RimWorld.Planet;
+using UnityEngine;
+using Verse;
+using RimWorld;
+using System.Collections.Generic;
+
+namespace WulaFallenEmpire
+{
+ public class WorldObject_FlyOverCarrier : WorldObject
+ {
+ public int destinationTile = -1;
+ public FlyOverConfig flyOverConfig;
+ public Building_FlyOverBeacon sourceBeacon;
+
+ private int initialTile = -1;
+ private float traveledPct;
+ private const float TravelSpeed = 0.0001f; // 比导弹慢,适合侦察
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_Values.Look(ref destinationTile, "destinationTile", -1);
+ Scribe_Deep.Look(ref flyOverConfig, "flyOverConfig");
+ Scribe_References.Look(ref sourceBeacon, "sourceBeacon");
+ Scribe_Values.Look(ref initialTile, "initialTile", -1);
+ Scribe_Values.Look(ref traveledPct, "traveledPct", 0f);
+ }
+
+ public override void PostAdd()
+ {
+ base.PostAdd();
+ this.initialTile = this.Tile;
+ Log.Message($"[FlyOverCarrier] Launched from tile {initialTile} to {destinationTile}");
+ }
+
+ private Vector3 StartPos => Find.WorldGrid.GetTileCenter(this.initialTile);
+ private Vector3 EndPos => Find.WorldGrid.GetTileCenter(this.destinationTile);
+
+ public override Vector3 DrawPos => Vector3.Slerp(StartPos, EndPos, traveledPct);
+
+ protected override void Tick()
+ {
+ base.Tick();
+
+ if (this.destinationTile < 0)
+ {
+ Log.Error("FlyOverCarrier has invalid destination tile");
+ Find.WorldObjects.Remove(this);
+ return;
+ }
+
+ float distance = GenMath.SphericalDistance(StartPos.normalized, EndPos.normalized);
+ if (distance > 0)
+ {
+ traveledPct += TravelSpeed / distance;
+ }
+ else
+ {
+ traveledPct = 1;
+ }
+
+ // 更新世界图标位置
+ if (Find.WorldRenderer != null)
+ {
+ Find.WorldRenderer.Notify_WorldObjectPosChanged(this);
+ }
+
+ if (traveledPct >= 1f)
+ {
+ Arrived();
+ }
+ }
+
+ private void Arrived()
+ {
+ Log.Message($"[FlyOverCarrier] Arrived at destination tile {destinationTile}");
+
+ Map targetMap = GetTargetMap();
+ if (targetMap != null)
+ {
+ CreateFlyOverInTargetMap(targetMap);
+ }
+ else
+ {
+ Log.Warning($"[FlyOverCarrier] Could not find or generate map for tile {destinationTile}");
+ }
+
+ // 通知源信标任务完成
+ if (sourceBeacon != null && !sourceBeacon.Destroyed)
+ {
+ Messages.Message($"飞越单位已到达 {flyOverConfig.targetMap?.Parent?.Label ?? "目标区域"}",
+ sourceBeacon, MessageTypeDefOf.PositiveEvent);
+ }
+
+ Find.WorldObjects.Remove(this);
+ }
+
+ private Map GetTargetMap()
+ {
+ // 优先使用配置中的目标地图
+ if (flyOverConfig.targetMap != null && !flyOverConfig.targetMap.Destroyed)
+ {
+ return flyOverConfig.targetMap;
+ }
+
+ // 生成临时地图
+ return GetOrGenerateMapUtility.GetOrGenerateMap(destinationTile, Find.World.info.initialMapSize, null);
+ }
+
+ private void CreateFlyOverInTargetMap(Map targetMap)
+ {
+ if (flyOverConfig.flyOverDef == null)
+ {
+ Log.Warning("[FlyOverCarrier] No fly over def specified, using default");
+ flyOverConfig.flyOverDef = DefDatabase.GetNamedSilentFail("ARA_HiveScout");
+ }
+
+ // 确保目标地图有视野
+ targetMap.fogGrid.ClearAllFog();
+
+ // 验证并调整飞越路径
+ IntVec3 startPos = ValidateAndAdjustPosition(flyOverConfig.startPos, targetMap);
+ IntVec3 endPos = ValidateAndAdjustPosition(flyOverConfig.endPos, targetMap);
+
+ Log.Message($"[FlyOverCarrier] Creating flyover: {startPos} -> {endPos} in {targetMap}");
+
+ // 创建飞越物体
+ FlyOver flyOver = FlyOver.MakeFlyOver(
+ flyOverConfig.flyOverDef,
+ startPos,
+ endPos,
+ targetMap,
+ flyOverConfig.flightSpeed,
+ flyOverConfig.altitude,
+ casterPawn: null
+ );
+
+ // 配置飞越属性
+ flyOver.spawnContentsOnImpact = flyOverConfig.dropContentsOnImpact;
+ flyOver.playFlyOverSound = true;
+ flyOver.faction = sourceBeacon?.Faction;
+
+ // 配置特殊组件
+ ConfigureFlyOverComponents(flyOver);
+
+ // 创建到达视觉效果
+ CreateArrivalEffects(targetMap, startPos);
+ }
+
+ private IntVec3 ValidateAndAdjustPosition(IntVec3 pos, Map map)
+ {
+ if (pos.IsValid && pos.InBounds(map))
+ return pos;
+
+ // 如果位置无效,使用地图边缘位置
+ return CellFinder.RandomEdgeCell(Rand.Range(0, 4), map);
+ }
+
+ private void ConfigureFlyOverComponents(FlyOver flyOver)
+ {
+ // 地面扫射配置
+ if (flyOverConfig.enableStrafing)
+ {
+ CompGroundStrafing strafingComp = flyOver.GetComp();
+ if (strafingComp != null)
+ {
+ // 计算扫射区域
+ Vector3 flightDirection = (flyOverConfig.endPos.ToVector3() - flyOverConfig.startPos.ToVector3()).normalized;
+ List impactCells = CalculateStrafingImpactCells(flyOverConfig.targetCell, flightDirection);
+ List confirmedTargets = PreprocessStrafingTargets(impactCells, 0.7f);
+
+ strafingComp.SetConfirmedTargets(confirmedTargets);
+ }
+ }
+
+ // 监视功能配置
+ if (flyOverConfig.enableSurveillance)
+ {
+ // 可以在这里添加监视组件的配置
+ Log.Message("[FlyOverCarrier] Surveillance mode configured");
+ }
+ }
+
+ private List CalculateStrafingImpactCells(IntVec3 targetCell, Vector3 flightDirection)
+ {
+ // 简化的扫射区域计算
+ List cells = new List();
+ Map map = Find.CurrentMap;
+
+ if (map != null)
+ {
+ Vector3 perpendicular = new Vector3(-flightDirection.z, 0f, flightDirection.x).normalized;
+
+ for (int i = -2; i <= 2; i++)
+ {
+ for (int j = -5; j <= 5; j++)
+ {
+ Vector3 offset = perpendicular * i + flightDirection * j;
+ IntVec3 cell = targetCell + new IntVec3((int)offset.x, 0, (int)offset.z);
+
+ if (cell.InBounds(map))
+ {
+ cells.Add(cell);
+ }
+ }
+ }
+ }
+
+ return cells;
+ }
+
+ private List PreprocessStrafingTargets(List potentialTargets, float fireChance)
+ {
+ List confirmedTargets = new List();
+
+ foreach (IntVec3 cell in potentialTargets)
+ {
+ if (Rand.Value <= fireChance)
+ {
+ confirmedTargets.Add(cell);
+ }
+ }
+
+ return confirmedTargets;
+ }
+
+ private void CreateArrivalEffects(Map targetMap, IntVec3 entryPos)
+ {
+ // 进入视觉效果
+ MoteMaker.MakeStaticMote(entryPos.ToVector3Shifted(), targetMap, ThingDefOf.Mote_PsycastAreaEffect, 2f);
+
+ for (int i = 0; i < 5; i++)
+ {
+ FleckMaker.ThrowAirPuffUp(entryPos.ToVector3Shifted(), targetMap);
+ }
+
+ // 进入音效
+ SoundDefOf.PsychicPulse.PlayOneShot(new TargetInfo(entryPos, targetMap));
+ }
+ }
+}
diff --git a/Source/WulaFallenEmpire/EventSystem/Effect.cs b/Source/WulaFallenEmpire/EventSystem/Effect/EffectBase.cs
similarity index 95%
rename from Source/WulaFallenEmpire/EventSystem/Effect.cs
rename to Source/WulaFallenEmpire/EventSystem/Effect/EffectBase.cs
index 1645269b..02288374 100644
--- a/Source/WulaFallenEmpire/EventSystem/Effect.cs
+++ b/Source/WulaFallenEmpire/EventSystem/Effect/EffectBase.cs
@@ -7,13 +7,13 @@ using RimWorld;
namespace WulaFallenEmpire
{
- public abstract class Effect
+ public abstract class EffectBase
{
public float weight = 1.0f;
public abstract void Execute(Window dialog = null);
}
- public class Effect_OpenCustomUI : Effect
+ public class Effect_OpenCustomUI : EffectBase
{
public string defName;
public int delayTicks = 0;
@@ -88,7 +88,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_CloseDialog : Effect
+ public class Effect_CloseDialog : EffectBase
{
public override void Execute(Window dialog = null)
{
@@ -96,7 +96,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_ShowMessage : Effect
+ public class Effect_ShowMessage : EffectBase
{
public string message;
public MessageTypeDef messageTypeDef;
@@ -111,7 +111,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_FireIncident : Effect
+ public class Effect_FireIncident : EffectBase
{
public IncidentDef incident;
@@ -136,7 +136,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_ChangeFactionRelation : Effect
+ public class Effect_ChangeFactionRelation : EffectBase
{
public FactionDef faction;
public int goodwillChange;
@@ -160,7 +160,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_SetVariable : Effect
+ public class Effect_SetVariable : EffectBase
{
public string name;
public string value;
@@ -195,7 +195,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_ChangeFactionRelation_FromVariable : Effect
+ public class Effect_ChangeFactionRelation_FromVariable : EffectBase
{
public FactionDef faction;
public string goodwillVariableName;
@@ -220,7 +220,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_SpawnPawnAndStore : Effect
+ public class Effect_SpawnPawnAndStore : EffectBase
{
public PawnKindDef kindDef;
public int count = 1;
@@ -260,7 +260,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_GiveThing : Effect
+ public class Effect_GiveThing : EffectBase
{
public ThingDef thingDef;
public int count = 1;
@@ -290,7 +290,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_SpawnPawn : Effect
+ public class Effect_SpawnPawn : EffectBase
{
public PawnKindDef kindDef;
public int count = 1;
@@ -350,7 +350,7 @@ namespace WulaFallenEmpire
Divide
}
- public class Effect_ModifyVariable : Effect
+ public class Effect_ModifyVariable : EffectBase
{
public string name;
public string value;
@@ -431,7 +431,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_ClearVariable : Effect
+ public class Effect_ClearVariable : EffectBase
{
public string name;
@@ -446,7 +446,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_AddQuest : Effect
+ public class Effect_AddQuest : EffectBase
{
public QuestScriptDef quest;
@@ -465,7 +465,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_FinishResearch : Effect
+ public class Effect_FinishResearch : EffectBase
{
public ResearchProjectDef research;
@@ -480,7 +480,7 @@ namespace WulaFallenEmpire
Find.ResearchManager.FinishProject(research);
}
}
- public class Effect_TriggerRaid : Effect
+ public class Effect_TriggerRaid : EffectBase
{
public float points;
public FactionDef faction;
@@ -559,7 +559,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_CheckFactionGoodwill : Effect
+ public class Effect_CheckFactionGoodwill : EffectBase
{
public FactionDef factionDef;
public string variableName;
@@ -589,7 +589,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_StoreRealPlayTime : Effect
+ public class Effect_StoreRealPlayTime : EffectBase
{
public string variableName;
@@ -608,7 +608,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_StoreDaysPassed : Effect
+ public class Effect_StoreDaysPassed : EffectBase
{
public string variableName;
@@ -627,7 +627,7 @@ namespace WulaFallenEmpire
}
}
- public class Effect_StoreColonyWealth : Effect
+ public class Effect_StoreColonyWealth : EffectBase
{
public string variableName;
diff --git a/Source/WulaFallenEmpire/EventSystem/EventDef.cs b/Source/WulaFallenEmpire/EventSystem/EventDef.cs
index a9d6a589..d8c25a1d 100644
--- a/Source/WulaFallenEmpire/EventSystem/EventDef.cs
+++ b/Source/WulaFallenEmpire/EventSystem/EventDef.cs
@@ -92,14 +92,14 @@ namespace WulaFallenEmpire
{
public int count = 1;
public string countVariableName;
- public List effects;
+ public List effects;
}
public class ConditionalEffects
{
public List conditions;
- public List effects;
- public List randomlistEffects;
+ public List effects;
+ public List randomlistEffects;
public List loopEffects;
public void Execute(Window dialog)
diff --git a/Source/WulaFallenEmpire/HarmonyPatches/MapParent_ShouldRemoveMapNow_Patch.cs b/Source/WulaFallenEmpire/HarmonyPatches/MapParent_ShouldRemoveMapNow_Patch.cs
index 9946c2a0..f6adba9b 100644
--- a/Source/WulaFallenEmpire/HarmonyPatches/MapParent_ShouldRemoveMapNow_Patch.cs
+++ b/Source/WulaFallenEmpire/HarmonyPatches/MapParent_ShouldRemoveMapNow_Patch.cs
@@ -1,7 +1,8 @@
-using System.Linq;
using HarmonyLib;
using RimWorld.Planet;
+using System.Linq;
using Verse;
+using WulaFallenEmpire;
namespace WulaFallenEmpire
{
@@ -11,7 +12,7 @@ namespace WulaFallenEmpire
[HarmonyPrefix]
public static bool Prefix(MapParent __instance)
{
- // 如果该 MapParent 没有地图,则直接放行,执行原方法(虽然原方法也会检查 HasMap,但这里提前返回更清晰)
+ // 如果该 MapParent 没有地图,则直接放行
if (!__instance.HasMap)
{
return true;
@@ -19,25 +20,34 @@ namespace WulaFallenEmpire
try
{
- // 在当前地图上查找所有武装穿梭机
+ // 检查是否有活跃的观察者正在监测这个地图
+ bool isBeingObserved = Building_MapObserver.activeObservers
+ .Any(observer => observer.IsObservingMap(__instance));
+
+ if (isBeingObserved)
+ {
+ // 如果地图正在被监测,阻止地图被移除
+ Log.Message($"[MapObserver] 阻止地图移除: {__instance.Label} 正在被监测");
+ return false;
+ }
+
+ // 原有的穿梭机检查逻辑(保留你的原有功能)
foreach (var shuttle in __instance.Map.listerBuildings.AllBuildingsColonistOfClass())
{
- // 检查穿梭机是否有已生成的口袋地图,并且该地图里是否有人
if (shuttle != null && shuttle.PocketMapGenerated && shuttle.PocketMap != null && shuttle.PocketMap.mapPawns.AnyPawnBlockingMapRemoval)
{
- // 如果找到了这样的穿梭机,则阻止原方法 CheckRemoveMapNow 的执行,从而阻止地图被移除。
- // Log.Message($"[WULA] Prevented removal of map '{__instance.Map}' because shuttle '{shuttle.Label}' still contains pawns in its pocket dimension.");
- return false; // 返回 false 以跳过原方法的执行
+ Log.Message($"[WULA] 阻止地图移除: 穿梭机 '{shuttle.Label}' 的口袋维度中仍有生物");
+ return false;
}
}
}
catch (System.Exception ex)
{
- Log.Error($"[WULA] Error in MapParent_CheckRemoveMapNow_Patch Prefix: {ex}");
+ Log.Error($"[MapObserver] MapParent_CheckRemoveMapNow_Patch 错误: {ex}");
}
- // 如果没有找到需要保护的穿梭机,则允许原方法 CheckRemoveMapNow 继续执行其正常的逻辑
+ // 如果没有找到需要保护的情况,允许原方法继续执行
return true;
}
}
-}
\ No newline at end of file
+}
diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/Patch_FloatMenuOptionProvider_SelectedPawnValid.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_FloatMenuOptionProvider_SelectedPawnValid.cs
similarity index 100%
rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/Patch_FloatMenuOptionProvider_SelectedPawnValid.cs
rename to Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_FloatMenuOptionProvider_SelectedPawnValid.cs
diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/Patch_IsColonyMechPlayerControlled.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_IsColonyMechPlayerControlled.cs
similarity index 100%
rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/Patch_IsColonyMechPlayerControlled.cs
rename to Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_IsColonyMechPlayerControlled.cs
diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/Patch_MechanitorUtility_CanDraftMech.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_MechanitorUtility_CanDraftMech.cs
similarity index 100%
rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/Patch_MechanitorUtility_CanDraftMech.cs
rename to Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_MechanitorUtility_CanDraftMech.cs
diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/Patch_UncontrolledMechDrawPulse.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_UncontrolledMechDrawPulse.cs
similarity index 100%
rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/Patch_UncontrolledMechDrawPulse.cs
rename to Source/WulaFallenEmpire/HarmonyPatches/WULA_AutonomousMech/Patch_UncontrolledMechDrawPulse.cs
diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
index ce6ffd2e..f4a472f1 100644
--- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
+++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
@@ -82,6 +82,7 @@
+
@@ -89,6 +90,8 @@
+
+
@@ -102,7 +105,7 @@
-
+
@@ -138,6 +141,10 @@
+
+
+
+
@@ -146,10 +153,6 @@
-
-
-
-