This commit is contained in:
2025-09-17 12:54:07 +08:00
parent 9e992fc08b
commit c77def63c2
123 changed files with 130 additions and 130 deletions

View File

@@ -0,0 +1,273 @@
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 (QualityCategory quality, float baseScore, float penalty) GetEstimatedQualityDetails(QueuedProcessOrder order)
{
if (order == null || Props.qualityThresholds.NullOrEmpty())
{
return (QualityCategory.Normal, 0f, 0f);
}
float progress = (order.process.productionTicks > 0) ? (float)order.ticksUnderOptimalConditions / order.process.productionTicks : 1f;
float finalQualityPercent = Mathf.Clamp01(progress - order.temperaturePenaltyPercent);
QualityCategory finalQuality = QualityCategory.Awful;
foreach (var threshold in Props.qualityThresholds.OrderByDescending(q => q.threshold))
{
if (finalQualityPercent >= threshold.threshold)
{
finalQuality = threshold.quality;
break;
}
}
return (finalQuality, progress, order.temperaturePenaltyPercent);
}
private void FinishProduction(QueuedProcessOrder order)
{
var qualityDetails = GetEstimatedQualityDetails(order);
QualityCategory finalQuality = qualityDetails.quality;
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;
var qualityDetails = GetEstimatedQualityDetails(order);
sb.AppendLine($" - {order.process.thingDef.LabelCap}: {remainingTicks.ToStringTicksToPeriod(true, false, true, true)} (品质: {qualityDetails.quality.GetLabel()})");
}
}
// 添加温度信息
string tempStr = "CurrentTemperature".Translate(parent.AmbientTemperature.ToStringTemperature("F0"));
tempStr += $" ({"SafeTemperatureRange".Translate()}: {Props.minSafeTemperature.ToStringTemperature("F0")} ~ {Props.maxSafeTemperature.ToStringTemperature("F0")})";
sb.AppendLine(tempStr);
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);
}
};
}
}
}
}