using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RimWorld;
using RimWorld.Planet;
using UnityEngine;
using Verse;
using Verse.AI;
using Verse.AI.Group;
using Verse.Sound;
namespace WulaFallenEmpire
{
///
/// 内置空间武装穿梭机 - 基于原版MapPortal机制的口袋空间实现
/// 结合了武装防御能力和口袋空间技术的复合型载具
///
[StaticConstructorOnStartup]
public class Building_ArmedShuttleWithPocket : Building_ArmedShuttle, IThingHolder
{
#region 静态图标定义(使用原版MapPortal的图标)
/// 取消进入图标
private static readonly Texture2D CancelEnterTex = ContentFinder.Get("UI/Designators/Cancel");
/// 默认进入图标
private static readonly Texture2D DefaultEnterTex = ContentFinder.Get("UI/Commands/LoadTransporter");
#endregion
#region 口袋空间字段
/// 内部口袋地图实例
private Map pocketMap;
/// 口袋地图是否已生成
private bool pocketMapGenerated;
/// 内部空间大小
private IntVec2 pocketMapSize = new IntVec2(80, 80);
/// 地图生成器定义
private MapGeneratorDef mapGenerator;
/// 退出点定义
private ThingDef exitDef;
/// 允许直接访问(无需骇入)
private bool allowDirectAccess = true;
/// 传送功能是否暂停(飞行时为 true)
private bool transportDisabled = false;
// 注意:我们不再使用自定义的innerContainer,
// 所有物品都存储在CompTransporter.innerContainer中,保持简单和一致
/// 新的口袋空间物品容器
private PocketSpaceThingHolder pocketSpaceContainer;
/// 口袋地图退出点(模仿原版 MapPortal.exit)
public Building_PocketMapExit exit;
/// 是否已经进入过(模仿原版 MapPortal.beenEntered)
protected bool beenEntered;
/// 待加载物品列表(模仿原版 MapPortal.leftToLoad)
public List leftToLoad;
/// 是否已通知无法加载更多(模仿原版 MapPortal.notifiedCantLoadMore)
public bool notifiedCantLoadMore;
#endregion
#region 属性
/// 获取内部口袋地图
public Map PocketMap => pocketMap;
/// 口袋地图是否已生成
public bool PocketMapGenerated => pocketMapGenerated;
/// 是否允许直接访问口袋空间
public bool AllowDirectAccess => allowDirectAccess;
// 注意:我们不再提供InnerContainer属性,因为所有物品都在CompTransporter.innerContainer中
///
/// 获取进入按钮的图标
///
protected virtual Texture2D EnterTex => DefaultEnterTex;
///
/// 获取进入按钮的文本
///
public virtual string EnterString => "WULA.PocketSpace.Enter".Translate();
///
/// 获取取消进入按钮的文本
///
public virtual string CancelEnterString => "WULA.PocketSpace.CancelEnter".Translate();
///
/// 获取进入中的文本
///
public virtual string EnteringString => "WULA.PocketSpace.Entering".Translate();
/// 加载是否正在进行(模仿原版 MapPortal.LoadInProgress)
public bool LoadInProgress
{
get
{
if (leftToLoad != null)
{
return leftToLoad.Any();
}
return false;
}
}
/// 是否有Pawn可以加载任何东西(模仿原版 MapPortal.AnyPawnCanLoadAnythingNow)
public bool AnyPawnCanLoadAnythingNow
{
get
{
if (!LoadInProgress)
{
return false;
}
if (!Spawned)
{
return false;
}
// 简化版本,只检查基本条件
return Map.mapPawns.AllPawnsSpawned.Any(p => p.IsColonist && p.CanReach(this, PathEndMode.Touch, Danger.Deadly));
}
}
#endregion
#region IThingHolder 实现 (模仿 MapPortal)
///
/// 获取直接持有的物品(模仿 MapPortal.GetDirectlyHeldThings)
///
public ThingOwner GetDirectlyHeldThings()
{
return pocketSpaceContainer.innerContainer;
}
///
/// 获取子持有者(模仿 MapPortal.GetChildHolders)
///
public void GetChildHolders(List outChildren)
{
// 目前没有子持有者,留空
}
///
/// 实现IThingHolder.ParentHolder属性
///
public new IThingHolder ParentHolder => this;
#endregion
#region 构造函数
public Building_ArmedShuttleWithPocket()
{
Log.Message("[WULA-DEBUG] Building_ArmedShuttleWithPocket constructor called");
pocketSpaceContainer = new PocketSpaceThingHolder(this);
}
#endregion
#region 基础重写方法
public override void PostMake()
{
Log.Message("[WULA-DEBUG] PostMake called");
base.PostMake();
// 不再初始化innerContainer,只使用CompTransporter的容器
}
public override void ExposeData()
{
Log.Message($"[WULA-DEBUG] ExposeData called, mode: {Scribe.mode}");
base.ExposeData();
Scribe_Deep.Look(ref pocketMap, "pocketMap");
Scribe_Values.Look(ref pocketMapGenerated, "pocketMapGenerated", false);
Scribe_Values.Look(ref pocketMapSize, "pocketMapSize", new IntVec2(80, 80));
Scribe_Defs.Look(ref mapGenerator, "mapGenerator");
Scribe_Defs.Look(ref exitDef, "exitDef");
Scribe_Values.Look(ref allowDirectAccess, "allowDirectAccess", true);
Scribe_Values.Look(ref transportDisabled, "transportDisabled", false);
Scribe_Deep.Look(ref pocketSpaceContainer, "pocketSpaceContainer", this);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
Log.Message("[WULA-DEBUG] PostLoadInit: Validating components after load");
// 验证CompTransporter组件是否正常
CompTransporter transporter = this.GetComp();
if (transporter == null)
{
Log.Error("[WULA-ERROR] CompTransporter is missing after load! This will cause item storage issues.");
}
else
{
Log.Message($"[WULA-DEBUG] CompTransporter loaded successfully with {transporter.innerContainer?.Count ?? 0} items");
}
}
}
public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish)
{
Log.Message($"[WULA-DEBUG] DeSpawn called with mode: {mode}");
// 只在真正销毁时清理口袋地图,发射时保留
if (ShouldDestroyPocketMapOnDeSpawn(mode))
{
if (pocketMap != null && pocketMapGenerated)
{
try
{
Log.Message("[WULA-DEBUG] Destroying pocket map due to shuttle destruction");
// 将口袋空间中的物品和人员转移到主地图
TransferAllFromPocketToMainMap();
// 销毁口袋地图
PocketMapUtility.DestroyPocketMap(pocketMap);
pocketMap = null;
pocketMapGenerated = false;
}
catch (Exception ex)
{
Log.Error($"[WULA-ERROR] Error cleaning up pocket map: {ex}");
}
}
}
else
{
Log.Message("[WULA-DEBUG] Preserving pocket map during shuttle launch/transport");
transportDisabled = true;
if (pocketMap != null && exit != null)
{
Log.Message("[WULA-DEBUG] Transport functionality disabled during flight");
}
}
base.DeSpawn(mode);
}
///
/// 判断是否应该在DeSpawn时销毁口袋地图
///
private bool ShouldDestroyPocketMapOnDeSpawn(DestroyMode mode)
{
switch (mode)
{
case DestroyMode.Vanish:
return false;
case DestroyMode.Deconstruct:
return true;
case DestroyMode.KillFinalize:
return true;
case DestroyMode.Cancel:
return true;
case DestroyMode.Refund:
return true;
case DestroyMode.FailConstruction:
return true;
default:
Log.Warning($"[WULA-WARNING] Unknown DestroyMode: {mode}, defaulting to preserve pocket map");
return false;
}
}
public override string GetInspectString()
{
StringBuilder sb = new StringBuilder(base.GetInspectString());
if (pocketMapGenerated)
{
sb.AppendLine("WULA.PocketSpace.Status".Translate() + ": " + "WULA.PocketSpace.Ready".Translate());
CompTransporter transporter = this.GetComp();
int mainContainerItems = transporter?.innerContainer?.Count ?? 0;
if (mainContainerItems > 0)
{
sb.AppendLine($"容器物品: {mainContainerItems}");
}
if (pocketMap != null)
{
int pocketItems = pocketMap.listerThings.AllThings.Count(t => t.def.category == ThingCategory.Item && t.def.EverHaulable);
int pawnCount = pocketMap.mapPawns.AllPawnsSpawned.Where(p => p.IsColonist).Count();
if (pocketItems > 0)
{
sb.AppendLine($"口袋空间物品: {pocketItems}");
}
if (pawnCount > 0)
{
sb.AppendLine("WULA.PocketSpace.PawnCount".Translate(pawnCount));
}
}
if (Prefs.DevMode)
{
sb.AppendLine($"[Debug] {GetPocketSpaceDebugInfo()}");
}
}
else
{
sb.AppendLine("WULA.PocketSpace.Status".Translate() + ": " + "WULA.PocketSpace.NotGenerated".Translate());
}
return sb.ToString().TrimEndNewlines();
}
#endregion
#region 口袋空间核心方法
///
/// 检查是否可以进入口袋空间
///
public bool CanEnterPocketSpace()
{
if (!allowDirectAccess)
{
return false;
}
if (!Spawned)
{
return false;
}
if (transportDisabled)
{
return false;
}
return true;
}
///
/// 进入口袋空间 - 基于原版PocketMapUtility实现
///
public void EnterPocketSpace(IEnumerable pawns = null)
{
if (!CanEnterPocketSpace())
{
Messages.Message("WULA.PocketSpace.CannotEnter".Translate(), this, MessageTypeDefOf.RejectInput);
return;
}
// 创建或获取口袋地图
if (pocketMap == null && !pocketMapGenerated)
{
CreatePocketMap();
}
if (pocketMap == null)
{
Messages.Message("WULA.PocketSpace.CreationFailed".Translate(), this, MessageTypeDefOf.RejectInput);
return;
}
// 传送玩家到口袋空间
List pawnsToTransfer = new List();
if (pawns != null)
{
pawnsToTransfer.AddRange(pawns.Where(p => p != null && p.Spawned && p.IsColonist));
}
else
{
// 如果没有指定人员,传送选中的殖民者
pawnsToTransfer.AddRange(Find.Selector.SelectedPawns.Where(p => p.IsColonist));
}
if (pawnsToTransfer.Count == 0)
{
Messages.Message("WULA.PocketSpace.NoPawnsSelected".Translate(), this, MessageTypeDefOf.RejectInput);
return;
}
// 执行传送
int transferredCount = 0;
foreach (Pawn pawn in pawnsToTransfer)
{
if (pawn.Spawned)
{
pawn.DeSpawn();
}
if (pocketSpaceContainer.innerContainer.TryAdd(pawn))
{
transferredCount++;
}
else
{
Log.Warning($"[WULA-WARNING] Failed to add pawn {pawn.LabelShort} to pocketSpaceContainer.");
}
}
if (transferredCount > 0)
{
Messages.Message("WULA.PocketSpace.TransferSuccess".Translate(transferredCount), MessageTypeDefOf.PositiveEvent);
// 切换到口袋地图
Current.Game.CurrentMap = pocketMap;
Find.CameraDriver.JumpToCurrentMapLoc(pocketMap.Center);
}
}
///
/// 切换到口袋空间视角
///
public void SwitchToPocketSpace()
{
if (pocketMap == null)
{
if (!pocketMapGenerated)
{
CreatePocketMap();
}
if (pocketMap == null)
{
Messages.Message("WULA.PocketSpace.CreationFailed".Translate(), this, MessageTypeDefOf.RejectInput);
return;
}
}
Current.Game.CurrentMap = pocketMap;
Find.CameraDriver.JumpToCurrentMapLoc(pocketMap.Center);
}
///
/// 创建口袋地图 - 使用原版PocketMapUtility(模仿 MapPortal.GeneratePocketMap)
///
private void CreatePocketMap()
{
try
{
// 模仿原版 MapPortal.GeneratePocketMap 的实现
PocketMapUtility.currentlyGeneratingPortal = null; // 我们不是 MapPortal,但可以设为 null
pocketMap = GeneratePocketMapInt();
PocketMapUtility.currentlyGeneratingPortal = null;
if (pocketMap != null)
{
pocketMapGenerated = true;
// 在口袋地图中心放置退出点
CreateExitPoint();
Log.Message($"[WULA] Successfully created pocket map of size {pocketMapSize} for armed shuttle");
}
else
{
Log.Error("[WULA] Failed to create pocket map");
}
}
catch (Exception ex)
{
Log.Error($"[WULA] Exception creating pocket map: {ex}");
}
}
///
/// 生成口袋地图的内部实现(模仿 MapPortal.GeneratePocketMapInt)
///
protected virtual Map GeneratePocketMapInt()
{
return PocketMapUtility.GeneratePocketMap(new IntVec3(pocketMapSize.x, 1, pocketMapSize.z), mapGenerator, GetExtraGenSteps(), this.Map);
}
///
/// 获取额外的生成步骤(模仿 MapPortal.GetExtraGenSteps)
///
protected virtual IEnumerable GetExtraGenSteps()
{
return System.Linq.Enumerable.Empty();
}
///
/// 在口袋地图中创建退出点(模仿原版)
///
private void CreateExitPoint()
{
if (pocketMap == null || exitDef == null) return;
try
{
// 在地图中心找一个合适的位置
IntVec3 exitPos = CellFinder.RandomClosewalkCellNear(pocketMap.Center, pocketMap, 5, (IntVec3 c) => c.IsValid && c.Standable(pocketMap) && !c.Roofed(pocketMap));
if (exitPos.IsValid)
{
exit = (Building_PocketMapExit)ThingMaker.MakeThing(exitDef);
GenPlace.TryPlaceThing(exit, exitPos, pocketMap, ThingPlaceMode.Direct);
Log.Message($"[WULA] Created exit point at {exitPos} in pocket map.");
}
else
{
Log.Warning("[WULA] Could not find valid position for exit point in pocket map");
}
}
catch (Exception ex)
{
Log.Error($"[WULA] Error creating exit point: {ex}");
}
}
///
/// 将单个Pawn传送到口袋空间
///
private bool TransferPawnToPocketSpace(Pawn pawn)
{
if (pawn == null || !pawn.Spawned || pocketMap == null) return false;
try
{
// 找一个安全的位置
IntVec3 spawnPos = CellFinder.RandomClosewalkCellNear(pocketMap.Center, pocketMap, 10,
p => p.Standable(pocketMap) && !p.GetThingList(pocketMap).Any(t => t is Pawn));
if (spawnPos.IsValid)
{
pawn.DeSpawn();
GenPlace.TryPlaceThing(pawn, spawnPos, pocketMap, ThingPlaceMode.Near);
return true;
}
}
catch (Exception ex)
{
Log.Error($"[WULA] Error transferring pawn {pawn?.LabelShort} to pocket space: {ex}");
}
return false;
}
///
/// 将所有物品和人员从口袋空间转移到主地图
///
private void TransferAllFromPocketToMainMap()
{
Log.Message("[WULA-DEBUG] TransferAllFromPocketToMainMap started");
if (pocketMap == null)
{
Log.Warning("[WULA-DEBUG] TransferAllFromPocketToMainMap: pocketMap is null, nothing to transfer");
return;
}
if (!Spawned)
{
Log.Error("[WULA-ERROR] TransferAllFromPocketToMainMap: Shuttle not spawned, cannot transfer items");
return;
}
try
{
// 转移所有殖民者到 pocketSpaceContainer
List pawnsToTransfer = pocketMap.mapPawns.AllPawnsSpawned.ToList();
Log.Message($"[WULA-DEBUG] Found {pawnsToTransfer.Count} pawns to transfer from pocket map.");
foreach (Pawn pawn in pawnsToTransfer)
{
if (pawn.Spawned)
{
pawn.DeSpawn();
}
pocketSpaceContainer.innerContainer.TryAdd(pawn);
}
// 转移所有物品到 pocketSpaceContainer
List itemsToTransfer = pocketMap.listerThings.AllThings.Where(t => t.def.category == ThingCategory.Item && t.def.EverHaulable).ToList();
Log.Message($"[WULA-DEBUG] Found {itemsToTransfer.Count} items to transfer from pocket map.");
foreach (Thing item in itemsToTransfer)
{
if (item.Spawned)
{
item.DeSpawn();
}
pocketSpaceContainer.innerContainer.TryAdd(item);
}
Log.Message($"[WULA] Transferred all pawns and items from pocket map to pocketSpaceContainer.");
// 调用新的同步方法,将 pocketSpaceContainer 中的所有物品和 Pawn 转移到主地图的 CompTransporter
TransferPocketContainerToMainTransporter();
}
catch (Exception ex)
{
Log.Error($"[WULA-ERROR] Error transferring from pocket map: {ex}");
Log.Error($"[WULA-ERROR] Stack trace: {ex.StackTrace}");
}
}
///
/// 将pocketSpaceContainer中的所有物品和Pawn转移到主地图的CompTransporter
///
public void TransferPocketContainerToMainTransporter()
{
Log.Message("[WULA-DEBUG] TransferPocketContainerToMainTransporter started.");
CompTransporter transporter = this.GetComp();
if (transporter == null)
{
Log.Error("[WULA-ERROR] CompTransporter not found on shuttle! Cannot transfer items from pocketSpaceContainer.");
return;
}
List thingsToTransfer = pocketSpaceContainer.innerContainer.ToList();
int transferredCount = 0;
foreach (Thing t in thingsToTransfer)
{
if (pocketSpaceContainer.innerContainer.Remove(t))
{
if (transporter.innerContainer.TryAdd(t))
{
transferredCount++;
}
else
{
Log.Warning($"[WULA-WARNING] Failed to add {t.LabelShort} to main transporter container. Dropping on ground.");
GenPlace.TryPlaceThing(t, this.Position, this.Map, ThingPlaceMode.Near);
}
}
}
Log.Message($"[WULA] Transferred {transferredCount} items/pawns from pocketSpaceContainer to main transporter.");
}
///
/// 获取口袋空间状态信息(用于调试)
///
public string GetPocketSpaceDebugInfo()
{
if (!pocketMapGenerated || pocketMap == null)
{
return "Pocket space not initialized";
}
CompTransporter transporter = this.GetComp();
int pocketItems = pocketMap.listerThings.AllThings.Count(t => t.def.category == ThingCategory.Item && t.def.EverHaulable);
int mainContainerItems = transporter?.innerContainer?.Count ?? 0;
return $"Pocket: {pocketItems}, Main: {mainContainerItems}";
}
#endregion
#region Gizmo方法
public override IEnumerable GetGizmos()
{
foreach (Gizmo gizmo in base.GetGizmos())
{
yield return gizmo;
}
if (allowDirectAccess)
{
// 进入口袋空间按钮(模仿原版MapPortal)
Command_Action enterCommand = new Command_Action();
enterCommand.action = delegate
{
// 使用自定义的殖民者选择对话框,模仿原版Dialog_EnterPortal的行为
OpenPawnSelectionDialog();
};
enterCommand.icon = EnterTex;
enterCommand.defaultLabel = EnterString + "...";
enterCommand.defaultDesc = "WULA.PocketSpace.EnterDesc".Translate();
// 检查是否可以进入(模仿原版MapPortal.IsEnterable)
string reason;
enterCommand.Disabled = !IsEnterable(out reason);
enterCommand.disabledReason = reason;
yield return enterCommand;
}
}
#endregion
#region MapPortal兼容接口(使Dialog_EnterPortal能正常工作)
///
/// 判断是否可以进入(模仿原版MapPortal.IsEnterable)
///
public virtual bool IsEnterable(out string reason)
{
reason = "";
if (!Spawned)
{
reason = "WULA.PocketSpace.NotSpawned".Translate();
return false;
}
if (transportDisabled)
{
reason = "WULA.PocketSpace.TransportDisabled".Translate();
return false;
}
if (!this.CanEnterPocketSpace())
{
reason = "WULA.PocketSpace.CannotEnterReason".Translate();
return false;
}
return true;
}
///
/// 获取另一个地图(模仿原版MapPortal.GetOtherMap)
///
public virtual Map GetOtherMap()
{
if (PocketMap == null)
{
CreatePocketMap();
}
return PocketMap;
}
///
/// 获取目标位置(模仿原版MapPortal.GetDestinationLocation)
///
public virtual IntVec3 GetDestinationLocation()
{
return exit?.Position ?? IntVec3.Invalid;
}
///
/// 进入时回调(模仿原版MapPortal.OnEntered)
///
public virtual void OnEntered(Pawn pawn)
{
// 将Pawn添加到口袋空间容器
if (pawn.Spawned)
{
pawn.DeSpawn();
}
pocketSpaceContainer.innerContainer.TryAdd(pawn);
if (!beenEntered)
{
beenEntered = true;
// 这里可以添加一些首次进入的信件/事件
}
if (Find.CurrentMap == base.Map)
{
// def.portal.traverseSound?.PlayOneShot(this); // 暂时移除,避免NRE
}
else if (Find.CurrentMap == exit.Map)
{
// def.portal.traverseSound?.PlayOneShot(exit); // 暂时移除,避免NRE
}
}
///
/// 打开殖民者选择对话框(模仿原版Dialog_EnterPortal)
///
private void OpenPawnSelectionDialog()
{
List pawns = CaravanFormingUtility.AllSendablePawns(this.Map, true, true, true, true, true, 0).ToList();
List items = CaravanFormingUtility.AllReachableColonyItems(this.Map, true, true).ToList();
// 创建并显示对话框
Dialog_EnterPortal window = new Dialog_EnterPortal(new global::WulaFallenEmpire.MapPortalAdapter(this)); // 使用适配器
Find.WindowStack.Add(window);
}
///
/// 通知物品被添加到此持有者(从IThingHolder继承,但现在由PocketSpaceThingHolder处理)
///
public void Notify_ThingAdded(Thing t)
{
// 这个方法现在由 PocketSpaceThingHolder 内部处理,这里只是为了满足IThingHolder接口
// 或者,如果Building_ArmedShuttleWithPocket仍然需要实现IThingHolder,则可以将其转发
// Log.Message($"[WULA] Building_ArmedShuttleWithPocket.Notify_ThingAdded called for {t.LabelCap}");
// pocketSpaceContainer.innerContainer.Notify_ThingAdded(t); // 转发给内部容器
}
///
/// 添加到待加载列表(模仿原版MapPortal.AddToTheToLoadList)
///
public void AddToTheToLoadList(TransferableOneWay t, int count)
{
if (!t.HasAnyThing || count <= 0)
{
return;
}
if (leftToLoad == null)
{
leftToLoad = new List();
}
TransferableOneWay transferableOneWay = TransferableUtility.TransferableMatching(t.AnyThing, leftToLoad, TransferAsOneMode.PodsOrCaravanPacking);
if (transferableOneWay != null)
{
for (int i = 0; i < t.things.Count; i++)
{
if (!transferableOneWay.things.Contains(t.things[i]))
{
transferableOneWay.things.Add(t.things[i]);
}
}
if (transferableOneWay.CanAdjustBy(count).Accepted)
{
transferableOneWay.AdjustBy(count);
}
}
else
{
TransferableOneWay transferableOneWay2 = new TransferableOneWay();
leftToLoad.Add(transferableOneWay2);
transferableOneWay2.things.AddRange(t.things);
transferableOneWay2.AdjustTo(count);
}
}
///
/// 从待加载列表移除(模仿原版MapPortal.SubtractFromToLoadList)
///
public int SubtractFromToLoadList(Thing t, int count)
{
if (leftToLoad == null)
{
return 0;
}
TransferableOneWay transferableOneWay = TransferableUtility.TransferableMatchingDesperate(t, leftToLoad, TransferAsOneMode.PodsOrCaravanPacking);
if (transferableOneWay == null)
{
return 0;
}
if (transferableOneWay.CountToTransfer <= 0)
{
return 0;
}
int num = Mathf.Min(count, transferableOneWay.CountToTransfer);
transferableOneWay.AdjustBy(-num);
transferableOneWay.things.Remove(t);
if (transferableOneWay.CountToTransfer <= 0)
{
leftToLoad.Remove(transferableOneWay);
}
return num;
}
///
/// 取消加载(模仿原版MapPortal.CancelLoad)
///
public void CancelLoad()
{
Lord lord = base.Map.lordManager.lords.FirstOrDefault((Lord l) => l.LordJob is LordJob_LoadAndEnterPortal lordJob_LoadAndEnterPortal && lordJob_LoadAndEnterPortal.portal is global::WulaFallenEmpire.MapPortalAdapter adapter && adapter.shuttle == this);
if (lord != null)
{
base.Map.lordManager.RemoveLord(lord);
}
leftToLoad.Clear();
}
#endregion
#region 生命周期方法
///
/// 更新退出点目标
///
public void UpdateExitPointTarget()
{
if (exit == null) return;
if (base.Map == null)
{
Log.Warning("[WULA] UpdateExitPointTarget: Shuttle map is null, cannot update exit point target.");
return;
}
try
{
exit.targetMap = base.Map;
exit.targetPos = base.Position;
Log.Message($"[WULA] Updated exit point target to map {base.Map.uniqueID} at position {base.Position}");
}
catch (Exception ex)
{
Log.Error($"[WULA] Error updating exit point target: {ex}");
}
}
///
/// 重写Tick方法,定期检查穿梭机状态变化和物品同步
///
protected override void Tick()
{
base.Tick();
// 每隔一段时间检查退出点目标是否需要更新(处理穿梭机移动的情况)
if (this.IsHashIntervalTick(2500) && pocketMapGenerated && exit != null)
{
UpdateExitPointTarget();
}
// 定期检查并同步口袋空间中的物品(每5分钟检查一次)
if (this.IsHashIntervalTick(18000) && pocketMapGenerated && pocketMap != null)
{
// 自动同步口袋空间中的物品到主容器
try
{
int itemsInPocket = pocketMap.listerThings.AllThings.Count(t => t.def.category == ThingCategory.Item && t.def.EverHaulable && t.Spawned);
if (itemsInPocket > 0)
{
TransferPocketContainerToMainTransporter();
if (Prefs.DevMode)
{
Log.Message($"[WULA] Auto-synced pocket items. Current status: {GetPocketSpaceDebugInfo()}");
}
}
}
catch (Exception ex)
{
Log.Error($"[WULA] Error during auto-sync: {ex}");
}
}
}
///
/// 重写 SpawnSetup,确保位置变化时更新退出点
///
public override void SpawnSetup(Map map, bool respawningAfterLoad)
{
Log.Message($"[WULA-DEBUG] Building_ArmedShuttleWithPocket.SpawnSetup START. Instance ID: {this.ThingID}, Map param: {map?.GetUniqueLoadID() ?? "null"}, Respawning: {respawningAfterLoad}");
// 保存旧位置信息
Map oldMap = this.Map;
IntVec3 oldPos = this.Position;
base.SpawnSetup(map, respawningAfterLoad);
// 验证关键组件
CompTransporter transporter = this.GetComp();
if (transporter == null)
{
Log.Error("[WULA-ERROR] CompTransporter missing in SpawnSetup! This will cause serious issues.");
}
else
{
Log.Message($"[WULA-DEBUG] CompTransporter found with {transporter.innerContainer?.Count ?? 0} items");
}
// 更新退出点目标(处理穿梭机重新部署的情况)
UpdateExitPointTarget();
// 如果是从飞行状态恢复,重新启用传送功能
if (transportDisabled)
{
Log.Message("[WULA-DEBUG] Re-enabling transport functionality after landing");
transportDisabled = false;
// 如果有口袋空间,确保退出点正确连接到新地图
if (pocketMapGenerated && pocketMap != null && exit != null)
{
Log.Message($"[WULA-DEBUG] Reconnecting pocket space exit to new map: {map?.GetUniqueLoadID() ?? "null"} at {this.Position}");
}
}
// 从 ThingDef 中读取 portal 配置
if (def.HasModExtension())
{
if (this.Map == null)
{
Log.Error($"[WULA-ERROR] Building_ArmedShuttleWithPocket {this.ThingID} Map is NULL after SpawnSetup!");
}
PocketMapProperties props = def.GetModExtension();
pocketMapSize = props.pocketMapSize;
mapGenerator = props.mapGenerator;
exitDef = props.exitDef;
allowDirectAccess = props.allowDirectAccess;
}
}
}
public class PocketMapProperties : DefModExtension
{
public IntVec2 pocketMapSize = new IntVec2(80, 80);
public MapGeneratorDef mapGenerator;
public ThingDef exitDef;
public bool allowDirectAccess = true;
}
///
/// 适配器类,使Building_ArmedShuttleWithPocket能够作为MapPortal被Dialog_EnterPortal使用
///
public class MapPortalAdapter : MapPortal
{
public Building_ArmedShuttleWithPocket shuttle;
public MapPortalAdapter() { } // Scribe需要无参数构造函数
public MapPortalAdapter(Building_ArmedShuttleWithPocket shuttle)
{
this.shuttle = shuttle;
}
public new Map PocketMap => shuttle?.PocketMap;
public new bool PocketMapExists => shuttle?.PocketMap != null; // 修正
public new bool AutoDraftOnEnter => false; // 修正
protected new Texture2D EnterTex => ContentFinder.Get("UI/Commands/LoadTransporter"); // 修正
public new string EnterString => shuttle?.EnterString;
public new string CancelEnterString => shuttle?.CancelEnterString;
public new string EnteringString => shuttle?.EnteringString;
public new bool LoadInProgress => shuttle?.LoadInProgress ?? false;
public new bool AnyPawnCanLoadAnythingNow => shuttle?.AnyPawnCanLoadAnythingNow ?? false;
public override void ExposeData()
{
base.ExposeData();
Scribe_References.Look(ref shuttle, "shuttle");
}
public override void SpawnSetup(Map map, bool respawningAfterLoad)
{
// 适配器不应该被Spawn,此方法留空或报错
Log.Error("MapPortalAdapter should not be spawned directly.");
}
protected override void Tick()
{
// 适配器不应该Tick,此方法留空
}
public new ThingOwner GetDirectlyHeldThings()
{
return shuttle?.GetDirectlyHeldThings();
}
public new void GetChildHolders(List outChildren)
{
shuttle?.GetChildHolders(outChildren);
}
public new void Notify_ThingAdded(Thing t)
{
shuttle?.Notify_ThingAdded(t);
}
public new void AddToTheToLoadList(TransferableOneWay t, int count)
{
shuttle?.AddToTheToLoadList(t, count);
}
public new int SubtractFromToLoadList(Thing t, int count)
{
return shuttle?.SubtractFromToLoadList(t, count) ?? 0;
}
public new void CancelLoad()
{
shuttle?.CancelLoad();
}
public new bool IsEnterable(out string reason)
{
if (shuttle == null)
{
reason = "WULA.PocketSpace.AdapterError".Translate();
return false;
}
return shuttle.IsEnterable(out reason);
}
public new Map GetOtherMap()
{
return shuttle?.GetOtherMap();
}
public new IntVec3 GetDestinationLocation()
{
return shuttle?.GetDestinationLocation() ?? IntVec3.Invalid;
}
public new void OnEntered(Pawn pawn)
{
shuttle?.OnEntered(pawn);
}
public new IEnumerable GetGizmos()
{
// 适配器不直接提供Gizmo,Gizmo应该由shuttle提供
return base.GetGizmos(); // 或者返回空的IEnumerable
}
}
#endregion // MapPortal兼容接口
}