This commit is contained in:
2025-08-25 15:06:43 +08:00
parent 087081cae6
commit 8dae1194fb
8 changed files with 1461 additions and 904 deletions

View File

@@ -102,7 +102,6 @@
<pawnExitSound>Shuttle_PawnExit</pawnExitSound>
<showMassInInspectString>true</showMassInInspectString>
</li>
<li Class="WulaFallenEmpire.CompProperties_PocketMapPortal"></li>
<li Class="CompProperties_Refuelable">
<fuelCapacity>500</fuelCapacity>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>

File diff suppressed because one or more lines are too long

View File

@@ -1,94 +0,0 @@
using HarmonyLib;
using RimWorld;
using Verse;
using System.Reflection;
using System.Collections.Generic;
using System.Reflection.Emit;
namespace WulaFallenEmpire
{
[HarmonyPatch(typeof(Dialog_EnterPortal), "CalculateAndRecacheTransferables")]
public static class DialogEnterPortal_CalculateAndRecacheTransferables_Patch
{
// Transpiler 负责修改方法的 IL 代码
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
var codes = new List<CodeInstruction>(instructions);
// 找到 Thing.Map 属性的 getter 方法 (MapPortal 继承自 Thing)
var mapPropertyGetter = AccessTools.PropertyGetter(typeof(Verse.Thing), "Map");
// 找到我们自定义的静态方法,它将返回正确的 Map
var getShuttleMapMethod = AccessTools.Method(typeof(DialogEnterPortal_CalculateAndRecacheTransferables_Patch), nameof(GetShuttleMap));
Log.Message("[WULA-DEBUG] Transpiler for CalculateAndRecacheTransferables started.");
for (int i = 0; i < codes.Count; i++)
{
// 查找对 Thing.Map 的 get 访问
if (codes[i].opcode == OpCodes.Call && codes[i].operand is MethodInfo method && method == mapPropertyGetter)
{
Log.Message($"[WULA-DEBUG] Transpiler found Thing.Map getter at index {i}.");
// 替换为调用我们的静态方法
yield return new CodeInstruction(OpCodes.Call, getShuttleMapMethod);
}
else
{
yield return codes[i];
}
}
Log.Message("[WULA-DEBUG] Transpiler for CalculateAndRecacheTransferables finished.");
}
// 这个静态方法将由 Transpiler 注入,用于返回正确的 Map
// 参数 portalInstance 是原始方法中对 MapPortal 实例的引用
public static Map GetShuttleMap(MapPortal portalInstance)
{
if (portalInstance is ShuttlePortalAdapter adapter)
{
Log.Message($"[WULA-DEBUG] portalInstance is ShuttlePortalAdapter. adapter.shuttle: {adapter.shuttle?.def.defName ?? "null"}");
if (adapter.shuttle != null)
{
// 确保 adapter.shuttle.Map 不为 null
if (adapter.shuttle.Map != null)
{
return adapter.shuttle.Map;
}
else
{
Log.Error($"[WULA] Shuttle {adapter.shuttle.def.defName} is not spawned on any map when trying to get its map.");
return null; // 返回 null让后续代码处理
}
}
}
// 如果不是我们的适配器,或者适配器中的 shuttle 为空,
// 则尝试获取原始 MapPortal 的 Map。
// 这里需要非常小心,因为 portalInstance 本身也可能是 null
// 或者它继承自 Thing 的 Map 属性是 null。
if (portalInstance == null)
{
Log.Error("[WULA] GetShuttleMap received a null portalInstance.");
return null;
}
var originalMapGetter = AccessTools.PropertyGetter(typeof(Thing), "Map");
if (originalMapGetter == null)
{
Log.Error("[WULA] Could not get Thing.Map getter via AccessTools.");
return null;
}
Map result = null;
try
{
result = (Map)originalMapGetter.Invoke(portalInstance, null);
}
catch (System.Exception ex)
{
Log.Error($"[WULA] Error invoking original Thing.Map getter: {ex.Message}");
}
Log.Message($"[WULA-DEBUG] GetShuttleMap returning original Map. Result: {result?.ToString() ?? "null"}");
return result;
}
}
}

