Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/BuildingComp/WULA_SkyfallerCaller/CompSkyfallerCaller.cs
Tourswen 6a5c5bd9aa 1
2025-11-30 14:39:22 +08:00

777 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<WulaSkyfallerWorldComponent>();
}
return _worldComponent;
}
}
private GlobalStorageWorldComponent _globalStorage;
private GlobalStorageWorldComponent GlobalStorage
{
get
{
if (_globalStorage == null)
{
_globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
}
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<Thing>();
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<CompFlyOverFacilities>();
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<ThingDefCountClass> 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<ThingDef, int> BeaconMaterials;
public Dictionary<ThingDef, int> GlobalMaterials;
}
// 修改:新的资源检查方法,优先检查信标附近,然后检查全局储存器
protected ResourceCheckResult CheckAndConsumeMaterials()
{
var result = new ResourceCheckResult
{
HasEnoughMaterials = false,
UsedGlobalStorage = false,
BeaconMaterials = new Dictionary<ThingDef, int>(),
GlobalMaterials = new Dictionary<ThingDef, int>()
};
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<ThingDef, int> CollectBeaconMaterials()
{
var materials = new Dictionary<ThingDef, int>();
if (parent.Map == null) return materials;
foreach (Building_OrbitalTradeBeacon beacon in Building_OrbitalTradeBeacon.AllPowered(parent.Map))
{
foreach (IntVec3 cell in beacon.TradeableCells)
{
List<Thing> 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<ThingDef, int> CheckGlobalStorageMaterials()
{
var materials = new Dictionary<ThingDef, int>();
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<ThingDef, int> beaconMaterials, List<ThingDefCountClass> costList)
{
var tradeableThings = new List<Thing>();
foreach (Building_OrbitalTradeBeacon beacon in Building_OrbitalTradeBeacon.AllPowered(parent.Map))
{
foreach (IntVec3 cell in beacon.TradeableCells)
{
List<Thing> 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<ThingDef, int> beaconMaterials, Dictionary<ThingDef, int> globalMaterials, List<ThingDefCountClass> costList)
{
// 先消耗信标附近的物资
var tradeableThings = new List<Thing>();
foreach (Building_OrbitalTradeBeacon beacon in Building_OrbitalTradeBeacon.AllPowered(parent.Map))
{
foreach (IntVec3 cell in beacon.TradeableCells)
{
List<Thing> 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<Gizmo> 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<Texture2D>.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<Texture2D>.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;
}
}
}