This commit is contained in:
2025-09-21 16:17:08 +08:00
parent 4fb30a03aa
commit d5d986e62a
14 changed files with 251 additions and 116 deletions

View File

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

View File

@@ -137,59 +137,21 @@ namespace ArachnaeSwarm
}
}
public override IEnumerable<FloatMenuOption> GetFloatMenuOptions(Pawn selPawn)
{
foreach (var option in base.GetFloatMenuOptions(selPawn))
{
yield return option;
}
if (status == WormholePortalStatus.Linked)
{
yield break;
}
if (!selPawn.CanReach(this, PathEndMode.Touch, Danger.Deadly))
{
yield return new FloatMenuOption("CannotUseNoPath".Translate(), null);
yield break;
}
var compLaunchable = this.GetComp<CompLaunchableWormhole>();
var compRefuelable = this.GetComp<CompRefuelable>();
if (compRefuelable.Fuel < compLaunchable.Props.fuelNeededToLaunch)
{
yield return new FloatMenuOption("CommandDeployWormholePortalB_Pilot".Translate() + " (" + "NotEnoughFuel".Translate() + ")", null);
yield break;
}
// TODO: Create ARA_DeployWormhole JobDef
var jobDef = DefDatabase<JobDef>.GetNamed("ARA_DeployWormhole", false);
if (jobDef == null)
{
yield return new FloatMenuOption("DEV: JobDef ARA_DeployWormhole not found", null);
yield break;
}
void action()
{
var job = JobMaker.MakeJob(jobDef, this);
selPawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
}
yield return new FloatMenuOption("CommandDeployWormholePortalB_Pilot".Translate(), action);
}
public override string GetInspectString()
{
string text = base.GetInspectString();
text += "\n" + "Status".Translate() + ": " + status.ToString().Translate();
System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
stringBuilder.Append(base.GetInspectString());
if (stringBuilder.Length > 0) stringBuilder.AppendLine();
stringBuilder.Append("Status".Translate() + ": " + status.ToString().Translate());
if (linkedPortalB != null && !linkedPortalB.Destroyed)
{
text += "\n" + "LinkedTo".Translate() + ": " + linkedPortalB.Map.Parent.LabelCap;
stringBuilder.AppendLine();
stringBuilder.Append("LinkedTo".Translate() + ": " + linkedPortalB.Map.Parent.LabelCap);
}
return text;
return stringBuilder.ToString();
}
}

View File

@@ -106,16 +106,20 @@ namespace ArachnaeSwarm
public override string GetInspectString()
{
string text = base.GetInspectString();
System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
stringBuilder.Append(base.GetInspectString());
if (linkedPortalA != null && !linkedPortalA.Destroyed)
{
text += "\n" + "LinkedTo".Translate() + ": " + linkedPortalA.Map.Parent.LabelCap;
if (stringBuilder.Length > 0) stringBuilder.AppendLine();
stringBuilder.Append("LinkedTo".Translate() + ": " + linkedPortalA.Map.Parent.LabelCap);
}
else
{
text += "\n" + "ConnectionLost".Translate();
if (stringBuilder.Length > 0) stringBuilder.AppendLine();
stringBuilder.Append("ConnectionLost".Translate());
}
return text;
return stringBuilder.ToString();
}
}

View File

