Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/WULA_Shuttle/Building_ArmedShuttleWithPocket.cs
2025-08-25 10:53:07 +08:00

485 lines
17 KiB
C#

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
{
[StaticConstructorOnStartup]
public class Building_ArmedShuttleWithPocket : Building_ArmedShuttle, IThingHolder
{
#region
private static readonly Texture2D ViewPocketMapTex = ContentFinder<Texture2D>.Get("UI/Commands/ViewCave");
private static readonly Texture2D EnterPocketMapTex = ContentFinder<Texture2D>.Get("UI/Commands/EnterCave");
private static readonly Texture2D TeleportAndLoadTex = ContentFinder<Texture2D>.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;
public Building_PocketMapExit exit;
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;
public bool PocketMapExists
{
get
{
if (pocketMap != null && pocketMap.Parent?.HasMap == false)
{
pocketMap = null;
}
return pocketMap != null;
}
}
public bool PocketMapGenerated => pocketMapGenerated;
#endregion
#region
public override void ExposeData()
{
base.ExposeData();
if (Scribe.mode == LoadSaveMode.Saving && pocketMap != null && pocketMap.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_References.Look(ref exit, "exit");
}
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 on DeSpawn: {ex}");
}
}
base.DeSpawn(mode);
}
protected override void Tick()
{
base.Tick();
if (!Spawned) return;
bool isLoading = this.TransporterComp.leftToLoad != null && this.TransporterComp.leftToLoad.Any(x => x.CountToTransfer > 0);
if (wasLoading && !isLoading && doTeleportAfterLoading)
{
TeleportContentsToPocketDimension();
doTeleportAfterLoading = false;
}
wasLoading = isLoading;
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 (pocketMap.mapPawns.AllPawnsSpawned.Any(p => p.IsColonist))
{
int pawnCount = pocketMap.mapPawns.AllPawnsSpawned.Count(p => p.IsColonist);
sb.AppendLine("WULA.PocketSpace.PawnCount".Translate(pawnCount));
}
}
else
{
sb.AppendLine("WULA.PocketSpace.Status".Translate() + ": " + "WULA.PocketSpace.NotGenerated".Translate());
}
return sb.ToString().TrimEndNewlines();
}
#endregion
#region Gizmos
public override IEnumerable<Gizmo> GetGizmos()
{
foreach (var baseGizmo in base.GetGizmos())
{
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 (pocketMapGenerated && PocketMapExists)
{
yield return new Command_Action
{
defaultLabel = "WULA.ViewPocketSpace".Translate(),
defaultDesc = "WULA.ViewPocketSpaceDesc".Translate(),
icon = ViewPocketMapTex,
action = SwitchToPocketSpace
};
}
else
{
yield return new Command_Action
{
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<Texture2D>.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<Pawn> 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 (!PocketMapExists)
{
if (!pocketMapGenerated)
{
CreatePocketMap();
}
if (!PocketMapExists)
{
Messages.Message("WULA.PocketSpace.CreationFailed".Translate(), this, MessageTypeDefOf.RejectInput);
return;
}
}
Current.Game.CurrentMap = pocketMap;
Find.CameraDriver.JumpToCurrentMapLoc(pocketMap.Center);
}
private void CreatePocketMap()
{
try
{
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}");
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;
}
}
protected virtual Map GeneratePocketMapInt()
{
if (mapGenerator == null)
{
mapGenerator = DefDatabase<MapGeneratorDef>.GetNamed("WULA_PocketSpace_Small", false)
?? DefDatabase<MapGeneratorDef>.GetNamed("AncientStockpile", false)
?? MapGeneratorDefOf.Base_Player;
}
IntVec3 mapSize = new IntVec3(pocketMapSize.x, 1, pocketMapSize.z);
return PocketMapUtility.GeneratePocketMap(mapSize, mapGenerator, GetExtraGenSteps(), this.Map);
}
protected virtual IEnumerable<GenStepWithParams> GetExtraGenSteps()
{
return Enumerable.Empty<GenStepWithParams>();
}
private void PlaceExitInPocketMap()
{
if (pocketMap == null || exitDef == null) return;
try
{
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;
}
GenPlace.TryPlaceThing(exitBuilding, exitPos, pocketMap, ThingPlaceMode.Direct);
}
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}");
}
}
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<Thing> thingsToTransfer = new List<Thing>(pocketMap.listerThings.AllThings);
foreach (Thing thing in thingsToTransfer)
{
if (thing.def.category != ThingCategory.Mote && thing.def.category != ThingCategory.Filth)
{
if(this.TransporterComp.innerContainer.TryAddOrTransfer(thing))
{
//Success
}
else
{
thing.Destroy();
}
}
}
Log.Message($"[WULA] Transferred {thingsToTransfer.Count} things from pocket space to shuttle cargo.");
}
catch (Exception ex)
{
Log.Error($"[WULA] Error transferring from pocket map: {ex}");
}
}
#endregion
#region IThingHolder
public void GetChildHolders(List<IThingHolder> outChildren)
{
if (this.TransporterComp != null)
{
outChildren.Add(this.TransporterComp);
}
}
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<PocketMapProperties>();
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;
}
}