暂存物品池
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
// Data contract for a single production order in the queue
|
||||
public class QueuedProcessOrder : IExposable
|
||||
{
|
||||
public ProcessDef process; // Using the existing ProcessDef
|
||||
public int productionUntilTick = -1;
|
||||
|
||||
// Quality-related fields
|
||||
public int ticksUnderOptimalConditions;
|
||||
public float temperaturePenaltyPercent;
|
||||
|
||||
public void ExposeData()
|
||||
{
|
||||
Scribe_Deep.Look(ref process, "process");
|
||||
Scribe_Values.Look(ref productionUntilTick, "productionUntilTick", -1);
|
||||
Scribe_Values.Look(ref ticksUnderOptimalConditions, "ticksUnderOptimalConditions", 0);
|
||||
Scribe_Values.Look(ref temperaturePenaltyPercent, "temperaturePenaltyPercent", 0f);
|
||||
}
|
||||
}
|
||||
|
||||
// Properties for the new queued producer component
|
||||
public class CompProperties_QueuedInteractiveProducer : CompProperties
|
||||
{
|
||||
public List<ProcessDef> processes;
|
||||
public List<PawnKindDef> whitelist;
|
||||
public int productionQueueLimit = 1;
|
||||
public float minNutritionToStart = 0.1f;
|
||||
|
||||
// Quality-related properties from CompInteractiveProducer
|
||||
public float minSafeTemperature = 7f;
|
||||
public float maxSafeTemperature = 32f;
|
||||
public float penaltyPerDegreePerTick = 0.00001f;
|
||||
public List<QualityThreshold> qualityThresholds;
|
||||
public IntRange spawnCount = new IntRange(1, 1);
|
||||
|
||||
public CompProperties_QueuedInteractiveProducer()
|
||||
{
|
||||
compClass = typeof(CompQueuedInteractiveProducer);
|
||||
}
|
||||
}
|
||||
|
||||
// The main component logic
|
||||
public class CompQueuedInteractiveProducer : ThingComp
|
||||
{
|
||||
private List<QueuedProcessOrder> productionOrders = new List<QueuedProcessOrder>();
|
||||
public ProcessDef selectedProcess; // For passing to the JobDriver
|
||||
|
||||
private CompRefuelableNutrition _fuelComp;
|
||||
private CompAffectedByFacilities _facilitiesComp;
|
||||
|
||||
public CompProperties_QueuedInteractiveProducer Props => (CompProperties_QueuedInteractiveProducer)props;
|
||||
private CompRefuelableNutrition FuelComp => _fuelComp ?? (_fuelComp = parent.GetComp<CompRefuelableNutrition>());
|
||||
private CompAffectedByFacilities FacilitiesComp => _facilitiesComp ?? (_facilitiesComp = parent.GetComp<CompAffectedByFacilities>());
|
||||
|
||||
public override void Initialize(CompProperties props)
|
||||
{
|
||||
base.Initialize(props);
|
||||
_fuelComp = parent.GetComp<CompRefuelableNutrition>();
|
||||
_facilitiesComp = parent.GetComp<CompAffectedByFacilities>();
|
||||
}
|
||||
|
||||
public override IEnumerable<FloatMenuOption> CompFloatMenuOptions(Pawn selPawn)
|
||||
{
|
||||
if (Props.whitelist == null || !Props.whitelist.Contains(selPawn.kindDef)) yield break;
|
||||
if (FuelComp != null && (!FuelComp.HasFuel || FuelComp.NutritionStored < Props.minNutritionToStart))
|
||||
{
|
||||
yield return new FloatMenuOption("CannotStartProduction".Translate(), null);
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (var process in Props.processes)
|
||||
{
|
||||
if (process.requiredResearch != null && !process.requiredResearch.IsFinished)
|
||||
{
|
||||
yield return new FloatMenuOption("StartProduction".Translate(process.thingDef.label) + " (" + "Requires".Translate() + ": " + process.requiredResearch.label + ")", null);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new FloatMenuOption("StartProduction".Translate(process.thingDef.label), () =>
|
||||
{
|
||||
this.selectedProcess = process;
|
||||
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("ARA_AddProcessToQueueJob"), parent);
|
||||
selPawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddToQueue()
|
||||
{
|
||||
if (selectedProcess == null) return;
|
||||
productionOrders.Add(new QueuedProcessOrder { process = selectedProcess });
|
||||
selectedProcess = null;
|
||||
}
|
||||
|
||||
public override void CompTick()
|
||||
{
|
||||
base.CompTick();
|
||||
var producingOrders = productionOrders.Where(o => o.productionUntilTick > 0).ToList();
|
||||
bool hasFuel = FuelComp?.HasFuel ?? true;
|
||||
float ambientTemperature = parent.AmbientTemperature;
|
||||
bool isTempSafe = ambientTemperature >= Props.minSafeTemperature && ambientTemperature <= Props.maxSafeTemperature;
|
||||
|
||||
// Update progress and penalties for active orders
|
||||
foreach(var order in producingOrders)
|
||||
{
|
||||
if(hasFuel && isTempSafe)
|
||||
{
|
||||
order.ticksUnderOptimalConditions++;
|
||||
}
|
||||
if (!isTempSafe)
|
||||
{
|
||||
float tempDelta = (ambientTemperature > Props.maxSafeTemperature) ? ambientTemperature - Props.maxSafeTemperature : Props.minSafeTemperature - ambientTemperature;
|
||||
order.temperaturePenaltyPercent = Mathf.Min(1f, order.temperaturePenaltyPercent + tempDelta * Props.penaltyPerDegreePerTick);
|
||||
}
|
||||
if (!hasFuel)
|
||||
{
|
||||
order.productionUntilTick++; // Pause production
|
||||
}
|
||||
}
|
||||
|
||||
// Update fuel consumption
|
||||
if (FuelComp != null)
|
||||
{
|
||||
float totalConsumptionRatePerDay = 0f;
|
||||
if(hasFuel)
|
||||
{
|
||||
foreach (var order in producingOrders)
|
||||
{
|
||||
if (order.process.totalNutritionNeeded > 0 && order.process.productionTicks > 0)
|
||||
{
|
||||
totalConsumptionRatePerDay += (order.process.totalNutritionNeeded / order.process.productionTicks) * 60000f;
|
||||
}
|
||||
}
|
||||
}
|
||||
FuelComp.currentConsumptionRate = totalConsumptionRatePerDay;
|
||||
}
|
||||
|
||||
// Finish completed orders
|
||||
productionOrders.RemoveAll(order =>
|
||||
{
|
||||
if (order.productionUntilTick > 0 && Find.TickManager.TicksGame >= order.productionUntilTick)
|
||||
{
|
||||
FinishProduction(order);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Start new orders
|
||||
int currentlyProducingCount = productionOrders.Count(o => o.productionUntilTick > 0);
|
||||
if (currentlyProducingCount < Props.productionQueueLimit)
|
||||
{
|
||||
var waitingOrder = productionOrders.FirstOrDefault(o => o.productionUntilTick == -1);
|
||||
if (waitingOrder != null)
|
||||
{
|
||||
float speedFactor = 1f + (FacilitiesComp?.GetStatOffset(StatDef.Named("ARA_IncubationSpeedFactor")) ?? 0f);
|
||||
int modifiedDelay = (int)(waitingOrder.process.productionTicks / speedFactor);
|
||||
waitingOrder.productionUntilTick = Find.TickManager.TicksGame + modifiedDelay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FinishProduction(QueuedProcessOrder order)
|
||||
{
|
||||
float progress = (float)order.ticksUnderOptimalConditions / order.process.productionTicks;
|
||||
float finalQualityPercent = Mathf.Clamp01(progress - order.temperaturePenaltyPercent);
|
||||
QualityCategory finalQuality = QualityCategory.Awful;
|
||||
if (!Props.qualityThresholds.NullOrEmpty())
|
||||
{
|
||||
foreach (var threshold in Props.qualityThresholds.OrderByDescending(q => q.threshold))
|
||||
{
|
||||
if (finalQualityPercent >= threshold.threshold) { finalQuality = threshold.quality; break; }
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < Props.spawnCount.RandomInRange; i++)
|
||||
{
|
||||
Thing product = ThingMaker.MakeThing(order.process.thingDef);
|
||||
product.TryGetComp<CompQuality>()?.SetQuality(finalQuality, ArtGenerationContext.Colony);
|
||||
GenPlace.TryPlaceThing(product, parent.Position, parent.Map, ThingPlaceMode.Near);
|
||||
}
|
||||
}
|
||||
|
||||
public override string CompInspectStringExtra()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int producingCount = productionOrders.Count(o => o.productionUntilTick > 0);
|
||||
int queuedCount = productionOrders.Count - producingCount;
|
||||
|
||||
sb.AppendLine($"生产槽位: {producingCount} / {Props.productionQueueLimit}");
|
||||
if (queuedCount > 0) sb.AppendLine($"等待队列: {queuedCount}");
|
||||
|
||||
if (FacilitiesComp != null)
|
||||
{
|
||||
float speedFactor = 1f + FacilitiesComp.GetStatOffset(StatDef.Named("ARA_IncubationSpeedFactor"));
|
||||
if(speedFactor != 1f) sb.AppendLine($"生产速度: {speedFactor.ToStringPercent()}");
|
||||
}
|
||||
|
||||
var producingNow = productionOrders.Where(o => o.productionUntilTick > 0).OrderBy(o => o.productionUntilTick);
|
||||
if (producingNow.Any())
|
||||
{
|
||||
sb.AppendLine("正在生产:");
|
||||
foreach (var order in producingNow)
|
||||
{
|
||||
int remainingTicks = order.productionUntilTick - Find.TickManager.TicksGame;
|
||||
sb.AppendLine($" - {order.process.thingDef.LabelCap}: {remainingTicks.ToStringTicksToPeriod(true, false, true, true)}");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
|
||||
public override void PostExposeData()
|
||||
{
|
||||
base.PostExposeData();
|
||||
Scribe_Collections.Look(ref productionOrders, "productionOrders", LookMode.Deep);
|
||||
Scribe_Deep.Look(ref selectedProcess, "selectedProcess");
|
||||
}
|
||||
|
||||
public override IEnumerable<Gizmo> 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.process.thingDef.LabelCap,
|
||||
defaultDesc = "CommandCancelProductionDesc".Translate(),
|
||||
icon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel"),
|
||||
action = () =>
|
||||
{
|
||||
productionOrders.Remove(lastOrder);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class JobDriver_AddProcessToQueue : JobDriver
|
||||
{
|
||||
private const TargetIndex BuildingInd = TargetIndex.A;
|
||||
|
||||
protected Building Building => (Building)job.GetTarget(BuildingInd).Thing;
|
||||
|
||||
public override bool TryMakePreToilReservations(bool errorOnFailed)
|
||||
{
|
||||
return pawn.Reserve(Building, job, 1, -1, null, errorOnFailed);
|
||||
}
|
||||
|
||||
protected override IEnumerable<Toil> MakeNewToils()
|
||||
{
|
||||
this.FailOnDespawnedNullOrForbidden(BuildingInd);
|
||||
|
||||
yield return Toils_Goto.GotoThing(BuildingInd, PathEndMode.InteractionCell);
|
||||
|
||||
Toil work = ToilMaker.MakeToil("MakeNewToils");
|
||||
work.initAction = delegate
|
||||
{
|
||||
// Call the new component's method
|
||||
Building.GetComp<CompQueuedInteractiveProducer>().AddToQueue();
|
||||
};
|
||||
work.defaultCompleteMode = ToilCompleteMode.Instant;
|
||||
yield return work;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,6 +118,8 @@
|
||||
<Compile Include="ARA_CompInteractiveProducer\CompRefuelableNutrition.cs" />
|
||||
<Compile Include="ARA_CompInteractiveProducer\DataContracts.cs" />
|
||||
<Compile Include="ARA_CompInteractiveProducer\CompTemperatureRuinableDamage.cs" />
|
||||
<Compile Include="ARA_CompInteractiveProducer\CompQueuedInteractiveProducer.cs" />
|
||||
<Compile Include="ARA_CompInteractiveProducer\JobDriver_AddProcessToQueue.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ARA_HuggingFace\Hediff_Possession.cs" />
|
||||
|
||||
Reference in New Issue
Block a user