This commit is contained in:
2025-09-21 15:15:41 +08:00
parent 182cb72cb0
commit 5d2d2c5e4d
7 changed files with 162 additions and 253 deletions

Binary file not shown.

View File

@@ -23,7 +23,6 @@
<tickerType>Normal</tickerType>
<category>Building</category>
<pathCost>50</pathCost>
<fillPercent>0.5</fillPercent>
<statBases>
<MaxHitPoints>250</MaxHitPoints>
<WorkToBuild>8000</WorkToBuild>
@@ -61,6 +60,9 @@
<building>
<ai_chillDestination>false</ai_chillDestination>
</building>
<portal>
<traverseSound>Message_FleshbeastsDiscovered</traverseSound>
</portal>
</ThingDef>
<!-- ==================== B端传送门 ==================== -->
@@ -90,6 +92,9 @@
<li Class="CompProperties_Flickable"/>
</comps>
<tradeability>None</tradeability>
<portal>
<traverseSound>Message_FleshbeastsDiscovered</traverseSound>
</portal>
</ThingDef>
<!-- CompProperties for our custom launchable -->

View File

@@ -209,7 +209,6 @@
<Compile Include="Wormhole\Building_WormholePortal_A.cs" />
<Compile Include="Wormhole\Building_WormholePortal_B.cs" />
<Compile Include="Wormhole\CompLaunchableWormhole.cs" />
<Compile Include="Wormhole\Dialog_WormholeTransfer.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 自定义清理任务删除obj文件夹中的临时文件 -->

View File

@@ -11,7 +11,7 @@ namespace ArachnaeSwarm
Linked
}
public class Building_WormholePortal_A : Building
public class Building_WormholePortal_A : MapPortal
{
private Building_WormholePortal_B linkedPortalB;
public WormholePortalStatus status = WormholePortalStatus.Idle;
@@ -32,6 +32,68 @@ namespace ArachnaeSwarm
base.SpawnSetup(map, respawningAfterLoad);
refuelableComp = GetComp<CompRefuelable>();
}
// --- MapPortal Overrides ---
public override bool IsEnterable(out string reason)
{
if (status != WormholePortalStatus.Linked)
{
reason = "WormholeNotLinked".Translate();
return false;
}
if (linkedPortalB == null || linkedPortalB.Destroyed)
{
reason = "WormholeLinkLost".Translate();
Notify_B_Destroyed();
return false;
}
reason = "";
return true;
}
public override Map GetOtherMap()
{
return linkedPortalB?.Map;
}
public override IntVec3 GetDestinationLocation()
{
return linkedPortalB.Position;
}
public override void OnEntered(Pawn pawn)
{
// The base class handles adding the pawn to the container.
base.OnEntered(pawn);
// Now, transfer everything in the container to the destination.
if (linkedPortalB != null && !linkedPortalB.Destroyed)
{
// Transfer pawns first
List<Pawn> pawnsInPortal = new List<Pawn>();
foreach (Thing item in GetDirectlyHeldThings())
{
if (item is Pawn p)
{
pawnsInPortal.Add(p);
}
}
foreach(Pawn p in pawnsInPortal)
{
p.DeSpawn();
GenSpawn.Spawn(p, CellFinder.RandomClosewalkCellNear(linkedPortalB.Position, linkedPortalB.Map, 5), linkedPortalB.Map, WipeMode.Vanish);
GetDirectlyHeldThings().Remove(p);
}
// Transfer items
GetDirectlyHeldThings().TryDropAll(linkedPortalB.Position, linkedPortalB.Map, ThingPlaceMode.Near);
}
// Custom sound/effect can be played here.
EffecterDefOf.Skip_Exit.Spawn(pawn.Position, pawn.Map);
}
public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish)
{
@@ -67,29 +129,11 @@ namespace ArachnaeSwarm
public override IEnumerable<Gizmo> GetGizmos()
{
// Use the base (MapPortal) gizmos, which include "Enter" and "Cancel Load"
foreach (Gizmo g in base.GetGizmos())
{
yield return g;
}
if (status == WormholePortalStatus.Linked)
{
if (linkedPortalB == null || linkedPortalB.Destroyed)
{
// 安全检查,处理幽灵链接
Notify_B_Destroyed();
yield break;
}
Command_Action enterCommand = new Command_Action
{
defaultLabel = "EnterWormhole".Translate(),
defaultDesc = "EnterWormholeDesc".Translate(),
icon = ContentFinder<UnityEngine.Texture2D>.Get("UI/Commands/EnterCave"),
action = BeginTeleportation
};
yield return enterCommand;
}
}
public override string GetInspectString()
@@ -103,9 +147,5 @@ namespace ArachnaeSwarm
return text;
}
private void BeginTeleportation()
{
Find.WindowStack.Add(new Dialog_WormholeTransfer(this, this.LinkedPortal));
}
}
}

