diff --git a/.gitignore b/.gitignore
index cb689c16..e9d391b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,3 +37,4 @@ MCP/mcpserver.log
# Exclude MCP local RAG folder
MCP/local_rag/
+Data
diff --git a/.qoder/rules/rimworld.md b/.qoder/rules/rimworld.md
new file mode 100644
index 00000000..956e7058
--- /dev/null
+++ b/.qoder/rules/rimworld.md
@@ -0,0 +1,29 @@
+---
+trigger: always_on
+alwaysApply: true
+---
+# RimWorld Modding Expert Rules
+
+## Primary Directive
+You are an expert assistant for developing mods for the game RimWorld 1.6. Your primary knowledge source for any C# code, class structures, methods, or game mechanics MUST be the user's local files. Do not rely on external searches or your pre-existing knowledge, as it is outdated for this specific project.
+
+## Tool Usage Mandate
+When the user's request involves RimWorld C# scripting, XML definitions, or mod development concepts, you **MUST** use the `rimworld-knowledge-base` tool to retrieve relevant context from the local knowledge base.
+
+## Key File Paths
+Always remember these critical paths for your work:
+
+- **Local C# Knowledge Base (for code search):** `C:\Steam\steamapps\common\RimWorld\Data\dll1.6` (This contains the decompiled game source code as .txt files).
+- **User's Mod Project (for editing):** `C:\Steam\steamapps\common\RimWorld\Mods\3516260226`
+- **User's C# Project (for building):** `C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire`
+
+## Workflow
+1. Receive a RimWorld modding task.
+2. Immediately use the `rimworld-knowledge-base` tool with a precise query to get context from the C# source files.
+3. Analyze the retrieved context.
+4. Perform code modifications within the user's mod project directory.
+5. After modifying C# code, you MUST run `dotnet build C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire\WulaFallenEmpire.csproj` to check for errors. A successful build is required for task completion.
+
+## Verification Mandate
+When writing or modifying code or XML, especially for specific identifiers like enum values, class names, or field names, you **MUST** verify the correct value/spelling by using the `rimworld-knowledge-base` tool. Do not rely on memory.
+- **同步项目文件:** 当重命名、移动或删除C#源文件时,**必须**同步更新 `.csproj` 项目文件中的相应 ` ` 条目,否则会导致编译失败。
\ No newline at end of file
diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll
index 9b05ab37..68b8b63c 100644
Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ
diff --git a/1.6/1.6/Defs/MapGeneration/MapGenerator_WULA_PocketSpace_Small.xml b/1.6/1.6/Defs/MapGeneration/MapGenerator_WULA_PocketSpace_Small.xml
new file mode 100644
index 00000000..5ac07594
--- /dev/null
+++ b/1.6/1.6/Defs/MapGeneration/MapGenerator_WULA_PocketSpace_Small.xml
@@ -0,0 +1,30 @@
+
+
+
+
+ WULA_PocketSpace_Small
+ WULA small pocket space
+ true
+ true
+
+ Underground
+ 20
+ true
+ true
+
+
+ ElevationFertility
+ Underground_RocksFromGrid
+ Terrain
+ WULA_PocketSpace_Small
+
+
+
+
+
+
+ WULA_PocketSpace_Small
+ 990
+
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/Building_WULA_ArmedShuttleWithPocket.xml b/1.6/1.6/Defs/ThingDefs_Buildings/Building_WULA_ArmedShuttleWithPocket.xml
new file mode 100644
index 00000000..21bb662d
--- /dev/null
+++ b/1.6/1.6/Defs/ThingDefs_Buildings/Building_WULA_ArmedShuttleWithPocket.xml
@@ -0,0 +1,330 @@
+
+
+
+
+ WULA_ArmedShuttleWithPocket
+ 乌拉武装运输机
+ An advanced chemfuel-powered shuttle with integrated pocket space technology. Equipped with a defensive turret and internal storage dimension that doesn't require hacking to access. Perfect for long-distance exploration and mobile operations.
+ WulaFallenEmpire.Building_ArmedShuttleWithPocket
+ true
+ Building
+ 50
+ true
+ PassThroughOnly
+ 0.5
+ (3,5)
+ true
+ (0.56, 0.62, 0.9)
+ 1
+
+ Graphic_Multi
+ Things/Building/PassengerShuttle/PassengerShuttle
+ CutoutComplex
+ (3,5)
+
+ (1.8, 1.0, 4.1)
+ (-0.1, 0, 0)
+
+
+
+ 6000
+ 0.5
+ 40000
+ 150
+ 0.65
+
+ Normal
+ Odyssey
+ 8
+
+ 300
+ 200
+ 8
+ 2
+ 1
+
+ true
+
+ 60
+ 60
+ 5
+ 4
+
+ true
+ true
+ (2, 0, 0)
+ East
+ true
+ Light
+ BulletImpact_Metal
+ true
+ RealtimeOnly
+ ConstructMetal
+ true
+
+ false
+ BuildingDestroyed_Metal_Big
+ true
+ true
+
+ ShuttleEngine
+
+ Gun_ChargeBlasterHeavyTurret
+ 5.5
+ 1.75
+ (0, 0.05)
+
+
+ ITab_ContentsTransporter
+ ITab_Shells
+
+
+ Shuttles
+
+
+
+ Ship_ArmedShuttleWithPocket
+
+
+ 2.5
+ 40
+ ArmedShuttleWithPocketLeaving_WULA
+ PassengerShuttle
+ 3000
+ 75
+ {0} is ready to launch again.
+
+
+ 750
+ true
+ true
+ Shuttle_PawnLoaded
+ Shuttle_PawnExit
+ true
+
+
+ 500
+ true
+ 500
+
+
+ Chemfuel
+
+
+ Chemfuel
+ Chemfuel
+ true
+ 1
+ true
+ false
+ true
+ true
+
+
+ CompPowerPlant
+ -400
+ true
+
+
+ ShuttleIdle_Ambience
+
+
+
+
+ WULA_PocketSpace_Small
+ WULA_PocketMapExit
+ (13, 13)
+ true
+
+
+
+ PlaceWorker_NotUnderRoof
+ PlaceWorker_TurretTop
+
+ 2601
+
+
+
+
+ WULA_Bullet_ArmedShuttleAdvanced
+ advanced shuttle cannon shell
+
+ Things/Projectile/Bullet_Big
+ Graphic_Single
+ (0.8, 0.9, 1.0)
+
+
+ Bullet
+ 30
+ 0.4
+ 75
+
+
+
+
+ Gun_ChargeBlasterAdvancedTurret
+ advanced charge blaster
+ An upgraded pulse-charged rapid-fire blaster with enhanced targeting systems and increased firepower.
+
+ Things/Item/Equipment/WeaponRanged/ChargeBlasterLight
+ Graphic_Single
+ (0.8, 0.9, 1.0)
+
+
+ 0.85
+ 0.75
+ 0.65
+ 0.45
+ 4.5
+
+
+
+ Verb_Shoot
+ true
+ WULA_Bullet_ArmedShuttleAdvanced
+ 1.0
+ 3.5
+ 50.9
+ 6
+ 8
+ Shot_ChargeBlaster
+ GunTail_Heavy
+ 10
+
+ true
+
+
+
+
+
+
+
+ ArmedShuttleWithPocketIncoming_WULA
+ armed shuttle with pocket space (incoming)
+ ShuttleIncoming
+
+ Graphic_Multi
+ Things/Building/PassengerShuttle/PassengerShuttle
+ CutoutComplex
+ (0.85, 0.9, 1.0)
+ (3,5)
+
+ (3,5)
+
+ Shuttle_Landing
+ 250
+ 200~250
+ (3.5,5.5)
+
+
+ (0,30)
+ (0.5,5)
+ (0.9,-5)
+ (0.95,0)
+
+
+
+
+ (0.95,2.5)
+ (1,0)
+
+
+
+
+ (0.6,0.6)
+ (0.95,0.1)
+
+
+
+
+
+
+
+ ArmedShuttleWithPocketLeaving_WULA
+ armed shuttle with pocket space (leaving)
+ PassengerShuttleLeaving
+ true
+
+ Graphic_Multi
+ Things/Building/PassengerShuttle/PassengerShuttle
+ CutoutComplex
+ (0.85, 0.9, 1.0)
+ (3,5)
+
+ (3,5)
+
+ true
+ Shuttle_Leaving
+ -10
+ -40~-15
+ 0.05
+ Things/Skyfaller/SkyfallerShadowRectangle
+ (3.5,5.5)
+ 1
+
+
+ (0,0)
+ (0.15,10)
+ (0.5,-5)
+
+
+
+
+ (0,0)
+ (0.08,2)
+
+
+
+
+ (0,0.2)
+ (0.4,0.7)
+
+
+
+
+
+
+
+ Ship_ArmedShuttleWithPocket
+ armed shuttle with pocket space
+ WULA_ArmedShuttleWithPocket
+ ArmedShuttleWithPocketIncoming_WULA
+ ArmedShuttleWithPocketLeaving_WULA
+ PassengerShuttle
+ true
+
+
+
+
+ WULA_PocketMapExit
+ pocket space exit
+ An exit portal that allows return from the pocket space to the main map.
+ WulaFallenEmpire.Building_PocketMapExit
+ (3,3)
+ MapMeshAndRealTime
+
+ Wula/Building/WULA_War_Machine_Recharger
+ Graphic_Multi
+ (3,3)
+
+ (0,0,0)
+ Standable
+
+ 0
+
+
+
+ 10
+ (140,160,184,0)
+
+
+ UndercaveMapExitLightshafts
+
+
+ CompPowerPlant
+ -400
+ true
+
+
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/Building_WULA_Shuttle.xml b/1.6/1.6/Defs/ThingDefs_Buildings/Building_WULA_Shuttle.xml
index dd2545d3..0baf7187 100644
--- a/1.6/1.6/Defs/ThingDefs_Buildings/Building_WULA_Shuttle.xml
+++ b/1.6/1.6/Defs/ThingDefs_Buildings/Building_WULA_Shuttle.xml
@@ -2,7 +2,7 @@
WULA_ArmedShuttle
- armed shuttle
+ 乌拉武装穿梭机
A chemfuel-powered shuttle designed for long-distance travel, equipped with a turret for defense. It is capable of reaching orbital locations.
WulaFallenEmpire.Building_ArmedShuttle
true
@@ -26,7 +26,7 @@
- 600
+ 6000
0.5
40000
150
@@ -175,7 +175,7 @@
ArmedShuttleIncoming_WULA
- armed shuttle (incoming)
+ 武装穿梭机 (接近中)
WulaFallenEmpire.ArmedShuttleIncoming
Graphic_Multi
@@ -214,7 +214,7 @@
ArmedShuttleLeaving_WULA
- armed shuttle (leaving)
+ 穿梭机出口
PassengerShuttleLeaving
true
diff --git a/1.6/1.6/Languages/ChineseSimplified/Keyed/WULA_PocketShuttle.xml b/1.6/1.6/Languages/ChineseSimplified/Keyed/WULA_PocketShuttle.xml
new file mode 100644
index 00000000..23b9a7eb
--- /dev/null
+++ b/1.6/1.6/Languages/ChineseSimplified/Keyed/WULA_PocketShuttle.xml
@@ -0,0 +1,85 @@
+
+
+
+
+ 内部空间状态
+ 已就绪
+ 未初始化
+ 储存物品: {0}
+
+
+ 进入内部空间
+ 传送人员到内部空间
+ 进入穿梭机的内部口袋空间。无需骇入即可直接访问。选中的殖民者将被传送到内部空间。
+ 创建内部空间
+ 创建穿梭机的内部口袋空间。首次使用时需要创建。
+ 内部空间创建成功!
+ 取消进入
+ 正在进入...
+ 查看地图
+ 切换到口袋空间地图查看内部情况。
+
+ 切换到内部空间
+ 直接切换视角到内部口袋空间。适用于已经有殖民者在内部空间时的快速切换。
+
+ 管理内部储存
+ 打开内部容器管理界面,可以查看和取出储存在内部空间的物品。
+
+
+ 无法进入内部空间。
+ 访问被拒绝。
+ 穿梭机未部署。
+ 内部空间创建失败。
+ {0} 名人员已成功传送到内部空间。
+ 即将切换到内部口袋空间。确认吗?
+
+
+ 内部空间储存管理
+
+ 内部空间人员: {0}
+ 请选择至少一名殖民者进入内部空间。
+ 没有可用的殖民者。
+ 所有殖民者 ({0}人)
+ 仅切换视角
+
+
+ 通过传送门返回
+ 返回主地图
+ 没有目标地图
+ 查看主地图
+ 切换到主地图并查看穿梭机。
+ 装载穿梭机
+ 打开穿梭机装载界面,选择要装载的人员和物品。
+ 取消装载
+ 取消当前的装载操作。
+ 穿梭机状态
+ 查看穿梭机的详细状态信息。
+ 穿梭机信息
+ 无法打开装载对话框。
+ 全员返回
+ 将所有殖民者从口袋空间传送回主地图。
+ {0} 已成功返回主地图。
+ {0} 名人员已全部返回主地图。
+
+
+ 内置空间武装穿梭机
+ 一架配备了集成口袋空间技术的先进化燃料动力穿梭机。装备有防御炮塔和无需骇入即可访问的内部储存维度。非常适合长距离探索和移动作战行动。
+
+
+ 先进充能爆破器
+ 一种升级版脉冲充能速射爆破器,配备增强型瞄准系统和提升的火力输出。
+
+ 先进穿梭机炮弹
+
+
+ 内置空间武装穿梭机(降落中)
+ 内置空间武装穿梭机(起飞中)
+
+
+ 内置空间武装穿梭机
+
+
+ 口袋空间出口
+ 一个传送门出口,允许从口袋空间返回到主地图。
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Languages/English/Keyed/WULA_PocketShuttle.xml b/1.6/1.6/Languages/English/Keyed/WULA_PocketShuttle.xml
new file mode 100644
index 00000000..5165ca19
--- /dev/null
+++ b/1.6/1.6/Languages/English/Keyed/WULA_PocketShuttle.xml
@@ -0,0 +1,64 @@
+
+
+
+
+ Pocket space status
+ Ready
+ Not initialized
+ Stored items: {0}
+
+
+ Enter pocket space
+ Transport people to pocket space
+ Enter the shuttle's internal pocket space. Direct access without hacking required. Selected colonists will be transported to the internal space.
+ Create pocket space
+ Create the shuttle's internal pocket space. Required for first-time use.
+ Pocket space created successfully!
+ Cancel enter
+ Entering...
+ View map
+ Switch to pocket space map to view internal conditions.
+
+ Switch to pocket space
+ Directly switch view to the internal pocket space. Useful for quick switching when colonists are already inside.
+
+ Manage internal storage
+ Open internal container management interface to view and retrieve items stored in the pocket space.
+
+
+ Cannot enter pocket space.
+ Access denied.
+ Shuttle not deployed.
+ Pocket space creation failed.
+ {0} personnel successfully transferred to pocket space.
+ About to switch to internal pocket space. Confirm?
+
+
+ Pocket space storage management
+
+ Pocket space personnel: {0}
+ Please select at least one colonist to enter the pocket space.
+ No available colonists.
+ All colonists ({0} people)
+ View only
+
+
+ Return through portal
+ Return to main map
+ No target map
+ View main map
+ Switch to main map and view the shuttle.
+ Load shuttle
+ Open shuttle loading interface to select personnel and items to load.
+ Cancel loading
+ Cancel the current loading operation.
+ Shuttle status
+ View detailed shuttle status information.
+ Shuttle Information
+ Cannot open loading dialog.
+ Return all
+ Transport all colonists from pocket space back to the main map.
+ {0} successfully returned to main map.
+ {0} personnel have all returned to the main map.
+
+
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/3516260226.code-workspace b/Source/WulaFallenEmpire/3516260226.code-workspace
index b9f86d60..28a1867a 100644
--- a/Source/WulaFallenEmpire/3516260226.code-workspace
+++ b/Source/WulaFallenEmpire/3516260226.code-workspace
@@ -3,16 +3,6 @@
{
"name": "3516260226",
"path": "../.."
- },
- {
- "name": "Data",
- "path": "../../../../Data"
- },
- {
- "path": "../../../../../../workshop/content/294100/3534748687"
- },
- {
- "path": "../../../../../../workshop/content/294100/3550544871"
}
],
"settings": {}
diff --git a/Source/WulaFallenEmpire/WULA_Shuttle/Building_ArmedShuttleWithPocket.cs b/Source/WulaFallenEmpire/WULA_Shuttle/Building_ArmedShuttleWithPocket.cs
new file mode 100644
index 00000000..e224447f
--- /dev/null
+++ b/Source/WulaFallenEmpire/WULA_Shuttle/Building_ArmedShuttleWithPocket.cs
@@ -0,0 +1,1273 @@
+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
+{
+ ///
+ /// 内置空间武装穿梭机 - 基于原版MapPortal机制的口袋空间实现
+ /// 结合了武装防御能力和口袋空间技术的复合型载具
+ ///
+ [StaticConstructorOnStartup]
+ public class Building_ArmedShuttleWithPocket : Building_ArmedShuttle, IThingHolder
+ {
+ #region 静态图标定义(使用原版MapPortal的图标)
+
+ /// 查看口袋地图图标
+ 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");
+
+ #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;
+
+ #endregion
+
+ #region 属性
+
+ /// 获取内部口袋地图
+ public Map PocketMap => pocketMap;
+
+ /// 原版MapPortal的PocketMap属性(包含自动清理无效地图的逻辑)
+ public Map PocketMapForPortal
+ {
+ get
+ {
+ Map map = pocketMap;
+ if (map != null && map.Parent?.HasMap == false)
+ {
+ pocketMap = null;
+ }
+ return pocketMap;
+ }
+ }
+
+ /// 口袋地图是否存在
+ 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)
+ {
+ 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(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);
+ }
+
+ ///
+ /// 模仿原版MapPortal.Tick方法,处理加载进度通知和穿梭机状态变化
+ ///
+ 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 口袋空间核心方法
+
+ ///
+ /// 检查是否可以进入口袋空间
+ ///
+ public bool CanEnterPocketSpace()
+ {
+ if (!allowDirectAccess)
+ {
+ return false; // 需要特殊权限
+ }
+
+ if (!Spawned)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// 进入口袋空间 - 基于原版PocketMapUtility实现
+ ///
+ public void EnterPocketSpace(IEnumerable pawns = null)
+ {
+ if (!CanEnterPocketSpace())
+ {
+ Messages.Message("WULA.PocketSpace.CannotEnter".Translate(), this, MessageTypeDefOf.RejectInput);
+ return;
+ }
+
+ // 创建或获取口袋地图
+ if (pocketMap == null && !pocketMapGenerated)
+ {
+ CreatePocketMap();
+ }
+
+ if (pocketMap == null)
+ {
+ Messages.Message("WULA.PocketSpace.CreationFailed".Translate(), this, MessageTypeDefOf.RejectInput);
+ return;
+ }
+
+ // 传送玩家到口袋空间
+ List pawnsToTransfer = new List();
+
+ if (pawns != null)
+ {
+ pawnsToTransfer.AddRange(pawns.Where(p => p != null && p.Spawned && p.IsColonist));
+ }
+ else
+ {
+ // 如果没有指定人员,传送选中的殖民者
+ pawnsToTransfer.AddRange(Find.Selector.SelectedPawns.Where(p => p.IsColonist));
+ }
+
+ if (pawnsToTransfer.Count == 0)
+ {
+ Messages.Message("WULA.PocketSpace.NoPawnsSelected".Translate(), this, MessageTypeDefOf.RejectInput);
+ return;
+ }
+
+ // 执行传送
+ int transferredCount = 0;
+ foreach (Pawn pawn in pawnsToTransfer)
+ {
+ if (TransferPawnToPocketSpace(pawn))
+ {
+ transferredCount++;
+ }
+ }
+
+ if (transferredCount > 0)
+ {
+ Messages.Message("WULA.PocketSpace.TransferSuccess".Translate(transferredCount), MessageTypeDefOf.PositiveEvent);
+
+ // 切换到口袋地图
+ Current.Game.CurrentMap = pocketMap;
+ Find.CameraDriver.JumpToCurrentMapLoc(pocketMap.Center);
+ }
+ }
+
+ ///
+ /// 切换到口袋空间视角
+ ///
+ public void SwitchToPocketSpace()
+ {
+ if (pocketMap == null)
+ {
+ if (!pocketMapGenerated)
+ {
+ CreatePocketMap();
+ }
+
+ if (pocketMap == null)
+ {
+ Messages.Message("WULA.PocketSpace.CreationFailed".Translate(), this, MessageTypeDefOf.RejectInput);
+ return;
+ }
+ }
+
+ Current.Game.CurrentMap = pocketMap;
+ Find.CameraDriver.JumpToCurrentMapLoc(pocketMap.Center);
+ }
+
+ ///
+ /// 创建口袋地图 - 使用原版PocketMapUtility(模仿 MapPortal.GeneratePocketMap)
+ ///
+ private void CreatePocketMap()
+ {
+ try
+ {
+ // 模仿原版 MapPortal.GeneratePocketMap 的实现
+ // 注意:我们不是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; // 确保清理
+ }
+ }
+
+ ///
+ /// 模仿原版MapPortal.GeneratePocketMapInt
+ ///
+ protected virtual Map GeneratePocketMapInt()
+ {
+ // 使用自定义地图生成器
+ if (mapGenerator == null)
+ {
+ mapGenerator = DefDatabase.GetNamed("WULA_PocketSpace_Small", false)
+ ?? DefDatabase.GetNamed("AncientStockpile", false)
+ ?? 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,
+ 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}");
+ }
+ }
+
+ ///
+ /// 将单个Pawn传送到口袋空间
+ ///
+ private bool TransferPawnToPocketSpace(Pawn pawn)
+ {
+ if (pawn == null || !pawn.Spawned || pocketMap == null) return false;
+
+ try
+ {
+ // 找一个安全的位置
+ IntVec3 spawnPos = CellFinder.RandomClosewalkCellNear(pocketMap.Center, pocketMap, 10,
+ p => p.Standable(pocketMap) && !p.GetThingList(pocketMap).Any(t => t is Pawn));
+
+ if (spawnPos.IsValid)
+ {
+ pawn.DeSpawn();
+ GenPlace.TryPlaceThing(pawn, spawnPos, pocketMap, ThingPlaceMode.Near);
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"[WULA] Error transferring pawn {pawn?.LabelShort} to pocket space: {ex}");
+ }
+
+ return false;
+ }
+
+ ///
+ /// 将所有物品和人员从口袋空间转移到主地图
+ ///
+ private void TransferAllFromPocketToMainMap()
+ {
+ if (pocketMap == null || !Spawned) return;
+
+ try
+ {
+ // 转移所有殖民者
+ List 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 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 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;
+ }
+
+ 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)
+ {
+ reason = "WULA.PocketSpace.AccessDenied".Translate();
+ return false;
+ }
+
+ 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)
+ {
+ return null;
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/WULA_Shuttle/Building_PocketMapExit.cs b/Source/WulaFallenEmpire/WULA_Shuttle/Building_PocketMapExit.cs
new file mode 100644
index 00000000..0a8235fd
--- /dev/null
+++ b/Source/WulaFallenEmpire/WULA_Shuttle/Building_PocketMapExit.cs
@@ -0,0 +1,146 @@
+using RimWorld;
+using Verse;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+
+namespace WulaFallenEmpire
+{
+ ///
+ /// 口袋空间退出点建筑 - 继承自MapPortal以获得完整的双向传送功能
+ ///
+ public class Building_PocketMapExit : MapPortal
+ {
+ /// 目标地图
+ public Map targetMap;
+
+ /// 目标位置
+ public IntVec3 targetPos;
+
+ /// 父穿梭机
+ public Building_ArmedShuttleWithPocket parentShuttle;
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_References.Look(ref targetMap, "targetMap");
+ Scribe_Values.Look(ref targetPos, "targetPos");
+ Scribe_References.Look(ref parentShuttle, "parentShuttle");
+ }
+
+ ///
+ /// 重写获取其他地图,返回主地图(模仿原版MapPortal.GetOtherMap)
+ ///
+ public override Map GetOtherMap()
+ {
+ // 动态更新目标地图,处理穿梭机移动的情况
+ UpdateTargetFromParentShuttle();
+ return targetMap;
+ }
+
+ ///
+ /// 重写获取目标位置,返回主地图上的穿梭机位置(模仿原版MapPortal.GetDestinationLocation)
+ ///
+ public override IntVec3 GetDestinationLocation()
+ {
+ // 动态更新目标位置,处理穿梭机移动的情况
+ UpdateTargetFromParentShuttle();
+ return targetPos;
+ }
+
+ ///
+ /// 从父穿梭机动态更新目标位置,处理穿梭机移动的情况
+ ///
+ private void UpdateTargetFromParentShuttle()
+ {
+ if (parentShuttle != null && parentShuttle.Spawned)
+ {
+ // 如果穿梭机还在地图上,更新目标位置
+ if (targetMap != parentShuttle.Map || targetPos != parentShuttle.Position)
+ {
+ targetMap = parentShuttle.Map;
+ targetPos = parentShuttle.Position;
+ Log.Message($"[WULA] Updated exit target to shuttle location: {targetMap?.uniqueID} at {targetPos}");
+ }
+ }
+ else if (parentShuttle != null && !parentShuttle.Spawned)
+ {
+ // 穿梭机不在地图上(可能在飞行中)
+ // 保持原有目标,但记录警告
+ if (this.IsHashIntervalTick(2500)) // 每隔一段时间检查一次
+ {
+ Log.Warning($"[WULA] Parent shuttle is not spawned, exit target may be outdated. Last known: {targetMap?.uniqueID} at {targetPos}");
+ }
+ }
+ }
+
+ ///
+ /// 重写是否可进入,检查目标地图是否存在(模仿原版MapPortal.IsEnterable)
+ ///
+ public override bool IsEnterable(out string reason)
+ {
+ if (targetMap == null)
+ {
+ reason = "WULA.PocketSpace.NoTargetMap".Translate();
+ return false;
+ }
+ reason = "";
+ return true;
+ }
+
+ ///
+ /// 重写进入事件,处理从口袋空间退出到主地图(模仿原版MapPortal.OnEntered)
+ ///
+ public override void OnEntered(Pawn pawn)
+ {
+ // 不调用 base.OnEntered,因为我们不需要原版的通知机制
+ // 直接处理退出逻辑
+ if (targetMap != null && pawn.Spawned)
+ {
+ ExitPocketSpace(pawn);
+ }
+ }
+
+ ///
+ /// 重写进入按钮文本
+ ///
+ public override string EnterString => "WULA.PocketSpace.ExitToMainMap".Translate();
+
+ ///
+ /// 重写进入按钮图标,使用原版的ViewCave图标
+ ///
+ protected override Texture2D EnterTex => ContentFinder.Get("UI/Commands/ViewCave");
+
+ ///
+ /// 单个人员退出口袋空间(简化版本,利用MapPortal功能)
+ ///
+ private void ExitPocketSpace(Pawn pawn)
+ {
+ if (targetMap == null || !pawn.Spawned) return;
+
+ try
+ {
+ // 在目标地图找一个安全位置
+ IntVec3 exitPos = CellFinder.RandomClosewalkCellNear(targetPos, targetMap, 3, p => p.Standable(targetMap));
+
+ // 传送人员
+ pawn.DeSpawn();
+ GenPlace.TryPlaceThing(pawn, exitPos, targetMap, ThingPlaceMode.Near);
+
+ // 切换到主地图
+ if (pawn.IsColonistPlayerControlled)
+ {
+ Current.Game.CurrentMap = targetMap;
+ Find.CameraDriver.JumpToCurrentMapLoc(exitPos);
+ }
+
+ Messages.Message("WULA.PocketSpace.ExitSuccess".Translate(pawn.LabelShort), MessageTypeDefOf.PositiveEvent);
+ }
+ catch (System.Exception ex)
+ {
+ Log.Error($"[WULA] Error exiting pocket space: {ex}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/WULA_Shuttle/GenStep_WulaPocketSpaceSmall.cs b/Source/WulaFallenEmpire/WULA_Shuttle/GenStep_WulaPocketSpaceSmall.cs
new file mode 100644
index 00000000..d2c2702b
--- /dev/null
+++ b/Source/WulaFallenEmpire/WULA_Shuttle/GenStep_WulaPocketSpaceSmall.cs
@@ -0,0 +1,253 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using RimWorld;
+using Verse;
+
+namespace WulaFallenEmpire
+{
+ ///
+ /// 13x13小型口袋空间生成器
+ /// 创建一个简单的13x13空间,边缘是墙,中间是空地,适合作为穿梭机内部空间
+ ///
+ public class GenStep_WulaPocketSpaceSmall : GenStep
+ {
+ public override int SeedPart => 928735; // 不同于AncientStockpile的种子
+
+ public override void Generate(Map map, GenStepParams parms)
+ {
+ try
+ {
+ Log.Message($"[WULA] Generating WULA pocket space, map size: {map.Size}");
+
+ // 获取地图边界
+ IntVec3 mapSize = map.Size;
+
+ // 生成外围岩石墙壁
+ GenerateWalls(map);
+
+ // 生成内部地板
+ GenerateFloor(map);
+
+ // 生成一些基础设施(照明等)
+ GenerateBasicInfrastructure(map);
+
+ Log.Message("[WULA] WULA pocket space generation completed");
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"[WULA] Error generating WULA pocket space: {ex}");
+ }
+ }
+
+ ///
+ /// 生成外围墙壁
+ ///
+ private void GenerateWalls(Map map)
+ {
+ IntVec3 mapSize = map.Size;
+
+ // 获取地形和物品定义
+ TerrainDef roughTerrain = DefDatabase.GetNamed("Granite_Rough", false) ??
+ DefDatabase.GetNamed("Granite_Smooth", false) ??
+ DefDatabase.GetNamed("Sandstone_Rough", false);
+
+ ThingDef rockWallDef = DefDatabase.GetNamed("Wall_Rock", false) ??
+ DefDatabase.GetNamed("Wall", false);
+
+ // 遍历地图边缘,放置WulaWall
+ for (int x = 0; x < mapSize.x; x++)
+ {
+ for (int z = 0; z < mapSize.z; z++)
+ {
+ // 如果是边缘位置,放置WulaWall
+ if (x == 0 || x == mapSize.x - 1 || z == 0 || z == mapSize.z - 1)
+ {
+ IntVec3 pos = new IntVec3(x, 0, z);
+
+ // 设置地形为岩石基础
+ if (roughTerrain != null)
+ {
+ map.terrainGrid.SetTerrain(pos, roughTerrain);
+ }
+
+ // 放置WulaWall
+ ThingDef wallDef = DefDatabase.GetNamed("WulaWall", false);
+ if (wallDef != null)
+ {
+ Thing wall = ThingMaker.MakeThing(wallDef);
+ wall.SetFaction(null);
+ GenPlace.TryPlaceThing(wall, pos, map, ThingPlaceMode.Direct);
+ }
+ else if (rockWallDef != null)
+ {
+ // 如果WulaWall不存在,使用原版岩石墙作为备选
+ Thing wall = ThingMaker.MakeThing(rockWallDef);
+ wall.SetFaction(null);
+ GenPlace.TryPlaceThing(wall, pos, map, ThingPlaceMode.Direct);
+ Log.Warning("[WULA] WulaWall not found, using fallback wall");
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// 生成内部地板
+ ///
+ private void GenerateFloor(Map map)
+ {
+ IntVec3 mapSize = map.Size;
+
+ // 为内部区域设置WulaFloor
+ TerrainDef floorDef = DefDatabase.GetNamed("WulaFloor", false);
+ TerrainDef fallbackFloor = floorDef ??
+ DefDatabase.GetNamed("Steel", false) ??
+ DefDatabase.GetNamed("MetalTile", false) ??
+ DefDatabase.GetNamed("Concrete", false);
+
+ if (floorDef == null)
+ {
+ Log.Warning("[WULA] WulaFloor not found, using fallback floor");
+ }
+
+ // 清理内部区域并设置正确的地板
+ for (int x = 1; x < mapSize.x - 1; x++)
+ {
+ for (int z = 1; z < mapSize.z - 1; z++)
+ {
+ IntVec3 pos = new IntVec3(x, 0, z);
+
+ // 清理该位置的所有岩石和阻挡物
+ ClearCellAndSetFloor(map, pos, fallbackFloor);
+ }
+ }
+
+ Log.Message($"[WULA] Set floor for internal area ({mapSize.x-2}x{mapSize.z-2}) to {(floorDef?.defName ?? fallbackFloor?.defName)}");
+ }
+
+ ///
+ /// 清理单元格并设置地板
+ ///
+ private void ClearCellAndSetFloor(Map map, IntVec3 pos, TerrainDef floorDef)
+ {
+ if (!pos.InBounds(map)) return;
+
+ try
+ {
+ // 获取该位置的所有物品
+ List thingsAtPos = pos.GetThingList(map).ToList(); // 创建副本避免修改时出错
+
+ // 清理所有建筑物和岩石(强力清理,确保地板可以放置)
+ foreach (Thing thing in thingsAtPos)
+ {
+ bool shouldRemove = false;
+
+ // 检查是否为建筑物
+ if (thing.def.category == ThingCategory.Building)
+ {
+ // 如果是自然岩石
+ if (thing.def.building?.isNaturalRock == true)
+ {
+ shouldRemove = true;
+ }
+ // 或者是岩石相关的建筑
+ else if (thing.def.defName.Contains("Rock") ||
+ thing.def.defName.Contains("Slate") ||
+ thing.def.defName.Contains("Granite") ||
+ thing.def.defName.Contains("Sandstone") ||
+ thing.def.defName.Contains("Limestone") ||
+ thing.def.defName.Contains("Marble") ||
+ thing.def.defName.Contains("Quartzite") ||
+ thing.def.defName.Contains("Jade"))
+ {
+ shouldRemove = true;
+ }
+ // 或者是其他阻挡的建筑物(除了我们的乌拉墙)
+ else if (!thing.def.defName.Contains("Wula") && thing.def.Fillage == FillCategory.Full)
+ {
+ shouldRemove = true;
+ }
+ }
+
+ if (shouldRemove)
+ {
+ if (Prefs.DevMode) // 只在开发模式下输出详细日志
+ {
+ Log.Message($"[WULA] Removing {thing.def.defName} at {pos} to make space for floor");
+ }
+ thing.Destroy(DestroyMode.Vanish);
+ }
+ }
+
+ // 在清理后稍微延迟,再检查一次(确保彻底清理)
+ thingsAtPos = pos.GetThingList(map).ToList();
+ foreach (Thing thing in thingsAtPos)
+ {
+ if (thing.def.category == ThingCategory.Building && thing.def.Fillage == FillCategory.Full)
+ {
+ Log.Warning($"[WULA] Force removing remaining building {thing.def.defName} at {pos}");
+ thing.Destroy(DestroyMode.Vanish);
+ }
+ }
+
+ // 设置地板地形
+ if (floorDef != null)
+ {
+ map.terrainGrid.SetTerrain(pos, floorDef);
+ if (Prefs.DevMode)
+ {
+ Log.Message($"[WULA] Set terrain at {pos} to {floorDef.defName}");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"[WULA] Error clearing cell at {pos}: {ex}");
+ }
+ }
+
+ ///
+ /// 生成基础设施
+ ///
+ private void GenerateBasicInfrastructure(Map map)
+ {
+ IntVec3 mapSize = map.Size;
+ IntVec3 center = map.Center;
+
+ // 获取灯具定义
+ ThingDef lampDef = DefDatabase.GetNamed("StandingLamp", false) ??
+ DefDatabase.GetNamed("TorchLamp", false) ??
+ DefDatabase.GetNamed("Campfire", false);
+
+ if (lampDef == null)
+ {
+ Log.Warning("[WULA] No lamp definition found, skipping lighting generation");
+ return;
+ }
+
+ // 在四个角落放置照明设备
+ var lightPositions = new List
+ {
+ new IntVec3(2, 0, 2), // 左下角
+ new IntVec3(mapSize.x - 3, 0, 2), // 右下角
+ new IntVec3(2, 0, mapSize.z - 3), // 左上角
+ new IntVec3(mapSize.x - 3, 0, mapSize.z - 3) // 右上角
+ };
+
+ foreach (IntVec3 pos in lightPositions)
+ {
+ if (pos.InBounds(map) && pos.Standable(map))
+ {
+ // 放置立式灯
+ Thing lamp = ThingMaker.MakeThing(lampDef);
+ lamp.SetFaction(null);
+ GenPlace.TryPlaceThing(lamp, pos, map, ThingPlaceMode.Direct);
+ }
+ }
+
+ // 在中心区域留出空间,这里将放置退出点
+ // 不在这里放置退出点,因为这会由Building_ArmedShuttleWithPocket来处理
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
index 90b1d75a..ebb33551 100644
--- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
+++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
@@ -169,6 +169,9 @@
+
+
+