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