Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/BuildingComp/WULA_SkyfallerCaller/CompSkyfallerCaller.cs
2025-11-26 18:05:56 +08:00

551 lines
18 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 bool used = false;
private int callTick = -1;
private bool calling = 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);
}
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
{
Messages.Message("WULA_SkyfallerIncoming".Translate(delay.ToStringTicksToPeriod()), parent, MessageTypeDefOf.ThreatBig);
}
}
protected void ResetCall()
{
calling = false;
used = false;
callTick = -1;
}
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;
}
if (!HasEnoughMaterials())
{
Log.Message($"[SkyfallerCaller] Aborting skyfaller call due to insufficient materials.");
ResetCall();
return;
}
ConsumeMaterials();
// 检查屋顶并处理
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 bool HasEnoughMaterials()
{
if (DebugSettings.godMode) return true;
var costList = CostList;
if (costList.NullOrEmpty())
{
return true;
}
var availableThings = new List<Thing>();
if (parent.Map == null) return false;
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)
{
availableThings.Add(thing);
}
}
}
}
availableThings = availableThings.Distinct().ToList();
foreach (var cost in costList)
{
int count = 0;
foreach (var thing in availableThings)
{
if (thing.def == cost.thingDef)
{
count += thing.stackCount;
}
}
if (count < cost.count)
{
return false;
}
}
return true;
}
protected void ConsumeMaterials()
{
if (DebugSettings.godMode) return;
var costList = CostList;
if (costList.NullOrEmpty())
{
return;
}
var tradeableThings = new List<Thing>();
if (parent.Map == null) return;
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);
}
}
}
}
tradeableThings = tradeableThings.Distinct().ToList();
foreach (var cost in costList)
{
int remaining = cost.count;
for (int i = tradeableThings.Count - 1; i >= 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();
}
if (remaining <= 0)
{
break;
}
}
}
}
}
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;
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);
}
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)
{
sb.Append("WULA_SkyfallerArrivingIn".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;
}
}
}