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 } }