Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/BuildingComp/WULA_SkyfallerCaller/CompSkyfallerCaller.cs
ProjectKoi-Kalo\Kalo 98a0400c78 WulaFallenEmpireSettings.cs - 添加了 public bool enableDebugLogs = false; 字段和保存配置
 WulaLog.cs - 修改了DebugEnabled属性,仅检查enableDebugLogs设置(不检查DevMode)
 WulaFallenEmpireMod.cs - 在DoSettingsWindowContents中添加了UI复选框,显示"Enable Debug Logs"选项
 替换了所有848个Log.Message/Error/Warning调用为WulaLog.Debug()
2025-12-15 13:05:50 +08:00

897 lines
32 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
{
public 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;
}
}
public bool used = false;
private int callTick = -1;
public bool calling = false;
private bool usedGlobalStorage = false;
public bool autoCallScheduled = false; // 新增:标记是否已安排自动呼叫
public bool CanCall => !used && !calling;
// 新增:检查建筑是否属于非玩家派系
public bool IsNonPlayerFaction => parent.Faction != null && parent.Faction != Faction.OfPlayer;
// 新增:检查是否应该自动呼叫
private bool ShouldAutoCall => IsNonPlayerFaction && Props.canAutoCall && !autoCallScheduled && !used;
// 固定的显示标签
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);
}
}
WulaLog.Debug($"[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)
{
WulaLog.Debug($"[SkyfallerCaller] FlyOver at {flyOver.Position} has no CompFlyOverFacilities");
continue;
}
if (facilitiesComp.HasFacility("BuildingdropperFacility"))
{
WulaLog.Debug($"[SkyfallerCaller] Found valid FlyOver at {flyOver.Position} with BuildingdropperFacility");
return true;
}
else
{
WulaLog.Debug($"[SkyfallerCaller] FlyOver at {flyOver.Position} missing BuildingdropperFacility. Has: {string.Join(", ", facilitiesComp.GetActiveFacilities())}");
}
}
}
WulaLog.Debug("[SkyfallerCaller] No FlyOver with BuildingdropperFacility found");
return false;
}
catch (System.Exception ex)
{
WulaLog.Debug($"[SkyfallerCaller] Exception while checking for FlyOver: {ex}");
return false;
}
}
}
public 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 PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
if (!respawningAfterLoad && ShouldAutoCall)
{
// 安排自动呼叫
autoCallScheduled = true;
callTick = Find.TickManager.TicksGame + Props.autoCallDelayTicks;
calling = true;
WulaLog.Debug($"[SkyfallerCaller] Scheduled auto-call for non-player building {parent.Label} at tick {callTick}");
}
}
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);
Scribe_Values.Look(ref autoCallScheduled, "autoCallScheduled", false);
}
public override void CompTick()
{
base.CompTick();
// 处理自动呼叫
if (calling && callTick >= 0 && Find.TickManager.TicksGame >= callTick)
{
if (autoCallScheduled)
{
// 执行自动呼叫
ExecuteAutoSkyfallerCall();
}
else
{
// 执行手动呼叫
ExecuteSkyfallerCall();
}
}
}
// 新增:自动呼叫方法(非玩家派系专用)
protected virtual void ExecuteAutoSkyfallerCall()
{
try
{
WulaLog.Debug($"[SkyfallerCaller] Executing auto skyfaller call for non-player building at {parent.Position}");
if (Props.skyfallerDef == null)
{
WulaLog.Debug("[SkyfallerCaller] Skyfaller def is null!");
ResetCall();
return;
}
// 非玩家派系自动呼叫不需要资源检查
// 直接处理屋顶
HandleRoofDestruction();
// 创建Skyfaller
Skyfaller skyfaller = SkyfallerMaker.MakeSkyfaller(Props.skyfallerDef);
if (skyfaller == null)
{
WulaLog.Debug("[SkyfallerCaller] Failed to create skyfaller!");
ResetCall();
return;
}
IntVec3 spawnPos = parent.Position;
WulaLog.Debug($"[SkyfallerCaller] Spawning auto skyfaller at {spawnPos}");
GenSpawn.Spawn(skyfaller, spawnPos, parent.Map);
if (Props.destroyBuilding)
{
WulaLog.Debug($"[SkyfallerCaller] Destroying non-player building {parent.Label}");
parent.Destroy(DestroyMode.Vanish);
}
// 重置状态
ResetCall();
autoCallScheduled = false;
// 显示自动呼叫消息
Messages.Message("WULA_AutoSkyfallerCalled".Translate(parent.Faction.Name),
MessageTypeDefOf.NeutralEvent);
}
catch (System.Exception ex)
{
WulaLog.Debug($"[SkyfallerCaller] Error in ExecuteAutoSkyfallerCall: {ex}");
ResetCall();
}
}
public void CallSkyfaller(bool isAutoCall = false)
{
// 新增:非玩家派系不能手动呼叫
if (IsNonPlayerFaction && !isAutoCall)
{
Messages.Message("WULA_NonPlayerCannotCall".Translate(), parent, MessageTypeDefOf.RejectInput);
return;
}
if (!CanCallSkyfaller)
{
// 显示相应的错误消息
if (!HasRequiredFlyOver && Props.requireFlyOver)
{
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;
}
WulaLog.Debug($"[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;
autoCallScheduled = false;
}
protected virtual void ExecuteSkyfallerCall()
{
WulaLog.Debug($"[SkyfallerCaller] Executing skyfaller call at {parent.Position}");
if (Props.skyfallerDef == null)
{
WulaLog.Debug("[SkyfallerCaller] Skyfaller def is null!");
return;
}
// 修改:使用新的资源检查方法
var resourceCheck = CheckAndConsumeMaterials();
if (!resourceCheck.HasEnoughMaterials)
{
WulaLog.Debug($"[SkyfallerCaller] Aborting skyfaller call due to insufficient materials.");
ResetCall();
return;
}
// 记录是否使用了全局储存器
usedGlobalStorage = resourceCheck.UsedGlobalStorage;
// 检查屋顶并处理
HandleRoofDestruction();
// 创建Skyfaller
Skyfaller skyfaller = SkyfallerMaker.MakeSkyfaller(Props.skyfallerDef);
if (skyfaller == null)
{
WulaLog.Debug("[SkyfallerCaller] Failed to create skyfaller!");
return;
}
IntVec3 spawnPos = parent.Position;
WulaLog.Debug($"[SkyfallerCaller] Spawning skyfaller at {spawnPos}");
GenSpawn.Spawn(skyfaller, spawnPos, parent.Map);
if (Props.destroyBuilding)
{
WulaLog.Debug($"[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)
{
WulaLog.Debug($"[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 显示
public 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 中处理
WulaLog.Debug("[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;
autoCallScheduled = false;
Messages.Message("WULA_SkyfallerCallCancelled".Translate(), parent, MessageTypeDefOf.NeutralEvent);
}
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
foreach (var gizmo in base.CompGetGizmosExtra())
yield return gizmo;
// 新增:非玩家派系不显示呼叫按钮
if (IsNonPlayerFaction)
yield break;
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()
{
// 新增:非玩家派系不能手动呼叫
if (IsNonPlayerFaction)
{
return "WULA_NonPlayerCannotCall".Translate();
}
// 只在需要 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 (autoCallScheduled && calling)
{
int ticksLeft = callTick - Find.TickManager.TicksGame;
}
else 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)
{
// 新增:显示非玩家派系自动呼叫信息
if (IsNonPlayerFaction && Props.canAutoCall)
{
sb.Append("WULA_AutoSkyfallerReady".Translate());
}
else
{
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;
}
}
}