View File

@@ -1,10 +1,11 @@
using RimWorld;
using RimWorld.Planet;
using System.Collections.Generic;
using Verse;
namespace ArachnaeSwarm
{
public class Building_WormholePortal_B : Building
public class Building_WormholePortal_B : MapPortal
{
private Building_WormholePortal_A linkedPortalA;
@@ -41,22 +42,66 @@ namespace ArachnaeSwarm
public override IEnumerable<Gizmo> GetGizmos()
{
// Use the base (MapPortal) gizmos
foreach (Gizmo g in base.GetGizmos())
{
yield return g;
}
}
// --- MapPortal Overrides ---
public override bool IsEnterable(out string reason)
{
if (linkedPortalA == null || linkedPortalA.Destroyed)
{
reason = "WormholeLinkLost".Translate();
if (linkedPortalA != null) linkedPortalA.Notify_B_Destroyed();
this.Destroy(DestroyMode.Vanish);
return false;
}
reason = "";
return true;
}
public override Map GetOtherMap()
{
return linkedPortalA?.Map;
}
public override IntVec3 GetDestinationLocation()
{
return linkedPortalA.Position;
}
public override void OnEntered(Pawn pawn)
{
base.OnEntered(pawn);
if (linkedPortalA != null && !linkedPortalA.Destroyed)
{
Command_Action enterCommand = new Command_Action
// Transfer pawns first
List<Pawn> pawnsInPortal = new List<Pawn>();
foreach (Thing item in GetDirectlyHeldThings())
{
defaultLabel = "EnterWormhole".Translate(),
defaultDesc = "EnterWormholeDesc".Translate(),
icon = ContentFinder<UnityEngine.Texture2D>.Get("UI/Commands/EnterCave"),
action = BeginTeleportation
};
yield return enterCommand;
if (item is Pawn p)
{
pawnsInPortal.Add(p);
}
}
foreach(Pawn p in pawnsInPortal)
{
p.DeSpawn();
GenSpawn.Spawn(p, CellFinder.RandomClosewalkCellNear(linkedPortalA.Position, linkedPortalA.Map, 5), linkedPortalA.Map, WipeMode.Vanish);
GetDirectlyHeldThings().Remove(p);
}
// Transfer items
GetDirectlyHeldThings().TryDropAll(linkedPortalA.Position, linkedPortalA.Map, ThingPlaceMode.Near);
}
EffecterDefOf.Skip_Exit.Spawn(pawn.Position, pawn.Map);
}
public override string GetInspectString()
@@ -73,9 +118,5 @@ namespace ArachnaeSwarm
return text;
}
private void BeginTeleportation()
{
Find.WindowStack.Add(new Dialog_WormholeTransfer(this, this.LinkedPortal));
}
}
}

View File

