diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index 87431c1..e435abb 100644
Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ
diff --git a/1.6/1.6/Defs/JobDefs/ARA_Jobs_Interactive.xml b/1.6/1.6/Defs/JobDefs/ARA_Jobs_Interactive.xml
index 89ae943..eb0159d 100644
--- a/1.6/1.6/Defs/JobDefs/ARA_Jobs_Interactive.xml
+++ b/1.6/1.6/Defs/JobDefs/ARA_Jobs_Interactive.xml
@@ -8,4 +8,11 @@
true
+
+ ARA_AddToQueueJob
+ ArachnaeSwarm.JobDriver_AddToQueue
+ 正在添加生产订单。
+ true
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/Thing_building/ARA_BioforgeIncubator.xml b/1.6/1.6/Defs/Thing_building/ARA_BioforgeIncubator.xml
index 1341595..61098fb 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_BioforgeIncubator.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_BioforgeIncubator.xml
@@ -103,6 +103,8 @@
生物质
+ true
+ true
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/InteractiveProducer_Keys.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/InteractiveProducer_Keys.xml
index 00392aa..1daa519 100644
--- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/InteractiveProducer_Keys.xml
+++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/InteractiveProducer_Keys.xml
@@ -17,4 +17,8 @@
未在生产
未孵化,需要阿拉克涅工艺种交互
+
+ 所需营养
+ 孵化 {0}
+
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ARA_CompInteractiveProducer/CompRefuelableNutrition.cs b/Source/ArachnaeSwarm/ARA_CompInteractiveProducer/CompRefuelableNutrition.cs
index 1c029b0..a76339d 100644
--- a/Source/ArachnaeSwarm/ARA_CompInteractiveProducer/CompRefuelableNutrition.cs
+++ b/Source/ArachnaeSwarm/ARA_CompInteractiveProducer/CompRefuelableNutrition.cs
@@ -10,37 +10,32 @@ namespace ArachnaeSwarm
public CompProperties_RefuelableNutrition()
{
compClass = typeof(CompRefuelableNutrition);
+ // 默认启用这些Gizmo,除非在XML中明确设置为false
+ this.targetFuelLevelConfigurable = true;
+ this.showAllowAutoRefuelToggle = true;
}
}
[StaticConstructorOnStartup]
public class CompRefuelableNutrition : CompRefuelable
{
- private static readonly Texture2D FuelIcon = ContentFinder.Get("UI/Icons/ThingCategories/FoodMeals");
-
- // This rate is controlled externally, e.g., by a producer comp. Units: nutrition per day.
public float currentConsumptionRate = 0f;
-
public float NutritionStored => Fuel;
-
public new CompProperties_RefuelableNutrition Props => (CompProperties_RefuelableNutrition)props;
public override void CompTick()
{
- // Call the base tick for things like vacuum logic, but we will handle fuel consumption ourselves.
+ // 调用基类的Tick,让它处理真空等情况。
+ // 基类的燃料消耗逻辑将因为 fuelConsumptionRate 为0而无效。
base.CompTick();
- // External consumption logic
- if (currentConsumptionRate > 0)
+ // 我们自己的动态消耗逻辑
+ if (currentConsumptionRate > 0 && HasFuel)
{
- // Convert per-day rate to per-tick rate and consume
float consumptionPerTick = currentConsumptionRate / 60000f;
ConsumeFuel(consumptionPerTick);
}
}
-
- // Note: The base class's ConsumeFuel is sufficient.
- // public void ConsumeFuel(float amount) { ... }
public new void Refuel(List fuelThings)
{
@@ -48,49 +43,52 @@ namespace ArachnaeSwarm
if (fuelNeeded < 0.001f) return;
float totalNutritionGained = 0;
- List thingsToProcess = new List(fuelThings);
+ var thingsToProcess = new List(fuelThings);
foreach (var thing in thingsToProcess)
{
if (fuelNeeded <= 0) break;
-
float nutritionPerUnit = thing.GetStatValue(StatDefOf.Nutrition);
if (nutritionPerUnit <= 0) continue;
int numToTake = Mathf.CeilToInt(fuelNeeded / nutritionPerUnit);
numToTake = Mathf.Min(numToTake, thing.stackCount);
-
+
float nutritionFromThis = numToTake * nutritionPerUnit;
base.Refuel(nutritionFromThis);
totalNutritionGained += nutritionFromThis;
thing.SplitOff(numToTake).Destroy();
-
fuelNeeded = TargetFuelLevel - Fuel;
}
if (totalNutritionGained > 0 && Props.fuelGizmoLabel != null)
{
- // Removed PawnUtility.ShouldSendNotificationAbout check as it requires a Pawn.
Messages.Message("MessageRefueled".Translate(parent.LabelShort, totalNutritionGained.ToString("0.##"), Props.fuelGizmoLabel), parent, MessageTypeDefOf.PositiveEvent);
}
}
+
public override string CompInspectStringExtra()
{
- // Build the string from scratch to avoid the base class's incorrect time calculation.
string text = Props.FuelLabel + ": " + Fuel.ToStringDecimalIfSmall() + " / " + Props.fuelCapacity.ToStringDecimalIfSmall();
-
- // If we have a custom consumption rate, calculate and display our own time estimate.
+
if (currentConsumptionRate > 0f && HasFuel)
{
int numTicks = (int)(Fuel / (currentConsumptionRate / 60000f));
text += " (" + numTicks.ToStringTicksToPeriod() + ")";
}
-
+ else if (!HasFuel && !Props.outOfFuelMessage.NullOrEmpty())
+ {
+ text += "\n" + Props.outOfFuelMessage;
+ }
+
+ if (Props.targetFuelLevelConfigurable)
+ {
+ text += "\n" + "ConfiguredTargetFuelLevel".Translate(TargetFuelLevel.ToStringDecimalIfSmall());
+ }
+
return text;
}
-
- // Removed CompGetGizmosExtra override because Command_Refuel is a private class in CompRefuelable.
}
}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/CompQueuedPawnSpawner.cs b/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/CompQueuedPawnSpawner.cs
index 078b9e8..ddd0d22 100644
--- a/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/CompQueuedPawnSpawner.cs
+++ b/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/CompQueuedPawnSpawner.cs
@@ -4,11 +4,10 @@ using System.Linq;
using System.Text;
using Verse;
using Verse.AI;
+using UnityEngine;
namespace ArachnaeSwarm
{
-
- // CompProperties: 在XML中配置组件的属性
public class CompProperties_QueuedPawnSpawner : CompProperties
{
public List spawnablePawns;
@@ -22,38 +21,22 @@ namespace ArachnaeSwarm
}
}
- // 主组件类
public class CompQueuedPawnSpawner : ThingComp
{
private List productionOrders = new List();
-
- // 缓存对其他组件的引用以提高性能
+ public QueuedPawnSpawnEntry selectedEntry;
+
private CompRefuelableNutrition _fuelComp;
private CompAffectedByFacilities _facilitiesComp;
public CompProperties_QueuedPawnSpawner Props => (CompProperties_QueuedPawnSpawner)props;
- private CompRefuelableNutrition FuelComp
- {
- get
- {
- if (_fuelComp == null) _fuelComp = parent.GetComp();
- return _fuelComp;
- }
- }
- private CompAffectedByFacilities FacilitiesComp
- {
- get
- {
- if (_facilitiesComp == null) _facilitiesComp = parent.GetComp();
- return _facilitiesComp;
- }
- }
+ private CompRefuelableNutrition FuelComp => _fuelComp ?? (_fuelComp = parent.GetComp());
+ private CompAffectedByFacilities FacilitiesComp => _facilitiesComp ?? (_facilitiesComp = parent.GetComp());
public override void Initialize(CompProperties props)
{
base.Initialize(props);
- // 在初始化时获取一次,避免在每次访问时都调用GetComp
_fuelComp = parent.GetComp();
_facilitiesComp = parent.GetComp();
}
@@ -64,39 +47,37 @@ namespace ArachnaeSwarm
if (FuelComp != null && (!FuelComp.HasFuel || FuelComp.NutritionStored < Props.minNutritionToStart))
{
- yield return new FloatMenuOption("CannotStartProduction".Translate() + ": " + "NoFuel".Translate(), null);
+ yield return new FloatMenuOption("CannotStartProduction".Translate(), null);
yield break;
}
- foreach (QueuedPawnSpawnEntry entry in Props.spawnablePawns)
+ foreach (var entry in Props.spawnablePawns)
{
- if (entry.pawnKind == null) continue;
-
if (entry.requiredResearch != null && !entry.requiredResearch.IsFinished)
{
- string disabledText = "ARA_Incubate".Translate(entry.pawnKind.label) + " (" + "Requires".Translate() + ": " + entry.requiredResearch.label + ")";
- yield return new FloatMenuOption(disabledText, null);
+ yield return new FloatMenuOption("ARA_Incubate".Translate(entry.pawnKind.label) + " (" + "Requires".Translate() + ": " + entry.requiredResearch.label + ")", null);
}
else
{
- string label = "ARA_Incubate".Translate(entry.pawnKind.label);
- if (entry.totalNutritionNeeded > 0)
+ yield return new FloatMenuOption("ARA_Incubate".Translate(entry.pawnKind.label), () =>
{
- label += $" ({"NutritionNeeded".Translate()}: {entry.totalNutritionNeeded.ToString("0.0")})";
- }
- yield return new FloatMenuOption(label, () => AddToQueue(entry, selPawn));
+ this.selectedEntry = entry;
+ Job job = JobMaker.MakeJob(DefDatabase.GetNamed("ARA_AddToQueueJob"), parent);
+ selPawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
+ });
}
}
}
- private void AddToQueue(QueuedPawnSpawnEntry entry, Pawn selPawn)
+ public void AddToQueue()
{
- productionOrders.Add(new QueuedProductionOrder { entry = entry });
- // 给交互的Pawn一个反馈,表示订单已接受
- if (selPawn.jobs?.curJob != null)
+ if (selectedEntry == null)
{
- selPawn.jobs.EndCurrentJob(JobCondition.Succeeded);
+ Log.Error("Tried to add to queue but selectedEntry was null.");
+ return;
}
+ productionOrders.Add(new QueuedProductionOrder { entry = selectedEntry });
+ selectedEntry = null;
}
public override void CompTick()
@@ -106,7 +87,6 @@ namespace ArachnaeSwarm
var producingOrders = productionOrders.Where(o => o.spawnUntilTick > 0).ToList();
bool hasFuel = FuelComp?.HasFuel ?? true;
- // 处理无燃料情况:仅暂停生产
if (!hasFuel && FuelComp != null && producingOrders.Any())
{
foreach (var order in producingOrders)
@@ -115,11 +95,9 @@ namespace ArachnaeSwarm
}
}
- // 动态计算总燃料消耗速率
if (FuelComp != null)
{
float totalConsumptionRatePerDay = 0f;
- // 只计算有燃料时正在生产的订单
if(hasFuel)
{
foreach (var order in producingOrders)
@@ -133,7 +111,6 @@ namespace ArachnaeSwarm
FuelComp.currentConsumptionRate = totalConsumptionRatePerDay;
}
- // 检查并完成订单
productionOrders.RemoveAll(order =>
{
if (order.spawnUntilTick > 0 && Find.TickManager.TicksGame >= order.spawnUntilTick)
@@ -145,11 +122,10 @@ namespace ArachnaeSwarm
return false;
});
- // 检查并启动新订单
int currentlyProducingCount = productionOrders.Count(o => o.spawnUntilTick > 0);
if (currentlyProducingCount < Props.productionQueueLimit)
{
- QueuedProductionOrder waitingOrder = productionOrders.FirstOrDefault(o => o.spawnUntilTick == -1);
+ var waitingOrder = productionOrders.FirstOrDefault(o => o.spawnUntilTick == -1);
if (waitingOrder != null)
{
float speedFactor = 1f;
@@ -166,8 +142,7 @@ namespace ArachnaeSwarm
public override string CompInspectStringExtra()
{
StringBuilder sb = new StringBuilder();
- if (FuelComp != null) sb.AppendLine(FuelComp.CompInspectStringExtra());
-
+
int producingCount = productionOrders.Count(o => o.spawnUntilTick > 0);
int queuedCount = productionOrders.Count - producingCount;
@@ -198,6 +173,31 @@ namespace ArachnaeSwarm
{
base.PostExposeData();
Scribe_Collections.Look(ref productionOrders, "productionOrders", LookMode.Deep, new object[0]);
+ Scribe_Deep.Look(ref selectedEntry, "selectedEntry");
+ }
+
+ public override IEnumerable CompGetGizmosExtra()
+ {
+ foreach (Gizmo gizmo in base.CompGetGizmosExtra())
+ {
+ yield return gizmo;
+ }
+
+ if (productionOrders.Any())
+ {
+ var lastOrder = productionOrders.Last();
+
+ yield return new Command_Action
+ {
+ defaultLabel = "CommandCancelProduction".Translate() + ": " + lastOrder.entry.pawnKind.LabelCap,
+ defaultDesc = "CommandCancelProductionDesc".Translate(),
+ icon = ContentFinder.Get("UI/Designators/Cancel"),
+ action = () =>
+ {
+ productionOrders.Remove(lastOrder);
+ }
+ };
+ }
}
}
}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/CompSpawnPawnFromList.cs b/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/CompSpawnPawnFromList.cs
index 94450bb..5ffb744 100644
--- a/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/CompSpawnPawnFromList.cs
+++ b/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/CompSpawnPawnFromList.cs
@@ -4,6 +4,7 @@ using Verse;
using RimWorld;
using Verse.AI;
using Verse.AI.Group;
+using UnityEngine; // Add this for ContentFinder
namespace ArachnaeSwarm
{
@@ -13,7 +14,8 @@ namespace ArachnaeSwarm
private int spawnUntilTick = -1;
private PawnKindDef spawningPawnKind;
- private PawnSpawnEntry selectedEntry;
+ // This component uses its own PawnSpawnEntry, separate from the Queued spawner
+ private PawnSpawnEntry selectedEntry;
public bool IsHatching => spawnUntilTick > 0;
public override IEnumerable CompFloatMenuOptions(Pawn selPawn)
@@ -34,20 +36,17 @@ namespace ArachnaeSwarm
{
if (entry.pawnKind == null) continue;
- // 检查科技需求
if (entry.requiredResearch != null && !entry.requiredResearch.IsFinished)
{
- // 科技未完成,显示灰色不可点击选项
string disabledText = "ARA_Incubate".Translate(entry.pawnKind.label) + " (" + "Requires".Translate() + ": " + entry.requiredResearch.label + ")";
yield return new FloatMenuOption(disabledText, null);
}
else
{
- // 科技已完成或无需求,显示正常选项
yield return new FloatMenuOption("ARA_Incubate".Translate(entry.pawnKind.label), () =>
{
Job job = JobMaker.MakeJob(DefDatabase.GetNamed("ARA_IncubateJob"), parent);
- this.selectedEntry = entry; // 保存整个入口信息
+ this.selectedEntry = entry;
selPawn.jobs.TryTakeOrderedJob(job);
});
}
@@ -55,7 +54,6 @@ namespace ArachnaeSwarm
}
}
-
public void StartIncubation()
{
if (this.selectedEntry == null) return;
@@ -73,8 +71,6 @@ namespace ArachnaeSwarm
}
}
-
-
private void SpawnPawn(PawnKindDef pawnKind)
{
try
@@ -138,7 +134,6 @@ namespace ArachnaeSwarm
}
}
-
public override string CompInspectStringExtra()
{
if (spawnUntilTick > 0)
@@ -161,7 +156,31 @@ namespace ArachnaeSwarm
base.PostExposeData();
Scribe_Values.Look(ref spawnUntilTick, "spawnUntilTick", -1);
Scribe_Defs.Look(ref spawningPawnKind, "spawningPawnKind");
- // selectedEntry is transient and does not need to be saved.
+ // selectedEntry is not saved because the interaction is a one-off action.
+ }
+
+ // Moved to the correct position, at the class level.
+ public override IEnumerable CompGetGizmosExtra()
+ {
+ foreach (var g in base.CompGetGizmosExtra())
+ {
+ yield return g;
+ }
+
+ if (IsHatching)
+ {
+ yield return new Command_Action
+ {
+ defaultLabel = "CommandCancelProduction".Translate(),
+ defaultDesc = "CommandCancelProductionDesc".Translate(),
+ icon = ContentFinder.Get("UI/Designators/Cancel"),
+ action = () =>
+ {
+ spawnUntilTick = -1;
+ spawningPawnKind = null;
+ }
+ };
+ }
}
}
}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/JobDriver_AddToQueue.cs b/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/JobDriver_AddToQueue.cs
new file mode 100644
index 0000000..a606f7e
--- /dev/null
+++ b/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/JobDriver_AddToQueue.cs
@@ -0,0 +1,35 @@
+using System.Collections.Generic;
+using Verse;
+using Verse.AI;
+
+namespace ArachnaeSwarm
+{
+ public class JobDriver_AddToQueue : JobDriver
+ {
+ private const TargetIndex IncubatorInd = TargetIndex.A;
+
+ public override bool TryMakePreToilReservations(bool errorOnFailed)
+ {
+ return pawn.Reserve(job.GetTarget(IncubatorInd), job, 1, -1, null, errorOnFailed);
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ this.FailOnDespawnedNullOrForbidden(IncubatorInd);
+
+ yield return Toils_Goto.GotoThing(IncubatorInd, PathEndMode.Touch);
+
+ Toil addToQueue = new Toil();
+ addToQueue.initAction = () =>
+ {
+ CompQueuedPawnSpawner comp = job.GetTarget(IncubatorInd).Thing.TryGetComp();
+ if (comp != null)
+ {
+ comp.AddToQueue();
+ }
+ };
+ addToQueue.defaultCompleteMode = ToilCompleteMode.Instant;
+ yield return addToQueue;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
index aa94edc..8947351 100644
--- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
+++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
@@ -73,6 +73,7 @@
+