View File

@@ -1,366 +0,0 @@
using RimWorld;
using Verse;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
namespace WulaFallenEmpire
{
/// <summary>
/// 口袋空间传送门组件 - 只作为入口功能,附加在穿梭机上处理进入内部空间的逻辑
/// </summary>
public class CompPocketMapPortal : ThingComp
{
/// <summary>组件属性</summary>
public CompProperties_PocketMapPortal Props => (CompProperties_PocketMapPortal)props;
/// <summary>父建筑(必须是穿梭机)</summary>
public Building_ArmedShuttleWithPocket ParentShuttle => parent as Building_ArmedShuttleWithPocket;
/// <summary>MapPortal适配器用于使用原版Dialog_EnterPortal</summary>
private ShuttlePortalAdapter portalAdapter;
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
Log.Message($"[WULA-DEBUG] CompPocketMapPortal.PostSpawnSetup called. Parent: {parent?.def?.defName ?? "null"}");
// 检查父对象是否是穿梭机
if (ParentShuttle == null)
{
Log.Error($"[WULA] CompPocketMapPortal attached to non-shuttle building: {parent?.def?.defName}");
return; // Early exit if parent is not a shuttle
}
// 创建MapPortal适配器并设置其地图和位置信息
portalAdapter = new ShuttlePortalAdapter(ParentShuttle);
// 确保 portalAdapter 的 shuttle 引用被正确设置
if (portalAdapter != null)
{
portalAdapter.shuttle = ParentShuttle;
Log.Message($"[WULA-DEBUG] portalAdapter.shuttle set in PostSpawnSetup: {portalAdapter.shuttle?.def.defName ?? "null"}");
}
}
/// <summary>
/// 检查穿梭机是否可以进入(仅作为入口功能)
/// </summary>
public bool IsEnterable(out string reason)
{
if (ParentShuttle == null)
{
reason = "WULA.PocketSpace.NotSpawned".Translate();
return false;
}
if (!ParentShuttle.AllowDirectAccess)
{
reason = "WULA.PocketSpace.AccessDenied".Translate();
return false;
}
if (!ParentShuttle.Spawned)
{
reason = "WULA.PocketSpace.NotSpawned".Translate();
return false;
}
// 检查穿梭机的传送状态
var transportDisabledField = typeof(Building_ArmedShuttleWithPocket).GetField("transportDisabled",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (transportDisabledField != null)
{
bool transportDisabled = (bool)transportDisabledField.GetValue(ParentShuttle);
if (transportDisabled)
{
reason = "WULA.PocketSpace.TransportDisabled".Translate();
return false;
}
}
// 检查口袋地图是否存在
if (ParentShuttle.PocketMap == null)
{
reason = "WULA.PocketSpace.NoTargetMap".Translate();
return false;
}
reason = "";
return true;
}
/// <summary>
/// 获取组件额外的Gizmo按钮根据口袋空间初始化状态显示不同按钮
/// 重写CompGetGizmosExtra方法这样RimWorld会自动调用并显示按钮
/// </summary>
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
if (ParentShuttle == null) yield break;
// 检查口袋空间是否已初始化
bool pocketMapExists = ParentShuttle.PocketMap != null;
if (!pocketMapExists)
{
// 口袋空间未创建,显示初始化按钮
Command_Action initializeCommand = new Command_Action();
initializeCommand.action = delegate
{
// 创建口袋空间
InitializePocketSpace();
};
initializeCommand.icon = ContentFinder<Texture2D>.Get("UI/Commands/LoadTransporter");
initializeCommand.defaultLabel = "WULA.PocketSpace.Initialize".Translate();
initializeCommand.defaultDesc = "WULA.PocketSpace.InitializeDesc".Translate();
// 检查是否可以初始化
if (!ParentShuttle.Spawned)
{
initializeCommand.Disabled = true;
initializeCommand.disabledReason = "WULA.PocketSpace.NotSpawned".Translate();
}
yield return initializeCommand;
}
else
{
// 穿梭机已创建显示装载按钮使用原版Dialog_EnterPortal
Command_Action enterCommand = new Command_Action();
enterCommand.action = delegate
{
// 使用和Building_PocketMapExit一模一样的Dialog_EnterPortal方法
if (portalAdapter == null || portalAdapter.shuttle != ParentShuttle)
{
// 重新创建并设置适配器,确保其指向正确的穿梭机
portalAdapter = new ShuttlePortalAdapter(ParentShuttle);
// 再次尝试设置 MapPortal 基类的地图和位置信息
try
{
var mapField = typeof(Thing).GetField("mapIndexOrState",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var positionField = typeof(Thing).GetField("positionInt",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (mapField != null && positionField != null && ParentShuttle.Spawned)
{
mapField.SetValue(portalAdapter, (sbyte)ParentShuttle.Map.Index); // 显式转换为 sbyte
positionField.SetValue(portalAdapter, ParentShuttle.Position);
}
}
catch (System.Exception ex)
{
Log.Error($"[WULA] Error setting MapPortal base fields during Gizmo click: {ex.Message}");
}
}
if (portalAdapter != null)
{
Log.Message($"[WULA-DEBUG] Opening Dialog_EnterPortal with portalAdapter. Type: {portalAdapter.GetType().Name}. Shuttle: {portalAdapter.shuttle?.def.defName ?? "null"}");
var dialog = new Dialog_EnterPortal(portalAdapter);
Find.WindowStack.Add(dialog);
}
else
{
Messages.Message("WULA.PocketSpace.AdapterError".Translate(), ParentShuttle, MessageTypeDefOf.RejectInput);
Log.Error("[WULA] Portal adapter is null after recreation attempt.");
}
};
enterCommand.icon = ContentFinder<Texture2D>.Get(Props.buttonIconPath);
enterCommand.defaultLabel = Props.enterButtonTextKey.Translate() + "...";
enterCommand.defaultDesc = Props.enterButtonDescKey.Translate();
// 检查是否可以进入
string reason;
enterCommand.Disabled = !IsEnterable(out reason);
enterCommand.disabledReason = reason;
yield return enterCommand;
// 查看口袋地图按钮
yield return new Command_Action
{
defaultLabel = "WULA.PocketSpace.SwitchTo".Translate(),
defaultDesc = "WULA.PocketSpace.SwitchToDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Commands/ViewCave"),
action = delegate
{
Current.Game.CurrentMap = ParentShuttle.PocketMap;
Find.CameraDriver.JumpToCurrentMapLoc(ParentShuttle.PocketMap.Center);
}
};
}
}
/// <summary>
/// 初始化口袋空间
/// </summary>
private void InitializePocketSpace()
{
if (ParentShuttle == null || !ParentShuttle.Spawned)
{
Messages.Message("WULA.PocketSpace.CannotInitialize".Translate(), ParentShuttle, MessageTypeDefOf.RejectInput);
return;
}
if (ParentShuttle.PocketMap != null)
{
Messages.Message("WULA.PocketSpace.AlreadyInitialized".Translate(), ParentShuttle, MessageTypeDefOf.RejectInput);
return;
}
try
{
Log.Message("[WULA] Starting pocket space initialization via component");
// 使用穿梭机的SwitchToPocketSpace方法它会自动创建口袋空间
ParentShuttle.SwitchToPocketSpace();
if (ParentShuttle.PocketMap != null)
{
Messages.Message("WULA.PocketSpace.InitializeSuccess".Translate(), ParentShuttle, MessageTypeDefOf.PositiveEvent);
Log.Message("[WULA] Pocket space initialization completed successfully");
}
else
{
Messages.Message("WULA.PocketSpace.InitializeFailed".Translate(), ParentShuttle, MessageTypeDefOf.RejectInput);
Log.Error("[WULA] Pocket space initialization failed");
}
}
catch (System.Exception ex)
{
Log.Error($"[WULA] Error during pocket space initialization: {ex}");
Messages.Message("WULA.PocketSpace.InitializeFailed".Translate(), ParentShuttle, MessageTypeDefOf.RejectInput);
}
}
}
/// <summary>
/// 口袋空间传送门组件属性
/// </summary>
public class CompProperties_PocketMapPortal : CompProperties
{
/// <summary>进入按钮文本键</summary>
public string enterButtonTextKey = "WULA.PocketSpace.Enter";
/// <summary>进入按钮描述键</summary>
public string enterButtonDescKey = "WULA.PocketSpace.EnterDesc";
/// <summary>按钮图标路径</summary>
public string buttonIconPath = "UI/Commands/LoadTransporter";
public CompProperties_PocketMapPortal()
{
this.compClass = typeof(CompPocketMapPortal);
}
}
/// <summary>
/// MapPortal适配器类将Building_ArmedShuttleWithPocket适配为MapPortal接口
/// 完全模仿Building_PocketMapExit的实现方式
/// </summary>
public class ShuttlePortalAdapter : MapPortal
{
/// <summary>关联的穿梭机</summary>
public Building_ArmedShuttleWithPocket shuttle;
/// <summary>
/// 默认构造函数RimWorld组件系统要求
/// </summary>
public ShuttlePortalAdapter()
{
// 在这里不初始化 shuttle因为它将在 PostSpawnSetup 中设置
}
public ShuttlePortalAdapter(Building_ArmedShuttleWithPocket shuttle)
{
this.shuttle = shuttle;
}
/// <summary>
/// 重写获取其他地图返回口袋空间模仿Building_PocketMapExit.GetOtherMap
/// </summary>
public override Map GetOtherMap()
{
if (shuttle?.PocketMap == null)
{
// 如口袋空间还没创建,先创建它
shuttle?.SwitchToPocketSpace();
}
return shuttle?.PocketMap;
}
/// <summary>
/// 重写获取目标位置返回口袋空间中心模仿Building_PocketMapExit.GetDestinationLocation
/// </summary>
public override IntVec3 GetDestinationLocation()
{
return shuttle?.PocketMap?.Center ?? IntVec3.Invalid;
}
/// <summary>
/// 重写是否可进入检查穿梭机状态模仿Building_PocketMapExit.IsEnterable
/// </summary>
public override bool IsEnterable(out string reason)
{
if (shuttle == null || !shuttle.Spawned)
{
reason = "WULA.PocketSpace.NotSpawned".Translate();
return false;
}
if (!shuttle.AllowDirectAccess)
{
reason = "WULA.PocketSpace.AccessDenied".Translate();
return false;
}
// 检查穿梭机的传送状态
var transportDisabledField = typeof(Building_ArmedShuttleWithPocket).GetField("transportDisabled",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (transportDisabledField != null)
{
bool transportDisabled = (bool)transportDisabledField.GetValue(shuttle);
if (transportDisabled)
{
reason = "WULA.PocketSpace.TransportDisabled".Translate();
return false;
}
}
reason = "";
return true;
}
/// <summary>
/// 重写进入事件处理进入口袋空间模仿Building_PocketMapExit.OnEntered
/// </summary>
public override void OnEntered(Pawn pawn)
{
// 通知穿梭机有物品被添加(用于统计和管理)
shuttle?.Notify_ThingAdded(pawn);
// 播放传送音效(如果存在)
if (Find.CurrentMap == shuttle?.Map)
{
// 可以在这里添加音效播放
// def.portal?.traverseSound?.PlayOneShot(this);
}
}
/// 重写进入按钮文本
/// </summary>
public override string EnterString => "WULA.PocketSpace.Enter".Translate();
/// <summary>
/// 重写进入按钮图标,使用装载按钮的贴图
/// </summary>
protected override Texture2D EnterTex => ContentFinder<Texture2D>.Get("UI/Commands/LoadTransporter");
// 移除了 new 关键字的 Map, Position, def 属性,因为它们在 MapPortal 基类中可能不是 virtual 的
// 并且我们依赖 PostSpawnSetup 中的反射来设置 MapPortal 基类的私有字段
// 这确保了 Dialog_EnterPortal 能够直接访问到正确的地图和位置信息。
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using Verse;
namespace WulaFallenEmpire
{
/// <summary>
/// 用于武装穿梭机口袋空间的IThingHolder实现与CompTransporter的容器分离
/// </summary>
public class PocketSpaceThingHolder : IThingHolder, IExposable
{
/// <summary>持有的物品容器</summary>
public ThingOwner<Thing> innerContainer;
/// <summary>该容器的拥有者通常是Building_ArmedShuttleWithPocket</summary>
private IThingHolder owner;
/// <summary>实现IThingHolder.ParentHolder属性</summary>
public IThingHolder ParentHolder => owner;
public PocketSpaceThingHolder()
{
innerContainer = new ThingOwner<Thing>(this);
}
public PocketSpaceThingHolder(IThingHolder owner) : this()
{
this.owner = owner;
}
/// <summary>
/// 获取直接持有的物品
/// </summary>
public ThingOwner GetDirectlyHeldThings()
{
return innerContainer;
}
/// <summary>
/// 获取子持有者
/// </summary>
public void GetChildHolders(List<IThingHolder> outChildren)
{
// 目前没有子持有者,留空
}
/// <summary>
/// 通知物品被添加
/// </summary>
public void Notify_ThingAdded(Thing t)
{
// 这里可以添加逻辑来处理物品被添加到口袋空间的情况
Log.Message($"[WULA] Item {t.LabelCap} added to pocket space container.");
}
/// <summary>
/// 通知物品被移除
/// </summary>
public void Notify_ThingRemoved(Thing t)
{
// 这里可以添加逻辑来处理物品被从口袋空间移除的情况
Log.Message($"[WULA] Item {t.LabelCap} removed from pocket space container.");
}
public void ExposeData()
{
Scribe_Deep.Look(ref innerContainer, "innerContainer", this);
// owner 通常在构造函数中设置,不需要序列化
}
}
}

View File

@@ -96,7 +96,6 @@
<Compile Include="EventSystem\EventUIConfigDef.cs" />
<Compile Include="EventSystem\Letter_EventChoice.cs" />
<Compile Include="EventSystem\QuestNode_Root_EventLetter.cs" />
<Compile Include="HarmonyPatches\DialogEnterPortal_MapPatch.cs" />
<Compile Include="HarmonyPatches\Caravan_NeedsTracker_TrySatisfyPawnNeeds_Patch.cs" />
<Compile Include="HarmonyPatches\DamageInfo_Constructor_Patch.cs" />
<Compile Include="HarmonyPatches\FloatMenuOptionProvider_Ingest_Patch.cs" />
@@ -172,8 +171,8 @@
<Compile Include="WULA_Shuttle\Building_ArmedShuttle.cs" />
<Compile Include="WULA_Shuttle\Building_ArmedShuttleWithPocket.cs" />
<Compile Include="WULA_Shuttle\Building_PocketMapExit.cs" />
<Compile Include="WULA_Shuttle\CompPocketMapPortal.cs" />
<Compile Include="WULA_Shuttle\GenStep_WulaPocketSpaceSmall.cs" />
<Compile Include="WULA_Shuttle\PocketSpaceThingHolder.cs" />
<Compile Include="HarmonyPatches\Patch_DropCellFinder_SkyfallerCanLandAt.cs" />
<Compile Include="MechWeapon\FloatMenuProvider_Mech.cs" />
<Compile Include="MechWeapon\Patch_MissingWeapon.cs" />