@@ -1,214 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using RimWorld.Planet;
using UnityEngine;
using Verse;
using Verse.Sound;
namespace ArachnaeSwarm
{
public class Dialog_WormholeTransfer : Window
{
private enum Tab
{
Pawns,
Items
}
private const float TitleRectHeight = 35f;
private const float BottomAreaHeight = 55f;
private readonly Vector2 BottomButtonSize = new Vector2(160f, 40f);
private Building sourcePortal; // 通用源传送门
private Building destinationPortal; // 通用目标传送门
private List<TransferableOneWay> transferables;
private TransferableOneWayWidget pawnsTransfer;
private TransferableOneWayWidget itemsTransfer;
private Tab tab;
private static List<TabRecord> tabsList = new List<TabRecord>();
public override Vector2 InitialSize => new Vector2(1024f, UI.screenHeight);
protected override float Margin => 0f;
public Dialog_WormholeTransfer(Building sourcePortal, Building destinationPortal)
{
this.sourcePortal = sourcePortal;
this.destinationPortal = destinationPortal;
forcePause = true;
absorbInputAroundWindow = true;
}
public override void PostOpen()
{
base.PostOpen();
CalculateAndRecacheTransferables();
}
public override void DoWindowContents(Rect inRect)
{
Rect rect = new Rect(0f, 0f, inRect.width, TitleRectHeight);
Text.Font = GameFont.Medium;
Text.Anchor = TextAnchor.MiddleCenter;
Widgets.Label(rect, "EnterWormhole".Translate());
Text.Font = GameFont.Small;
Text.Anchor = TextAnchor.UpperLeft;
tabsList.Clear();
tabsList.Add(new TabRecord("PawnsTab".Translate(), () => tab = Tab.Pawns, tab == Tab.Pawns));
tabsList.Add(new TabRecord("ItemsTab".Translate(), () => tab = Tab.Items, tab == Tab.Items));
inRect.yMin += 67f;
Widgets.DrawMenuSection(inRect);
TabDrawer.DrawTabs(inRect, tabsList);
inRect = inRect.ContractedBy(17f);
Widgets.BeginGroup(inRect);
Rect rect2 = inRect.AtZero();
DoBottomButtons(rect2);
Rect inRect2 = rect2;
inRect2.yMax -= 76f;
bool anythingChanged = false;
switch (tab)
{
case Tab.Pawns:
pawnsTransfer.OnGUI(inRect2, out anythingChanged);
break;
case Tab.Items:
itemsTransfer.OnGUI(inRect2, out anythingChanged);
break;
}
if (anythingChanged)
{
// 可以添加一些计数或质量更新的逻辑
}
Widgets.EndGroup();
}
private void DoBottomButtons(Rect rect)
{
float buttonY = rect.height - BottomAreaHeight + 17;
if (Widgets.ButtonText(new Rect(rect.width / 2f - BottomButtonSize.x / 2f, buttonY, BottomButtonSize.x, BottomButtonSize.y), "ResetButton".Translate()))
{
SoundDefOf.Tick_Low.PlayOneShotOnCamera();
CalculateAndRecacheTransferables();
}
if (Widgets.ButtonText(new Rect(0f, buttonY, BottomButtonSize.x, BottomButtonSize.y), "CancelButton".Translate()))
{
Close();
}
if (Widgets.ButtonText(new Rect(rect.width - BottomButtonSize.x, buttonY, BottomButtonSize.x, BottomButtonSize.y), "AcceptButton".Translate()))
{
if (TryAccept())
{
SoundDefOf.Tick_High.PlayOneShotOnCamera();
Close(doCloseSound: false);
}
}
}
private bool TryAccept()
{
List<TransferableOneWay> toTransfer = transferables.Where(x => x.CountToTransfer > 0).ToList();
if (!toTransfer.Any())
{
Messages.Message("NothingToTransfer".Translate(), MessageTypeDefOf.RejectInput);
return false;
}
foreach (var trans in toTransfer)
{
// 传送逻辑
var things = trans.things.Take(trans.CountToTransfer).ToList();
foreach (var thing in things)
{
Pawn pawn = thing as Pawn;
if (pawn != null)
{
pawn.DeSpawn();
GenSpawn.Spawn(pawn, CellFinder.RandomClosewalkCellNear(destinationPortal.Position, destinationPortal.Map, 5), destinationPortal.Map, WipeMode.Vanish);
}
else
{
thing.DeSpawn();
GenSpawn.Spawn(thing, CellFinder.RandomClosewalkCellNear(destinationPortal.Position, destinationPortal.Map, 5), destinationPortal.Map, WipeMode.Vanish);
}
}
}
// 切换视角
if (toTransfer.Any(x => x.ThingDef.category == ThingCategory.Pawn))
{
var firstPawn = toTransfer.First(x => x.ThingDef.category == ThingCategory.Pawn).AnyThing as Pawn;
CameraJumper.TryJump(new GlobalTargetInfo(destinationPortal.Position, destinationPortal.Map));
if (firstPawn != null)
{
CameraJumper.TrySelect(firstPawn);
}
}
Messages.Message("WormholeTransferComplete".Translate(), MessageTypeDefOf.PositiveEvent);
return true;
}
private void CalculateAndRecacheTransferables()
{
transferables = new List<TransferableOneWay>();
AddPawnsToTransferables();
AddItemsToTransferables();
pawnsTransfer = new TransferableOneWayWidget(transferables.Where(x => x.ThingDef.category == ThingCategory.Pawn), null, null, "TransferableCount".Translate(),
drawMass: true,
ignorePawnInventoryMass: IgnorePawnsInventoryMode.Ignore,
includePawnsMassInMassUsage: true,
availableMassGetter: () => float.MaxValue, // 无质量限制
extraHeaderSpace: 0f,
ignoreSpawnedCorpseGearAndInventoryMass: false,
tile: sourcePortal.Map.Tile,
drawMarketValue: true,
drawEquippedWeapon: true);
itemsTransfer = new TransferableOneWayWidget(transferables.Where(x => x.ThingDef.category != ThingCategory.Pawn), null, null, "TransferableCount".Translate(),
drawMass: true,
ignorePawnInventoryMass: IgnorePawnsInventoryMode.Ignore,
includePawnsMassInMassUsage: true,
availableMassGetter: () => float.MaxValue,
extraHeaderSpace: 0f,
ignoreSpawnedCorpseGearAndInventoryMass: false,
tile: sourcePortal.Map.Tile,
drawMarketValue: true);
}
private void AddToTransferables(Thing t)
{
var transferable = TransferableUtility.TransferableMatching(t, transferables, TransferAsOneMode.PodsOrCaravanPacking);
if (transferable == null)
{
transferable = new TransferableOneWay();
transferables.Add(transferable);
}
transferable.things.Add(t);
}
private void AddPawnsToTransferables()
{
foreach (Pawn p in sourcePortal.Map.mapPawns.AllPawnsSpawned.Where(p => p.Faction == Faction.OfPlayer))
{
AddToTransferables(p);
}
}
private void AddItemsToTransferables()
{
foreach (Thing t in sourcePortal.Map.listerThings.AllThings.Where(t => t.def.category == ThingCategory.Item && t.def.EverHaulable && t.Position.IsValid && !t.Position.Fogged(t.Map)))
{
AddToTransferables(t);
}
}
}
}

