diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml
index 4d0a2413..ccf8586f 100644
--- a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml
+++ b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml
@@ -1,5 +1,62 @@
+
+
+ WULA_AreaTeleportBeacon
+
+ 负责协调殖民地与轨道上的乌拉帝国舰队进行材料输送的信标,空投建筑会优先从信标覆盖区域吸纳资源完成空投。
+ Building
+
+ BuildingsMisc
+
+
+ Wula/Building/WULA_OrbitalTradeBeacon
+ Graphic_Single
+
+ (0.3, 0.2, 0.3)
+ (0,0,-0.1)
+
+
+ Building
+ MinifiedThing
+
+ 75
+ 800
+ 0.5
+ 5
+
+ MapMeshAndRealTime
+ true
+ 0.15
+
+ 40
+ 1
+
+ Normal
+
+ BuildingDestroyed_Metal_Small
+
+
+
+ (13, 13)
+ 120
+ WULA_Colony_License_LV1_Technology
+
+
+ false
+ 14
+ WULA_Buildings
+ 2100
+ false
+
+ PlaceWorker_ShowTradeBeaconRadius
+
+ Misc2
+
+ WULA_Colony_License_LV1_Technology
+
+
+
WULA_OrbitalTradeBeacon
@@ -973,4 +1030,6 @@
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Prefab_Beacons.xml b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Prefab_Beacons.xml
index 84b81cea..e4eb405d 100644
--- a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Prefab_Beacons.xml
+++ b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Prefab_Beacons.xml
@@ -1,5 +1,27 @@
+
+
+ WULA_TeleportLandingMarker
+
+ 用于标记传送降落位置。
+ WulaFallenEmpire.WULA_TeleportLandingMarker
+
+ Wula/Building/WULA_Dropping_Building_Cleanzone_Plus
+ Graphic_Multi
+ (13,13)
+
+ false
+
+
+ Building
+ false
+ true
+ true
+ RealtimeOnly
+ true
+
+
WULA_Prefab_Cleanzone_NewColonyBase_Beacon
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml
index e369a522..c4ce1f45 100644
--- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml
+++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/WULA_Keyed.xml
@@ -113,5 +113,33 @@
请先选择一个分类
正在收集材料
已暂停
+
+ 取消传送
+ 取消当前的传送预热。
+ 启动传送
+ 启动区域传送程序,将信标周围的物体传送到指定位置。
+ 需要研究:{0}
+ 缺少必要的研究:{0}
+ 无法生成目标地图。
+ 传送预热已开始。
+ 传送已取消。
+ 无效的目标位置。
+ 无法获取目标地图。
+ 传送成功。
+ 选择降落点
+ 选择传送到达的具体位置。
+ 超出地图边界。
+ 位于不可建造区域。
+ 目标位置被迷雾覆盖。
+ 被不可破坏的物体阻挡:{0}
+ 目标地形不可通过。
+ 被厚岩顶阻挡。
+ 必须选择一个降落点。
+ 确认传送
+ 确认当前位置并开始传送。
+ 移动标记
+ 重新选择降落位置。
+ 由于传送取消,生成的临时地图已被清理。
+ 燃料不足。
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/3516260226.code-workspace b/Source/WulaFallenEmpire/3516260226.code-workspace
index a2433624..aef43a98 100644
--- a/Source/WulaFallenEmpire/3516260226.code-workspace
+++ b/Source/WulaFallenEmpire/3516260226.code-workspace
@@ -13,6 +13,7 @@
"path": "../../../../Data"
},
{
+ "name": "dll1.6",
"path": "../../../../dll1.6"
}
],
diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/CompMapTeleporter.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/CompMapTeleporter.cs
new file mode 100644
index 00000000..2aa55e1e
--- /dev/null
+++ b/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/CompMapTeleporter.cs
@@ -0,0 +1,363 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using RimWorld;
+using RimWorld.Planet;
+using UnityEngine;
+using Verse;
+using Verse.AI;
+using Verse.Sound;
+
+namespace WulaFallenEmpire
+{
+ public class CompMapTeleporter : ThingComp
+ {
+ public CompProperties_MapTeleporter Props => (CompProperties_MapTeleporter)props;
+
+ private bool isWarmingUp = false;
+ private int warmupTicksLeft = 0;
+ private GlobalTargetInfo target;
+ private WULA_TeleportLandingMarker activeMarker;
+
+ public override void PostExposeData()
+ {
+ base.PostExposeData();
+ Scribe_Values.Look(ref isWarmingUp, "isWarmingUp", false);
+ Scribe_Values.Look(ref warmupTicksLeft, "warmupTicksLeft", 0);
+ Scribe_TargetInfo.Look(ref target, "target");
+ Scribe_References.Look(ref activeMarker, "activeMarker");
+ }
+
+ public override void PostDrawExtraSelectionOverlays()
+ {
+ base.PostDrawExtraSelectionOverlays();
+ GenDraw.DrawFieldEdges(CellRect.CenteredOn(parent.Position, Props.areaSize.x, Props.areaSize.z).Cells.ToList());
+ }
+
+ public override void CompTick()
+ {
+ base.CompTick();
+ if (isWarmingUp)
+ {
+ warmupTicksLeft--;
+ if (warmupTicksLeft % 60 == 0)
+ {
+ Log.Message($"[WULA] Teleport warmup: {warmupTicksLeft} ticks left.");
+ Props.warmupEffecter?.Spawn(parent, parent.Map).Cleanup();
+ }
+
+ if (warmupTicksLeft <= 0)
+ {
+ Log.Message("[WULA] Warmup finished. Attempting teleport...");
+ TryTeleport();
+ isWarmingUp = false;
+ }
+ }
+ }
+
+ public override IEnumerable CompGetGizmosExtra()
+ {
+ foreach (var gizmo in base.CompGetGizmosExtra())
+ {
+ yield return gizmo;
+ }
+
+ if (parent.Faction != Faction.OfPlayer)
+ yield break;
+
+ if (isWarmingUp)
+ {
+ yield return new Command_Action
+ {
+ defaultLabel = "WULA_CancelTeleport".Translate(),
+ defaultDesc = "WULA_CancelTeleportDesc".Translate(),
+ icon = ContentFinder.Get("UI/Designators/Cancel"),
+ action = CancelTeleport
+ };
+ }
+ else if (activeMarker != null && !activeMarker.Destroyed)
+ {
+ yield return new Command_Action
+ {
+ defaultLabel = "WULA_CancelTeleport".Translate(),
+ defaultDesc = "WULA_CancelTeleportDesc".Translate(),
+ icon = ContentFinder.Get("UI/Designators/Cancel"),
+ action = CancelTeleport
+ };
+ }
+ else
+ {
+ string reason = GetDisabledReason();
+ Command_Action teleportCmd = new Command_Action
+ {
+ defaultLabel = "WULA_InitiateTeleport".Translate(),
+ defaultDesc = GetDescription(),
+ icon = ContentFinder.Get("UI/Commands/LaunchShip"),
+ action = StartTargeting,
+ disabledReason = reason
+ };
+
+ if (!string.IsNullOrEmpty(reason))
+ {
+ teleportCmd.Disable(reason);
+ }
+
+ yield return teleportCmd;
+ }
+ }
+
+ private string GetDisabledReason()
+ {
+ if (Props.requiredResearch != null && !Props.requiredResearch.IsFinished)
+ {
+ return "WULA_MissingResearch".Translate(Props.requiredResearch.label);
+ }
+
+ return null;
+ }
+
+ private string GetDescription()
+ {
+ System.Text.StringBuilder sb = new System.Text.StringBuilder();
+ sb.Append("WULA_InitiateTeleportDesc".Translate());
+
+ if (Props.requiredResearch != null)
+ {
+ sb.AppendLine().Append("WULA_RequiresResearch".Translate(Props.requiredResearch.label));
+ }
+
+ return sb.ToString();
+ }
+
+ private void StartTargeting()
+ {
+ CameraJumper.TryJump(CameraJumper.GetWorldTarget(parent));
+ Find.WorldSelector.ClearSelection();
+ Find.WorldTargeter.BeginTargeting(ChoseWorldTarget, true, null, true, null, null);
+ }
+
+ private bool ChoseWorldTarget(GlobalTargetInfo targetInfo)
+ {
+ if (!targetInfo.IsValid) return false;
+
+ this.target = targetInfo;
+
+ MapParent mapParent = Find.WorldObjects.MapParentAt(targetInfo.Tile);
+
+ if (mapParent == null)
+ {
+ SettleUtility.AddNewHome(targetInfo.Tile, Faction.OfPlayer);
+ mapParent = Find.WorldObjects.MapParentAt(targetInfo.Tile);
+ }
+
+ if (mapParent != null)
+ {
+ if (!mapParent.HasMap)
+ {
+ IntVec3 mapSize = Find.World.info.initialMapSize;
+ GetOrGenerateMapUtility.GetOrGenerateMap(targetInfo.Tile, mapSize, null);
+ }
+
+ if (mapParent.HasMap)
+ {
+ CameraJumper.TryJump(mapParent.Map.Center, mapParent.Map);
+
+ if (activeMarker != null && !activeMarker.Destroyed)
+ {
+ activeMarker.Destroy();
+ }
+
+ activeMarker = (WULA_TeleportLandingMarker)ThingMaker.MakeThing(DefDatabase.GetNamed("WULA_TeleportLandingMarker"));
+ activeMarker.sourceThing = parent;
+ GenSpawn.Spawn(activeMarker, mapParent.Map.Center, mapParent.Map);
+
+ Find.Selector.ClearSelection();
+ Find.Selector.Select(activeMarker);
+ Find.DesignatorManager.Select(new Designator_TeleportArrival(this, mapParent.Map, activeMarker));
+
+ return true;
+ }
+ }
+
+ Messages.Message("WULA_TeleportFailed_MapGeneration".Translate(), MessageTypeDefOf.RejectInput);
+ return false;
+ }
+
+ public void ConfirmArrival(IntVec3 cell, Map map)
+ {
+ this.target = new GlobalTargetInfo(cell, map);
+ StartWarmup();
+ }
+
+ private void StartWarmup()
+ {
+ isWarmingUp = true;
+ warmupTicksLeft = Props.warmupTicks;
+ Props.warmupSound?.PlayOneShot(parent);
+ Messages.Message("WULA_TeleportWarmupStarted".Translate(), parent, MessageTypeDefOf.NeutralEvent);
+ }
+
+ private void CancelTeleport()
+ {
+ isWarmingUp = false;
+ warmupTicksLeft = 0;
+
+ if (activeMarker != null && !activeMarker.Destroyed)
+ {
+ activeMarker.Destroy();
+ activeMarker = null;
+ }
+
+ Messages.Message("WULA_TeleportCancelled".Translate(), parent, MessageTypeDefOf.NeutralEvent);
+ }
+
+ private void TryTeleport()
+ {
+ Log.Message($"[WULA] TryTeleport called. Target valid: {target.IsValid}, Tile: {target.Tile}, Cell: {target.Cell}");
+
+ if (!target.IsValid)
+ {
+ Messages.Message("WULA_TeleportFailed_InvalidTarget".Translate(), parent, MessageTypeDefOf.RejectInput);
+ CancelTeleport();
+ return;
+ }
+
+ Map targetMap = target.Map;
+ IntVec3 targetCell = target.Cell;
+
+ if (targetMap == null)
+ {
+ Log.Message($"[WULA] Target map is null. Generating map for tile {target.Tile}...");
+ targetMap = GetOrGenerateTargetMap(target.Tile);
+ if (targetMap == null)
+ {
+ Messages.Message("WULA_TeleportFailed_NoMap".Translate(), parent, MessageTypeDefOf.RejectInput);
+ CancelTeleport();
+ return;
+ }
+ targetCell = targetMap.Center;
+ }
+
+ Log.Message($"[WULA] Teleporting to map {targetMap.Index}, cell {targetCell}");
+ TeleportContents(targetMap, targetCell);
+ }
+
+ private Map GetOrGenerateTargetMap(int tile)
+ {
+ MapParent mapParent = Find.WorldObjects.MapParentAt(tile);
+
+ if (mapParent == null)
+ {
+ SettleUtility.AddNewHome(tile, Faction.OfPlayer);
+ mapParent = Find.WorldObjects.MapParentAt(tile);
+ }
+
+ if (mapParent != null)
+ {
+ if (!mapParent.HasMap)
+ {
+ IntVec3 mapSize = Find.World.info.initialMapSize;
+ return GetOrGenerateMapUtility.GetOrGenerateMap(tile, mapSize, null);
+ }
+ return mapParent.Map;
+ }
+
+ return null;
+ }
+
+ private struct ThingToTeleport
+ {
+ public Thing thing;
+ public IntVec3 relativePos;
+ }
+
+ private void TeleportContents(Map targetMap, IntVec3 targetCenter)
+ {
+ Map sourceMap = parent.Map;
+ CellRect rect = CellRect.CenteredOn(parent.Position, Props.areaSize.x, Props.areaSize.z);
+ IntVec3 center = parent.Position;
+
+ List thingsToTeleport = new List();
+ List> terrainToTeleport = new List>();
+
+ Log.Message($"[WULA] Collecting data from {rect.Area} cells around {center} with size {Props.areaSize}");
+
+ // 1. 收集数据
+ HashSet collectedThings = new HashSet();
+ foreach (IntVec3 cell in rect)
+ {
+ if (!cell.InBounds(sourceMap)) continue;
+
+ terrainToTeleport.Add(new Pair(cell - center, cell.GetTerrain(sourceMap)));
+
+ List thingList = sourceMap.thingGrid.ThingsListAt(cell);
+ for (int i = thingList.Count - 1; i >= 0; i--)
+ {
+ Thing t = thingList[i];
+ if (t != parent && !collectedThings.Contains(t) &&
+ (t.def.category == ThingCategory.Item ||
+ t.def.category == ThingCategory.Pawn ||
+ t.def.category == ThingCategory.Building))
+ {
+ if (!t.def.destroyable) continue;
+
+ collectedThings.Add(t);
+ thingsToTeleport.Add(new ThingToTeleport { thing = t, relativePos = t.Position - center });
+ }
+ }
+ }
+
+ // 2. 准备传送 (PreSwapMap)
+ foreach (var data in thingsToTeleport) data.thing.PreSwapMap();
+ parent.PreSwapMap();
+
+ // 3. 从源地图移除 (DeSpawn)
+ foreach (var data in thingsToTeleport)
+ {
+ if (data.thing.Spawned) data.thing.DeSpawn(DestroyMode.WillReplace);
+ }
+ if (parent.Spawned) parent.DeSpawn(DestroyMode.WillReplace);
+
+ // 4. 修改地形
+ foreach (var pair in terrainToTeleport)
+ {
+ IntVec3 newPos = targetCenter + pair.First;
+ newPos = newPos.ClampInsideMap(targetMap);
+
+ List targetThings = targetMap.thingGrid.ThingsListAt(newPos);
+ for (int i = targetThings.Count - 1; i >= 0; i--)
+ {
+ if (targetThings[i].def.destroyable) targetThings[i].Destroy();
+ }
+
+ if (pair.Second != null)
+ {
+ targetMap.terrainGrid.SetTerrain(newPos, pair.Second);
+ sourceMap.terrainGrid.SetTerrain(center + pair.First, TerrainDefOf.Soil);
+ }
+ }
+
+ // 5. 放置到新地图 (Spawn)
+ foreach (var data in thingsToTeleport)
+ {
+ if (data.thing.Destroyed) continue;
+ IntVec3 newPos = targetCenter + data.relativePos;
+ newPos = newPos.ClampInsideMap(targetMap);
+ GenSpawn.Spawn(data.thing, newPos, targetMap, data.thing.Rotation);
+ }
+ GenSpawn.Spawn(parent, targetCenter, targetMap, parent.Rotation);
+
+ // 6. 传送后处理 (PostSwapMap)
+ foreach (var data in thingsToTeleport)
+ {
+ if (!data.thing.Destroyed) data.thing.PostSwapMap();
+ }
+ parent.PostSwapMap();
+
+ // 7. 完成
+ CameraJumper.TryJump(targetCenter, targetMap);
+ Props.teleportSound?.PlayOneShot(new TargetInfo(targetCenter, targetMap, false));
+ Messages.Message("WULA_TeleportSuccessful".Translate(), new TargetInfo(targetCenter, targetMap, false), MessageTypeDefOf.PositiveEvent);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/CompProperties_MapTeleporter.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/CompProperties_MapTeleporter.cs
new file mode 100644
index 00000000..55b52901
--- /dev/null
+++ b/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/CompProperties_MapTeleporter.cs
@@ -0,0 +1,21 @@
+using RimWorld;
+using Verse;
+
+namespace WulaFallenEmpire
+{
+ public class CompProperties_MapTeleporter : CompProperties
+ {
+ public IntVec2 areaSize = new IntVec2(13, 13);
+ public int warmupTicks = 120;
+ public EffecterDef warmupEffecter;
+ public SoundDef warmupSound;
+ public SoundDef teleportSound;
+
+ public ResearchProjectDef requiredResearch;
+
+ public CompProperties_MapTeleporter()
+ {
+ compClass = typeof(CompMapTeleporter);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/WULA_TeleportLandingMarker.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/WULA_TeleportLandingMarker.cs
new file mode 100644
index 00000000..0787d2f3
--- /dev/null
+++ b/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/WULA_TeleportLandingMarker.cs
@@ -0,0 +1,83 @@
+using System.Collections.Generic;
+using System.Linq;
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace WulaFallenEmpire
+{
+ public class WULA_TeleportLandingMarker : Thing
+ {
+ public Thing sourceThing;
+
+ public CompMapTeleporter SourceTeleporter => sourceThing?.TryGetComp();
+
+ public override CellRect? CustomRectForSelector
+ {
+ get
+ {
+ if (SourceTeleporter != null)
+ {
+ return CellRect.CenteredOn(Position, SourceTeleporter.Props.areaSize.x, SourceTeleporter.Props.areaSize.z);
+ }
+ return null;
+ }
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_References.Look(ref sourceThing, "sourceThing");
+ }
+
+ public override IEnumerable GetGizmos()
+ {
+ foreach (var gizmo in base.GetGizmos())
+ {
+ yield return gizmo;
+ }
+
+ yield return new Command_Action
+ {
+ defaultLabel = "WULA_ConfirmTeleport".Translate(),
+ defaultDesc = "WULA_ConfirmTeleportDesc".Translate(),
+ icon = ContentFinder.Get("UI/Commands/LaunchShip"),
+ action = Confirm
+ };
+
+ yield return new Command_Action
+ {
+ defaultLabel = "WULA_MoveMarker".Translate(),
+ defaultDesc = "WULA_MoveMarkerDesc".Translate(),
+ icon = ContentFinder.Get("UI/Commands/Install"),
+ action = StartMove
+ };
+ }
+
+ private void StartMove()
+ {
+ if (SourceTeleporter != null)
+ {
+ Find.DesignatorManager.Select(new Designator_TeleportArrival(SourceTeleporter, Map, this));
+ }
+ }
+
+ private void Confirm()
+ {
+ if (SourceTeleporter != null)
+ {
+ SourceTeleporter.ConfirmArrival(Position, Map);
+ }
+ Destroy();
+ }
+
+ protected override void DrawAt(Vector3 drawLoc, bool flip = false)
+ {
+ base.DrawAt(drawLoc, flip);
+ if (SourceTeleporter != null)
+ {
+ GenDraw.DrawFieldEdges(CellRect.CenteredOn(Position, SourceTeleporter.Props.areaSize.x, SourceTeleporter.Props.areaSize.z).Cells.ToList());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/Damage/202512031732.xml b/Source/WulaFallenEmpire/Damage/202512031732.xml
deleted file mode 100644
index e9f71f0e..00000000
--- a/Source/WulaFallenEmpire/Damage/202512031732.xml
+++ /dev/null
@@ -1,147 +0,0 @@
-
-
- Damage_WithExtraEffects
-
- WulaFallenEmpire.DamageWorker_ExtraDamage
-
-
-
-
-
-
-
-
- Damage_WithBurn
-
- WulaFallenEmpire.DamageWorker_ExtraDamage
-
-
-
-
-
-
-
-
- Damage_MultiEffect
-
- WulaFallenEmpire.DamageWorker_ExtraDamage
-
-
-
-
-
-
-
-
- Gun_AdvancedRifle
-
-
-
-
- Verb_Shoot
- Bullet_Advanced
-
-
-
-
-
-
- Bullet_Advanced
-
-
- Damage_MultiEffect
- 25
-
-
diff --git a/Source/WulaFallenEmpire/Designator/Designator_TeleportArrival.cs b/Source/WulaFallenEmpire/Designator/Designator_TeleportArrival.cs
new file mode 100644
index 00000000..815e4363
--- /dev/null
+++ b/Source/WulaFallenEmpire/Designator/Designator_TeleportArrival.cs
@@ -0,0 +1,167 @@
+using System.Collections.Generic;
+using System.Linq;
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace WulaFallenEmpire
+{
+ public class Designator_TeleportArrival : Designator
+ {
+ private CompMapTeleporter teleporter;
+ private Map targetMap;
+ private WULA_TeleportLandingMarker marker;
+ private List thingsToTeleport = new List();
+ private IntVec3 sourceCenter;
+
+ public override string Label => "WULA_SelectArrivalPoint".Translate();
+ public override string Desc => "WULA_SelectArrivalPointDesc".Translate();
+
+ public Designator_TeleportArrival(CompMapTeleporter teleporter, Map targetMap, WULA_TeleportLandingMarker marker = null)
+ {
+ this.teleporter = teleporter;
+ this.targetMap = targetMap;
+ this.marker = marker;
+ this.useMouseIcon = true;
+ this.soundDragSustain = SoundDefOf.Designate_DragStandard;
+ this.soundDragChanged = SoundDefOf.Designate_DragStandard_Changed;
+ this.soundSucceeded = SoundDefOf.Designate_PlaceBuilding;
+ }
+
+ public override AcceptanceReport CanDesignateCell(IntVec3 loc)
+ {
+ if (!loc.InBounds(targetMap)) return false;
+
+ // 检查区域是否有效
+ CellRect rect = CellRect.CenteredOn(loc, teleporter.Props.areaSize.x, teleporter.Props.areaSize.z);
+ foreach (IntVec3 cell in rect)
+ {
+ if (!cell.InBounds(targetMap)) return "WULA_OutOfBounds".Translate();
+
+ // 检查地图边缘
+ if (cell.InNoBuildEdgeArea(targetMap))
+ {
+ return "WULA_InNoBuildArea".Translate();
+ }
+
+ // 检查迷雾
+ if (cell.Fogged(targetMap))
+ {
+ return "WULA_BlockedByFog".Translate();
+ }
+
+ // 检查是否有不可覆盖的建筑
+ List things = targetMap.thingGrid.ThingsListAt(cell);
+ foreach (Thing t in things)
+ {
+ if (t.def.category == ThingCategory.Building)
+ {
+ if (!t.def.destroyable)
+ {
+ return "WULA_BlockedByIndestructible".Translate(t.Label);
+ }
+ }
+ }
+
+ // 检查地形是否支持建造
+ TerrainDef terrain = cell.GetTerrain(targetMap);
+ if (terrain.passability == Traversability.Impassable && !terrain.IsWater)
+ {
+ return "WULA_TerrainImpassable".Translate();
+ }
+ }
+
+ return true;
+ }
+
+ public override void DesignateSingleCell(IntVec3 c)
+ {
+ if (marker != null)
+ {
+ marker.Position = c;
+ Find.DesignatorManager.Deselect();
+ Find.Selector.ClearSelection();
+ Find.Selector.Select(marker);
+ }
+ else
+ {
+ teleporter.ConfirmArrival(c, targetMap);
+ Find.DesignatorManager.Deselect();
+ }
+ }
+
+ public override void Selected()
+ {
+ CacheThings();
+ DrawRect();
+ }
+
+ public override void SelectedUpdate()
+ {
+ DrawRect();
+ DrawGhosts();
+ }
+
+ public override void DrawMouseAttachments()
+ {
+ base.DrawMouseAttachments();
+ DrawRect();
+ }
+
+ private void DrawRect()
+ {
+ GenDraw.DrawFieldEdges(CellRect.CenteredOn(UI.MouseCell(), teleporter.Props.areaSize.x, teleporter.Props.areaSize.z).Cells.ToList());
+ }
+
+ private void CacheThings()
+ {
+ thingsToTeleport.Clear();
+ if (teleporter.parent == null || teleporter.parent.Map == null) return;
+
+ sourceCenter = teleporter.parent.Position;
+ Map sourceMap = teleporter.parent.Map;
+ CellRect rect = CellRect.CenteredOn(sourceCenter, teleporter.Props.areaSize.x, teleporter.Props.areaSize.z);
+
+ foreach (IntVec3 cell in rect)
+ {
+ if (!cell.InBounds(sourceMap)) continue;
+ foreach (Thing t in sourceMap.thingGrid.ThingsListAt(cell))
+ {
+ if (t.def.category == ThingCategory.Building || t.def.category == ThingCategory.Item || t.def.category == ThingCategory.Pawn)
+ {
+ if (t != teleporter.parent) thingsToTeleport.Add(t);
+ }
+ }
+ }
+ // 添加自身
+ thingsToTeleport.Add(teleporter.parent);
+ }
+
+ private void DrawGhosts()
+ {
+ IntVec3 mouseCell = UI.MouseCell();
+ if (!mouseCell.InBounds(targetMap)) return;
+
+ foreach (Thing t in thingsToTeleport)
+ {
+ if (t == null || t.Destroyed) continue;
+ if (t.Graphic == null) continue;
+
+ IntVec3 relativePos = t.Position - sourceCenter;
+ IntVec3 drawPos = mouseCell + relativePos;
+
+ if (drawPos.InBounds(targetMap))
+ {
+ try
+ {
+ GhostUtility.GhostGraphicFor(t.Graphic, t.def, Color.white).DrawFromDef(drawPos.ToVector3ShiftedWithAltitude(AltitudeLayer.Blueprint), t.Rotation, t.def);
+ }
+ catch
+ {
+ // 忽略绘制错误,防止UI崩溃
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/EventSystem/Letter_EventChoice.cs b/Source/WulaFallenEmpire/EventSystem/Letter_EventChoice.cs
deleted file mode 100644
index cbf39751..00000000
--- a/Source/WulaFallenEmpire/EventSystem/Letter_EventChoice.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-using RimWorld;
-using RimWorld.QuestGen;
-using System;
-using System.Collections.Generic;
-using Verse;
-
-namespace WulaFallenEmpire
-{
- public class Letter_EventChoice : ChoiceLetter
- {
- // These fields are now inherited from the base Letter class
- // public string letterLabel;
- // public string letterTitle;
- // public string letterText;
- public List options;
- public new Quest quest;
-
- public override IEnumerable Choices
- {
- get
- {
- if (options.NullOrEmpty())
- {
- yield break;
- }
-
- foreach (var optionDef in options)
- {
- var currentOption = optionDef;
- Action choiceAction = delegate
- {
- if (!currentOption.optionEffects.NullOrEmpty())
- {
- foreach (var conditionalEffect in currentOption.optionEffects)
- {
- string reason;
- if (AreConditionsMet(conditionalEffect.conditions, out reason))
- {
- conditionalEffect.Execute(null);
- }
- }
- }
- if (quest != null && !quest.hidden && !quest.Historical)
- {
- quest.End(QuestEndOutcome.Success);
- }
- Find.LetterStack.RemoveLetter(this);
- };
-
- var diaOption = new DiaOption(currentOption.label.Translate())
- {
- action = choiceAction,
- resolveTree = true
- };
- yield return diaOption;
- }
- }
- }
-
- public override bool CanDismissWithRightClick => false;
-
- private bool AreConditionsMet(List conditions, out string reason)
- {
- reason = "";
- if (conditions.NullOrEmpty())
- {
- return true;
- }
-
- foreach (var condition in conditions)
- {
- if (!condition.IsMet(out string singleReason))
- {
- reason = singleReason;
- return false;
- }
- }
- return true;
- }
-
- public override void ExposeData()
- {
- base.ExposeData();
- // Scribe_Values.Look(ref letterLabel, "letterLabel"); // Now uses base.label
- // Scribe_Values.Look(ref letterTitle, "letterTitle"); // Now uses base.title
- // Scribe_Values.Look(ref letterText, "letterText"); // Now uses base.text
- Scribe_Collections.Look(ref options, "options", LookMode.Deep);
- if (Scribe.mode != LoadSaveMode.Saving || quest != null)
- {
- Scribe_References.Look(ref quest, "quest");
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/EventSystem/QuestNode_Root_EventLetter.cs b/Source/WulaFallenEmpire/EventSystem/QuestNode_Root_EventLetter.cs
deleted file mode 100644
index 5142eff6..00000000
--- a/Source/WulaFallenEmpire/EventSystem/QuestNode_Root_EventLetter.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using RimWorld;
-using RimWorld.QuestGen;
-using System;
-using System.Collections.Generic;
-using Verse;
-
-namespace WulaFallenEmpire
-{
- public class QuestNode_Root_EventLetter : QuestNode
- {
- // Fields to be set from the QuestScriptDef XML
- public SlateRef letterLabel;
- public SlateRef letterTitle;
- public SlateRef letterText;
- public List