@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using RimWorld.Planet;
using UnityEngine;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
@@ -21,8 +23,49 @@ namespace ArachnaeSwarm
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
// The gizmo is now replaced by a float menu option.
yield break;
if (PortalA?.status == WormholePortalStatus.Linked)
{
yield break;
}
Command_Action launchCommand = new Command_Action();
launchCommand.defaultLabel = "CommandDeployWormholePortalB_Pilot".Translate();
launchCommand.defaultDesc = "CommandDeployWormholePortalB_PilotDesc".Translate();
launchCommand.icon = ContentFinder<Texture2D>.Get("UI/Commands/LaunchShip");
// Fuel check is now more complex, we can show max range or check on target selection.
// For simplicity, we'll disable if there's basically no fuel.
if (refuelableComp.Fuel < Props.fuelPerTile)
{
launchCommand.Disable("NotEnoughFuel".Translate());
}
List<Pawn> pilots = parent.Map.mapPawns.AllPawnsSpawned
.Where(p => p.IsColonistPlayerControlled && !p.Downed && !p.IsBurning() && p.CanReach(parent, PathEndMode.Touch, Danger.Deadly))
.ToList();
if (!pilots.Any())
{
launchCommand.Disable("NoPilotAvailable".Translate());
}
launchCommand.action = delegate
{
List<FloatMenuOption> options = new List<FloatMenuOption>();
foreach (Pawn p in pilots)
{
void pilotAction()
{
var jobDef = DefDatabase<JobDef>.GetNamed("ARA_DeployWormhole");
var job = JobMaker.MakeJob(jobDef, parent);
p.jobs.TryTakeOrderedJob(job, JobTag.Misc);
}
options.Add(new FloatMenuOption(p.LabelCap, pilotAction));
}
Find.WindowStack.Add(new FloatMenu(options));
};
yield return launchCommand;
}
public void StartChoosingDestination(Pawn pilot)
@@ -30,13 +73,14 @@ namespace ArachnaeSwarm
CameraJumper.TryJump(CameraJumper.GetWorldTarget(this.parent));
Find.WorldSelector.ClearSelection();
int tile = this.parent.Map.Tile;
Find.WorldTargeter.BeginTargeting((GlobalTargetInfo t) => ChoseWorldTarget(t, pilot), true, CompLaunchable.TargeterMouseAttachment, true, delegate
int maxDist = (int)(refuelableComp.Fuel / Props.fuelPerTile);
Find.WorldTargeter.BeginTargeting((GlobalTargetInfo t) => ChoseWorldTarget(t, pilot, maxDist), true, CompLaunchable.TargeterMouseAttachment, true, delegate
{
GenDraw.DrawWorldRadiusRing(tile, this.Props.maxLaunchDistance);
GenDraw.DrawWorldRadiusRing(tile, maxDist);
}, (GlobalTargetInfo t) => "Select target tile");
}
private bool ChoseWorldTarget(GlobalTargetInfo t, Pawn pilot)
private bool ChoseWorldTarget(GlobalTargetInfo t, Pawn pilot, int maxDist)
{
if (!t.IsValid)
{
@@ -49,51 +93,35 @@ namespace ArachnaeSwarm
return false;
}
MapParent mapParent = Find.World.worldObjects.MapParentAt(t.Tile);
if (mapParent?.HasMap ?? false)
int distance = Find.WorldGrid.TraversalDistanceBetween(parent.Map.Tile, t.Tile, true);
if (distance > maxDist)
{
Deploy(mapParent, pilot);
}
else
{
LongEventHandler.QueueLongEvent(delegate
{
var newMap = GetOrGenerateMapUtility.GetOrGenerateMap(t.Tile, WorldObjectDefOf.Camp);
Deploy(newMap.Parent, pilot);
}, "GeneratingMap", doAsynchronously: false, null);
Messages.Message("NotEnoughFuel".Translate(), MessageTypeDefOf.RejectInput);
return false;
}
return true;
}
private void Deploy(MapParent mapParent, Pawn pilot)
{
refuelableComp.ConsumeFuel(this.Props.fuelNeededToLaunch);
float fuelCost = distance * Props.fuelPerTile;
refuelableComp.ConsumeFuel(fuelCost);
// 传送驾驶员
// Despawn pilot from the source map
pilot.DeSpawn();
EffecterDefOf.Skip_Entry.Spawn(this.parent.Position, this.parent.Map);
Building_WormholePortal_B portalB = (Building_WormholePortal_B)ThingMaker.MakeThing(ThingDef.Named("ARA_WormholePortal_B"));
IntVec3 cell = DropCellFinder.RandomDropSpot(mapParent.Map);
GenSpawn.Spawn(portalB, cell, mapParent.Map, WipeMode.Vanish);
// 在B端生成驾驶员
IntVec3 pilotSpawnCell = CellFinder.RandomSpawnCellForPawnNear(portalB.Position, mapParent.Map, 5);
GenSpawn.Spawn(pilot, pilotSpawnCell, mapParent.Map, WipeMode.Vanish);
EffecterDefOf.Skip_Exit.Spawn(cell, mapParent.Map);
// Create the traveling object
var travelingWormhole = (TravelingWormhole)WorldObjectMaker.MakeWorldObject(DefDatabase<WorldObjectDef>.GetNamed("ARA_TravelingWormhole"));
travelingWormhole.sourcePortal = this.PortalA;
travelingWormhole.StartMove(parent.Map.Tile, t.Tile, pilot);
PortalA.SetLinkedPortal(portalB);
portalB.SetLinkedPortal(PortalA);
Find.WorldObjects.Add(travelingWormhole);
return true;
}
}
public class CompProperties_LaunchableWormhole : CompProperties
{
public float fuelNeededToLaunch;
public int maxLaunchDistance;
public float fuelPerTile = 1f; // Default fuel cost per tile
public CompProperties_LaunchableWormhole()
{

View File

@@ -0,0 +1,9 @@
using Verse;
namespace ArachnaeSwarm
{
public class DefModExtension_TravelingWormhole : DefModExtension
{
public float travelSpeed = 20f; // Default speed in tiles per day
}
}

View File

@@ -0,0 +1,97 @@
using RimWorld;
using RimWorld.Planet;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
[StaticConstructorOnStartup]
public class TravelingWormhole : WorldObject, IThingHolder
{
public Building_WormholePortal_A sourcePortal;
public int destinationTile;
private int initialTile;
private float traveledPct;
private ThingOwner contents;
public override Material Material => def.Material;
public override Vector3 DrawPos => Vector3.Slerp(
Find.WorldGrid.GetTileCenter(initialTile),
Find.WorldGrid.GetTileCenter(destinationTile),
traveledPct);
public TravelingWormhole()
{
contents = new ThingOwner<Thing>(this, true, LookMode.Deep);
}
public override void ExposeData()
{
base.ExposeData();
Scribe_References.Look(ref sourcePortal, "sourcePortal");
Scribe_Values.Look(ref destinationTile, "destinationTile", 0);
Scribe_Values.Look(ref initialTile, "initialTile", 0);
Scribe_Values.Look(ref traveledPct, "traveledPct", 0f);
Scribe_Deep.Look(ref contents, "contents", this);
}
public void StartMove(int startTile, int destTile, Pawn pilot)
{
initialTile = startTile;
destinationTile = destTile;
this.Tile = startTile;
contents.TryAdd(pilot, true);
}
protected override void Tick()
{
base.Tick();
var speed = def.GetModExtension<DefModExtension_TravelingWormhole>()?.travelSpeed ?? 20f;
traveledPct += (1f / GenDate.TicksPerDay) * speed;
if (traveledPct >= 1f)
{
traveledPct = 1f;
Arrived();
}
}
private void Arrived()
{
if (contents.Any)
{
Pawn pilot = contents.First() as Pawn;
if (pilot != null)
{
Map targetMap = GetOrGenerateMapUtility.GetOrGenerateMap(destinationTile, null);
Building_WormholePortal_B portalB = (Building_WormholePortal_B)ThingMaker.MakeThing(ThingDef.Named("ARA_WormholePortal_B"));
IntVec3 cell = DropCellFinder.RandomDropSpot(targetMap);
GenSpawn.Spawn(portalB, cell, targetMap, WipeMode.Vanish);
IntVec3 pilotSpawnCell = CellFinder.RandomClosewalkCellNear(portalB.Position, targetMap, 5);
GenSpawn.Spawn(pilot, pilotSpawnCell, targetMap, WipeMode.Vanish);
contents.Remove(pilot);
EffecterDefOf.Skip_Exit.Spawn(cell, targetMap);
if (sourcePortal != null && !sourcePortal.Destroyed)
{
sourcePortal.SetLinkedPortal(portalB);
portalB.SetLinkedPortal(sourcePortal);
}
}
}
Find.WorldObjects.Remove(this);
}
public ThingOwner GetDirectlyHeldThings() => contents;
public void GetChildHolders(List<IThingHolder> outChildren) => ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, GetDirectlyHeldThings());
}
}