View File

@@ -0,0 +1,38 @@
# Find the latest installed .NET SDK and build the project.
try {
Write-Host "Finding latest .NET SDK..."
$sdkList = dotnet --list-sdks
$latestSdk = $sdkList.Split([Environment]::NewLine) | Select-Object -Last 1
if ($null -eq $latestSdk -or $latestSdk -eq "") {
Write-Host "No .NET SDKs found. Please install the .NET SDK."
exit 1
}
$sdkVersion = ($latestSdk.Split(' '))[0]
Write-Host "Using .NET SDK version: $sdkVersion"
# Since the project is .NET Framework 4.8, msbuild is more suitable.
# We need to find msbuild.exe location.
# A common path for Visual Studio's MSBuild:
$vsWherePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
if(Test-Path $vsWherePath) {
$vsPath = & $vsWherePath -latest -property installationPath
$msBuildPath = Join-Path $vsPath "MSBuild\Current\Bin\MSBuild.exe"
if(Test-Path $msBuildPath) {
Write-Host "Found MSBuild at: $msBuildPath"
& $msBuildPath "ArachnaeSwarm.sln" /t:Rebuild /p:Configuration=Debug
} else {
Write-Host "MSBuild.exe not found at the expected path. Falling back to dotnet build."
dotnet build "ArachnaeSwarm.sln"
}
} else {
Write-Host "vswhere.exe not found. Falling back to dotnet build."
dotnet build "ArachnaeSwarm.sln"
}
} catch {
Write-Host "An error occurred during the build process."
Write-Host $_
exit 1
}