diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll
index 68b8b63c..cb8b7889 100644
Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ
diff --git a/Source/WulaFallenEmpire/WULA_Shuttle/Building_ArmedShuttleWithPocket.cs b/Source/WulaFallenEmpire/WULA_Shuttle/Building_ArmedShuttleWithPocket.cs
index e224447f..4ee76348 100644
--- a/Source/WulaFallenEmpire/WULA_Shuttle/Building_ArmedShuttleWithPocket.cs
+++ b/Source/WulaFallenEmpire/WULA_Shuttle/Building_ArmedShuttleWithPocket.cs
@@ -11,178 +11,57 @@ using Verse.Sound;
namespace WulaFallenEmpire
{
- ///
- /// 内置空间武装穿梭机 - 基于原版MapPortal机制的口袋空间实现
- /// 结合了武装防御能力和口袋空间技术的复合型载具
- ///
[StaticConstructorOnStartup]
public class Building_ArmedShuttleWithPocket : Building_ArmedShuttle, IThingHolder
{
- #region 静态图标定义(使用原版MapPortal的图标)
+ #region 静态图标定义
- /// 查看口袋地图图标
private static readonly Texture2D ViewPocketMapTex = ContentFinder.Get("UI/Commands/ViewCave");
-
- /// 取消进入图标
- private static readonly Texture2D CancelEnterTex = ContentFinder.Get("UI/Designators/Cancel");
-
- /// 默认进入图标
- private static readonly Texture2D DefaultEnterTex = ContentFinder.Get("UI/Commands/EnterCave");
-
+ private static readonly Texture2D EnterPocketMapTex = ContentFinder.Get("UI/Commands/EnterCave");
+ private static readonly Texture2D TeleportAndLoadTex = 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;
-
- /// 口袋空间内的物品容器
- private ThingOwner innerContainer;
-
- /// 原版MapPortal的容器代理(必须有这个字段才能与Dialog_EnterPortal正常工作)
- public PortalContainerProxy containerProxy;
-
- /// 口袋地图退出点(模仿原版 MapPortal.exit)
public Building_PocketMapExit exit;
- /// 是否已经进入过(模仿原版 MapPortal.beenEntered)
- protected bool beenEntered;
-
- /// 待加载物品列表(模仿原版 MapPortal.leftToLoad)
- public List leftToLoad;
-
- /// 是否已通知无法加载更多(模仿原版 MapPortal.notifiedCantLoadMore)
- public bool notifiedCantLoadMore;
-
+ private bool doTeleportAfterLoading = false;
+ private bool wasLoading = false;
+
#endregion
#region 属性
- /// 获取内部口袋地图
+ // We use the public properties from the base class: this.ShuttleComp and this.TransporterComp
public Map PocketMap => pocketMap;
-
- /// 原版MapPortal的PocketMap属性(包含自动清理无效地图的逻辑)
- public Map PocketMapForPortal
+ public bool PocketMapExists
{
get
{
- Map map = pocketMap;
- if (map != null && map.Parent?.HasMap == false)
+ if (pocketMap != null && pocketMap.Parent?.HasMap == false)
{
pocketMap = null;
}
- return pocketMap;
+ return pocketMap != null;
}
}
-
- /// 口袋地图是否存在
- public bool PocketMapExists => PocketMapForPortal != null;
-
- /// 口袋地图是否已生成
public bool PocketMapGenerated => pocketMapGenerated;
-
- /// 是否允许直接访问口袋空间
- public bool AllowDirectAccess => allowDirectAccess;
-
- /// 内部容器
- public ThingOwner InnerContainer => innerContainer;
-
- ///
- /// 获取进入按钮的图标
- ///
- protected virtual Texture2D EnterTex => DefaultEnterTex;
-
- ///
- /// 获取进入按钮的文本 - 专门用于人员传送
- ///
- public virtual string EnterString => "WULA.PocketSpace.EnterPawns".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 构造函数
-
- public Building_ArmedShuttleWithPocket()
- {
- innerContainer = new ThingOwner(this, oneStackOnly: false);
- }
#endregion
#region 基础重写方法
-
-
-
- public override void PostMake()
- {
- base.PostMake();
- if (innerContainer == null)
- {
- innerContainer = new ThingOwner(this, oneStackOnly: false);
- }
- }
-
+
public override void ExposeData()
{
base.ExposeData();
- // 模仿原版MapPortal.ExposeData的逻辑
- Map map = pocketMap;
- if (map != null && map.Parent?.HasMap == false)
+ if (Scribe.mode == LoadSaveMode.Saving && pocketMap != null && pocketMap.Parent?.HasMap == false)
{
pocketMap = null;
}
@@ -192,62 +71,40 @@ namespace WulaFallenEmpire
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(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}");
+ Log.Error($"[WULA] Error cleaning up pocket map on DeSpawn: {ex}");
}
}
base.DeSpawn(mode);
}
- ///
- /// 模仿原版MapPortal.Tick方法,处理加载进度通知和穿梭机状态变化
- ///
protected override void Tick()
{
base.Tick();
-
- // 模仿原版MapPortal的Tick逻辑:处理加载进度通知
- if (this.IsHashIntervalTick(60) && Spawned && LoadInProgress && !notifiedCantLoadMore && !AnyPawnCanLoadAnythingNow && leftToLoad?[0]?.AnyThing != null)
+
+ if (!Spawned) return;
+
+ bool isLoading = this.TransporterComp.leftToLoad != null && this.TransporterComp.leftToLoad.Any(x => x.CountToTransfer > 0);
+ if (wasLoading && !isLoading && doTeleportAfterLoading)
{
- notifiedCantLoadMore = true;
- Messages.Message("MessageCantLoadMoreIntoPortal".Translate(Label, Faction.OfPlayer.def.pawnsPlural, leftToLoad[0].AnyThing), this, MessageTypeDefOf.CautionInput);
+ TeleportContentsToPocketDimension();
+ doTeleportAfterLoading = false;
}
-
- // 每隔一段时间检查退出点目标是否需要更新(处理穿梭机移动的情况)
+ wasLoading = isLoading;
+
if (this.IsHashIntervalTick(2500) && pocketMapGenerated && exit != null)
{
UpdateExitPointTarget();
@@ -261,19 +118,10 @@ namespace WulaFallenEmpire
if (pocketMapGenerated)
{
sb.AppendLine("WULA.PocketSpace.Status".Translate() + ": " + "WULA.PocketSpace.Ready".Translate());
- if (innerContainer.Count > 0)
+ if (pocketMap.mapPawns.AllPawnsSpawned.Any(p => p.IsColonist))
{
- 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));
- }
+ int pawnCount = pocketMap.mapPawns.AllPawnsSpawned.Count(p => p.IsColonist);
+ sb.AppendLine("WULA.PocketSpace.PawnCount".Translate(pawnCount));
}
}
else
@@ -286,101 +134,150 @@ namespace WulaFallenEmpire
#endregion
- #region 口袋空间核心方法
+ #region Gizmos
- ///
- /// 检查是否可以进入口袋空间
- ///
- public bool CanEnterPocketSpace()
+ public override IEnumerable GetGizmos()
{
- if (!allowDirectAccess)
+ foreach (var baseGizmo in base.GetGizmos())
{
- return false; // 需要特殊权限
+ var command = baseGizmo as Command;
+ if (command != null && (command.defaultLabel == "CommandLoadTransporter".Translate() || command.defaultLabel == "CommandLoadTransporter".Translate() + "..."))
+ {
+ yield return CreateLoadGizmo(false);
+ if (PocketMapExists) // Only show teleport load if pocket map exists
+ {
+ yield return CreateLoadGizmo(true);
+ }
+ }
+ else
+ {
+ yield return baseGizmo;
+ }
}
- if (!Spawned)
+ if (pocketMapGenerated && PocketMapExists)
{
- 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));
+ yield return new Command_Action
+ {
+ defaultLabel = "WULA.ViewPocketSpace".Translate(),
+ defaultDesc = "WULA.ViewPocketSpaceDesc".Translate(),
+ icon = ViewPocketMapTex,
+ action = SwitchToPocketSpace
+ };
}
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 (TransferPawnToPocketSpace(pawn))
+ yield return new Command_Action
{
- transferredCount++;
- }
- }
-
- if (transferredCount > 0)
- {
- Messages.Message("WULA.PocketSpace.TransferSuccess".Translate(transferredCount), MessageTypeDefOf.PositiveEvent);
-
- // 切换到口袋地图
- Current.Game.CurrentMap = pocketMap;
- Find.CameraDriver.JumpToCurrentMapLoc(pocketMap.Center);
+ defaultLabel = "WULA.CreatePocketSpace".Translate(),
+ defaultDesc = "WULA.CreatePocketSpaceDesc".Translate(),
+ icon = EnterPocketMapTex,
+ action = CreatePocketMap
+ };
}
}
- ///
- /// 切换到口袋空间视角
- ///
+ private Command_Action CreateLoadGizmo(bool teleport)
+ {
+ var command = new Command_Action();
+ var originalLoadGizmo = this.TransporterComp.CompGetGizmosExtra().FirstOrDefault(g => g is Command && (((Command)g).defaultLabel == "CommandLoadTransporter".Translate() || ((Command)g).defaultLabel == "CommandLoadTransporter".Translate() + "...")) as Command;
+
+ if (teleport)
+ {
+ command.defaultLabel = "WULA.LoadAndTeleport".Translate();
+ command.defaultDesc = "WULA.LoadAndTeleportDesc".Translate();
+ command.icon = TeleportAndLoadTex;
+ }
+ else
+ {
+ command.defaultLabel = "WULA.LoadIntoCargo".Translate();
+ command.defaultDesc = "WULA.LoadIntoCargoDesc".Translate();
+ command.icon = originalLoadGizmo?.icon ?? ContentFinder.Get("UI/Commands/LoadTransporter");
+ }
+
+ if (originalLoadGizmo != null)
+ {
+ command.action = () =>
+ {
+ doTeleportAfterLoading = teleport;
+ originalLoadGizmo.ProcessInput(null);
+ };
+
+ if (originalLoadGizmo.Disabled)
+ {
+ command.Disable(originalLoadGizmo.disabledReason);
+ }
+ }
+ else
+ {
+ command.Disable("Error: Could not find original load command.".Translate());
+ }
+
+ // This disabling logic is now redundant if we control visibility in GetGizmos,
+ // but keeping it here for safety against direct calls.
+ if (teleport && !PocketMapExists)
+ {
+ command.Disable("WULA.PocketSpace.NotGenerated".Translate());
+ }
+
+ return command;
+ }
+
+ #endregion
+
+ #region 口袋空间核心方法
+
+ public void TeleportContentsToPocketDimension()
+ {
+ if (!PocketMapExists || this.TransporterComp == null) return;
+
+ var thingsToTeleport = this.TransporterComp.innerContainer.ToList();
+ if (!thingsToTeleport.Any()) return;
+
+ Log.Message($"[WULA] Teleporting {thingsToTeleport.Count} things to pocket dimension.");
+
+ IntVec3 spawnCenter = exit?.Position ?? pocketMap.Center;
+
+ this.TransporterComp.innerContainer.TryDropAll(spawnCenter, pocketMap, ThingPlaceMode.Near);
+
+ Messages.Message("WULA.TeleportComplete".Translate(thingsToTeleport.Count), this, MessageTypeDefOf.PositiveEvent);
+ }
+
+ public void EnterPocketSpace(IEnumerable pawns)
+ {
+ if (!PocketMapExists)
+ {
+ Messages.Message("WULA.PocketSpace.NotGenerated".Translate(), this, MessageTypeDefOf.RejectInput);
+ return;
+ }
+
+ if (pawns == null || !pawns.Any())
+ {
+ return;
+ }
+
+ foreach (Pawn pawn in pawns.ToList())
+ {
+ if (pawn != null && pawn.Spawned)
+ {
+ TransferPawnToPocketSpace(pawn);
+ }
+ }
+
+ Messages.Message("WULA.PocketSpace.TransferSuccess".Translate(pawns.Count()), MessageTypeDefOf.PositiveEvent);
+ Current.Game.CurrentMap = pocketMap;
+ }
+
public void SwitchToPocketSpace()
{
- if (pocketMap == null)
+ if (!PocketMapExists)
{
if (!pocketMapGenerated)
{
CreatePocketMap();
}
- if (pocketMap == null)
+ if (!PocketMapExists)
{
Messages.Message("WULA.PocketSpace.CreationFailed".Translate(), this, MessageTypeDefOf.RejectInput);
return;
@@ -391,15 +288,10 @@ namespace WulaFallenEmpire
Find.CameraDriver.JumpToCurrentMapLoc(pocketMap.Center);
}
- ///
- /// 创建口袋地图 - 使用原版PocketMapUtility(模仿 MapPortal.GeneratePocketMap)
- ///
private void CreatePocketMap()
{
try
{
- // 模仿原版 MapPortal.GeneratePocketMap 的实现
- // 注意:我们不是MapPortal,所以设为null
PocketMapUtility.currentlyGeneratingPortal = null;
pocketMap = GeneratePocketMapInt();
PocketMapUtility.currentlyGeneratingPortal = null;
@@ -407,30 +299,25 @@ namespace WulaFallenEmpire
if (pocketMap != null)
{
pocketMapGenerated = true;
-
- // 在口袋地图中心放置退出点
PlaceExitInPocketMap();
-
Log.Message($"[WULA] Pocket map created successfully with size {pocketMap.Size}");
+ Messages.Message("WULA.PocketSpace.CreationSuccess".Translate(), this, MessageTypeDefOf.PositiveEvent);
}
else
{
Log.Error("[WULA] Failed to create pocket map");
+ Messages.Message("WULA.PocketSpace.CreationFailed".Translate(), this, MessageTypeDefOf.RejectInput);
}
}
catch (Exception ex)
{
Log.Error($"[WULA] Error creating pocket map: {ex}");
- PocketMapUtility.currentlyGeneratingPortal = null; // 确保清理
+ PocketMapUtility.currentlyGeneratingPortal = null;
}
}
-
- ///
- /// 模仿原版MapPortal.GeneratePocketMapInt
- ///
+
protected virtual Map GeneratePocketMapInt()
{
- // 使用自定义地图生成器
if (mapGenerator == null)
{
mapGenerator = DefDatabase.GetNamed("WULA_PocketSpace_Small", false)
@@ -438,52 +325,36 @@ namespace WulaFallenEmpire
?? MapGeneratorDefOf.Base_Player;
}
- // 使用自定义尺寸
IntVec3 mapSize = new IntVec3(pocketMapSize.x, 1, pocketMapSize.z);
-
return PocketMapUtility.GeneratePocketMap(mapSize, mapGenerator, GetExtraGenSteps(), this.Map);
}
- ///
- /// 模仿原版MapPortal.GetExtraGenSteps
- ///
protected virtual IEnumerable GetExtraGenSteps()
{
return Enumerable.Empty();
}
- ///
- /// 在口袋地图中创建退出点(模仿原版)
- ///
+
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,
+ IntVec3 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
+ exit = exitPortal;
}
GenPlace.TryPlaceThing(exitBuilding, exitPos, pocketMap, ThingPlaceMode.Direct);
- Log.Message($"[WULA] Created exit point at {exitPos} in pocket map");
}
else
{
@@ -496,16 +367,12 @@ namespace WulaFallenEmpire
}
}
- ///
- /// 将单个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));
@@ -524,53 +391,29 @@ namespace WulaFallenEmpire
return false;
}
- ///
- /// 将所有物品和人员从口袋空间转移到主地图
- ///
private void TransferAllFromPocketToMainMap()
{
if (pocketMap == null || !Spawned) return;
try
{
- // 转移所有殖民者
- List pawnsToTransfer = pocketMap.mapPawns.AllPawnsSpawned
- .Where(p => p.IsColonist).ToList();
-
- foreach (Pawn pawn in pawnsToTransfer)
+ List thingsToTransfer = new List(pocketMap.listerThings.AllThings);
+ foreach (Thing thing in thingsToTransfer)
{
- 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)
+ if (thing.def.category != ThingCategory.Mote && thing.def.category != ThingCategory.Filth)
{
- pawn.DeSpawn();
- GenPlace.TryPlaceThing(pawn, spawnPos, this.Map, ThingPlaceMode.Near);
- }
- }
-
- // 转移所有物品到内部容器
- List 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);
- }
- }
+ if(this.TransporterComp.innerContainer.TryAddOrTransfer(thing))
+ {
+ //Success
+ }
+ else
+ {
+ thing.Destroy();
+ }
}
}
- Log.Message($"[WULA] Transferred {pawnsToTransfer.Count} pawns and {itemsToTransfer.Count} items from pocket space");
+ Log.Message($"[WULA] Transferred {thingsToTransfer.Count} things from pocket space to shuttle cargo.");
}
catch (Exception ex)
{
@@ -579,695 +422,64 @@ namespace WulaFallenEmpire
}
#endregion
-
- #region Gizmo方法
-
- public override IEnumerable 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;
- }
- else
- {
- // 进入口袋空间按钮(直接复制原版MapPortal的逻辑)
- Command_Action enterCommand = new Command_Action();
- enterCommand.action = delegate
- {
- try
- {
- Log.Message("[WULA] Creating MapPortalAdapter...");
- var adapter = new MapPortalAdapter(this);
- Log.Message($"[WULA] Adapter created. Map: {adapter.Map?.uniqueID}, Spawned: {adapter.Spawned}");
-
- Log.Message("[WULA] Creating Dialog_EnterPortal...");
- Dialog_EnterPortal window = new Dialog_EnterPortal(adapter);
- Log.Message("[WULA] Dialog created, adding to WindowStack...");
- Find.WindowStack.Add(window);
- Log.Message("[WULA] Dialog added to WindowStack successfully.");
- }
- catch (Exception ex)
- {
- Log.Error($"[WULA] Error opening Dialog_EnterPortal: {ex}");
- Messages.Message("WULA.PocketSpace.LoadingDialogError".Translate(), MessageTypeDefOf.RejectInput);
- }
- };
- 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;
- }
-
- // 查看口袋地图按钮(模仿原版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()
- {
- // 返回containerProxy,与Dialog_EnterPortal兼容
- return containerProxy;
- }
+
+ #region IThingHolder
public void GetChildHolders(List outChildren)
{
- ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, GetDirectlyHeldThings());
- }
-
- #endregion
-
- #region MapPortal兼容接口(使Dialog_EnterPortal能正常工作)
-
- ///
- /// 检查是否可以进入(模仿原版MapPortal.IsEnterable)
- ///
- public virtual bool IsEnterable(out string reason)
- {
- if (!allowDirectAccess)
+ if (this.TransporterComp != null)
{
- reason = "WULA.PocketSpace.AccessDenied".Translate();
- return false;
+ outChildren.Add(this.TransporterComp);
}
-
- if (!Spawned)
- {
- reason = "WULA.PocketSpace.NotSpawned".Translate();
- return false;
- }
-
- reason = "";
- return true;
- }
-
- ///
- /// 获取目标地图(模仿原版MapPortal.GetOtherMap)
- ///
- public virtual Map GetOtherMap()
- {
- if (pocketMap == null)
- {
- CreatePocketMap();
- }
- return pocketMap;
- }
-
- ///
- /// 获取目标位置(模仿原版MapPortal.GetDestinationLocation)
- ///
- public virtual IntVec3 GetDestinationLocation()
- {
- if (exit != null)
- {
- return exit.Position;
- }
- return pocketMap?.Center ?? IntVec3.Invalid;
- }
-
- ///
- /// 处理进入事件(模仿原版MapPortal.OnEntered)
- ///
- public virtual void OnEntered(Pawn pawn)
- {
- // 通知物品被添加(用于统计和管理)
- Notify_ThingAdded(pawn);
-
- // 播放传送音效(如果存在)
- if (Find.CurrentMap == this.Map)
- {
- // 可以在这里添加音效播放
- // def.portal?.traverseSound?.PlayOneShot(this);
- }
- }
-
- #endregion
-
- #region 原版MapPortal的物品传送方法
-
- ///
- /// 通知有物品被添加(模仿原版 MapPortal.Notify_ThingAdded)
- ///
- public void Notify_ThingAdded(Thing t)
- {
- Log.Message($"[WULA] Notify_ThingAdded called for: {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();
- 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");
- }
- }
-
- ///
- /// 添加到加载列表(模仿原版 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)
- {
- 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;
- }
-
- ///
- /// 取消加载(模仿原版 MapPortal.CancelLoad)
- ///
- public void CancelLoad(MapPortal portal = null)
- {
- // 简化版本:只清理leftToLoad列表
- // 原版需要查找MapPortal相关的Lord,但我们不是MapPortal类型
- if (leftToLoad != null)
- {
- leftToLoad.Clear();
- }
- }
-
-
-
- #endregion
-
- #region 穿梭机状态变化处理
-
- ///
- /// 更新口袋空间中退出点的目标位置(处理穿梭机位置变化)
- ///
- 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}");
- }
- }
-
-
-
- ///
- /// 重写 SpawnSetup,确保位置变化时更新退出点
- ///
- 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(this, oneStackOnly: false);
- }
-
- // 初始化containerProxy(模仿原版MapPortal)
- containerProxy = new PortalContainerProxy
- {
- portal = this
- };
-
- // 更新退出点目标(处理穿梭机重新部署的情况)
- UpdateExitPointTarget();
-
- // 从 ThingDef 中读取 portal 配置
- if (def.HasModExtension())
- {
- var portalProps = def.GetModExtension();
- 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.GetNamed("AncientStockpile", false)
- ?? DefDatabase.GetNamed("Base_Player", false)
- ?? MapGeneratorDefOf.Base_Player;
- }
-
- if (exitDef == null)
- {
- exitDef = DefDatabase.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
- }
-
- ///
- /// 口袋空间属性配置类
- ///
- public class PocketMapProperties : DefModExtension
- {
- /// 口袋地图生成器
- public MapGeneratorDef pocketMapGenerator;
-
- /// 退出点定义
- public ThingDef exitDef;
-
- /// 口袋地图大小
- public IntVec2 pocketMapSize = new IntVec2(13, 13);
-
- /// 允许直接访问
- public bool allowDirectAccess = true;
- }
-
- ///
- /// MapPortal适配器类,使非MapPortal类型能够使用Dialog_EnterPortal
- /// 直接继承MapPortal并委托给Building_ArmedShuttleWithPocket实现
- ///
- public class MapPortalAdapter : MapPortal
- {
- private Building_ArmedShuttleWithPocket shuttleBuilding;
-
- public MapPortalAdapter(Building_ArmedShuttleWithPocket shuttle)
- {
- Log.Message($"[WULA] MapPortalAdapter constructor called for shuttle: {shuttle?.def?.defName}");
- shuttleBuilding = shuttle;
-
- // 确保基础属性正确设置
- this.def = shuttle.def ?? ThingDefOf.Wall; // 提供默认值避免null
- this.HitPoints = shuttle.HitPoints;
-
- // 关键:手动设置Map和Position属性以避免null引用
- if (shuttle.Spawned && shuttle.Map != null)
- {
- // 手动调用父类的SpawnSetup,但要小心处理
- try
- {
- base.SpawnSetup(shuttle.Map, false);
- Log.Message($"[WULA] SpawnSetup completed for map: {shuttle.Map.uniqueID}");
- }
- catch (Exception ex)
- {
- Log.Warning($"[WULA] SpawnSetup failed, manually setting properties: {ex.Message}");
- // 如果SpawnSetup失败,手动设置关键属性
- }
- }
-
- // 设置基础MapPortal属性
- this.pocketMap = shuttle.PocketMapForPortal;
- this.leftToLoad = shuttle.leftToLoad ?? new List();
-
- // 确保exit属性被正确设置
- // 注意:由于类型不兼容,暂时设为null,在GetDestinationLocation中处理
- this.exit = null; // 原版PocketMapExit类型与我们的Building_PocketMapExit不兼容
-
- Log.Message($"[WULA] Synced pocketMap: {pocketMap?.uniqueID}, leftToLoad count: {leftToLoad?.Count}, exit: {exit != null}");
-
- // 使用原版的PortalContainerProxy
- try
- {
- this.containerProxy = new RimWorld.PortalContainerProxy
- {
- portal = this
- };
- Log.Message("[WULA] Created RimWorld.PortalContainerProxy successfully");
- }
- catch (Exception ex)
- {
- Log.Error($"[WULA] Failed to create RimWorld.PortalContainerProxy: {ex}");
- // 使用我们自己的实现作为回退
- Log.Message("[WULA] Using custom PortalContainerProxy as fallback");
- }
- Log.Message("[WULA] MapPortalAdapter initialization complete");
- }
-
- // 委托给shuttleBuilding的关键属性(使用new隐藏基类属性)
- // 委托给shuttleBuilding的关键属性(使用new隐藏基类属性)
- public new Map Map
- {
- get
- {
- // 优先返回shuttleBuilding的Map
- if (shuttleBuilding?.Map != null)
- {
- return shuttleBuilding.Map;
- }
-
- // 如果shuttleBuilding的Map为null,返回基类的Map
- if (base.Map != null)
- {
- return base.Map;
- }
-
- // 最后的回退:返回当前游戏地图(避免null)
- Log.Warning("[WULA] Both shuttleBuilding.Map and base.Map are null, using Current.Game.CurrentMap as fallback");
- return Find.CurrentMap ?? Current.Game.Maps?.FirstOrDefault();
- }
- }
- public new IntVec3 Position => shuttleBuilding?.Position ?? base.Position;
- public new bool Spawned => shuttleBuilding?.Spawned ?? base.Spawned;
- public new string Label => shuttleBuilding?.Label ?? base.Label;
-
- // 委托给shuttleBuilding的关键方法(重写虚拟方法)
- public override bool IsEnterable(out string reason)
- {
- return shuttleBuilding.IsEnterable(out reason);
- }
-
- public override Map GetOtherMap()
- {
- return shuttleBuilding.GetOtherMap();
- }
-
- public override IntVec3 GetDestinationLocation()
- {
- return shuttleBuilding.GetDestinationLocation();
- }
-
- public override void OnEntered(Pawn pawn)
- {
- shuttleBuilding.OnEntered(pawn);
- }
-
- // 委托给shuttleBuilding的物品管理方法(使用new隐藏基类方法)
- public new void Notify_ThingAdded(Thing t)
- {
- shuttleBuilding.Notify_ThingAdded(t);
- }
-
- public new void AddToTheToLoadList(TransferableOneWay t, int count)
- {
- shuttleBuilding.AddToTheToLoadList(t, count);
- }
-
- public new int SubtractFromToLoadList(Thing t, int count)
- {
- return shuttleBuilding.SubtractFromToLoadList(t, count);
- }
-
- public new void CancelLoad()
- {
- // 调用shuttleBuilding的CancelLoad方法
- shuttleBuilding.CancelLoad();
- }
-
- // 重写原版MapPortal的关键属性
- public override string EnterString => shuttleBuilding.EnterString;
- public override string CancelEnterString => shuttleBuilding.CancelEnterString;
- public override string EnteringString => shuttleBuilding.EnteringString;
-
- // 隐藏LoadInProgress属性,确保Dialog_EnterPortal能正确读取
- public new bool LoadInProgress => shuttleBuilding?.LoadInProgress ?? false;
-
- // 确保SpawnSetup正确处理
- public override void SpawnSetup(Map map, bool respawningAfterLoad)
- {
- // 调用基类的SpawnSetup来正确初始化MapPortal的基础设施
- base.SpawnSetup(map, respawningAfterLoad);
-
- // 同步关键字段
- this.pocketMap = shuttleBuilding?.PocketMapForPortal;
- this.leftToLoad = shuttleBuilding?.leftToLoad ?? new List();
- }
-
- // 重写AddItemsToTransferables,让Dialog_EnterPortal只处理人员
- // 因为物品已经通过装载按钮正确传送到内部空间了
- protected virtual void AddItemsToTransferables()
- {
- // 不添加任何物品,因为物品传送由装载按钮处理
- // 这样Dialog_EnterPortal只专注于人员传送
- Log.Message("[WULA] AddItemsToTransferables: Skipping items, handled by loading button");
- }
- }
-
- ///
- /// 专为Building_ArmedShuttleWithPocket设计的PortalContainerProxy适配器
- /// 模仿原版PortalContainerProxy的行为,但适配非-MapPortal类型
- ///
- public class PortalContainerProxy : ThingOwner
- {
- public Building_ArmedShuttleWithPocket portal;
-
- public override int Count => 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)
- {
- if (portal == null) return false;
-
- Log.Message($"[WULA] PortalContainerProxy.TryAdd called for: {item?.def?.defName} x{item?.stackCount}");
-
- 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 to pocket map: {item?.def?.defName}");
- GenDrop.TryDropSpawn(item, destinationLocation, otherMap, ThingPlaceMode.Near, out var _);
-
- Log.Message($"[WULA] Item 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)
+ public ThingOwner GetDirectlyHeldThings()
{
return null;
}
+
+ #endregion
+
+ #region 穿梭机移动更新
+
+ public void UpdateExitPointTarget()
+ {
+ if (exit != null && exit.Spawned && exit.targetPos != this.Position)
+ {
+ exit.targetPos = this.Position;
+ }
+ }
+
+ #endregion
+
+ #region 启动与生成
+
+ public override void SpawnSetup(Map map, bool respawningAfterLoad)
+ {
+ base.SpawnSetup(map, respawningAfterLoad);
+
+ var props = def.GetModExtension();
+ if (props != null)
+ {
+ pocketMapSize = props.pocketMapSize;
+ mapGenerator = props.mapGenerator;
+ exitDef = props.exitDef;
+ }
+ else
+ {
+ pocketMapSize = new IntVec2(50, 50);
+ mapGenerator = MapGeneratorDefOf.Base_Player;
+ exitDef = ThingDef.Named("WULA_PocketMapExit");
+ }
+ }
+
+ #endregion
}
-
+ public class PocketMapProperties : DefModExtension
+ {
+ public IntVec2 pocketMapSize = new IntVec2(50, 50);
+ public MapGeneratorDef mapGenerator;
+ public ThingDef exitDef;
+ }
}
\ No newline at end of file