using System.Collections.Generic; using System.Linq; using RimWorld; using RimWorld.Planet; using UnityEngine; using Verse; using Verse.AI; namespace WulaFallenEmpire { public class CompSkyfallerCaller : ThingComp { protected CompProperties_SkyfallerCaller Props => (CompProperties_SkyfallerCaller)props; private WulaSkyfallerWorldComponent _worldComponent; private WulaSkyfallerWorldComponent WorldComp { get { if (_worldComponent == null) { _worldComponent = Find.World.GetComponent(); } return _worldComponent; } } private GlobalStorageWorldComponent _globalStorage; private GlobalStorageWorldComponent GlobalStorage { get { if (_globalStorage == null) { _globalStorage = Find.World.GetComponent(); } return _globalStorage; } } private bool used = false; private int callTick = -1; private bool calling = false; private bool usedGlobalStorage = false; // 新增:标记是否使用了全局储存器 public bool CanCall => !used && !calling; // 固定的显示标签 public string RequiredFlyOverLabel => "建筑空投飞行器"; // 检查是否有拥有 BuildingdropperFacility 设施的 FlyOver public bool HasRequiredFlyOver { get { // 如果不需要 FlyOver,直接返回 true if (!Props.requireFlyOver) return true; if (parent?.Map == null) return false; try { // 检查所有FlyOver类型的物体 var allFlyOvers = new List(); var dynamicObjects = parent.Map.dynamicDrawManager.DrawThings; foreach (var thing in dynamicObjects) { if (thing is FlyOver) { allFlyOvers.Add(thing); } } Log.Message($"[SkyfallerCaller] Found {allFlyOvers.Count} FlyOvers on map"); foreach (var thing in allFlyOvers) { if (thing is FlyOver flyOver && !flyOver.Destroyed) { // 检查设施 var facilitiesComp = flyOver.GetComp(); if (facilitiesComp == null) { Log.Warning($"[SkyfallerCaller] FlyOver at {flyOver.Position} has no CompFlyOverFacilities"); continue; } if (facilitiesComp.HasFacility("BuildingdropperFacility")) { Log.Message($"[SkyfallerCaller] Found valid FlyOver at {flyOver.Position} with BuildingdropperFacility"); return true; } else { Log.Message($"[SkyfallerCaller] FlyOver at {flyOver.Position} missing BuildingdropperFacility. Has: {string.Join(", ", facilitiesComp.GetActiveFacilities())}"); } } } Log.Message("[SkyfallerCaller] No FlyOver with BuildingdropperFacility found"); return false; } catch (System.Exception ex) { Log.Error($"[SkyfallerCaller] Exception while checking for FlyOver: {ex}"); return false; } } } private bool CheckRoofConditions { get { if (parent?.Map == null) return true; RoofDef roof = parent.Position.GetRoof(parent.Map); if (roof == null) return true; if (roof.isThickRoof && !Props.allowThickRoof) return false; if (!roof.isThickRoof && !Props.allowThinRoof) return false; return true; } } public bool CanCallSkyfaller => CanCall && HasRequiredFlyOver && CheckRoofConditions; public override void PostExposeData() { base.PostExposeData(); Scribe_Values.Look(ref used, "used", false); Scribe_Values.Look(ref callTick, "callTick", -1); Scribe_Values.Look(ref calling, "calling", false); Scribe_Values.Look(ref usedGlobalStorage, "usedGlobalStorage", false); } public override void CompTick() { base.CompTick(); if (calling && callTick >= 0 && Find.TickManager.TicksGame >= callTick) { ExecuteSkyfallerCall(); } } public void CallSkyfaller(bool isAutoCall = false) { if (!CanCallSkyfaller) { // 显示相应的错误消息 if (!HasRequiredFlyOver && Props.requireFlyOver) // 只在需要 FlyOver 时才显示此消息 { Messages.Message("WULA_NoBuildingDropperFlyOver".Translate(), parent, MessageTypeDefOf.RejectInput); } else if (!CheckRoofConditions) { if (parent.Position.GetRoof(parent.Map)?.isThickRoof == true) { Messages.Message("WULA_ThickRoofBlocking".Translate(), parent, MessageTypeDefOf.RejectInput); } else { Messages.Message("WULA_RoofBlocking".Translate(), parent, MessageTypeDefOf.RejectInput); } } return; } Log.Message($"[SkyfallerCaller] Starting skyfaller call from {parent.Label} at {parent.Position}"); calling = true; used = true; int delay = isAutoCall ? Props.autoCallDelayTicks : Props.delayTicks; callTick = Find.TickManager.TicksGame + delay; if (delay <= 0) { ExecuteSkyfallerCall(); } else { // 修改:根据资源来源显示不同的消息 string messageKey = usedGlobalStorage ? "WULA_SkyfallerIncomingFromGlobal" : "WULA_SkyfallerIncoming"; Messages.Message(messageKey.Translate(delay.ToStringTicksToPeriod()), parent, MessageTypeDefOf.ThreatBig); } } protected void ResetCall() { calling = false; used = false; callTick = -1; usedGlobalStorage = false; } protected virtual void ExecuteSkyfallerCall() { Log.Message($"[SkyfallerCaller] Executing skyfaller call at {parent.Position}"); if (Props.skyfallerDef == null) { Log.Error("[SkyfallerCaller] Skyfaller def is null!"); return; } // 修改:使用新的资源检查方法 var resourceCheck = CheckAndConsumeMaterials(); if (!resourceCheck.HasEnoughMaterials) { Log.Message($"[SkyfallerCaller] Aborting skyfaller call due to insufficient materials."); ResetCall(); return; } // 记录是否使用了全局储存器 usedGlobalStorage = resourceCheck.UsedGlobalStorage; // 检查屋顶并处理 HandleRoofDestruction(); // 创建Skyfaller Skyfaller skyfaller = SkyfallerMaker.MakeSkyfaller(Props.skyfallerDef); if (skyfaller == null) { Log.Error("[SkyfallerCaller] Failed to create skyfaller!"); return; } IntVec3 spawnPos = parent.Position; Log.Message($"[SkyfallerCaller] Spawning skyfaller at {spawnPos}"); GenSpawn.Spawn(skyfaller, spawnPos, parent.Map); if (Props.destroyBuilding) { Log.Message($"[SkyfallerCaller] Destroying building {parent.Label}"); parent.Destroy(DestroyMode.Vanish); } calling = false; callTick = -1; } private void HandleRoofDestruction() { if (parent?.Map == null) return; IntVec3 targetPos = parent.Position; RoofDef roof = targetPos.GetRoof(parent.Map); if (roof != null && !roof.isThickRoof && Props.allowThinRoof) { Log.Message($"[SkyfallerCaller] Destroying thin roof at {targetPos}"); parent.Map.roofGrid.SetRoof(targetPos, null); // 生成屋顶破坏效果 FleckMaker.ThrowDustPuffThick(targetPos.ToVector3Shifted(), parent.Map, 2f, new Color(1f, 1f, 1f, 2f)); } } protected virtual List CostList { get { if (parent.def?.costList.NullOrEmpty() ?? true) { return null; } return parent.def.costList; } } // 新增:资源检查结果结构 protected struct ResourceCheckResult { public bool HasEnoughMaterials; public bool UsedGlobalStorage; public Dictionary BeaconMaterials; public Dictionary GlobalMaterials; } // 修改:新的资源检查方法,优先检查信标附近,然后检查全局储存器 protected ResourceCheckResult CheckAndConsumeMaterials() { var result = new ResourceCheckResult { HasEnoughMaterials = false, UsedGlobalStorage = false, BeaconMaterials = new Dictionary(), GlobalMaterials = new Dictionary() }; if (DebugSettings.godMode) { result.HasEnoughMaterials = true; return result; } var costList = CostList; if (costList.NullOrEmpty()) { result.HasEnoughMaterials = true; return result; } if (parent.Map == null) { return result; } // 第一步:收集信标附近的可用物资 var beaconMaterials = CollectBeaconMaterials(); result.BeaconMaterials = beaconMaterials; // 第二步:检查信标附近物资是否足够 bool beaconHasEnough = true; foreach (var cost in costList) { int availableInBeacon = beaconMaterials.ContainsKey(cost.thingDef) ? beaconMaterials[cost.thingDef] : 0; if (availableInBeacon < cost.count) { beaconHasEnough = false; break; } } // 第三步:如果信标附近物资足够,只消耗信标附近的 if (beaconHasEnough) { ConsumeBeaconMaterials(beaconMaterials, costList); result.HasEnoughMaterials = true; result.UsedGlobalStorage = false; return result; } // 第四步:如果信标附近物资不足,检查全局储存器 var globalMaterials = CheckGlobalStorageMaterials(); result.GlobalMaterials = globalMaterials; bool globalHasEnough = true; foreach (var cost in costList) { int availableInBeacon = beaconMaterials.ContainsKey(cost.thingDef) ? beaconMaterials[cost.thingDef] : 0; int availableInGlobal = globalMaterials.ContainsKey(cost.thingDef) ? globalMaterials[cost.thingDef] : 0; if (availableInBeacon + availableInGlobal < cost.count) { globalHasEnough = false; break; } } if (globalHasEnough) { // 先消耗信标附近的,不足部分从全局储存器扣除 ConsumeMixedMaterials(beaconMaterials, globalMaterials, costList); result.HasEnoughMaterials = true; result.UsedGlobalStorage = true; return result; } // 两种来源加起来都不够 result.HasEnoughMaterials = false; return result; } // 新增:收集信标附近的物资 private Dictionary CollectBeaconMaterials() { var materials = new Dictionary(); if (parent.Map == null) return materials; foreach (Building_OrbitalTradeBeacon beacon in Building_OrbitalTradeBeacon.AllPowered(parent.Map)) { foreach (IntVec3 cell in beacon.TradeableCells) { List thingList = parent.Map.thingGrid.ThingsListAt(cell); for (int i = 0; i < thingList.Count; i++) { Thing thing = thingList[i]; if (thing.def.EverHaulable) { if (materials.ContainsKey(thing.def)) { materials[thing.def] += thing.stackCount; } else { materials[thing.def] = thing.stackCount; } } } } } return materials; } // 新增:检查全局储存器中的物资 private Dictionary CheckGlobalStorageMaterials() { var materials = new Dictionary(); if (GlobalStorage == null) return materials; var costList = CostList; if (costList.NullOrEmpty()) return materials; foreach (var cost in costList) { int globalCount = GlobalStorage.GetInputStorageCount(cost.thingDef); if (globalCount > 0) { materials[cost.thingDef] = globalCount; } } return materials; } // 新增:只消耗信标附近的物资 private void ConsumeBeaconMaterials(Dictionary beaconMaterials, List costList) { var tradeableThings = new List(); foreach (Building_OrbitalTradeBeacon beacon in Building_OrbitalTradeBeacon.AllPowered(parent.Map)) { foreach (IntVec3 cell in beacon.TradeableCells) { List thingList = parent.Map.thingGrid.ThingsListAt(cell); for (int i = 0; i < thingList.Count; i++) { Thing thing = thingList[i]; if (thing.def.EverHaulable) { tradeableThings.Add(thing); } } } } foreach (var cost in costList) { int remaining = cost.count; for (int i = tradeableThings.Count - 1; i >= 0 && remaining > 0; i--) { var thing = tradeableThings[i]; if (thing.def == cost.thingDef) { if (thing.stackCount > remaining) { thing.SplitOff(remaining); remaining = 0; } else { remaining -= thing.stackCount; thing.Destroy(); tradeableThings.RemoveAt(i); } } } } } // 新增:混合消耗信标和全局储存器的物资 private void ConsumeMixedMaterials(Dictionary beaconMaterials, Dictionary globalMaterials, List costList) { // 先消耗信标附近的物资 var tradeableThings = new List(); foreach (Building_OrbitalTradeBeacon beacon in Building_OrbitalTradeBeacon.AllPowered(parent.Map)) { foreach (IntVec3 cell in beacon.TradeableCells) { List thingList = parent.Map.thingGrid.ThingsListAt(cell); for (int i = 0; i < thingList.Count; i++) { Thing thing = thingList[i]; if (thing.def.EverHaulable) { tradeableThings.Add(thing); } } } } // 对每种所需材料进行处理 foreach (var cost in costList) { int remaining = cost.count; // 第一步:消耗信标附近的物资 for (int i = tradeableThings.Count - 1; i >= 0 && remaining > 0; i--) { var thing = tradeableThings[i]; if (thing.def == cost.thingDef) { if (thing.stackCount > remaining) { thing.SplitOff(remaining); remaining = 0; } else { remaining -= thing.stackCount; thing.Destroy(); tradeableThings.RemoveAt(i); } } } // 第二步:如果还有剩余,从全局储存器扣除 if (remaining > 0 && GlobalStorage != null) { GlobalStorage.RemoveFromInputStorage(cost.thingDef, remaining); } } } // 保留原有的 HasEnoughMaterials 方法用于 Gizmo 显示 protected bool HasEnoughMaterials() { if (DebugSettings.godMode) return true; var costList = CostList; if (costList.NullOrEmpty()) { return true; } // 第一步:检查信标附近物资 var beaconMaterials = CollectBeaconMaterials(); bool beaconHasEnough = true; foreach (var cost in costList) { int availableInBeacon = beaconMaterials.ContainsKey(cost.thingDef) ? beaconMaterials[cost.thingDef] : 0; if (availableInBeacon < cost.count) { beaconHasEnough = false; break; } } if (beaconHasEnough) return true; // 第二步:检查全局储存器(如果信标附近不够) if (GlobalStorage == null) return false; foreach (var cost in costList) { int availableInBeacon = beaconMaterials.ContainsKey(cost.thingDef) ? beaconMaterials[cost.thingDef] : 0; int availableInGlobal = GlobalStorage.GetInputStorageCount(cost.thingDef); if (availableInBeacon + availableInGlobal < cost.count) { return false; } } return true; } // 原有的 ConsumeMaterials 方法现在只用于 God Mode 情况 protected void ConsumeMaterials() { if (DebugSettings.godMode) return; // 在非 God Mode 下,这个方法不应该被调用 // 实际的消耗在 CheckAndConsumeMaterials 中处理 Log.Warning("[SkyfallerCaller] ConsumeMaterials called in non-God mode, this shouldn't happen"); } // 其余方法保持不变... private string GetCostString() { var costList = CostList; if (costList.NullOrEmpty()) { return ""; } var sb = new System.Text.StringBuilder(); foreach (var cost in costList) { sb.AppendLine($" - {cost.thingDef.LabelCap}: {cost.count}"); } return sb.ToString(); } private void CancelCall() { calling = false; used = false; callTick = -1; usedGlobalStorage = false; Messages.Message("WULA_SkyfallerCallCancelled".Translate(), parent, MessageTypeDefOf.NeutralEvent); } public override IEnumerable CompGetGizmosExtra() { foreach (var gizmo in base.CompGetGizmosExtra()) yield return gizmo; if (calling) { Command_Action cancelCommand = new Command_Action { defaultLabel = "WULA_CancelSkyfaller".Translate(), defaultDesc = "WULA_CancelSkyfallerDesc".Translate(), icon = ContentFinder.Get("UI/Designators/Cancel"), action = CancelCall }; yield return cancelCommand; } if (CanCall) { string reason = GetDisabledReason(); Command_Action callCommand = new Command_Action { defaultLabel = "WULA_CallSkyfaller".Translate(), defaultDesc = GetCallDescription(), icon = ContentFinder.Get("Wula/UI/Commands/WULA_DropBuilding"), action = () => CallSkyfaller(false), disabledReason = reason }; if (!string.IsNullOrEmpty(reason)) { callCommand.Disable(reason); } yield return callCommand; } } private string GetCallDescription() { var sb = new System.Text.StringBuilder(); sb.Append("WULA_CallSkyfallerDesc".Translate()); if (Props.requireFlyOver && !HasRequiredFlyOver) { sb.AppendLine().Append("WULA_RequiresBuildingDropperFlyOver".Translate()); } if (parent?.Map != null) { RoofDef roof = parent.Position.GetRoof(parent.Map); if (roof != null) { if (roof.isThickRoof && !Props.allowThickRoof) { sb.AppendLine().Append("WULA_ThickRoofBlockingDesc".Translate()); } else if (!roof.isThickRoof && !Props.allowThinRoof) { sb.AppendLine().Append("WULA_RoofBlockingDesc".Translate()); } } } string costString = GetCostString(); if (!string.IsNullOrEmpty(costString)) { sb.AppendLine().AppendLine().Append("WULA_RequiredMaterials".Translate()); sb.Append(costString); } // 新增:显示资源来源信息 sb.AppendLine().AppendLine().Append("WULA_ResourcePriorityInfo".Translate()); return sb.ToString(); } private string GetDisabledReason() { // 只在需要 FlyOver 时检查并显示相关原因 if (Props.requireFlyOver && !HasRequiredFlyOver) { return "WULA_NoBuildingDropperFlyOver".Translate(); } if (!CheckRoofConditions) { // 添加 null 检查 if (parent?.Map != null) { RoofDef roof = parent.Position.GetRoof(parent.Map); if (roof?.isThickRoof == true) { return "WULA_ThickRoofBlocking".Translate(); } else { return "WULA_RoofBlocking".Translate(); } } } if (!HasEnoughMaterials()) { return "WULA_InsufficientMaterials".Translate(); } return null; } public override string CompInspectStringExtra() { if (parent?.Map == null) { return base.CompInspectStringExtra(); } var sb = new System.Text.StringBuilder(); if (calling) { int ticksLeft = callTick - Find.TickManager.TicksGame; if (ticksLeft > 0) { string messageKey = usedGlobalStorage ? "WULA_SkyfallerArrivingInFromGlobal" : "WULA_SkyfallerArrivingIn"; sb.Append(messageKey.Translate(ticksLeft.ToStringTicksToPeriod())); } } else if (!used) { sb.Append("WULA_ReadyToCallSkyfaller".Translate()); if (Props.requireFlyOver && !HasRequiredFlyOver) { sb.AppendLine().Append("WULA_MissingBuildingDropperFlyOver".Translate()); } RoofDef roof = parent.Position.GetRoof(parent.Map); if (roof != null) { if (roof.isThickRoof && !Props.allowThickRoof) { sb.AppendLine().Append("WULA_BlockedByThickRoof".Translate()); } else if (!roof.isThickRoof && !Props.allowThinRoof) { sb.AppendLine().Append("WULA_BlockedByRoof".Translate()); } } string costString = GetCostString(); if (!string.IsNullOrEmpty(costString)) { sb.AppendLine().AppendLine("WULA_RequiredMaterials".Translate()); sb.Append(costString); } } string baseInspectString = base.CompInspectStringExtra(); if (!string.IsNullOrEmpty(baseInspectString)) { if (sb.Length > 0) { sb.AppendLine(); } sb.Append(baseInspectString); } return sb.Length > 0 ? sb.ToString().TrimEnd() : null; } } }