diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index b5bdba4..87431c1 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/Stats/ARA_Stats.xml b/1.6/1.6/Defs/Stats/ARA_Stats.xml new file mode 100644 index 0000000..ffd3c86 --- /dev/null +++ b/1.6/1.6/Defs/Stats/ARA_Stats.xml @@ -0,0 +1,15 @@ + + + + + ARA_IncubationSpeedFactor + + 应用于孵化池的孵化速度乘数。 + Building + 1 + 0.001 + PercentZero + false + + + \ 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 new file mode 100644 index 0000000..10151aa --- /dev/null +++ b/1.6/1.6/Defs/Thing_building/ARA_BioforgeIncubator.xml @@ -0,0 +1,84 @@ + + + + + + ARA_IncubationAccelerator + + 一个辅助性的生物机械装置,当放置在大型孵化池旁边时,可以加速其内部的孵化过程。 + + Things/Building/Misc/ToolCabinet + Graphic_Multi + + (1,1) + + +
  • + + + 0.10 + +
  • +
    +
    + + + + + ARA_BioforgeIncubator + + 一个大型的、需要消耗大量营养物质的孵化设施,可以同时孵化多个单位,并能通过链接外部设备来提高效率。 + + Things/Building/Production/BiofuelRefinery + Graphic_Multi + (3,3) + + (3,3) + Normal + + + +
  • + 5 + 0.5 + +
  • ARA_ArachnaeQueen
  • + + +
  • + ArachnaeNode_Race_Drone + 60000 + 2.0 +
  • +
  • + ArachnaeNode_Race_Warrior + 90000 + 5.0 + ARA_AdvancedBiology +
  • +
    + + + +
  • + 20.0 + + +
  • FoodMeals
  • +
  • AnimalProductRaw
  • + + + 营养 + + + +
  • + +
  • ARA_IncubationAccelerator
  • + + + +
    +
    + +
    \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/CompQueuedPawnSpawner.cs b/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/CompQueuedPawnSpawner.cs new file mode 100644 index 0000000..078b9e8 --- /dev/null +++ b/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/CompQueuedPawnSpawner.cs @@ -0,0 +1,203 @@ +using RimWorld; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Verse; +using Verse.AI; + +namespace ArachnaeSwarm +{ + + // CompProperties: 在XML中配置组件的属性 + public class CompProperties_QueuedPawnSpawner : CompProperties + { + public List spawnablePawns; + public List whitelist; + public int productionQueueLimit = 1; + public float minNutritionToStart = 0.1f; + + public CompProperties_QueuedPawnSpawner() + { + compClass = typeof(CompQueuedPawnSpawner); + } + } + + // 主组件类 + public class CompQueuedPawnSpawner : ThingComp + { + private List productionOrders = new List(); + + // 缓存对其他组件的引用以提高性能 + 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; + } + } + + public override void Initialize(CompProperties props) + { + base.Initialize(props); + // 在初始化时获取一次,避免在每次访问时都调用GetComp + _fuelComp = parent.GetComp(); + _facilitiesComp = parent.GetComp(); + } + + public override IEnumerable 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() + ": " + "NoFuel".Translate(), null); + yield break; + } + + foreach (QueuedPawnSpawnEntry 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); + } + else + { + string label = "ARA_Incubate".Translate(entry.pawnKind.label); + if (entry.totalNutritionNeeded > 0) + { + label += $" ({"NutritionNeeded".Translate()}: {entry.totalNutritionNeeded.ToString("0.0")})"; + } + yield return new FloatMenuOption(label, () => AddToQueue(entry, selPawn)); + } + } + } + + private void AddToQueue(QueuedPawnSpawnEntry entry, Pawn selPawn) + { + productionOrders.Add(new QueuedProductionOrder { entry = entry }); + // 给交互的Pawn一个反馈,表示订单已接受 + if (selPawn.jobs?.curJob != null) + { + selPawn.jobs.EndCurrentJob(JobCondition.Succeeded); + } + } + + public override void CompTick() + { + base.CompTick(); + + 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) + { + order.spawnUntilTick++; + } + } + + // 动态计算总燃料消耗速率 + if (FuelComp != null) + { + float totalConsumptionRatePerDay = 0f; + // 只计算有燃料时正在生产的订单 + if(hasFuel) + { + foreach (var order in producingOrders) + { + if (order.entry.totalNutritionNeeded > 0 && order.entry.delayTicks > 0) + { + totalConsumptionRatePerDay += (order.entry.totalNutritionNeeded / order.entry.delayTicks) * 60000f; + } + } + } + FuelComp.currentConsumptionRate = totalConsumptionRatePerDay; + } + + // 检查并完成订单 + productionOrders.RemoveAll(order => + { + if (order.spawnUntilTick > 0 && Find.TickManager.TicksGame >= order.spawnUntilTick) + { + Pawn pawn = PawnGenerator.GeneratePawn(new PawnGenerationRequest(order.entry.pawnKind, parent.Faction)); + if (pawn != null) GenPlace.TryPlaceThing(pawn, parent.Position, parent.Map, ThingPlaceMode.Near); + return true; + } + return false; + }); + + // 检查并启动新订单 + int currentlyProducingCount = productionOrders.Count(o => o.spawnUntilTick > 0); + if (currentlyProducingCount < Props.productionQueueLimit) + { + QueuedProductionOrder waitingOrder = productionOrders.FirstOrDefault(o => o.spawnUntilTick == -1); + if (waitingOrder != null) + { + float speedFactor = 1f; + if (FacilitiesComp != null) + { + speedFactor += FacilitiesComp.GetStatOffset(StatDef.Named("ARA_IncubationSpeedFactor")); + } + int modifiedDelay = (int)(waitingOrder.entry.delayTicks / speedFactor); + waitingOrder.spawnUntilTick = Find.TickManager.TicksGame + modifiedDelay; + } + } + } + + 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; + + 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.spawnUntilTick > 0).OrderBy(o => o.spawnUntilTick); + if (producingNow.Any()) + { + sb.AppendLine("正在孵化:"); + foreach (var order in producingNow) + { + int remainingTicks = order.spawnUntilTick - Find.TickManager.TicksGame; + sb.AppendLine($" - {order.entry.pawnKind.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, new object[0]); + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/ProductionContracts.cs b/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/ProductionContracts.cs new file mode 100644 index 0000000..b17430d --- /dev/null +++ b/Source/ArachnaeSwarm/ARA_SpawnPawnFromList/ProductionContracts.cs @@ -0,0 +1,49 @@ +using RimWorld; +using Verse; + +namespace ArachnaeSwarm +{ + // 重命名以避免冲突 + public class QueuedPawnSpawnEntry : IExposable + { + public PawnKindDef pawnKind; + public int delayTicks = 0; + public ResearchProjectDef requiredResearch; + public float totalNutritionNeeded = 0f; + + public void ExposeData() + { + Scribe_Defs.Look(ref pawnKind, "pawnKind"); + Scribe_Values.Look(ref delayTicks, "delayTicks", 0); + Scribe_Defs.Look(ref requiredResearch, "requiredResearch"); + Scribe_Values.Look(ref totalNutritionNeeded, "totalNutritionNeeded", 0f); + } + } + + // 重命名以避免冲突 + public class QueuedProductionOrder : IExposable + { + public QueuedPawnSpawnEntry entry; + public int spawnUntilTick = -1; + + public void ExposeData() + { + if (Scribe.mode == LoadSaveMode.Saving || Scribe.mode == LoadSaveMode.LoadingVars) + { + if (Scribe.EnterNode("entry")) + { + try + { + if (entry == null) entry = new QueuedPawnSpawnEntry(); + entry.ExposeData(); + } + finally + { + Scribe.ExitNode(); + } + } + } + Scribe_Values.Look(ref spawnUntilTick, "spawnUntilTick", -1); + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 78f4b07..aa94edc 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -71,6 +71,8 @@ + +