Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/WULA_Shuttle/Building_ArmedShuttleWithPocket.cs
2025-08-24 21:15:38 +08:00

1206 lines
47 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RimWorld;
using RimWorld.Planet;
using UnityEngine;
using Verse;
using Verse.AI;
using Verse.Sound;
namespace WulaFallenEmpire
{
/// <summary>
/// 内置空间武装穿梭机 - 基于原版MapPortal机制的口袋空间实现
/// 结合了武装防御能力和口袋空间技术的复合型载具
/// </summary>
[StaticConstructorOnStartup]
public class Building_ArmedShuttleWithPocket : Building_ArmedShuttle, IThingHolder
{
#region 使MapPortal的图标
/// <summary>查看口袋地图图标</summary>
private static readonly Texture2D ViewPocketMapTex = ContentFinder<Texture2D>.Get("UI/Commands/ViewCave");
/// <summary>取消进入图标</summary>
private static readonly Texture2D CancelEnterTex = ContentFinder<Texture2D>.Get("UI/Designators/Cancel");
/// <summary>默认进入图标</summary>
private static readonly Texture2D DefaultEnterTex = ContentFinder<Texture2D>.Get("UI/Commands/EnterCave");
#endregion
#region
/// <summary>内部口袋地图实例</summary>
private Map pocketMap;
/// <summary>口袋地图是否已生成</summary>
private bool pocketMapGenerated;
/// <summary>内部空间大小</summary>
private IntVec2 pocketMapSize = new IntVec2(80, 80);
/// <summary>地图生成器定义</summary>
private MapGeneratorDef mapGenerator;
/// <summary>退出点定义</summary>
private ThingDef exitDef;
/// <summary>允许直接访问(无需骇入)</summary>
private bool allowDirectAccess = true;
/// <summary>口袋空间内的物品容器</summary>
private ThingOwner innerContainer;
/// <summary>原版MapPortal的容器代理必须有这个字段才能与Dialog_EnterPortal正常工作</summary>
public PortalContainerProxy containerProxy;
/// <summary>口袋地图退出点(模仿原版 MapPortal.exit</summary>
public Building_PocketMapExit exit;
/// <summary>是否已经进入过(模仿原版 MapPortal.beenEntered</summary>
protected bool beenEntered;
/// <summary>待加载物品列表(模仿原版 MapPortal.leftToLoad</summary>
public List<TransferableOneWay> leftToLoad;
/// <summary>是否已通知无法加载更多(模仿原版 MapPortal.notifiedCantLoadMore</summary>
public bool notifiedCantLoadMore;
#endregion
#region
/// <summary>获取内部口袋地图</summary>
public Map PocketMap => pocketMap;
/// <summary>原版MapPortal的PocketMap属性包含自动清理无效地图的逻辑</summary>
public Map PocketMapForPortal
{
get
{
Map map = pocketMap;
if (map != null && map.Parent?.HasMap == false)
{
pocketMap = null;
}
return pocketMap;
}
}
/// <summary>口袋地图是否存在</summary>
public bool PocketMapExists => PocketMapForPortal != null;
/// <summary>口袋地图是否已生成</summary>
public bool PocketMapGenerated => pocketMapGenerated;
/// <summary>是否允许直接访问口袋空间</summary>
public bool AllowDirectAccess => allowDirectAccess;
/// <summary>内部容器</summary>
public ThingOwner InnerContainer => innerContainer;
/// <summary>
/// 获取进入按钮的图标
/// </summary>
protected virtual Texture2D EnterTex => DefaultEnterTex;
// 人员传送相关属性已删除 - 不再使用Dialog_EnterPortal
/// <summary>加载是否正在进行(模仿原版 MapPortal.LoadInProgress</summary>
public bool LoadInProgress
{
get
{
if (leftToLoad != null)
{
return leftToLoad.Any();
}
return false;
}
}
/// <summary>是否有Pawn可以加载任何东西模仿原版 MapPortal.AnyPawnCanLoadAnythingNow</summary>
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
public Building_ArmedShuttleWithPocket()
{
innerContainer = new ThingOwner<Thing>(this, oneStackOnly: false);
}
#endregion
#region
public override void PostMake()
{
base.PostMake();
if (innerContainer == null)
{
innerContainer = new ThingOwner<Thing>(this, oneStackOnly: false);
}
}
public override void ExposeData()
{
base.ExposeData();
// 模仿原版MapPortal.ExposeData的逻辑
Map map = pocketMap;
if (map != null && map.Parent?.HasMap == false)
{
pocketMap = null;
}
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_Deep.Look(ref innerContainer, "innerContainer", this);
// 模仿原版MapPortal持久化leftToLoad和exit
Scribe_References.Look(ref exit, "exit");
Scribe_Values.Look(ref beenEntered, "beenEntered", defaultValue: false);
Scribe_Collections.Look(ref leftToLoad, "leftToLoad", LookMode.Deep);
Scribe_Values.Look(ref notifiedCantLoadMore, "notifiedCantLoadMore", false);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
if (innerContainer == null)
{
innerContainer = new ThingOwner<Thing>(this, oneStackOnly: false);
}
// 模仿原版MapPortal清理无效的leftToLoad条目
leftToLoad?.RemoveAll((TransferableOneWay x) => x.AnyThing == null);
}
}
public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish)
{
// 清理口袋地图
if (pocketMap != null && pocketMapGenerated)
{
try
{
// 将口袋空间中的物品和人员转移到主地图
TransferAllFromPocketToMainMap();
// 销毁口袋地图
PocketMapUtility.DestroyPocketMap(pocketMap);
}
catch (Exception ex)
{
Log.Error($"[WULA] Error cleaning up pocket map: {ex}");
}
}
base.DeSpawn(mode);
}
/// <summary>
/// 模仿原版MapPortal.Tick方法处理加载进度通知和穿梭机状态变化
/// </summary>
protected override void Tick()
{
base.Tick();
// 模仿原版MapPortal的Tick逻辑处理加载进度通知
if (this.IsHashIntervalTick(60) && Spawned && LoadInProgress && !notifiedCantLoadMore && !AnyPawnCanLoadAnythingNow && leftToLoad?[0]?.AnyThing != null)
{
notifiedCantLoadMore = true;
Messages.Message("MessageCantLoadMoreIntoPortal".Translate(Label, Faction.OfPlayer.def.pawnsPlural, leftToLoad[0].AnyThing), this, MessageTypeDefOf.CautionInput);
}
// 每隔一段时间检查退出点目标是否需要更新(处理穿梭机移动的情况)
if (this.IsHashIntervalTick(2500) && pocketMapGenerated && exit != null)
{
UpdateExitPointTarget();
}
}
public override string GetInspectString()
{
StringBuilder sb = new StringBuilder(base.GetInspectString());
if (pocketMapGenerated)
{
sb.AppendLine("WULA.PocketSpace.Status".Translate() + ": " + "WULA.PocketSpace.Ready".Translate());
if (innerContainer.Count > 0)
{
sb.AppendLine("WULA.PocketSpace.ItemCount".Translate(innerContainer.Count));
}
// 显示口袋空间中的人员数量
if (pocketMap != null)
{
int pawnCount = pocketMap.mapPawns.AllPawnsSpawned.Where(p => p.IsColonist).Count();
if (pawnCount > 0)
{
sb.AppendLine("WULA.PocketSpace.PawnCount".Translate(pawnCount));
}
}
}
else
{
sb.AppendLine("WULA.PocketSpace.Status".Translate() + ": " + "WULA.PocketSpace.NotGenerated".Translate());
}
return sb.ToString().TrimEndNewlines();
}
#endregion
#region
// 人员传送方法已删除 - 不再使用Dialog_EnterPortal进行人员传送
/// <summary>
/// 切换到口袋空间视角
/// </summary>
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);
}
/// <summary>
/// 创建口袋地图 - 使用原版PocketMapUtility模仿 MapPortal.GeneratePocketMap
/// </summary>
private void CreatePocketMap()
{
try
{
// 模仿原版 MapPortal.GeneratePocketMap 的实现
// 注意我们不是MapPortal所以设为null
PocketMapUtility.currentlyGeneratingPortal = null;
pocketMap = GeneratePocketMapInt();
PocketMapUtility.currentlyGeneratingPortal = null;
if (pocketMap != null)
{
pocketMapGenerated = true;
// 在口袋地图中心放置退出点
PlaceExitInPocketMap();
Log.Message($"[WULA] Pocket map created successfully with size {pocketMap.Size}");
}
else
{
Log.Error("[WULA] Failed to create pocket map");
}
}
catch (Exception ex)
{
Log.Error($"[WULA] Error creating pocket map: {ex}");
PocketMapUtility.currentlyGeneratingPortal = null; // 确保清理
}
}
/// <summary>
/// 模仿原版MapPortal.GeneratePocketMapInt
/// </summary>
protected virtual Map GeneratePocketMapInt()
{
// 使用自定义地图生成器
if (mapGenerator == null)
{
mapGenerator = DefDatabase<MapGeneratorDef>.GetNamed("WULA_PocketSpace_Small", false)
?? DefDatabase<MapGeneratorDef>.GetNamed("AncientStockpile", false)
?? MapGeneratorDefOf.Base_Player;
}
// 使用自定义尺寸
IntVec3 mapSize = new IntVec3(pocketMapSize.x, 1, pocketMapSize.z);
return PocketMapUtility.GeneratePocketMap(mapSize, mapGenerator, GetExtraGenSteps(), this.Map);
}
/// <summary>
/// 模仿原版MapPortal.GetExtraGenSteps
/// </summary>
protected virtual IEnumerable<GenStepWithParams> GetExtraGenSteps()
{
return Enumerable.Empty<GenStepWithParams>();
}
/// <summary>
/// 在口袋地图中创建退出点(模仿原版)
/// </summary>
private void PlaceExitInPocketMap()
{
if (pocketMap == null || exitDef == null) return;
try
{
// 在地图中心找一个合适的位置
IntVec3 exitPos = pocketMap.Center;
// 寻找可建造的位置
if (!exitPos.Standable(pocketMap) || exitPos.GetThingList(pocketMap).Any(t => t.def.category == ThingCategory.Building))
{
exitPos = CellFinder.RandomClosewalkCellNear(pocketMap.Center, pocketMap, 5,
p => p.Standable(pocketMap) && !p.GetThingList(pocketMap).Any(t => t.def.category == ThingCategory.Building));
}
if (exitPos.IsValid)
{
// 创建退出点建筑
Thing exitBuilding = ThingMaker.MakeThing(exitDef);
if (exitBuilding is Building_PocketMapExit exitPortal)
{
exitPortal.targetMap = this.Map;
exitPortal.targetPos = this.Position;
exitPortal.parentShuttle = this;
exit = exitPortal; // 设置 exit 引用,模仿原版 MapPortal
}
GenPlace.TryPlaceThing(exitBuilding, 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}");
}
}
// TransferPawnToPocketSpace方法已删除 - 不再使用单个人员传送
/// <summary>
/// 将所有物品和人员从口袋空间转移到主地图
/// </summary>
private void TransferAllFromPocketToMainMap()
{
if (pocketMap == null || !Spawned) return;
try
{
// 转移所有殖民者
List<Pawn> pawnsToTransfer = pocketMap.mapPawns.AllPawnsSpawned
.Where(p => p.IsColonist).ToList();
foreach (Pawn pawn in pawnsToTransfer)
{
IntVec3 spawnPos = CellFinder.RandomClosewalkCellNear(this.Position, this.Map, 5,
p => p.Standable(this.Map) && !p.GetThingList(this.Map).Any(t => t is Pawn));
if (spawnPos.IsValid)
{
pawn.DeSpawn();
GenPlace.TryPlaceThing(pawn, spawnPos, this.Map, ThingPlaceMode.Near);
}
}
// 转移所有物品到内部容器
List<Thing> itemsToTransfer = pocketMap.listerThings.AllThings
.Where(t => t.def.category == ThingCategory.Item && t.def.EverHaulable).ToList();
foreach (Thing item in itemsToTransfer)
{
if (item.Spawned)
{
item.DeSpawn();
if (!innerContainer.TryAdd(item))
{
// 如果容器满了,丢到穿梭机附近
IntVec3 dropPos = CellFinder.RandomClosewalkCellNear(this.Position, this.Map, 3);
if (dropPos.IsValid)
{
GenPlace.TryPlaceThing(item, dropPos, this.Map, ThingPlaceMode.Near);
}
}
}
}
Log.Message($"[WULA] Transferred {pawnsToTransfer.Count} pawns and {itemsToTransfer.Count} items from pocket space");
}
catch (Exception ex)
{
Log.Error($"[WULA] Error transferring from pocket map: {ex}");
}
}
#endregion
#region Gizmo方法
public override IEnumerable<Gizmo> GetGizmos()
{
foreach (Gizmo gizmo in base.GetGizmos())
{
yield return gizmo;
}
if (allowDirectAccess)
{
if (pocketMap == null)
{
// 创建口袋空间按钮
Command_Action createCommand = new Command_Action();
createCommand.action = delegate
{
try
{
Log.Message("[WULA] Creating pocket map...");
CreatePocketMap();
if (pocketMap != null)
{
Messages.Message("WULA.PocketSpace.CreationSuccess".Translate(), this, MessageTypeDefOf.PositiveEvent);
}
else
{
Messages.Message("WULA.PocketSpace.CreationFailed".Translate(), this, MessageTypeDefOf.RejectInput);
}
}
catch (Exception ex)
{
Log.Error($"[WULA] Error creating pocket map: {ex}");
Messages.Message("WULA.PocketSpace.CreationFailed".Translate(), this, MessageTypeDefOf.RejectInput);
}
};
createCommand.icon = EnterTex;
createCommand.defaultLabel = "WULA.PocketSpace.CreateMap".Translate();
createCommand.defaultDesc = "WULA.PocketSpace.CreateMapDesc".Translate();
// 检查是否可以创建
string reason;
createCommand.Disabled = !IsEnterable(out reason);
createCommand.disabledReason = reason;
yield return createCommand;
}
// 进入口袋空间按钮已删除 - 按钮无法正常工作
// 查看口袋地图按钮模仿原版MapPortal
if (pocketMap != null)
{
yield return new Command_Action
{
defaultLabel = "WULA.PocketSpace.ViewMap".Translate(),
defaultDesc = "WULA.PocketSpace.ViewMapDesc".Translate(),
icon = ViewPocketMapTex,
action = delegate
{
// 模仿原版,跳转到口袋地图并选中退出点
if (exit != null)
{
CameraJumper.TryJumpAndSelect(exit);
}
else
{
SwitchToPocketSpace();
}
}
};
}
}
}
#endregion
#region IThingHolder接口实现
public ThingOwner GetDirectlyHeldThings()
{
// 仅在第一次调用时记录日志
if (containerProxy != null)
{
return containerProxy;
}
else
{
Log.Warning("[WULA] containerProxy is null! Creating new one...");
containerProxy = new PortalContainerProxy { portal = this };
Log.Message($"[WULA] Created new containerProxy - portal set: {containerProxy.portal != null}");
return containerProxy;
}
}
public void GetChildHolders(List<IThingHolder> outChildren)
{
ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, GetDirectlyHeldThings());
}
#endregion
#region MapPortal兼容接口
/// <summary>
/// 检查是否可以进入模仿原版MapPortal.IsEnterable
/// </summary>
public virtual bool IsEnterable(out string reason)
{
if (!allowDirectAccess)
{
reason = "WULA.PocketSpace.AccessDenied".Translate();
return false;
}
if (!Spawned)
{
reason = "WULA.PocketSpace.NotSpawned".Translate();
return false;
}
reason = "";
return true;
}
/// <summary>
/// 获取目标地图模仿原版MapPortal.GetOtherMap
/// </summary>
public virtual Map GetOtherMap()
{
if (pocketMap == null)
{
CreatePocketMap();
}
return pocketMap;
}
/// <summary>
/// 获取目标位置模仿原版MapPortal.GetDestinationLocation
/// </summary>
public virtual IntVec3 GetDestinationLocation()
{
if (exit != null)
{
return exit.Position;
}
return pocketMap?.Center ?? IntVec3.Invalid;
}
// OnEntered方法已删除 - 不再使用Dialog_EnterPortal机制
#endregion
#region MapPortal的物品传送方法
/// <summary>
/// 通知有物品被添加(模仿原版 MapPortal.Notify_ThingAdded
/// </summary>
public void Notify_ThingAdded(Thing t)
{
// 检查是否为Pawn类型
if (t is Pawn pawn)
{
Log.Message($"[WULA] Notify_ThingAdded called for PAWN: {pawn.LabelShort} ({pawn.def.defName})");
// 对于Pawn我们不需要更新leftToLoad列表因为原版也不这样做
// 只需要通知CompTransporter
}
else
{
Log.Message($"[WULA] Notify_ThingAdded called for ITEM: {t?.def?.defName} x{t?.stackCount}");
Log.Message($"[WULA] leftToLoad count before: {leftToLoad?.Count ?? 0}");
int removedCount = SubtractFromToLoadList(t, t.stackCount);
Log.Message($"[WULA] Removed {removedCount} items from leftToLoad list");
Log.Message($"[WULA] leftToLoad count after: {leftToLoad?.Count ?? 0}");
}
// 同时通知CompTransporter组件确保原版装载系统也得到通知
var compTransporter = this.GetComp<CompTransporter>();
if (compTransporter != null)
{
Log.Message($"[WULA] Notifying CompTransporter about thing added: {t?.def?.defName}");
try
{
// 调用CompTransporter的Notify_ThingAdded方法如果存在
var method = compTransporter.GetType().GetMethod("Notify_ThingAdded", new[] { typeof(Thing) });
if (method != null)
{
method.Invoke(compTransporter, new object[] { t });
Log.Message("[WULA] Successfully called CompTransporter.Notify_ThingAdded");
}
else
{
Log.Message("[WULA] CompTransporter.Notify_ThingAdded method not found");
}
}
catch (Exception ex)
{
Log.Warning($"[WULA] Failed to notify CompTransporter: {ex.Message}");
}
}
else
{
Log.Message("[WULA] No CompTransporter found on this building");
}
}
/// <summary>
/// 添加到加载列表(模仿原版 MapPortal.AddToTheToLoadList
/// </summary>
public void AddToTheToLoadList(TransferableOneWay t, int count)
{
if (!t.HasAnyThing || count <= 0)
{
return;
}
if (leftToLoad == null)
{
leftToLoad = new List<TransferableOneWay>();
}
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);
}
}
/// <summary>
/// 从加载列表中减去(模仿原版 MapPortal.SubtractFromToLoadList
/// </summary>
public int SubtractFromToLoadList(Thing t, int count)
{
Log.Message($"[WULA] SubtractFromToLoadList called for: {t?.def?.defName} x{count}");
if (leftToLoad == null)
{
Log.Message("[WULA] leftToLoad is null, returning 0");
return 0;
}
Log.Message($"[WULA] Searching in leftToLoad list with {leftToLoad.Count} entries");
TransferableOneWay transferableOneWay = TransferableUtility.TransferableMatchingDesperate(t, leftToLoad, TransferAsOneMode.PodsOrCaravanPacking);
if (transferableOneWay == null)
{
Log.Message($"[WULA] No matching transferable found for {t?.def?.defName}");
return 0;
}
Log.Message($"[WULA] Found matching transferable with CountToTransfer: {transferableOneWay.CountToTransfer}");
if (transferableOneWay.CountToTransfer <= 0)
{
Log.Message("[WULA] CountToTransfer <= 0, returning 0");
return 0;
}
int num = Mathf.Min(count, transferableOneWay.CountToTransfer);
Log.Message($"[WULA] Adjusting transferable by: -{num}");
transferableOneWay.AdjustBy(-num);
transferableOneWay.things.Remove(t);
Log.Message($"[WULA] After adjustment - CountToTransfer: {transferableOneWay.CountToTransfer}, things.Count: {transferableOneWay.things.Count}");
if (transferableOneWay.CountToTransfer <= 0)
{
Log.Message("[WULA] Removing transferable from leftToLoad list");
leftToLoad.Remove(transferableOneWay);
}
Log.Message($"[WULA] leftToLoad list now has {leftToLoad.Count} entries");
return num;
}
/// <summary>
/// 取消加载(模仿原版 MapPortal.CancelLoad
/// </summary>
public void CancelLoad(MapPortal portal = null)
{
// 简化版本只清理leftToLoad列表
// 原版需要查找MapPortal相关的Lord但我们不是MapPortal类型
if (leftToLoad != null)
{
leftToLoad.Clear();
}
}
#endregion
#region 穿
/// <summary>
/// 更新口袋空间中退出点的目标位置(处理穿梭机位置变化)
/// </summary>
public void UpdateExitPointTarget()
{
if (pocketMap == null || exit == null) return;
try
{
// 如果退出点是我们的Building_PocketMapExit类型更新其目标位置
if (exit is Building_PocketMapExit pocketExit)
{
// 更新目标地图和位置
if (this.Spawned)
{
// 穿梭机在地图上,更新目标位置
if (pocketExit.targetMap != this.Map || pocketExit.targetPos != this.Position)
{
pocketExit.targetMap = this.Map;
pocketExit.targetPos = this.Position;
pocketExit.parentShuttle = this;
Log.Message($"[WULA] Updated pocket map exit target to shuttle location: {this.Map?.uniqueID} at {this.Position}");
}
}
else
{
// 穿梭机不在地图上(可能在飞行中),记录警告但保持原有目标
Log.Warning($"[WULA] Shuttle not spawned, pocket map exit target may be outdated. Current target: {pocketExit.targetMap?.uniqueID} at {pocketExit.targetPos}");
}
}
}
catch (Exception ex)
{
Log.Error($"[WULA] Error updating exit point target: {ex}");
}
}
/// <summary>
/// 重写 SpawnSetup确保位置变化时更新退出点
/// </summary>
public override void SpawnSetup(Map map, bool respawningAfterLoad)
{
// 保存旧位置信息
Map oldMap = this.Map;
IntVec3 oldPos = this.Position;
base.SpawnSetup(map, respawningAfterLoad);
if (innerContainer == null)
{
innerContainer = new ThingOwner<Thing>(this, oneStackOnly: false);
}
// 初始化containerProxy模仿原版MapPortal
containerProxy = new PortalContainerProxy
{
portal = this
};
// 关键替换CompTransporter的innerContainer字段确保一切添加操作都经过我们的代理
var compTransporter = this.GetComp<CompTransporter>();
if (compTransporter != null)
{
try {
var field = typeof(CompTransporter).GetField("innerContainer",
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (field != null)
{
field.SetValue(compTransporter, containerProxy);
}
else
{
Log.Error("[WULA] Failed to find innerContainer field in CompTransporter");
}
}
catch (Exception ex)
{
Log.Error($"[WULA] Error replacing CompTransporter.innerContainer: {ex}");
}
}
// 更新退出点目标(处理穿梭机重新部署的情况)
UpdateExitPointTarget();
// 从 ThingDef 中读取 portal 配置
if (def.HasModExtension<PocketMapProperties>())
{
var portalProps = def.GetModExtension<PocketMapProperties>();
if (portalProps.pocketMapGenerator != null)
{
mapGenerator = portalProps.pocketMapGenerator;
}
if (portalProps.exitDef != null)
{
exitDef = portalProps.exitDef;
}
if (portalProps.pocketMapSize != IntVec2.Zero)
{
pocketMapSize = portalProps.pocketMapSize;
}
allowDirectAccess = portalProps.allowDirectAccess;
}
// 初始化地图生成器和退出点定义(如果 XML 中没有配置)
if (mapGenerator == null)
{
mapGenerator = DefDatabase<MapGeneratorDef>.GetNamed("AncientStockpile", false)
?? DefDatabase<MapGeneratorDef>.GetNamed("Base_Player", false)
?? MapGeneratorDefOf.Base_Player;
}
if (exitDef == null)
{
exitDef = DefDatabase<ThingDef>.GetNamed("WULA_PocketMapExit", false)
?? ThingDefOf.Door;
}
// 如果位置发生了变化,记录日志
if (oldMap != null && (oldMap != map || oldPos != this.Position))
{
Log.Message($"[WULA] Shuttle moved from {oldMap?.uniqueID}:{oldPos} to {map?.uniqueID}:{this.Position}, updating pocket map exit target");
}
}
#endregion
}
/// <summary>
/// 口袋空间属性配置类
/// </summary>
public class PocketMapProperties : DefModExtension
{
/// <summary>口袋地图生成器</summary>
public MapGeneratorDef pocketMapGenerator;
/// <summary>退出点定义</summary>
public ThingDef exitDef;
/// <summary>口袋地图大小</summary>
public IntVec2 pocketMapSize = new IntVec2(13, 13);
/// <summary>允许直接访问</summary>
public bool allowDirectAccess = true;
}
// MapPortalAdapter类已删除 - 不再使用Dialog_EnterPortal进行人员传送
/// <summary>
/// 专为Building_ArmedShuttleWithPocket设计的PortalContainerProxy适配器
/// 模仿原版PortalContainerProxy的行为但适配非-MapPortal类型
/// 这个类拦截所有向容器添加物品和人员的操作
/// </summary>
public class PortalContainerProxy : ThingOwner
{
public Building_ArmedShuttleWithPocket portal;
public override int Count
{
get
{
// 不记录日志,避免日志刷屏
return 0;
}
}
public override int TryAdd(Thing item, int count, bool canMergeWithExistingStacks = true)
{
if (TryAdd(item, canMergeWithExistingStacks))
{
return count;
}
return 0;
}
public override bool TryAdd(Thing item, bool canMergeWithExistingStacks = true)
{
return ProcessItemOrPawn(item, canMergeWithExistingStacks);
}
/// <summary>
/// 统一的物品和人员处理方法
/// 动物当作物品处理只有殖民者才当作Pawn处理
/// </summary>
private bool ProcessItemOrPawn(Thing item, bool canMergeWithExistingStacks = true)
{
if (portal == null)
{
Log.Error("[WULA] PortalContainerProxy.ProcessItemOrPawn: portal is null!");
return false;
}
Log.Message($"[WULA] PortalContainerProxy.ProcessItemOrPawn called for: {item?.def?.defName} ({item?.GetType()?.Name}) x{item?.stackCount}");
// 只有殖民者才当作Pawn处理动物当作物品处理
if (item is Pawn pawn && pawn.IsColonist)
{
Log.Message($"[WULA] INTERCEPTING COLONIST PAWN: {pawn.LabelShort} ({pawn.def.defName}) - Type: {pawn.GetType().Name}");
bool result = TransferPawnToPocketSpace(pawn);
Log.Message($"[WULA] Colonist transfer result: {result}");
return result;
}
// 动物和其他所有物品都当作物品处理
Map otherMap = portal.GetOtherMap();
IntVec3 destinationLocation = portal.GetDestinationLocation();
if (otherMap == null || !destinationLocation.IsValid)
{
Log.Warning("[WULA] PortalContainerProxy: Invalid target map or location, using inner container");
// 如果目标地图或位置无效,将物品放入内部容器
return portal.InnerContainer.TryAdd(item, canMergeWithExistingStacks);
}
// 关键:严格按照原版顺序 - 先通知,再传送
// 这样能确保leftToLoad列表在物品被传送前就得到更新
Log.Message($"[WULA] Calling portal.Notify_ThingAdded for: {item?.def?.defName} x{item?.stackCount}");
portal.Notify_ThingAdded(item);
// 传送物品(包括动物)到目标地图
Log.Message($"[WULA] Transporting item/animal to pocket map: {item?.def?.defName}");
GenDrop.TryDropSpawn(item, destinationLocation, otherMap, ThingPlaceMode.Near, out var _);
Log.Message($"[WULA] Item/animal transport completed successfully");
return true;
}
public override int IndexOf(Thing item)
{
return -1;
}
public override bool Remove(Thing item)
{
return false;
}
protected override Thing GetAt(int index)
{
return null;
}
/// <summary>
/// 传送单个Pawn到口袋空间使用直接传送机制
/// </summary>
private bool TransferPawnToPocketSpace(Pawn pawn)
{
if (pawn == null)
{
Log.Warning("[WULA] TransferPawnToPocketSpace: Pawn is null");
return false;
}
if (portal == null)
{
Log.Warning("[WULA] TransferPawnToPocketSpace: Portal is null");
return false;
}
if (!pawn.Spawned && pawn.holdingOwner == null)
{
Log.Warning($"[WULA] TransferPawnToPocketSpace: Pawn {pawn.LabelShort} is not spawned and not in a container");
// 即使武装没有spawned或不在容器中仍然尝试传送
// 记录原始状态并继续
Log.Message($"[WULA] Attempting to transfer pawn anyway: {pawn.LabelShort}");
}
// 创建口袋地图(如果需要)
Map targetMap = portal.GetOtherMap();
if (targetMap == null)
{
Log.Warning("[WULA] TransferPawnToPocketSpace: Failed to get or create pocket map");
return false;
}
IntVec3 destinationLocation = portal.GetDestinationLocation();
if (!destinationLocation.IsValid)
{
Log.Warning("[WULA] TransferPawnToPocketSpace: Invalid destination location");
return false;
}
try
{
// 保存重要状态
bool wasDrafted = pawn.Drafted;
Map sourceMap = pawn.Map; // 可能为null如果pawn在容器中
IntVec3 sourcePos = pawn.Position;
ThingOwner owner = pawn.holdingOwner;
Log.Message($"[WULA] Starting transfer of pawn {pawn.LabelShort} to pocket space");
// 通知系统人员被添加,更新装载状态
portal.Notify_ThingAdded(pawn);
// 寻找安全的生成位置
IntVec3 spawnPos = CellFinder.StandableCellNear(destinationLocation, targetMap, 5,
p => p.Standable(targetMap) && !p.GetThingList(targetMap).Any(t => t is Pawn));
if (!spawnPos.IsValid)
{
Log.Warning("[WULA] Could not find valid spawn position in pocket map, using center");
spawnPos = CellFinder.RandomClosewalkCellNear(targetMap.Center, targetMap, 10,
p => p.Standable(targetMap));
if (!spawnPos.IsValid)
{
Log.Error("[WULA] All spawn position attempts failed, using map center");
spawnPos = targetMap.Center;
}
}
// 关键情况记录
Log.Message($"[WULA] Found valid spawn position: {spawnPos}");
Log.Message($"[WULA] Pawn state - Spawned: {pawn.Spawned}, In container: {owner != null}");
// 从Pawn当前位置安全移除
if (owner != null)
{
Log.Message($"[WULA] Removing pawn from container");
if (!owner.Remove(pawn))
{
Log.Error($"[WULA] Failed to remove pawn from container!");
return false;
}
}
else if (pawn.Spawned)
{
Log.Message($"[WULA] Despawning pawn from map {pawn.Map?.uniqueID ?? -1}");
pawn.DeSpawn(DestroyMode.Vanish);
}
else
{
// Pawn既不在地图上也不在容器中可能被CompTransporter直接持有
Log.Message($"[WULA] Pawn in special state - ParentHolder: {pawn.ParentHolder?.ToString() ?? "None"}");
// 尝试从CompTransporter中直接提取
if (pawn.ParentHolder != null)
{
Log.Message($"[WULA] Attempting to extract pawn from ParentHolder: {pawn.ParentHolder}");
// 检查是否在特殊容器中
var parentHolder = pawn.ParentHolder;
if (parentHolder is ThingOwner thingOwner)
{
Log.Message($"[WULA] ParentHolder is a ThingOwner, attempting to remove pawn");
if (thingOwner.Contains(pawn))
{
bool removed = thingOwner.Remove(pawn);
Log.Message($"[WULA] Removed from ThingOwner: {removed}");
}
}
else if (parentHolder.ToString().Contains("CompTransporter"))
{
// 尝试使用反射从CompTransporter中移除
Log.Message($"[WULA] Attempting to extract from CompTransporter");
var compTransporter = portal.GetComp<CompTransporter>();
if (compTransporter != null)
{
var innerContainerField = typeof(CompTransporter).GetField("innerContainer",
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (innerContainerField != null)
{
var transporterContainer = innerContainerField.GetValue(compTransporter) as ThingOwner;
if (transporterContainer != null && transporterContainer.Contains(pawn))
{
bool removed = transporterContainer.Remove(pawn);
Log.Message($"[WULA] Removed from CompTransporter: {removed}");
}
}
}
}
}
}
// 确认是否已从原位置移除
if (pawn.Spawned)
{
Log.Error($"[WULA] Failed to despawn pawn! Still spawned after despawn attempt");
return false;
}
// 生成到目标位置
Log.Message($"[WULA] Spawning pawn at {spawnPos} in map {targetMap.uniqueID}");
GenSpawn.Spawn(pawn, spawnPos, targetMap);
// 验证是否成功生成
if (!pawn.Spawned || pawn.Map != targetMap)
{
Log.Error($"[WULA] Pawn failed to spawn correctly! Spawned: {pawn.Spawned}, Map: {pawn.Map?.uniqueID ?? -1}");
// 尝试恢复到原始位置
if (sourceMap != null && sourceMap.IsPlayerHome)
{
Log.Message($"[WULA] Attempting emergency recovery to original position");
GenSpawn.Spawn(pawn, sourcePos, sourceMap);
if (!pawn.Spawned)
{
Log.Error($"[WULA] Emergency recovery failed! Pawn is lost");
}
}
return false;
}
// 恢复状态
if (wasDrafted && pawn.drafter != null)
{
pawn.drafter.Drafted = true;
}
Log.Message($"[WULA] Pawn {pawn.LabelShort} successfully transferred to pocket space");
return true;
}
catch (Exception ex)
{
Log.Error($"[WULA] Exception during pawn transfer: {ex}");
return false;
}
}
}
}