diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index bc21300f..4b7b83f5 100644 Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/CompMapTeleporter.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/CompMapTeleporter.cs index 2aa55e1e..779ee395 100644 --- a/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/CompMapTeleporter.cs +++ b/Source/WulaFallenEmpire/BuildingComp/WULA_Teleporter/CompMapTeleporter.cs @@ -18,6 +18,100 @@ namespace WulaFallenEmpire private int warmupTicksLeft = 0; private GlobalTargetInfo target; private WULA_TeleportLandingMarker activeMarker; + + // Group caching + private List cachedGroupMembers; + private int lastGroupCheckTick = -1; + + // Cells caching + private List cachedGroupCells; + private int lastGroupCellsCheckTick = -1; + + public CellRect TeleportRect => CellRect.CenteredOn(parent.Position, Props.areaSize.x, Props.areaSize.z); + + private List GroupMembers + { + get + { + if (lastGroupCheckTick == Find.TickManager.TicksGame && cachedGroupMembers != null) + { + return cachedGroupMembers; + } + + lastGroupCheckTick = Find.TickManager.TicksGame; + cachedGroupMembers = new List(); + var openSet = new Queue(); + var closedSet = new HashSet(); + + openSet.Enqueue(this); + closedSet.Add(this); + + while (openSet.Count > 0) + { + var currentComp = openSet.Dequeue(); + cachedGroupMembers.Add(currentComp); + + var potentialNeighbors = parent.Map.listerThings.ThingsOfDef(parent.def); + foreach (var potentialNeighbor in potentialNeighbors) + { + var neighborComp = potentialNeighbor.TryGetComp(); + if (neighborComp == null || closedSet.Contains(neighborComp)) continue; + + if (currentComp.TeleportRect.ExpandedBy(1).Overlaps(neighborComp.TeleportRect)) + { + closedSet.Add(neighborComp); + openSet.Enqueue(neighborComp); + } + } + } + // Sort by ID to ensure consistent leader + cachedGroupMembers.SortBy(c => c.parent.thingIDNumber); + return cachedGroupMembers; + } + } + + public List GroupCells + { + get + { + if (lastGroupCellsCheckTick == Find.TickManager.TicksGame && cachedGroupCells != null) + { + return cachedGroupCells; + } + + lastGroupCellsCheckTick = Find.TickManager.TicksGame; + HashSet cells = new HashSet(); + foreach (var member in GroupMembers) + { + foreach (var cell in member.TeleportRect) + { + if (cell.InBounds(parent.Map)) + { + cells.Add(cell); + } + } + } + cachedGroupCells = cells.ToList(); + return cachedGroupCells; + } + } + + public List GetRelativeGroupCells() + { + var cells = GroupCells; + var center = parent.Position; + return cells.Select(c => c - center).ToList(); + } + + private CompMapTeleporter Leader + { + get + { + var members = GroupMembers; + if (members.Count == 0) return this; + return members[0]; + } + } public override void PostExposeData() { @@ -31,19 +125,43 @@ namespace WulaFallenEmpire public override void PostDrawExtraSelectionOverlays() { base.PostDrawExtraSelectionOverlays(); - GenDraw.DrawFieldEdges(CellRect.CenteredOn(parent.Position, Props.areaSize.x, Props.areaSize.z).Cells.ToList()); + + // Draw the combined field edges + GenDraw.DrawFieldEdges(GroupCells, Color.cyan); + + var leader = Leader; + if (leader != null) + { + // Mark the leader clearly + Vector3 center = leader.parent.TrueCenter(); + GenDraw.DrawLineBetween(center + new Vector3(-1f, 0, -1f), center + new Vector3(1f, 0, 1f), SimpleColor.Yellow); + GenDraw.DrawLineBetween(center + new Vector3(-1f, 0, 1f), center + new Vector3(1f, 0, -1f), SimpleColor.Yellow); + GenDraw.DrawCircleOutline(center, 1.5f, SimpleColor.Yellow); + + // Draw lines from members to leader + foreach (var member in GroupMembers) + { + if (member != leader) + { + GenDraw.DrawLineBetween(leader.parent.TrueCenter(), member.parent.TrueCenter(), SimpleColor.Cyan); + } + } + } } public override void CompTick() { base.CompTick(); - if (isWarmingUp) + if (Leader == this && isWarmingUp) { warmupTicksLeft--; if (warmupTicksLeft % 60 == 0) { Log.Message($"[WULA] Teleport warmup: {warmupTicksLeft} ticks left."); - Props.warmupEffecter?.Spawn(parent, parent.Map).Cleanup(); + foreach (var member in GroupMembers) + { + Props.warmupEffecter?.Spawn(member.parent, member.parent.Map).Cleanup(); + } } if (warmupTicksLeft <= 0) @@ -65,6 +183,10 @@ namespace WulaFallenEmpire if (parent.Faction != Faction.OfPlayer) yield break; + // Only the leader provides the gizmos + if (Leader != this) + yield break; + if (isWarmingUp) { yield return new Command_Action @@ -274,17 +396,17 @@ namespace WulaFallenEmpire private void TeleportContents(Map targetMap, IntVec3 targetCenter) { Map sourceMap = parent.Map; - CellRect rect = CellRect.CenteredOn(parent.Position, Props.areaSize.x, Props.areaSize.z); + List cells = GroupCells; 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}"); + Log.Message($"[WULA] Collecting data from {cells.Count} cells in group"); // 1. 收集数据 HashSet collectedThings = new HashSet(); - foreach (IntVec3 cell in rect) + foreach (IntVec3 cell in cells) { if (!cell.InBounds(sourceMap)) continue; @@ -294,7 +416,7 @@ namespace WulaFallenEmpire for (int i = thingList.Count - 1; i >= 0; i--) { Thing t = thingList[i]; - if (t != parent && !collectedThings.Contains(t) && + if (!collectedThings.Contains(t) && (t.def.category == ThingCategory.Item || t.def.category == ThingCategory.Pawn || t.def.category == ThingCategory.Building)) @@ -309,14 +431,12 @@ namespace WulaFallenEmpire // 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) @@ -345,14 +465,12 @@ namespace WulaFallenEmpire 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); diff --git a/Source/WulaFallenEmpire/Designator/Designator_TeleportArrival.cs b/Source/WulaFallenEmpire/Designator/Designator_TeleportArrival.cs index 815e4363..07a0342c 100644 --- a/Source/WulaFallenEmpire/Designator/Designator_TeleportArrival.cs +++ b/Source/WulaFallenEmpire/Designator/Designator_TeleportArrival.cs @@ -13,6 +13,7 @@ namespace WulaFallenEmpire private WULA_TeleportLandingMarker marker; private List thingsToTeleport = new List(); private IntVec3 sourceCenter; + private List relativeCells; public override string Label => "WULA_SelectArrivalPoint".Translate(); public override string Desc => "WULA_SelectArrivalPointDesc".Translate(); @@ -26,31 +27,35 @@ namespace WulaFallenEmpire this.soundDragSustain = SoundDefOf.Designate_DragStandard; this.soundDragChanged = SoundDefOf.Designate_DragStandard_Changed; this.soundSucceeded = SoundDefOf.Designate_PlaceBuilding; + + // Cache relative cells from the group + this.relativeCells = teleporter.GetRelativeGroupCells(); } 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) + // Check all cells in the group shape + foreach (IntVec3 offset in relativeCells) { + IntVec3 cell = loc + offset; + if (!cell.InBounds(targetMap)) return "WULA_OutOfBounds".Translate(); - // 检查地图边缘 + // Check map edge if (cell.InNoBuildEdgeArea(targetMap)) { return "WULA_InNoBuildArea".Translate(); } - // 检查迷雾 + // Check fog if (cell.Fogged(targetMap)) { return "WULA_BlockedByFog".Translate(); } - // 检查是否有不可覆盖的建筑 + // Check for indestructible buildings List things = targetMap.thingGrid.ThingsListAt(cell); foreach (Thing t in things) { @@ -63,7 +68,7 @@ namespace WulaFallenEmpire } } - // 检查地形是否支持建造 + // Check terrain passability TerrainDef terrain = cell.GetTerrain(targetMap); if (terrain.passability == Traversability.Impassable && !terrain.IsWater) { @@ -110,7 +115,13 @@ namespace WulaFallenEmpire private void DrawRect() { - GenDraw.DrawFieldEdges(CellRect.CenteredOn(UI.MouseCell(), teleporter.Props.areaSize.x, teleporter.Props.areaSize.z).Cells.ToList()); + IntVec3 center = UI.MouseCell(); + List drawCells = new List(); + foreach (var offset in relativeCells) + { + drawCells.Add(center + offset); + } + GenDraw.DrawFieldEdges(drawCells); } private void CacheThings() @@ -120,9 +131,11 @@ namespace WulaFallenEmpire 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) + // Use the group cells directly from the teleporter + List groupCells = teleporter.GroupCells; + + foreach (IntVec3 cell in groupCells) { if (!cell.InBounds(sourceMap)) continue; foreach (Thing t in sourceMap.thingGrid.ThingsListAt(cell)) @@ -133,7 +146,7 @@ namespace WulaFallenEmpire } } } - // 添加自身 + // Add self (leader) thingsToTeleport.Add(teleporter.parent); } @@ -158,7 +171,7 @@ namespace WulaFallenEmpire } catch { - // 忽略绘制错误,防止UI崩溃 + // Ignore drawing errors to prevent UI crash } } }