diff --git a/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml b/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml
index 49d69a4..26847d0 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml
@@ -579,13 +579,13 @@
ARA_Technology_4NPT
-
-
+
+
3
1.0
- ArachnaeNode_Race_WeaponSmith
+ ArachnaeNode_Race_WeaponSmith
@@ -705,8 +705,8 @@
-
-
+
+
5
0.5
diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompQueuedInteractiveProducerWithFlux.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompQueuedInteractiveProducerWithFlux.cs
new file mode 100644
index 0000000..4634d08
--- /dev/null
+++ b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompQueuedInteractiveProducerWithFlux.cs
@@ -0,0 +1,196 @@
+using RimWorld;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ public class CompProperties_QueuedInteractiveProducerWithFlux : CompProperties_QueuedInteractiveProducer
+ {
+ public CompProperties_QueuedInteractiveProducerWithFlux()
+ {
+ compClass = typeof(CompQueuedInteractiveProducerWithFlux);
+ }
+ }
+
+ public class CompQueuedInteractiveProducerWithFlux : CompQueuedInteractiveProducer, IFluxController
+ {
+ // === 通量系统字段 ===
+ private float neutronFlux = 0.5f;
+ private FluxMode fluxMode = FluxMode.Balance;
+
+ // === 接口实现 ===
+ public float NeutronFlux => neutronFlux;
+ public float RawFlux => neutronFlux;
+ public FluxMode CurrentFluxMode => fluxMode;
+ public float FluxEfficiency => IFluxController.GetEfficiency(neutronFlux);
+ public bool IsAutoMode => fluxMode != FluxMode.Manual;
+ public bool IsIncubating => IsAnyOrderActive;
+ public bool IsDormant => neutronFlux < 0.05f;
+
+ public void SetNeutronFlux(float value) => neutronFlux = Mathf.Clamp01(value);
+ public void CycleFluxMode() => fluxMode = (FluxMode)(((int)fluxMode + 1) % 4);
+
+ public string GetModeName() => fluxMode switch {
+ FluxMode.Manual => "手动",
+ FluxMode.Quality => "品质",
+ FluxMode.Balance => "平衡",
+ FluxMode.Speed => "速度",
+ _ => "?"
+ };
+
+ public string GetModeShort() => fluxMode switch {
+ FluxMode.Manual => "M",
+ FluxMode.Quality => "Q",
+ FluxMode.Balance => "B",
+ FluxMode.Speed => "S",
+ _ => "?"
+ };
+
+ private bool IsAnyOrderActive => productionOrders.Any(o => o.productionUntilTick > 0);
+
+ // 覆盖 Tick 逻辑
+ public override void CompTick()
+ {
+ float hasFuelVal = FuelComp?.Fuel ?? 10f;
+ bool hasFuel = hasFuelVal > 0.01f;
+
+ // 自动模式
+ if (IsAutoMode && parent.IsHashIntervalTick(250) && IsAnyOrderActive)
+ {
+ CalculateAutoFlux();
+ }
+
+ if (IsAnyOrderActive)
+ {
+ // 消耗燃料
+ if (FuelComp != null && neutronFlux > 0.01f)
+ {
+ float fuelPerTick = (80f * FluxEfficiency) / 60000f; // 物品孵化池消耗略高
+ FuelComp.ConsumeFuel(fuelPerTick);
+ }
+
+ if (hasFuel)
+ {
+ float ambientTemperature = parent.AmbientTemperature;
+ bool isTempSafe = ambientTemperature >= Props.minSafeTemperature && ambientTemperature <= Props.maxSafeTemperature;
+
+ // 计算通量速度倍率:100%活性时5倍速度
+ float speedFactor = 1f + (FacilitiesComp?.GetStatOffset(StatDef.Named("ARA_IncubationSpeedFactor")) ?? 0f);
+ float fluxSpeed = speedFactor * FluxEfficiency * 5f;
+
+ foreach (var order in productionOrders.Where(o => o.productionUntilTick > 0))
+ {
+ // 1. 进度推进(受通量倍率影响)
+ // 由于基类逻辑是每 Tick 递减,我们这里手动控制递减量
+ float progressStep = fluxSpeed;
+ int ticksToSubtract = Mathf.FloorToInt(progressStep);
+ if (Rand.Value < (progressStep - ticksToSubtract)) ticksToSubtract++;
+ order.productionUntilTick = Mathf.Max(0, order.productionUntilTick - ticksToSubtract);
+
+ // 2. 品质累积(基础:每 Tick 增加 1)
+ // 注意:如果 fluxSpeed > 1,生产变快,但品质累积速度不变,
+ // 这样总的 ticksUnderOptimalConditions 就会减少,从而降低品质分数。
+ // 这完美地实现了“速度越快,品质越低”的平衡逻辑。
+ if (!IsDormant)
+ {
+ if (isTempSafe)
+ {
+ order.ticksUnderOptimalConditions++;
+ }
+ else
+ {
+ float tempDelta = (ambientTemperature > Props.maxSafeTemperature) ? ambientTemperature - Props.maxSafeTemperature : Props.minSafeTemperature - ambientTemperature;
+ order.temperaturePenaltyPercent = Mathf.Min(1f, order.temperaturePenaltyPercent + tempDelta * Props.penaltyPerDegreePerTick);
+ }
+ }
+ else
+ {
+ // 休眠状态下品质会缓慢衰减
+ order.ticksUnderOptimalConditions = Mathf.Max(0, order.ticksUnderOptimalConditions - 2);
+ }
+ }
+ }
+ }
+
+ // 处理订单完成和消耗率更新(UI用)
+ UpdateLogic();
+ }
+
+ private void UpdateLogic()
+ {
+ // 完成订单
+ productionOrders.RemoveAll(order =>
+ {
+ if (order.productionUntilTick == 0)
+ {
+ FinishProduction(order);
+ return true;
+ }
+ return false;
+ });
+
+ // 启动新订单
+ int currentlyProducingCount = productionOrders.Count(o => o.productionUntilTick > 0);
+ if (currentlyProducingCount < Props.productionQueueLimit)
+ {
+ var waitingOrder = productionOrders.FirstOrDefault(o => o.productionUntilTick == -1);
+ if (waitingOrder != null)
+ {
+ // 初始 Tick 数,会被后续 Tick 动态加速
+ waitingOrder.productionUntilTick = waitingOrder.process.productionTicks;
+ }
+ }
+
+ // 更新燃料消耗率显示
+ if (FuelComp != null)
+ {
+ float totalConsumption = 0f;
+ if (IsAnyOrderActive && NeutronFlux > 0.01f)
+ {
+ totalConsumption = currentlyProducingCount * 80f * FluxEfficiency;
+ }
+ FuelComp.currentConsumptionRate = totalConsumption;
+ }
+ }
+
+ private void CalculateAutoFlux()
+ {
+ if (fluxMode == FluxMode.Manual) return;
+
+ float targetFlux = 0.5f;
+
+ // 简单逻辑:平衡模式趋向于 0.5;品质模式倾向于 0.1;速度模式倾向于 1.0
+ switch (fluxMode)
+ {
+ case FluxMode.Speed: targetFlux = 1.0f; break;
+ case FluxMode.Quality: targetFlux = 0.1f; break;
+ case FluxMode.Balance:
+ default: targetFlux = 0.5f; break;
+ }
+
+ // 资源保护
+ if (FuelComp != null && FuelComp.Fuel < 50f) targetFlux = Mathf.Min(targetFlux, 0.2f);
+
+ neutronFlux = Mathf.Lerp(neutronFlux, targetFlux, 0.05f);
+ }
+
+ public override IEnumerable CompGetGizmosExtra()
+ {
+ foreach (var g in base.CompGetGizmosExtra()) yield return g;
+ if (parent.Faction == Faction.OfPlayer)
+ {
+ yield return new Gizmo_NeutronFlux(this);
+ }
+ }
+
+ public override void PostExposeData()
+ {
+ base.PostExposeData();
+ Scribe_Values.Look(ref neutronFlux, "neutronFlux", 0.5f);
+ Scribe_Values.Look(ref fluxMode, "fluxMode", FluxMode.Balance);
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_SpawnPawnFromList/CompQueuedPawnSpawnerWithFlux.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_SpawnPawnFromList/CompQueuedPawnSpawnerWithFlux.cs
new file mode 100644
index 0000000..4e3dcd9
--- /dev/null
+++ b/Source/ArachnaeSwarm/Building_Comps/ARA_SpawnPawnFromList/CompQueuedPawnSpawnerWithFlux.cs
@@ -0,0 +1,211 @@
+using RimWorld;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ public class CompProperties_QueuedPawnSpawnerWithFlux : CompProperties_QueuedPawnSpawner
+ {
+ public CompProperties_QueuedPawnSpawnerWithFlux()
+ {
+ compClass = typeof(CompQueuedPawnSpawnerWithFlux);
+ }
+ }
+
+ public class CompQueuedPawnSpawnerWithFlux : CompQueuedPawnSpawner, IFluxController
+ {
+ // === 通量系统字段 ===
+ private float neutronFlux = 0.5f;
+ private FluxMode fluxMode = FluxMode.Balance;
+
+ // === 接口实现 ===
+ public float NeutronFlux => neutronFlux;
+ public float RawFlux => neutronFlux;
+ public FluxMode CurrentFluxMode => fluxMode;
+ public float FluxEfficiency => IFluxController.GetEfficiency(neutronFlux);
+ public bool IsAutoMode => fluxMode != FluxMode.Manual;
+ public bool IsIncubating => IsAnyOrderActive;
+ public bool IsDormant => neutronFlux < 0.05f;
+
+ public void SetNeutronFlux(float value) => neutronFlux = Mathf.Clamp01(value);
+ public void CycleFluxMode() => fluxMode = (FluxMode)(((int)fluxMode + 1) % 4);
+
+ public string GetModeName() => fluxMode switch {
+ FluxMode.Manual => "手动",
+ FluxMode.Quality => "品质",
+ FluxMode.Balance => "平衡",
+ FluxMode.Speed => "速度",
+ _ => "?"
+ };
+
+ public string GetModeShort() => fluxMode switch {
+ FluxMode.Manual => "M",
+ FluxMode.Quality => "Q",
+ FluxMode.Balance => "B",
+ FluxMode.Speed => "S",
+ _ => "?"
+ };
+
+ // 辅助属性
+ private bool IsAnyOrderActive => productionOrders.Any(o => o.spawnUntilTick > 0);
+
+ // 覆盖 Tick 逻辑以处理通量效果
+ public override void CompTick()
+ {
+ // 注意:我们直接重写逻辑,而不是仅仅调用 base.CompTick,
+ // 这样我们可以更精确地控制进度增加速度。
+
+ bool hasFuel = FuelComp?.HasFuel ?? true;
+
+ // 自动模式调节
+ if (IsAutoMode && parent.IsHashIntervalTick(250) && IsAnyOrderActive)
+ {
+ CalculateAutoFlux();
+ }
+
+ if (IsAnyOrderActive)
+ {
+ // 消耗燃料(基于通量效率)
+ if (FuelComp != null && neutronFlux > 0.01f)
+ {
+ float fuelPerTick = (50f * FluxEfficiency) / 60000f;
+ FuelComp.ConsumeFuel(fuelPerTick);
+ }
+
+ if (!hasFuel)
+ {
+ // 没燃料时进度停滞
+ }
+ else if (IsDormant)
+ {
+ // 休眠逻辑:目前排队组件暂不支持品质损耗,仅停滞
+ }
+ else
+ {
+ // 正常孵化:受通量加速影响
+ float speedFactor = 1f + (FacilitiesComp?.GetStatOffset(StatDef.Named("ARA_IncubationSpeedFactor")) ?? 0f);
+ float fluxSpeed = speedFactor * FluxEfficiency * 5f;
+
+ // 更新所有激活订单的进度
+ // 由于基类逻辑是基于 TickManager.TicksGame 的(spawnUntilTick),
+ // 我们需要手动调整这个目标 Tick 来实现加速。
+ //
+ // 复杂点:基类认为每 Tick 进度 +1。我们要实现每 Tick 进度 +fluxSpeed。
+ // 所以我们每 Tick 实际上让目标 Tick 靠近当前 Tick (fluxSpeed - 1) 个单位。
+ foreach (var order in productionOrders.Where(o => o.spawnUntilTick > 0))
+ {
+ // 计算需要减少的剩余时间量
+ float extraProgress = fluxSpeed - 1f;
+ // 我们通过增加一个随机概率或累积器来处理小数
+ // 这里简单处理:直接修改 spawnUntilTick
+ // (注意:如果 fluxSpeed < 1,spawnUntilTick 会延后)
+ if (extraProgress > 0)
+ {
+ // 实际应该减少的值。如果 fluxSpeed=5,则每 tick 应该减少 5 tick 的等待时间。
+ // 基类自然减少了 1,我们额外减少 4。
+ int extraTicks = Mathf.FloorToInt(extraProgress);
+ if (Rand.Value < (extraProgress - extraTicks)) extraTicks++;
+ order.spawnUntilTick -= extraTicks;
+ }
+ else if (extraProgress < 0)
+ {
+ // 减速逻辑
+ float delayExtra = 1f - fluxSpeed;
+ int delayTicks = Mathf.FloorToInt(delayExtra);
+ if (Rand.Value < (delayExtra - delayTicks)) delayTicks++;
+ order.spawnUntilTick += delayTicks;
+ }
+ }
+ }
+ }
+
+ // 处理订单完成和启动新订单(逻辑基本同基类,但我们需要确保燃料消耗正常更新)
+ TickProductionLogic();
+ }
+
+ private void TickProductionLogic()
+ {
+ // 完成订单
+ 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)
+ {
+ var waitingOrder = productionOrders.FirstOrDefault(o => o.spawnUntilTick == -1);
+ if (waitingOrder != null)
+ {
+ // 初始目标 Tick,之后会在 CompTick 中被通量系统动态调整
+ waitingOrder.spawnUntilTick = Find.TickManager.TicksGame + waitingOrder.entry.delayTicks;
+ }
+ }
+
+ // 更新燃料消耗显示(UI用)
+ if (FuelComp != null)
+ {
+ float totalConsumptionPerDay = 0f;
+ if (IsAnyOrderActive && NeutronFlux > 0.01f)
+ {
+ // 基础:每激活一个槽位在 100% 活性下消耗 50/天?
+ // 或者统一由组件控制
+ totalConsumptionPerDay = currentlyProducingCount * 50f * FluxEfficiency;
+ }
+ FuelComp.currentConsumptionRate = totalConsumptionPerDay;
+ }
+ }
+
+ private void CalculateAutoFlux()
+ {
+ if (fluxMode == FluxMode.Manual) return;
+
+ // 排队组件目前主要影响速度。平衡模式下,如果燃料充足,保持中等偏高速度。
+ float targetFlux = 0.5f;
+
+ switch (fluxMode)
+ {
+ case FluxMode.Speed: targetFlux = 1.0f; break;
+ case FluxMode.Quality: targetFlux = 0.2f; break; // 排队组件目前无品质机制,所以此模式意义较小
+ case FluxMode.Balance:
+ default: targetFlux = 0.6f; break;
+ }
+
+ // 资源保护
+ if (FuelComp != null && FuelComp.Fuel < 20f) targetFlux = Mathf.Min(targetFlux, 0.3f);
+
+ neutronFlux = Mathf.Lerp(neutronFlux, targetFlux, 0.05f);
+ }
+
+ public override IEnumerable CompGetGizmosExtra()
+ {
+ foreach (var g in base.CompGetGizmosExtra()) yield return g;
+
+ if (parent.Faction == Faction.OfPlayer)
+ {
+ // 注意:进度 Gizmo 由 Building 负责显示,
+ // 但如果是通用 Building 挂载此组件,可能需要在此提供。
+ // 鉴于目前是特定 Building 类,我们暂不在此重复,保持现有的逻辑。
+ // 不过,我们需要确保通量控制 Gizmo 可用。
+ yield return new Gizmo_NeutronFlux(this);
+ }
+ }
+
+ public override void PostExposeData()
+ {
+ base.PostExposeData();
+ Scribe_Values.Look(ref neutronFlux, "neutronFlux", 0.5f);
+ Scribe_Values.Look(ref fluxMode, "fluxMode", FluxMode.Balance);
+ }
+ }
+}
diff --git a/Source/ArachnaeSwarm/Building_Comps/CompRefuelableNutrition.cs b/Source/ArachnaeSwarm/Building_Comps/CompRefuelableNutrition.cs
index 87de355..bf5a2e9 100644
--- a/Source/ArachnaeSwarm/Building_Comps/CompRefuelableNutrition.cs
+++ b/Source/ArachnaeSwarm/Building_Comps/CompRefuelableNutrition.cs
@@ -115,5 +115,14 @@ namespace ArachnaeSwarm
ConsumeFuel(Props.fuelConsumptionRate / 60000f);
}
}
+
+ public override bool ShouldAutoRefuelNow =>
+ allowAutoRefuel && TargetFuelLevel > 0.01f && (Fuel < TargetFuelLevel * 0.75f || Fuel == 0f);
+
+ public override bool ShouldAutoRefuelNowIgnoringFuelPct =>
+ allowAutoRefuel && TargetFuelLevel > 0.01f && Fuel < TargetFuelLevel;
+
+ // 覆盖基类属性,确保 WorkGiver 扫描时拦截
+ public new float TargetFuelLevel => allowAutoRefuel ? base.TargetFuelLevel : 0f;
}
}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs
index 9f37ef8..b8ac223 100644
--- a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs
+++ b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs
@@ -10,98 +10,64 @@ namespace ArachnaeSwarm
{
public class Building_EquipmentOotheca : Building, IFluxController
{
- // 引用组件
- public CompEquipmentIncubatorData EquipmentIncubatorData => this.TryGetComp();
-
- // 孵化状态
+ // === 通量系统字段 ===
+ private float neutronFlux = 0.5f;
+ private FluxMode fluxMode = FluxMode.Balance;
+
+ // === 孵化核心字段 ===
public bool isIncubating = false;
+ public ThingDef incubatingThingDef = null;
public float incubationProgress = 0f;
public float incubationDuration = 0f;
- public ThingDef incubatingThingDef = null;
+ public float qualityProgress = 0f;
+ public float qualityTotal = 0f;
+ public float qualityMultiplier = 1.0f;
+ public float speedMultiplier = 1.0f;
- // 幼虫交互相关
+ // 幼虫相关
public Pawn assignedLarva = null;
public int larvaOperateTicksRemaining = 0;
- // 速度乘数系统
- private float speedMultiplier = 1.0f;
- private int lastMultiplierUpdateTick = -1;
- private const int MultiplierUpdateInterval = 250;
-
- // 质量系统
- private float qualityMultiplier = 1.0f;
- private float qualityProgress = 0f;
- private float qualityTotal = 0f;
-
- // ======= 孵化活性系统 =======
- private float neutronFlux = 0.5f; // 0-1, 默认50%
- private FluxMode fluxMode = FluxMode.Balance; // 默认平衡模式
- private CompRefuelableNutrition fuelComp; // 燃料组件缓存
- private Comp_SwarmMaintenance maintenanceComp; // 维护组件缓存
-
- // 孵化活性公开属性
- public float NeutronFlux => isIncubating ? neutronFlux : 0f;
- public float RawFlux => neutronFlux;
- public FluxMode CurrentFluxMode => fluxMode;
+ // 时间控制
+ protected int lastMultiplierUpdateTick = -1;
+ protected const int MultiplierUpdateInterval = 250;
+
+ // === 属性 ===
+ public IFluxController FluxController => this;
+ public float NeutronFlux => neutronFlux;
+ public FluxMode FluxMode => fluxMode;
+ public float FluxEfficiency => IFluxController.GetEfficiency(neutronFlux);
public bool IsAutoMode => fluxMode != FluxMode.Manual;
- public bool IsIncubating => isIncubating;
+ public bool IsDormant => neutronFlux < 0.05f;
+
+ public CompRefuelableNutrition FuelComp => GetComp();
+ public Comp_SwarmMaintenance MaintenanceComp => GetComp();
+ public CompEquipmentIncubatorData EquipmentIncubatorData => GetComp();
+
+ public void SetNeutronFlux(float value) => neutronFlux = Mathf.Clamp01(value);
+ public void CycleFluxMode() => fluxMode = (FluxMode)(((int)fluxMode + 1) % 4);
+ public void SetFluxMode(FluxMode mode) => fluxMode = mode;
- // 获取燃料组件
- public CompRefuelableNutrition FuelComp => fuelComp ?? (fuelComp = this.TryGetComp());
- public Comp_SwarmMaintenance MaintenanceComp => maintenanceComp ?? (maintenanceComp = this.TryGetComp());
-
- // 孵化活性效率曲线(非线性:flux²)
- public float FluxEfficiency => NeutronFlux * NeutronFlux;
-
- // 是否处于休眠状态
- public bool IsDormant => NeutronFlux < 0.01f || (FuelComp != null && !FuelComp.HasFuel);
-
- // 设置活性值
- public void SetNeutronFlux(float value)
- {
- neutronFlux = Mathf.Clamp01(value);
- }
-
- // 切换模式
- public void CycleFluxMode()
- {
- fluxMode = (FluxMode)(((int)fluxMode + 1) % 4);
- }
-
- public void SetFluxMode(FluxMode mode)
- {
- fluxMode = mode;
- }
-
- public string GetModeName()
- {
- switch (fluxMode)
- {
- case FluxMode.Manual: return "手动";
- case FluxMode.Quality: return "品质";
- case FluxMode.Balance: return "平衡";
- case FluxMode.Speed: return "速度";
- default: return "?";
- }
- }
-
- public string GetModeShort()
- {
- switch (fluxMode)
- {
- case FluxMode.Manual: return "M";
- case FluxMode.Quality: return "Q";
- case FluxMode.Balance: return "B";
- case FluxMode.Speed: return "S";
- default: return "?";
- }
- }
-
- // 孵化进度百分比(给 Gizmo 用)
+ public string GetModeName() => fluxMode switch {
+ FluxMode.Manual => "手动",
+ FluxMode.Quality => "品质",
+ FluxMode.Balance => "平衡",
+ FluxMode.Speed => "速度",
+ _ => "?"
+ };
+
+ public string GetModeShort() => fluxMode switch {
+ FluxMode.Manual => "M",
+ FluxMode.Quality => "Q",
+ FluxMode.Balance => "B",
+ FluxMode.Speed => "S",
+ _ => "?"
+ };
+
public float IncubationProgress => incubationDuration > 0 ? incubationProgress / incubationDuration : 0f;
public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f;
-
- // 速度乘数
+ public float AdjustedProgressPercent => IncubationProgress;
+
public float SpeedMultiplier
{
get
@@ -114,56 +80,12 @@ namespace ArachnaeSwarm
}
}
- // 质量属性
public float QualityMultiplier => qualityMultiplier;
- public float QualityProgress => qualityProgress;
-
- // 临时保留:营养液相关字段(后续清理)
- private int totalNutrientCost = 0;
- private int currentNutrientCount = 0;
- private OothecaIncubatorExtension cachedExtension;
-
- public OothecaIncubatorExtension Ext
- {
- get
- {
- if (cachedExtension == null)
- cachedExtension = def.GetModExtension() ?? OothecaIncubatorExtension.Default;
- return cachedExtension;
- }
- }
-
- // 营养液速度加成(临时保留)
- public float NutrientSpeedBonus => currentNutrientCount * Ext.nutrientSolutionBonusPerTile;
-
- // 进度百分比
- public float AdjustedProgressPercent => incubationDuration > 0 ? incubationProgress / incubationDuration : 0f;
- // === 简化的初始化营养液方法 ===
- private void InitializeNutrientInfo()
- {
- if (incubatingThingDef == null)
- return;
-
- // 获取孵化成本统计值(仅用于信息显示)
- var costStat = DefDatabase.GetNamedSilentFail("ARA_IncubationCost");
- if (costStat != null)
- {
- totalNutrientCost = Mathf.RoundToInt(incubatingThingDef.GetStatValueAbstract(costStat, null));
- ArachnaeLog.Debug($"[ARA] 孵化 {incubatingThingDef.defName} 建议有 {totalNutrientCost} 个营养液地块以获得最佳速度");
- }
- else
- {
- totalNutrientCost = 0;
- ArachnaeLog.Debug($"[ARA] 孵化 {incubatingThingDef.defName} 不需要营养液加成");
- }
-
- // 立即更新一次营养液计数
- UpdateNutrientCount();
- }
+ private void InitializeNutrientInfo() { } // 清理完毕
// === Tick方法(带活性系统)===
- protected override void Tick()
+ public override void Tick()
{
base.Tick();
@@ -234,13 +156,13 @@ namespace ArachnaeSwarm
float targetFlux = 0.5f;
float incubationPercent = incubationDuration > 0 ? incubationProgress / incubationDuration : 0f;
- float qualityPercent = qualityTotal > 0 ? qualityProgress / qualityTotal : 0f;
- float gap = qualityPercent - incubationPercent;
+ float gQualityPercent = qualityTotal > 0 ? qualityProgress / qualityTotal : 0f;
+ float gap = gQualityPercent - incubationPercent;
switch (fluxMode)
{
case FluxMode.Quality:
- if (qualityPercent >= 0.98f) targetFlux = 1.0f;
+ if (gQualityPercent >= 0.98f) targetFlux = 1.0f;
else if (gap > 0.2f) targetFlux = 0.6f;
else if (gap > 0.1f) targetFlux = 0.45f;
else if (gap > 0f) targetFlux = 0.35f;
@@ -248,15 +170,15 @@ namespace ArachnaeSwarm
break;
case FluxMode.Speed:
- if (qualityPercent >= 0.95f) targetFlux = 1.0f;
- else if (qualityPercent < 0.3f && incubationPercent > 0.5f) targetFlux = 0.7f;
- else if (qualityPercent < 0.2f && incubationPercent > 0.7f) targetFlux = 0.5f;
+ if (gQualityPercent >= 0.95f) targetFlux = 1.0f;
+ else if (gQualityPercent < 0.3f && incubationPercent > 0.5f) targetFlux = 0.7f;
+ else if (gQualityPercent < 0.2f && incubationPercent > 0.7f) targetFlux = 0.5f;
else targetFlux = 1.0f;
break;
case FluxMode.Balance:
default:
- if (qualityPercent >= 0.95f) targetFlux = 1.0f;
+ if (gQualityPercent >= 0.95f) targetFlux = 1.0f;
else if (gap > 0.15f) targetFlux = 0.8f;
else if (gap > 0.05f) targetFlux = 0.6f;
else if (gap > -0.05f) targetFlux = 0.5f;
@@ -266,856 +188,244 @@ namespace ArachnaeSwarm
}
// 资源保护
- if (FuelComp != null && FuelComp.Fuel < 2f)
- targetFlux = Mathf.Min(targetFlux, 0.2f);
- if (MaintenanceComp != null && MaintenanceComp.CurrentMaintenance / MaintenanceComp.Props.maxMaintenance < 0.15f)
- targetFlux = Mathf.Min(targetFlux, 0.15f);
+ if (FuelComp != null && FuelComp.Fuel < 2f) targetFlux = Mathf.Min(targetFlux, 0.2f);
// 平滑调节
float adjustSpeed = 0.03f;
if (neutronFlux < targetFlux) neutronFlux = Mathf.Min(neutronFlux + adjustSpeed, targetFlux);
else if (neutronFlux > targetFlux) neutronFlux = Mathf.Max(neutronFlux - adjustSpeed, targetFlux);
+
neutronFlux = Mathf.Clamp(neutronFlux, 0.1f, 1.0f);
}
- // === 获取速度因子描述 ===
+ // === 获取描述 ===
public string GetSpeedFactorsDescription()
{
- var builder = new StringBuilder();
-
- builder.AppendLine("ARA_EquipmentIncubator.SpeedFactors".Translate());
- builder.AppendLine();
-
- // 1. 检查是否在孵化间中
- bool inIncubatorRoom = IsInIncubatorRoom();
- if (Ext.requiresIncubatorRoom)
- {
- builder.AppendLine(inIncubatorRoom ?
- "ARA_EquipmentIncubator.InIncubatorRoom".Translate() :
- "ARA_EquipmentIncubator.NotInIncubatorRoom".Translate());
- }
-
- // 2. 检查营养液数量
- int nutrientSolutionCount = CountNearbyNutrientSolutions();
- if (nutrientSolutionCount > 0)
- {
- builder.AppendLine("ARA_EquipmentIncubator.NutrientSolutionsBonus".Translate(
- nutrientSolutionCount,
- nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile * 100));
- }
- else
- {
- builder.AppendLine("ARA_EquipmentIncubator.NoNutrientSolutionsNearby".Translate());
- }
-
- builder.AppendLine();
- builder.AppendLine("ARA_EquipmentIncubator.NutrientDetectionRadius".Translate(Ext.nutrientSolutionRadius));
-
- builder.AppendLine();
- builder.AppendLine("ARA_EquipmentIncubator.TotalSpeedMultiplier".Translate(SpeedMultiplier.ToStringPercent()));
-
- return builder.ToString().TrimEndNewlines();
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine("速度因子");
+ sb.AppendLine();
+ sb.Append("总速度倍率: " + SpeedMultiplier.ToStringPercent());
+ return sb.ToString().TrimEndNewlines();
}
-
- // === 获取质量因子描述 ===
+
public string GetQualityFactorsDescription()
{
- var builder = new StringBuilder();
-
- builder.AppendLine("ARA_EquipmentIncubator.QualityFactors".Translate());
- builder.AppendLine();
-
- // 1. 建筑血量损失百分比
- if (Ext.healthAffectsQuality)
- {
- float healthPercent = (float)HitPoints / MaxHitPoints;
- builder.AppendLine("ARA_EquipmentIncubator.BuildingHealth".Translate(healthPercent.ToStringPercent()));
- }
-
- // 2. 房间质量因子
- if (Ext.useRoomQualityFactor)
- {
- float roomFactor = GetRoomQualityFactor();
- builder.AppendLine(roomFactor == 1.0f ?
- "ARA_EquipmentIncubator.RoomFactorNormal".Translate() :
- $"{(roomFactor > 1.0f ? "✓" : "✗")} {"ARA_EquipmentIncubator.RoomFactorModified".Translate()}{roomFactor.ToStringPercent()}");
- }
-
- // 3. 附近其他卵
- int nearbyOothecaCount = CountNearbyOtherOothecas();
- if (nearbyOothecaCount > 0)
- {
- builder.AppendLine("ARA_EquipmentIncubator.NearbyOothecas".Translate(
- nearbyOothecaCount,
- Mathf.Min(nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit * 100, 100)));
- }
- else
- {
- builder.AppendLine("ARA_EquipmentIncubator.NoNearbyOothecas".Translate());
- }
-
- builder.AppendLine();
- builder.AppendLine("ARA_EquipmentIncubator.OothecaDetectionRadius".Translate(Ext.nearbyOothecaRadius));
-
- builder.AppendLine();
- builder.AppendLine("ARA_EquipmentIncubator.TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent()));
-
- return builder.ToString().TrimEndNewlines();
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine("质量因子");
+ sb.AppendLine();
+ sb.Append("总质量倍率: " + QualityMultiplier.ToStringPercent());
+ return sb.ToString().TrimEndNewlines();
}
- // === 获取营养液信息描述 ===
- public string GetNutrientInfoDescription()
- {
- var builder = new StringBuilder();
-
- builder.AppendLine("ARA_EquipmentIncubator.NutrientInfo".Translate());
- builder.AppendLine();
-
- // 显示当前营养液数量和加成
- builder.AppendLine("ARA_EquipmentIncubator.CurrentNutrientCount".Translate(currentNutrientCount));
- builder.AppendLine("ARA_EquipmentIncubator.NutrientSpeedBonus".Translate(NutrientSpeedBonus.ToStringPercent()));
-
- return builder.ToString().TrimEndNewlines();
- }
-
- // === 构建呼叫幼虫描述 ===
private string BuildCallLarvaDescription(EquipmentIncubationConfig config)
{
- var builder = new StringBuilder();
-
- builder.AppendLine("ARA_EquipmentIncubator.CallLarvaTitle".Translate());
- builder.AppendLine();
- builder.AppendLine("ARA_EquipmentIncubator.LarvaWillCome".Translate());
- builder.AppendLine(config.thingDef.LabelCap);
- builder.AppendLine();
-
- if (Ext.larvaSearchRadius < 999f)
- {
- builder.AppendLine("ARA_EquipmentIncubator.LarvaSearchRadius".Translate(Ext.larvaSearchRadius));
- }
-
- return builder.ToString().TrimEndNewlines();
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine("呼叫幼虫激活制造舱");
+ sb.AppendLine();
+ sb.AppendLine("目标: " + config.thingDef.LabelCap);
+ return sb.ToString().TrimEndNewlines();
}
- // === 呼叫幼虫 ===
- private void CallLarva()
+ // === 幼虫交互 ===
+ public void CallLarva()
{
- // 检查是否已经在孵化中
- if (isIncubating)
- {
- Messages.Message("ARA_EquipmentIncubator.AlreadyIncubating".Translate() + " " + "ARA_EquipmentIncubator.CancelFirst".Translate(),
- MessageTypeDefOf.RejectInput);
- return;
- }
-
- // 检查是否有幼虫在任务中
- if (assignedLarva != null)
- {
- Messages.Message("ARA_EquipmentIncubator.LarvaAlreadyOnWay".Translate(),
- MessageTypeDefOf.RejectInput);
- return;
- }
-
- // 获取当前选择的配置
- var config = EquipmentIncubatorData?.SelectedConfig;
- if (config == null)
- {
- Messages.Message("ARA_EquipmentIncubator.NoTargetSelected".Translate(),
- MessageTypeDefOf.RejectInput);
- return;
- }
-
- // 检查研究是否完成
- if (!config.IsResearchComplete)
- {
- if (config.requiredResearch != null)
- {
- Messages.Message("ARA_EquipmentIncubator.ResearchRequiredFor".Translate(config.thingDef.LabelCap, config.requiredResearch.LabelCap),
- MessageTypeDefOf.RejectInput);
- }
- else
- {
- Messages.Message("ARA_EquipmentIncubator.ResearchNotCompleted".Translate(),
- MessageTypeDefOf.RejectInput);
- }
- return;
- }
-
- // 查找幼虫
+ if (isIncubating || assignedLarva != null) return;
+
var larva = FindLarva();
if (larva == null)
{
- Messages.Message("ARA_EquipmentIncubator.NoLarvaeFound".Translate() + " " + "ARA_EquipmentIncubator.LarvaMustBeRace".Translate(),
- MessageTypeDefOf.RejectInput);
+ Messages.Message("未找到可用的幼虫!", MessageTypeDefOf.RejectInput);
return;
}
-
- // 给幼虫分配任务
- var job = JobMaker.MakeJob(ARA_JobDefOf.ARA_OperateEquipmentIncubator, this);
- job.count = 1;
+
+ var job = JobMaker.MakeJob(ARA_JobDefOf.ARA_OperateIncubator, this);
larva.jobs.TryTakeOrderedJob(job, JobTag.MiscWork);
assignedLarva = larva;
-
- Messages.Message("ARA_EquipmentIncubator.LarvaCalled".Translate() + " " + "ARA_EquipmentIncubator.ArriveShortly".Translate(),
- MessageTypeDefOf.PositiveEvent);
+ Messages.Message("已呼叫幼虫。", MessageTypeDefOf.PositiveEvent);
+ }
+
+ private Pawn FindLarva()
+ {
+ float searchRadius = 30f;
+ Map map = Map;
+ if (map == null) return null;
+
+ foreach (Pawn pawn in map.mapPawns.AllPawnsSpawned)
+ {
+ if (pawn.def.defName == "ArachnaeBase_Race_Larva" && !pawn.Downed && !pawn.Dead && pawn.Faction == Faction.OfPlayer)
+ {
+ if (Position.DistanceTo(pawn.Position) <= searchRadius)
+ return pawn;
+ }
+ }
+ return null;
}
- // === 幼虫到达 ===
public void NotifyLarvaArrived(Pawn larva)
{
- if (larva.def.defName != "ArachnaeBase_Race_Larva")
- {
- Log.Error($"[ARA] 无效的幼虫到达: {larva.def.defName}");
- return;
- }
-
larvaOperateTicksRemaining = 180;
assignedLarva = larva;
-
- Messages.Message("ARA_EquipmentIncubator.LarvaArrived".Translate() + " " + "ARA_EquipmentIncubator.ActivatingOotheca".Translate(),
- MessageTypeDefOf.SilentInput);
}
- // === 幼虫完成操作 ===
public void NotifyLarvaOperationComplete(Pawn larva)
{
- if (larva != assignedLarva)
- {
- Log.Error("[ARA] 幼虫操作完成调用错误:不是分配的幼虫");
- return;
- }
-
var config = EquipmentIncubatorData?.SelectedConfig;
- if (config == null)
- {
- Log.Error("[ARA] 幼虫完成操作时没有选择孵化配置");
- return;
- }
-
+ if (config == null) return;
+
incubatingThingDef = config.thingDef;
- incubationDuration = config.DaysRequired * 60000f;
+ incubationDuration = config.daysRequired * 60000f;
incubationProgress = 0f;
- isIncubating = true;
qualityTotal = incubationDuration;
qualityProgress = 0f;
-
- UpdateQualityMultiplier();
- UpdateSpeedMultiplier();
-
- // 初始化营养液信息(仅用于显示和建议)
- InitializeNutrientInfo();
-
+ isIncubating = true;
assignedLarva = null;
larvaOperateTicksRemaining = 0;
-
- Messages.Message("ARA_EquipmentIncubator.IncubationStarted".Translate() + " " + incubatingThingDef.LabelCap + ". " +
- "ARA_EquipmentIncubator.ProcessWillComplete".Translate() + " " + config.DaysRequired + " " + "ARA_EquipmentIncubator.DaysBaseTime".Translate(),
- MessageTypeDefOf.PositiveEvent);
+
+ Messages.Message("孵化开始: " + incubatingThingDef.LabelCap, MessageTypeDefOf.PositiveEvent);
}
- // === 取消孵化 ===
- private void CancelIncubation()
+ public void CancelIncubation()
{
- if (!isIncubating) return;
-
- ArachnaeLog.Debug($"[ARA] 取消孵化: {incubatingThingDef?.defName}");
-
isIncubating = false;
- incubationProgress = 0f;
- incubationDuration = 0f;
incubatingThingDef = null;
- qualityProgress = 0f;
- qualityTotal = 0f;
-
- // 重置营养液信息
- totalNutrientCost = 0;
- currentNutrientCount = 0;
-
- Messages.Message("ARA_EquipmentIncubator.IncubationCancelled".Translate() + " " + "ARA_EquipmentIncubator.ContentsLost".Translate(),
- MessageTypeDefOf.NeutralEvent);
+ incubationProgress = 0f;
+ Messages.Message("孵化已取消。", MessageTypeDefOf.NeutralEvent);
}
- // === 完成孵化 ===
private void CompleteIncubation()
{
if (incubatingThingDef == null) return;
-
- ArachnaeLog.Debug($"[ARA] 完成孵化: {incubatingThingDef.defName}");
-
- float finalQualityPercent = QualityPercent;
-
- // 生成物品
+
+ float finalQuality = QualityPercent;
Thing thing = ThingMaker.MakeThing(incubatingThingDef);
- ApplyQualityEffects(thing, finalQualityPercent);
-
- // 放置物品
- var spawnPos = Position;
- GenPlace.TryPlaceThing(thing, spawnPos, Map, ThingPlaceMode.Near);
-
- // 重置状态
- ResetIncubationState();
-
- // 显示完成消息
- string qualityText = finalQualityPercent >= 0.9f ? "ARA_EquipmentIncubator.QualityExcellent".Translate() :
- finalQualityPercent >= 0.7f ? "ARA_EquipmentIncubator.QualityGood".Translate() :
- finalQualityPercent >= 0.5f ? "ARA_EquipmentIncubator.QualityAverage".Translate() :
- finalQualityPercent >= 0.3f ? "ARA_EquipmentIncubator.QualityPoor".Translate() :
- "ARA_EquipmentIncubator.QualityVeryPoor".Translate();
-
- Messages.Message("ARA_EquipmentIncubator.IncubationComplete".Translate() + " " + thing.LabelCap + " " +
- "ARA_EquipmentIncubator.HasEmergedWith".Translate() + " " + qualityText + " " +
- "ARA_EquipmentIncubator.Quality".Translate() + " (" + finalQualityPercent.ToStringPercent() + ").",
- MessageTypeDefOf.PositiveEvent);
+ // 应用质量
+ CompQuality compQuality = thing.TryGetComp();
+ if (compQuality != null)
+ {
+ QualityCategory qc = finalQuality switch {
+ >= 0.95f => QualityCategory.Legendary,
+ >= 0.85f => QualityCategory.Masterwork,
+ >= 0.70f => QualityCategory.Excellent,
+ >= 0.50f => QualityCategory.Good,
+ >= 0.30f => QualityCategory.Normal,
+ >= 0.15f => QualityCategory.Poor,
+ _ => QualityCategory.Awful
+ };
+ compQuality.SetQuality(qc, ArtGenerationContext.Colony);
+ }
+
+ GenSpawn.Spawn(thing, Position, Map);
+ isIncubating = false;
+ incubatingThingDef = null;
+ Messages.Message("孵化完成: " + thing.LabelCap, MessageTypeDefOf.PositiveEvent);
Destroy();
}
- // === 应用质量效果 ===
- private void ApplyQualityEffects(Thing thing, float qualityPercent)
+ // === 辅助方法 ===
+ private void UpdateSpeedMultiplier()
{
- // 应用质量效果到装备
- if (thing.TryGetComp() is CompQuality compQuality)
+ float multiplier = 1.0f;
+ var facilityComp = GetComp();
+ if (facilityComp != null)
{
- // 根据质量百分比设置质量等级
- QualityCategory qualityCategory = qualityPercent >= 0.99f ? QualityCategory.Legendary :
- qualityPercent >= 0.75f ? QualityCategory.Masterwork :
- qualityPercent >= 0.6f ? QualityCategory.Excellent :
- qualityPercent >= 0.45f ? QualityCategory.Good :
- qualityPercent >= 0.3f ? QualityCategory.Normal : QualityCategory.Poor;
-
- compQuality.SetQuality(qualityCategory, ArtGenerationContext.Outsider);
- }
-
- // 设置生命值百分比
- if (qualityPercent < 1.0f)
- {
- float healthFactor = Mathf.Lerp(0.5f, 1.0f, qualityPercent);
- thing.HitPoints = Mathf.RoundToInt(thing.MaxHitPoints * healthFactor);
+ var speedStat = DefDatabase.GetNamedSilentFail("ARA_IncubationSpeedFactor") ?? StatDefOf.WorkTableWorkSpeedFactor;
+ multiplier += facilityComp.GetStatOffset(speedStat);
}
+ speedMultiplier = multiplier;
+ lastMultiplierUpdateTick = Find.TickManager.TicksGame;
}
- // === 重置孵化状态 ===
- private void ResetIncubationState()
+ private void UpdateQualityMultiplier()
{
- isIncubating = false;
- incubationProgress = 0f;
- incubationDuration = 0f;
- incubatingThingDef = null;
- qualityProgress = 0f;
- qualityTotal = 0f;
- totalNutrientCost = 0;
- currentNutrientCount = 0;
+ qualityMultiplier = 1.0f;
}
- // === 获取剩余时间 ===
public float GetRemainingTicks()
{
- if (!isIncubating || incubationDuration <= incubationProgress) return 0f;
-
- float remainingProgress = incubationDuration - incubationProgress;
- float currentSpeed = SpeedMultiplier;
-
- if (currentSpeed <= 0) return float.MaxValue;
-
- return remainingProgress / currentSpeed;
- }
-
- public float GetRemainingDays()
- {
- return GetRemainingTicks() / 60000f;
- }
-
- public float GetRemainingHours()
- {
- float remainingTicks = GetRemainingTicks();
- return (remainingTicks % 60000f) / 2500f;
+ if (!isIncubating) return 0f;
+ return Mathf.Max(0f, (incubationDuration - incubationProgress) / (SpeedMultiplier * FluxEfficiency * 5f));
}
- // === 检查字符串 ===
+ public float GetRemainingDays() => GetRemainingTicks() / 60000f;
+ public float GetRemainingHours() => (GetRemainingTicks() % 60000f) / 2500f;
+
public override string GetInspectString()
{
- var baseString = base.GetInspectString();
- var builder = new StringBuilder();
-
- if (!string.IsNullOrEmpty(baseString))
- {
- builder.Append(baseString);
- }
-
+ StringBuilder sb = new StringBuilder();
if (isIncubating && incubatingThingDef != null)
{
- float progressPercent = AdjustedProgressPercent;
- float daysRemaining = GetRemainingDays();
- float hoursRemaining = GetRemainingHours();
-
- if (builder.Length > 0) builder.AppendLine();
- builder.Append("ARA_EquipmentIncubator.Incubating".Translate() + ": " + incubatingThingDef.LabelCap);
- builder.AppendLine();
- builder.Append("ARA_EquipmentIncubator.Progress".Translate() + ": " + progressPercent.ToStringPercent());
- builder.AppendLine();
-
- string timeText = "ARA_EquipmentIncubator.TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Days".Translate();
- if (hoursRemaining > 0.1f && daysRemaining < 1f)
- {
- timeText += " (" + hoursRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Hours".Translate() + ")";
- }
- builder.Append(timeText);
- builder.AppendLine();
- builder.Append("ARA_EquipmentIncubator.Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent());
+ sb.AppendLine("正在孵化: " + incubatingThingDef.LabelCap);
+ sb.AppendLine("进度: " + AdjustedProgressPercent.ToStringPercent());
+ sb.AppendLine("剩余时间: " + GetRemainingDays().ToString("F1") + " 天");
+ sb.Append("速度: " + SpeedMultiplier.ToStringPercent() + " | 质量: " + QualityMultiplier.ToStringPercent());
}
else if (assignedLarva != null)
{
- if (builder.Length > 0) builder.AppendLine();
- if (larvaOperateTicksRemaining > 0)
- {
- float secondsRemaining = larvaOperateTicksRemaining / 60f;
- builder.Append("ARA_EquipmentIncubator.LarvaOperating".Translate() + ": " + secondsRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.SRemaining".Translate());
- }
- else
- {
- builder.Append("ARA_EquipmentIncubator.LarvaOnWay".Translate());
- }
+ sb.Append(larvaOperateTicksRemaining > 0 ? "幼虫激活中..." : "幼虫赶路中...");
}
- else if (!isIncubating)
+ else
{
var config = EquipmentIncubatorData?.SelectedConfig;
if (config != null)
{
- if (builder.Length > 0) builder.AppendLine();
- builder.Append("ARA_EquipmentIncubator.Target".Translate() + ": " + config.thingDef.LabelCap);
- builder.AppendLine();
- builder.Append("ARA_EquipmentIncubator.SpeedMultiplier".Translate() + ": " + SpeedMultiplier.ToStringPercent());
-
- // 显示当前营养液加成
- UpdateNutrientCount();
- if (currentNutrientCount > 0)
- {
- builder.AppendLine();
- builder.Append("ARA_EquipmentIncubator.CurrentNutrientBonus".Translate() + ": " +
- currentNutrientCount + " tiles (" + NutrientSpeedBonus.ToStringPercent() + ")");
- }
+ sb.AppendLine("目标: " + config.thingDef.LabelCap);
+ sb.Append("速度: " + SpeedMultiplier.ToStringPercent());
}
}
-
- return builder.ToString().TrimEndNewlines();
+
+ var baseStr = base.GetInspectString();
+ if (!string.IsNullOrEmpty(baseStr))
+ {
+ if (sb.Length > 0) sb.AppendLine();
+ sb.Append(baseStr);
+ }
+ return sb.ToString().TrimEndNewlines();
}
- // === Gizmos ===
public override IEnumerable GetGizmos()
{
- // 过滤掉拆除按钮和半径显示
foreach (var gizmo in base.GetGizmos())
{
- // 跳过拆除和半径相关的 Gizmo
if (gizmo is Command_Action cmd && cmd.defaultLabel != null)
{
string label = cmd.defaultLabel.ToString();
- if (label.Contains("拆除") || label.Contains("Deconstruct") ||
- label.Contains("半径") || label.Contains("Radius"))
+ if (label.Contains("拆除") || label.Contains("Deconstruct") || label.Contains("半径") || label.Contains("Radius"))
continue;
}
yield return gizmo;
}
-
- // 只有玩家派系才显示Gizmo
+
if (Faction == Faction.OfPlayer)
{
- // 始终显示双进度条Gizmo
yield return new Gizmo_EquipmentIncubationProgress(this);
-
- // 始终显示活性 Gizmo
yield return new Gizmo_NeutronFlux(this);
-
- // 不在孵化中且研究完成时才显示呼叫按钮
- var config = EquipmentIncubatorData?.SelectedConfig;
- if (!isIncubating && config != null && config.IsResearchComplete)
+
+ if (!isIncubating && EquipmentIncubatorData?.SelectedConfig?.IsResearchComplete == true)
{
yield return new Command_Action
{
- defaultLabel = "ARA_EquipmentIncubator.CallLarva".Translate(),
- defaultDesc = BuildCallLarvaDescription(config),
- icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false) ?? BaseContent.BadTex,
- action = CallLarva,
- hotKey = KeyBindingDefOf.Misc3
+ defaultLabel = "呼叫幼虫",
+ icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false),
+ action = CallLarva
};
}
-
- // 如果正在孵化,显示取消按钮
+
if (isIncubating)
{
yield return new Command_Action
{
- defaultLabel = "ARA_EquipmentIncubator.CancelIncubation".Translate(),
- defaultDesc = "ARA_EquipmentIncubator.CancelIncubationDesc".Translate(),
- icon = ContentFinder.Get("UI/Commands/Cancel", false) ?? TexCommand.ClearPrioritizedWork,
+ defaultLabel = "取消孵化",
+ icon = ContentFinder.Get("UI/Commands/Cancel", false),
action = CancelIncubation
};
}
}
}
- // === 创建切换目标Gizmo ===
- private Gizmo CreateTargetSwitchGizmo()
- {
- var configs = EquipmentIncubatorData?.IncubationConfigs;
- if (configs == null || configs.Count == 0) return null;
-
- var props = EquipmentIncubatorData?.props as CompProperties_EquipmentIncubatorData;
- var selectedConfig = EquipmentIncubatorData?.SelectedConfig;
-
- var switchButton = new Command_Action
- {
- defaultLabel = BuildSwitchButtonLabel(selectedConfig, props),
- defaultDesc = BuildSwitchButtonDescription(selectedConfig, props),
- icon = GetConfigIcon(selectedConfig),
- action = ShowSelectionMenu,
- hotKey = KeyBindingDefOf.Misc2
- };
-
- return switchButton;
- }
-
- private string BuildSwitchButtonLabel(EquipmentIncubationConfig config, CompProperties_EquipmentIncubatorData props)
- {
- if (config != null && config.thingDef != null)
- {
- return (props?.buttonLabel ?? "ARA_EquipmentIncubator.IncubateLabel").Translate(config.thingDef.LabelCap);
- }
- return (props?.buttonLabel ?? "ARA_EquipmentIncubator.IncubateLabel").Translate("None");
- }
-
- private string BuildSwitchButtonDescription(EquipmentIncubationConfig config, CompProperties_EquipmentIncubatorData props)
- {
- var builder = new StringBuilder();
-
- builder.AppendLine();
-
- if (config != null)
- {
- if (config.thingDef != null)
- {
- builder.AppendLine($"ARA_EquipmentIncubator.ButtonLabel".Translate(config.thingDef.LabelCap));
- if (!string.IsNullOrEmpty(config.thingDef.description))
- {
- builder.AppendLine(config.thingDef.description);
- }
- }
-
- builder.AppendLine($"ARA_EquipmentIncubator.IncubationTime".Translate(config.DaysRequired));
-
- if (config.requiredResearch != null)
- {
- if (config.requiredResearch.IsFinished)
- {
- builder.AppendLine($"ARA_EquipmentIncubator.ResearchCompleted".Translate(config.requiredResearch.LabelCap));
- }
- else
- {
- builder.AppendLine($"ARA_EquipmentIncubator.ResearchRequired".Translate(config.requiredResearch.LabelCap));
- }
- }
- else
- {
- builder.AppendLine("ARA_EquipmentIncubator.NoResearchRequired".Translate());
- }
- }
-
- builder.AppendLine();
- builder.AppendLine("ARA_EquipmentIncubator.ButtonDesc".Translate());
-
- return builder.ToString().TrimEndNewlines();
- }
-
- private Texture2D GetConfigIcon(EquipmentIncubationConfig config)
- {
- if (config == null)
- return BaseContent.BadTex;
-
- if (config.thingDef?.uiIcon != null)
- return config.thingDef.uiIcon;
-
- return ContentFinder.Get("UI/Commands/Default", false) ?? BaseContent.BadTex;
- }
-
- private void ShowSelectionMenu()
- {
- var configs = EquipmentIncubatorData?.IncubationConfigs;
- var props = EquipmentIncubatorData?.props as CompProperties_EquipmentIncubatorData;
- if (configs == null || configs.Count == 0) return;
-
- var options = new List();
- int currentIndex = EquipmentIncubatorData.GetSelectedIndex();
-
- for (int i = 0; i < configs.Count; i++)
- {
- int index = i;
- var config = configs[i];
- if (config == null || config.thingDef == null) continue;
-
- string label = config.thingDef.LabelCap;
- string description = config.GetDescription();
-
- string prefix = (i == currentIndex) ? "✓ " : " ";
-
- Texture2D icon = config.thingDef?.uiIcon;
-
- FloatMenuOption option;
-
- if (icon != null)
- {
- option = new FloatMenuOption(
- prefix + label,
- () => SwitchToConfig(index),
- icon,
- Color.white,
- MenuOptionPriority.Default,
- null,
- null,
- 0f,
- null,
- null,
- true,
- 0,
- HorizontalJustification.Left,
- false
- );
- }
- else
- {
- option = new FloatMenuOption(
- prefix + label,
- () => SwitchToConfig(index)
- );
- }
-
- option.tooltip = description;
-
- // 如果研究未完成,禁用选项
- if (!config.IsResearchComplete)
- {
- option.Label = prefix + label;
- option.Disabled = true;
- if (config.requiredResearch != null)
- {
- option.tooltip = description + "\n\n " + "ARA_EquipmentIncubator.ResearchRequired".Translate() + " " + config.requiredResearch.LabelCap;
- }
- }
-
- options.Add(option);
- }
-
- if (options.Count > 0)
- {
- Find.WindowStack.Add(new FloatMenu(options,
- (props?.menuTitle ?? "ARA_EquipmentIncubator.MenuTitle").Translate()));
- }
- }
-
- private void SwitchToConfig(int index)
- {
- if (EquipmentIncubatorData != null)
- {
- EquipmentIncubatorData.SwitchToConfig(index);
- var config = EquipmentIncubatorData.SelectedConfig;
- if (config != null && config.thingDef != null)
- {
- Messages.Message($"ARA_EquipmentIncubator.TargetSwitched".Translate(config.thingDef.LabelCap),
- this, MessageTypeDefOf.SilentInput);
- }
- }
- }
-
- // === 查找幼虫 ===
- private Pawn FindLarva()
- {
- var map = Map;
- if (map == null) return null;
-
- float searchRadius = Ext.larvaSearchRadius;
-
- foreach (var pawn in map.mapPawns.SpawnedPawnsInFaction(Faction))
- {
- if (pawn.def.defName == "ArachnaeBase_Race_Larva")
- {
- if (searchRadius < 999f)
- {
- float distance = pawn.Position.DistanceTo(Position);
- if (distance > searchRadius)
- continue;
- }
-
- if (!pawn.Downed && !pawn.InMentalState &&
- pawn.mindState != null &&
- (pawn.CurJob == null || pawn.CurJob.def != ARA_JobDefOf.ARA_OperateEquipmentIncubator))
- {
- return pawn;
- }
- }
- }
-
- return null;
- }
-
- // === 检查是否在孵化间中 ===
- private bool IsInIncubatorRoom()
- {
- if (!Ext.requiresIncubatorRoom)
- return true;
-
- var room = this.GetRoom();
- if (room == null) return false;
-
- return room.Role != null && room.Role.defName == "ARA_Incubator_Room";
- }
-
- // === 计算营养液数量 ===
- private int CountNearbyNutrientSolutions()
- {
- var map = Map;
- if (map == null) return 0;
-
- int count = 0;
- int radius = Ext.NutrientSolutionRadiusInt;
-
- for (int x = -radius; x <= radius; x++)
- {
- for (int y = -radius; y <= radius; y++)
- {
- IntVec3 cell = Position + new IntVec3(x, 0, y);
-
- if (cell.InBounds(map))
- {
- TerrainDef terrain = map.terrainGrid.TerrainAt(cell);
- if (terrain != null && terrain.defName == "ARA_Incubator_Nutrient_Solution")
- {
- count++;
- }
- }
- }
- }
-
- return count;
- }
-
- // === 更新营养液计数 ===
- private void UpdateNutrientCount()
- {
- currentNutrientCount = CountNearbyNutrientSolutions();
- }
-
- // === 计算房间质量因子 ===
- private float GetRoomQualityFactor()
- {
- if (!Ext.useRoomQualityFactor)
- return 1.0f;
-
- var room = this.GetRoom();
- if (room == null) return 1.0f;
-
- var statDef = DefDatabase.GetNamedSilentFail("ARA_IncubatorQualityFactor");
- if (statDef != null)
- {
- return room.GetStat(statDef);
- }
-
- return 1.0f;
- }
-
- // === 计算附近其他卵的数量 ===
- private int CountNearbyOtherOothecas()
- {
- var map = Map;
- if (map == null) return 0;
-
- int count = 0;
- var allBuildings = map.listerThings.ThingsOfDef(this.def);
-
- foreach (var building in allBuildings)
- {
- if (building == this) continue;
-
- if (building.def.defName == "ARA_Pawn_Ootheca" || building.def.defName == "ARA_Equipment_Ootheca")
- {
- bool isNearby = false;
-
- if (Ext.checkSameRoomForOotheca)
- {
- var room = building.GetRoom();
- if (room != null && room == this.GetRoom())
- {
- isNearby = true;
- }
- }
-
- if (!isNearby)
- {
- float distance = building.Position.DistanceTo(this.Position);
- if (distance <= Ext.nearbyOothecaRadius)
- {
- isNearby = true;
- }
- }
-
- if (isNearby)
- {
- count++;
- }
- }
- }
-
- return count;
- }
-
- // === 更新速度乘数 ===
- private void UpdateSpeedMultiplier()
- {
- float multiplier = 1.0f;
-
- if (Ext.requiresIncubatorRoom && !IsInIncubatorRoom())
- {
- multiplier *= Ext.speedPenaltyOutsideIncubator;
- }
-
- // 更新营养液计数
- UpdateNutrientCount();
-
- // 应用营养液加成
- float nutrientBonus = 1.0f + (currentNutrientCount * Ext.nutrientSolutionBonusPerTile);
- multiplier *= nutrientBonus;
-
- speedMultiplier = multiplier;
- lastMultiplierUpdateTick = Find.TickManager.TicksGame;
- }
-
- // === 更新质量乘数 ===
- private void UpdateQualityMultiplier()
- {
- float multiplier = 1.0f;
-
- if (Ext.healthAffectsQuality)
- {
- float healthPercent = (float)HitPoints / MaxHitPoints;
- multiplier *= healthPercent;
- }
-
- if (Ext.useRoomQualityFactor)
- {
- float roomFactor = GetRoomQualityFactor();
- multiplier *= roomFactor;
- }
-
- int nearbyOothecaCount = CountNearbyOtherOothecas();
- float oothecaPenalty = Mathf.Max(0f, 1.0f - (nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit));
- multiplier *= oothecaPenalty;
-
- qualityMultiplier = Mathf.Clamp(multiplier, 0f, 1.0f);
- }
-
- // === 保存/加载 ===
public override void ExposeData()
{
base.ExposeData();
-
Scribe_Values.Look(ref isIncubating, "isIncubating", false);
Scribe_Values.Look(ref incubationProgress, "incubationProgress", 0f);
Scribe_Values.Look(ref incubationDuration, "incubationDuration", 0f);
@@ -1123,14 +433,9 @@ namespace ArachnaeSwarm
Scribe_References.Look(ref assignedLarva, "assignedLarva");
Scribe_Values.Look(ref larvaOperateTicksRemaining, "larvaOperateTicksRemaining", 0);
Scribe_Values.Look(ref speedMultiplier, "speedMultiplier", 1.0f);
- Scribe_Values.Look(ref lastMultiplierUpdateTick, "lastMultiplierUpdateTick", -1);
Scribe_Values.Look(ref qualityMultiplier, "qualityMultiplier", 1.0f);
Scribe_Values.Look(ref qualityProgress, "qualityProgress", 0f);
Scribe_Values.Look(ref qualityTotal, "qualityTotal", 0f);
- Scribe_Values.Look(ref totalNutrientCost, "totalNutrientCost", 0);
- Scribe_Values.Look(ref currentNutrientCount, "currentNutrientCount", 0);
-
- // 孵化活性系统
Scribe_Values.Look(ref neutronFlux, "neutronFlux", 0.5f);
Scribe_Values.Look(ref fluxMode, "fluxMode", FluxMode.Balance);
}
diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs
index eccba9a..179f056 100644
--- a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs
+++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs
@@ -10,10 +10,11 @@ namespace ArachnaeSwarm
{
public class Building_Ootheca : Building, IFluxController
{
- // 引用组件
- public CompIncubatorData IncubatorData => this.TryGetComp();
-
- // 孵化状态 - 设为public以便ITab访问
+ // === 通量系统字段 ===
+ private float neutronFlux = 0.5f;
+ private FluxMode fluxMode = FluxMode.Balance;
+
+ // === 孵化核心字段 ===
public bool isIncubating = false;
public float incubationProgress = 0f;
public float incubationDuration = 0f;
@@ -23,107 +24,52 @@ namespace ArachnaeSwarm
public Pawn assignedLarva = null;
public int larvaOperateTicksRemaining = 0;
- // 速度乘数系统
+ // 乘数系统
private float speedMultiplier = 1.0f;
- private int lastMultiplierUpdateTick = -1;
- private const int MultiplierUpdateInterval = 250; // 每250ticks更新一次
-
- // 质量系统
private float qualityMultiplier = 1.0f;
private float qualityProgress = 0f;
private float qualityTotal = 0f;
- // ======= 孵化活性系统 =======
- private float neutronFlux = 0.5f; // 0-1, 默认50%
- private FluxMode fluxMode = FluxMode.Balance; // 默认平衡模式
- private CompRefuelableNutrition fuelComp; // 燃料组件缓存
- private Comp_SwarmMaintenance maintenanceComp; // 维护组件缓存
-
- // 孵化活性公开属性(非孵化时锁定为0)
- public float NeutronFlux => isIncubating ? neutronFlux : 0f;
- public float RawFlux => neutronFlux; // 原始值用于 Gizmo 显示
- public FluxMode CurrentFluxMode => fluxMode;
+ // 时间控制
+ protected int lastMultiplierUpdateTick = -1;
+ protected const int MultiplierUpdateInterval = 250;
+
+ // === 属性 ===
+ public IFluxController FluxController => this;
+ public float NeutronFlux => neutronFlux;
+ public FluxMode FluxMode => fluxMode;
+ public float FluxEfficiency => IFluxController.GetEfficiency(neutronFlux);
public bool IsAutoMode => fluxMode != FluxMode.Manual;
+ public bool IsDormant => neutronFlux < 0.05f;
+
+ public CompRefuelableNutrition FuelComp => GetComp();
+ public Comp_SwarmMaintenance MaintenanceComp => GetComp();
+ public CompIncubatorData IncubatorData => GetComp();
+
+ public void SetNeutronFlux(float value) => neutronFlux = Mathf.Clamp01(value);
+ public void CycleFluxMode() => fluxMode = (FluxMode)(((int)fluxMode + 1) % 4);
+ public void SetFluxMode(FluxMode mode) => fluxMode = mode;
- // 获取燃料组件
- public CompRefuelableNutrition FuelComp => fuelComp ?? (fuelComp = this.TryGetComp());
- public Comp_SwarmMaintenance MaintenanceComp => maintenanceComp ?? (maintenanceComp = this.TryGetComp());
-
- // 孵化活性效率曲线(非线性:flux² 使低活性效率极低)
- public float FluxEfficiency => NeutronFlux * NeutronFlux;
-
- // 是否处于休眠状态(无燃料或活性为0)
- public bool IsDormant => NeutronFlux < 0.01f || (FuelComp != null && !FuelComp.HasFuel);
-
- // 是否正在孵化(公开属性)
- public bool IsIncubating => isIncubating;
-
- // 设置活性值
- public void SetNeutronFlux(float value)
- {
- neutronFlux = Mathf.Clamp01(value);
- }
-
- // 切换模式(循环切换)
- public void CycleFluxMode()
- {
- fluxMode = (FluxMode)(((int)fluxMode + 1) % 4);
- }
-
- // 设置模式
- public void SetFluxMode(FluxMode mode)
- {
- fluxMode = mode;
- }
-
- // 获取模式名称
- public string GetModeName()
- {
- switch (fluxMode)
- {
- case FluxMode.Manual: return "手动";
- case FluxMode.Quality: return "品质";
- case FluxMode.Balance: return "平衡";
- case FluxMode.Speed: return "速度";
- default: return "?";
- }
- }
-
- // 获取模式缩写
- public string GetModeShort()
- {
- switch (fluxMode)
- {
- case FluxMode.Manual: return "M";
- case FluxMode.Quality: return "Q";
- case FluxMode.Balance: return "B";
- case FluxMode.Speed: return "S";
- default: return "?";
- }
- }
-
- // 缓存的ModExtension
- private OothecaIncubatorExtension cachedExtension;
-
- // 获取ModExtension的辅助属性
- private OothecaIncubatorExtension Ext
- {
- get
- {
- if (cachedExtension == null)
- {
- cachedExtension = def.GetModExtension() ?? OothecaIncubatorExtension.Default;
- }
- return cachedExtension;
- }
- }
-
- // 属性访问器
+ public string GetModeName() => fluxMode switch {
+ FluxMode.Manual => "手动",
+ FluxMode.Quality => "品质",
+ FluxMode.Balance => "平衡",
+ FluxMode.Speed => "速度",
+ _ => "?"
+ };
+
+ public string GetModeShort() => fluxMode switch {
+ FluxMode.Manual => "M",
+ FluxMode.Quality => "Q",
+ FluxMode.Balance => "B",
+ FluxMode.Speed => "S",
+ _ => "?"
+ };
+
public float SpeedMultiplier
{
get
{
- // 每MultiplierUpdateInterval ticks更新一次
if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
{
UpdateSpeedMultiplier();
@@ -132,366 +78,332 @@ namespace ArachnaeSwarm
}
}
- // 质量属性
public float QualityMultiplier => qualityMultiplier;
public float QualityProgress => qualityProgress;
public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f;
-
- // 获取基于乘数调整后的进度百分比
- public float AdjustedProgressPercent
- {
- get
- {
- if (incubationDuration <= 0) return 0f;
- return incubationProgress / incubationDuration;
- }
- }
+ public float AdjustedProgressPercent => incubationDuration > 0 ? incubationProgress / incubationDuration : 0f;
+ // === 描述方法 ===
public string GetSpeedFactorsDescription()
{
var builder = new StringBuilder();
builder.AppendLine("速度因子");
- builder.AppendLine();
-
- // 检查是否在孵化间中
- bool inIncubatorRoom = IsInIncubatorRoom();
- if (Ext.requiresIncubatorRoom)
- {
- builder.AppendLine(inIncubatorRoom ? "✓ 在孵化间中" : "✗ 不在孵化间中");
- }
-
builder.AppendLine();
builder.AppendLine("总速度倍率: " + SpeedMultiplier.ToStringPercent());
-
return builder.ToString().TrimEndNewlines();
}
- // 获取质量乘数的详细因子信息(用于工具提示)
public string GetQualityFactorsDescription()
{
var builder = new StringBuilder();
-
- builder.AppendLine("ARA_OothecaIncubator.QualityFactors".Translate());
+ builder.AppendLine("质量因子");
builder.AppendLine();
-
- // 1. 建筑血量损失百分比(如果启用)
- if (Ext.healthAffectsQuality)
- {
- float healthPercent = (float)HitPoints / MaxHitPoints;
- builder.AppendLine("ARA_OothecaIncubator.BuildingHealth".Translate(healthPercent.ToStringPercent()));
- }
-
- // 2. 房间的ARA_IncubatorQualityFactor(如果启用)
- if (Ext.useRoomQualityFactor)
- {
- float roomFactor = GetRoomQualityFactor();
- builder.AppendLine(roomFactor == 1.0f ?
- "ARA_OothecaIncubator.RoomFactorNormal".Translate() :
- $"{(roomFactor > 1.0f ? "✓" : "✗")} {"ARA_OothecaIncubator.RoomFactorModified".Translate()}{roomFactor.ToStringPercent()}");
- }
-
- // 3. 附近每一个ARA_Pawn_Ootheca的惩罚
- int nearbyOothecaCount = CountNearbyOtherOothecas();
- if (nearbyOothecaCount > 0)
- {
- builder.AppendLine("ARA_OothecaIncubator.NearbyOothecas".Translate(
- nearbyOothecaCount,
- Mathf.Min(nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit * 100, 100)));
- }
- else
- {
- builder.AppendLine("ARA_OothecaIncubator.NoNearbyOothecas".Translate());
- }
-
- // 显示检测半径
- builder.AppendLine();
- builder.AppendLine("ARA_OothecaIncubator.OothecaDetectionRadius".Translate(Ext.nearbyOothecaRadius));
-
- builder.AppendLine();
- builder.AppendLine("ARA_OothecaIncubator.TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent()));
-
+ builder.AppendLine("总质量倍率: " + QualityMultiplier.ToStringPercent());
return builder.ToString().TrimEndNewlines();
}
- // 构建呼叫幼虫描述
private string BuildCallLarvaDescription(IncubationConfig config)
{
var builder = new StringBuilder();
-
builder.AppendLine("ARA_OothecaIncubator.CallLarvaTitle".Translate());
builder.AppendLine();
builder.AppendLine("ARA_OothecaIncubator.LarvaWillCome".Translate());
builder.AppendLine(config.pawnKind.LabelCap);
- builder.AppendLine();
-
- // 显示幼虫搜索半径
- if (Ext.larvaSearchRadius < 999f)
- {
- builder.AppendLine("ARA_OothecaIncubator.LarvaSearchRadius".Translate(Ext.larvaSearchRadius));
- }
-
return builder.ToString().TrimEndNewlines();
}
-
- // 呼叫幼虫
- private void CallLarva()
+
+ // === 幼虫交互 ===
+ public void CallLarva()
{
- // 检查是否已经在孵化中或有幼虫在任务中
- if (isIncubating)
- {
- Messages.Message("ARA_OothecaIncubator.AlreadyIncubating".Translate() + " " + "ARA_OothecaIncubator.CancelFirst".Translate(),
- MessageTypeDefOf.RejectInput);
- return;
- }
-
- if (assignedLarva != null)
- {
- Messages.Message("ARA_OothecaIncubator.LarvaAlreadyOnWay".Translate(),
- MessageTypeDefOf.RejectInput);
- return;
- }
-
- // 查找可用的幼虫
+ if (isIncubating || assignedLarva != null) return;
var larva = FindLarva();
if (larva == null)
{
- Messages.Message("ARA_OothecaIncubator.NoLarvaeFound".Translate() + " " + "ARA_OothecaIncubator.LarvaMustBeRace".Translate(),
- MessageTypeDefOf.RejectInput);
+ Messages.Message("ARA_OothecaIncubator.NoLarvaeFound".Translate(), MessageTypeDefOf.RejectInput);
return;
}
- // 给幼虫分配任务
var job = JobMaker.MakeJob(ARA_JobDefOf.ARA_OperateIncubator, this);
- job.count = 1;
larva.jobs.TryTakeOrderedJob(job, JobTag.MiscWork);
-
assignedLarva = larva;
-
- Messages.Message("ARA_OothecaIncubator.LarvaCalled".Translate() + " " + "ARA_OothecaIncubator.ArriveShortly".Translate(),
- MessageTypeDefOf.PositiveEvent);
+ Messages.Message("ARA_OothecaIncubator.LarvaCalled".Translate(), MessageTypeDefOf.PositiveEvent);
}
- // 幼虫开始操作
+ private Pawn FindLarva()
+ {
+ float searchRadius = 30f;
+ Map map = Map;
+ if (map == null) return null;
+
+ foreach (var pawn in map.mapPawns.SpawnedPawnsInFaction(Faction))
+ {
+ if (pawn.def.defName == "ArachnaeBase_Race_Larva" && !pawn.Downed && !pawn.Dead)
+ {
+ if (Position.DistanceTo(pawn.Position) <= searchRadius)
+ return pawn;
+ }
+ }
+ return null;
+ }
+
public void NotifyLarvaArrived(Pawn larva)
{
- // 验证幼虫种族
- if (larva.def.defName != "ArachnaeBase_Race_Larva")
- {
- ArachnaeLog.Debug($"Invalid larva arrived: {larva.def.defName}");
- return;
- }
-
- // 开始操作计时(3秒 = 180 ticks)
larvaOperateTicksRemaining = 180;
assignedLarva = larva;
-
- // 显示消息
- Messages.Message("ARA_OothecaIncubator.LarvaArrived".Translate() + " " + "ARA_OothecaIncubator.ActivatingOotheca".Translate(),
- MessageTypeDefOf.SilentInput);
}
- // 幼虫完成操作(由JobDriver调用)
public void NotifyLarvaOperationComplete(Pawn larva)
{
- // 验证是当前分配的幼虫
- if (larva != assignedLarva)
- {
- ArachnaeLog.Debug("Larva operation complete called with wrong larva.");
- return;
- }
-
var config = IncubatorData?.SelectedConfig;
- if (config == null)
- {
- ArachnaeLog.Debug("No incubation config selected when larva completed operation.");
- return;
- }
+ if (config == null) return;
- // 保存孵化信息
incubatingPawnKind = config.pawnKind;
- incubationDuration = config.daysRequired * 60000f; // RimWorld中1天=60000ticks
+ incubationDuration = config.daysRequired * 60000f;
incubationProgress = 0f;
- isIncubating = true;
-
- // 初始化质量系统
- qualityTotal = incubationDuration; // 质量总量与孵化时间相同
+ qualityTotal = incubationDuration;
qualityProgress = 0f;
- UpdateQualityMultiplier();
-
- // 更新速度乘数
- UpdateSpeedMultiplier();
-
- // 清除幼虫引用(幼虫已在JobDriver中被删除)
+ isIncubating = true;
assignedLarva = null;
larvaOperateTicksRemaining = 0;
- // 显示消息
- Messages.Message("ARA_OothecaIncubator.IncubationStarted".Translate() + " " + incubatingPawnKind.LabelCap + ". " +
- "ARA_OothecaIncubator.ProcessWillComplete".Translate() + " " + config.daysRequired + " " + "ARA_OothecaIncubator.DaysBaseTime".Translate(),
- MessageTypeDefOf.PositiveEvent);
+ Messages.Message("ARA_OothecaIncubator.IncubationStarted".Translate() + " " + incubatingPawnKind.LabelCap, MessageTypeDefOf.PositiveEvent);
}
-
- // 取消孵化
- private void CancelIncubation()
- {
- if (!isIncubating) return;
+ public void CancelIncubation()
+ {
isIncubating = false;
incubationProgress = 0f;
incubationDuration = 0f;
incubatingPawnKind = null;
- qualityProgress = 0f;
- qualityTotal = 0f;
-
- Messages.Message("ARA_OothecaIncubator.IncubationCancelled".Translate() + " " + "ARA_OothecaIncubator.ContentsLost".Translate(),
- MessageTypeDefOf.NeutralEvent);
+ Messages.Message("ARA_OothecaIncubator.IncubationCancelled".Translate(), MessageTypeDefOf.NeutralEvent);
}
- // 完成孵化
private void CompleteIncubation()
{
if (incubatingPawnKind == null) return;
- // 计算最终质量百分比
- float finalQualityPercent = QualityPercent;
- // 生成新的pawn
+
+ float finalQuality = QualityPercent;
var pawn = PawnGenerator.GeneratePawn(incubatingPawnKind, Faction);
- // 应用质量影响(包括Hediff奖励)
- ApplyQualityEffects(pawn, finalQualityPercent);
- // 尝试将pawn放置在地图上
- var spawnPos = Position;
- if (!pawn.Spawned)
- {
- GenSpawn.Spawn(pawn, spawnPos, Map);
- }
- // 重置孵化状态
+ ApplyQualityEffects(pawn, finalQuality);
+
+ GenSpawn.Spawn(pawn, Position, Map);
+
isIncubating = false;
- incubationProgress = 0f;
- incubationDuration = 0f;
incubatingPawnKind = null;
- qualityProgress = 0f;
- qualityTotal = 0f;
- // 显示消息
- string qualityText = finalQualityPercent >= 0.9f ? "ARA_OothecaIncubator.QualityExcellent".Translate() :
- finalQualityPercent >= 0.7f ? "ARA_OothecaIncubator.QualityGood".Translate() :
- finalQualityPercent >= 0.5f ? "ARA_OothecaIncubator.QualityAverage".Translate() :
- finalQualityPercent >= 0.3f ? "ARA_OothecaIncubator.QualityPoor".Translate() : "ARA_OothecaIncubator.QualityVeryPoor".Translate();
- Messages.Message("ARA_OothecaIncubator.IncubationComplete".Translate() + " " + pawn.LabelCap + " " +
- "ARA_OothecaIncubator.HasEmergedWith".Translate() + " " + qualityText + " " +
- "ARA_OothecaIncubator.Quality".Translate() + " (" + finalQualityPercent.ToStringPercent() + ").",
- MessageTypeDefOf.PositiveEvent);
+ Messages.Message("ARA_OothecaIncubator.IncubationComplete".Translate() + " " + pawn.LabelCap, MessageTypeDefOf.PositiveEvent);
Destroy();
}
- // 显示额外信息(方案A:孵化信息在前,维护信息在后)
-public override string GetInspectString()
-{
- var builder = new StringBuilder();
+ // === Tick方法 ===
+ public override void Tick()
+ {
+ base.Tick();
- // === 第一部分:孵化状态 ===
- if (isIncubating && incubatingPawnKind != null)
- {
- float progressPercent = AdjustedProgressPercent;
- float daysRemaining = GetRemainingDays();
- float hoursRemaining = GetRemainingHours();
-
- builder.AppendLine("正在孵化: " + incubatingPawnKind.LabelCap);
- builder.AppendLine("进度: " + progressPercent.ToStringPercent());
-
- // 显示剩余时间
- string timeText = "剩余时间: " + daysRemaining.ToString("F1") + " 天";
- if (hoursRemaining > 0.1f && daysRemaining < 1f)
- {
- timeText += " (" + hoursRemaining.ToString("F1") + " 小时)";
- }
- builder.AppendLine(timeText);
-
- // 显示速度和质量
- builder.Append("速度: " + SpeedMultiplier.ToStringPercent() + " | 质量: " + QualityMultiplier.ToStringPercent());
- }
- else if (assignedLarva != null)
- {
- if (larvaOperateTicksRemaining > 0)
- {
- float secondsRemaining = larvaOperateTicksRemaining / 60f;
- builder.Append("幼虫激活中: " + secondsRemaining.ToString("F1") + " 秒");
- }
- else
- {
- builder.Append("幼虫赶路中...");
- }
- }
- else if (!isIncubating)
- {
- var config = IncubatorData?.SelectedConfig;
- if (config != null)
- {
- builder.AppendLine("孵化目标: " + config.pawnKind.LabelCap);
- builder.Append("速度: " + SpeedMultiplier.ToStringPercent() + " | 质量: " + QualityMultiplier.ToStringPercent());
- }
- }
-
- // === 第二部分:基类信息(包括维护度等) ===
- var baseString = base.GetInspectString();
- if (!string.IsNullOrEmpty(baseString))
- {
- if (builder.Length > 0) builder.AppendLine();
- builder.Append(baseString);
- }
-
- return builder.ToString().TrimEndNewlines();
-}
-
- // 获取剩余时间(ticks)
- public float GetRemainingTicks()
- {
- if (!isIncubating || incubationDuration <= incubationProgress) return 0f;
-
- float remainingProgress = incubationDuration - incubationProgress;
- float currentSpeed = SpeedMultiplier;
-
- // 如果速度为0或负数,返回一个很大的数
- if (currentSpeed <= 0) return float.MaxValue;
-
- return remainingProgress / currentSpeed;
- }
-
- // 获取剩余天数
- public float GetRemainingDays()
- {
- return GetRemainingTicks() / 60000f; // RimWorld中1天=60000ticks
- }
-
- // 获取剩余小时
- public float GetRemainingHours()
- {
- float remainingTicks = GetRemainingTicks();
- return (remainingTicks % 60000f) / 2500f;
- }
-
- // Gizmos
- public override IEnumerable GetGizmos()
- {
- // 过滤掉拆除按钮
- foreach (var gizmo in base.GetGizmos())
+ if (larvaOperateTicksRemaining > 0)
{
- // 跳过拆除相关的 Gizmo
- if (gizmo is Command_Action cmd && cmd.defaultLabel != null &&
- (cmd.defaultLabel.ToString().Contains("拆除") || cmd.defaultLabel.ToString().Contains("Deconstruct")))
- continue;
+ larvaOperateTicksRemaining--;
+ }
+
+ if (isIncubating)
+ {
+ if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
+ {
+ UpdateSpeedMultiplier();
+ UpdateQualityMultiplier();
+ }
+
+ if (IsAutoMode && Find.TickManager.TicksGame % 250 == 0)
+ {
+ CalculateAutoFlux();
+ }
+
+ if (FuelComp != null && neutronFlux > 0.01f)
+ {
+ float fuelPerTick = (50f * FluxEfficiency) / 60000f;
+ FuelComp.ConsumeFuel(fuelPerTick);
+ }
+
+ if (IsDormant)
+ {
+ float qualityDecay = (qualityTotal * 0.1f) / 60000f;
+ qualityProgress = Mathf.Max(0f, qualityProgress - qualityDecay);
- yield return gizmo;
+ if (qualityProgress <= 0 && qualityTotal > 0)
+ {
+ Messages.Message("卵囊因品质归零而损坏!", this, MessageTypeDefOf.NegativeEvent);
+ Destroy(DestroyMode.KillFinalize);
+ return;
+ }
+ }
+ else
+ {
+ float fluxSpeed = SpeedMultiplier * FluxEfficiency * 5f;
+ incubationProgress += fluxSpeed;
+
+ float qualityBonus = 1f + (1f - neutronFlux) * 0.5f;
+ float qualityGain = SpeedMultiplier * QualityMultiplier * qualityBonus;
+ qualityProgress = Mathf.Min(qualityProgress + qualityGain, qualityTotal);
+ }
+
+ if (incubationProgress >= incubationDuration)
+ {
+ CompleteIncubation();
+ }
+ }
+ }
+
+ private void CalculateAutoFlux()
+ {
+ if (fluxMode == FluxMode.Manual) return;
+
+ float targetFlux = 0.5f;
+ float incubationPercent = AdjustedProgressPercent;
+ float gQualityPercent = QualityPercent;
+ float gap = gQualityPercent - incubationPercent;
+
+ switch (fluxMode)
+ {
+ case FluxMode.Quality:
+ if (gQualityPercent >= 0.98f) targetFlux = 1.0f;
+ else if (gap > 0.2f) targetFlux = 0.6f;
+ else if (gap > 0.1f) targetFlux = 0.45f;
+ else if (gap > 0f) targetFlux = 0.35f;
+ else targetFlux = 0.2f;
+ break;
+
+ case FluxMode.Speed:
+ if (gQualityPercent >= 0.95f) targetFlux = 1.0f;
+ else if (gQualityPercent < 0.3f && incubationPercent > 0.5f) targetFlux = 0.7f;
+ else if (gQualityPercent < 0.2f && incubationPercent > 0.7f) targetFlux = 0.5f;
+ else targetFlux = 1.0f;
+ break;
+
+ case FluxMode.Balance:
+ default:
+ if (gQualityPercent >= 0.95f) targetFlux = 1.0f;
+ else if (gap > 0.15f) targetFlux = 0.8f;
+ else if (gap > 0.05f) targetFlux = 0.6f;
+ else if (gap > -0.05f) targetFlux = 0.5f;
+ else if (gap > -0.15f) targetFlux = 0.35f;
+ else targetFlux = 0.2f;
+ break;
}
- // 只有玩家派系才显示Gizmo
+ if (FuelComp != null && FuelComp.Fuel < 2f) targetFlux = Mathf.Min(targetFlux, 0.2f);
+
+ float adjustSpeed = 0.03f;
+ if (neutronFlux < targetFlux) neutronFlux = Mathf.Min(neutronFlux + adjustSpeed, targetFlux);
+ else if (neutronFlux > targetFlux) neutronFlux = Mathf.Max(neutronFlux - adjustSpeed, targetFlux);
+
+ neutronFlux = Mathf.Clamp(neutronFlux, 0.1f, 1.0f);
+ }
+
+ // === 质量效果与奖励 ===
+ private void ApplyQualityEffects(Pawn pawn, float qualityPercent)
+ {
+ ApplyHediffRewards(pawn, qualityPercent);
+ }
+
+ private void ApplyHediffRewards(Pawn pawn, float qualityPercent)
+ {
+ var config = IncubatorData?.SelectedConfig;
+ if (config == null) return;
+
+ List rewardHediffs = config.GetRewardHediffs(qualityPercent);
+ if (rewardHediffs == null || rewardHediffs.Count == 0) return;
+
+ int appliedCount = 0;
+ foreach (var hediffDef in rewardHediffs)
+ {
+ if (hediffDef == null) continue;
+ if (!pawn.health.hediffSet.HasHediff(hediffDef))
+ {
+ pawn.health.AddHediff(HediffMaker.MakeHediff(hediffDef, pawn));
+ appliedCount++;
+ }
+ }
+
+ if (appliedCount > 0)
+ {
+ string message = config.GetRewardMessage(qualityPercent);
+ if (string.IsNullOrEmpty(message))
+ message = "ARA_QualityReward_Default".Translate(pawn.LabelShortCap, appliedCount);
+ Messages.Message(message, pawn, MessageTypeDefOf.PositiveEvent);
+ }
+ }
+
+ // === 辅助与UI ===
+ private void UpdateSpeedMultiplier()
+ {
+ speedMultiplier = 1.0f;
+ lastMultiplierUpdateTick = Find.TickManager.TicksGame;
+ }
+
+ private void UpdateQualityMultiplier()
+ {
+ qualityMultiplier = 1.0f;
+ }
+
+ public float GetRemainingTicks()
+ {
+ if (!isIncubating) return 0f;
+ return Mathf.Max(0f, (incubationDuration - incubationProgress) / (SpeedMultiplier * FluxEfficiency * 5f));
+ }
+
+ public float GetRemainingDays() => GetRemainingTicks() / 60000f;
+ public float GetRemainingHours() => (GetRemainingTicks() % 60000f) / 2500f;
+
+ public override string GetInspectString()
+ {
+ StringBuilder sb = new StringBuilder();
+ if (isIncubating && incubatingPawnKind != null)
+ {
+ sb.AppendLine("正在孵化: " + incubatingPawnKind.LabelCap);
+ sb.AppendLine("进度: " + AdjustedProgressPercent.ToStringPercent());
+ sb.AppendLine("剩余时间: " + GetRemainingDays().ToString("F1") + " 天");
+ sb.Append("速度: " + SpeedMultiplier.ToStringPercent() + " | 质量: " + QualityMultiplier.ToStringPercent());
+ }
+ else if (assignedLarva != null)
+ {
+ sb.Append(larvaOperateTicksRemaining > 0 ? "幼虫激活中..." : "幼虫赶路中...");
+ }
+ else
+ {
+ var config = IncubatorData?.SelectedConfig;
+ if (config != null)
+ {
+ sb.AppendLine("目标: " + config.pawnKind.LabelCap);
+ sb.Append("速度: " + SpeedMultiplier.ToStringPercent());
+ }
+ }
+
+ var baseStr = base.GetInspectString();
+ if (!string.IsNullOrEmpty(baseStr))
+ {
+ if (sb.Length > 0) sb.AppendLine();
+ sb.Append(baseStr);
+ }
+ return sb.ToString().TrimEndNewlines();
+ }
+
+ public override IEnumerable GetGizmos()
+ {
+ foreach (var gizmo in base.GetGizmos())
+ {
+ if (gizmo is Command_Action cmd && cmd.defaultLabel != null)
+ {
+ string label = cmd.defaultLabel.ToString();
+ if (label.Contains("拆除") || label.Contains("Deconstruct") || label.Contains("半径") || label.Contains("Radius"))
+ continue;
+ }
+ yield return gizmo;
+ }
+
if (Faction == Faction.OfPlayer)
{
- // 始终显示双进度条Gizmo
yield return new Gizmo_IncubationProgress(this);
-
- // 中子通量控制 Gizmo
yield return new Gizmo_NeutronFlux(this);
-
- // 不在孵化中且研究完成时才显示呼叫按钮
+
var config = IncubatorData?.SelectedConfig;
if (!isIncubating && config != null && config.IsResearchComplete)
{
@@ -499,642 +411,26 @@ public override string GetInspectString()
{
defaultLabel = "ARA_OothecaIncubator.CallLarva".Translate(),
defaultDesc = BuildCallLarvaDescription(config),
- icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false) ?? BaseContent.BadTex,
- action = CallLarva,
- hotKey = KeyBindingDefOf.Misc3
+ icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false),
+ action = CallLarva
};
}
-
- // 如果正在孵化,显示取消按钮
+
if (isIncubating)
{
yield return new Command_Action
{
defaultLabel = "ARA_OothecaIncubator.CancelIncubation".Translate(),
- defaultDesc = "ARA_OothecaIncubator.CancelIncubationDesc".Translate(),
- icon = ContentFinder.Get("UI/Commands/Cancel", false) ?? TexCommand.ClearPrioritizedWork,
+ icon = ContentFinder.Get("UI/Commands/Cancel", false),
action = CancelIncubation
};
}
}
}
-
- // 创建目标切换Gizmo
- private Gizmo CreateTargetSwitchGizmo()
- {
- var configs = IncubatorData?.IncubationConfigs;
- if (configs == null || configs.Count == 0) return null;
-
- var props = IncubatorData?.props as CompProperties_IncubatorData;
- var selectedConfig = IncubatorData?.SelectedConfig;
-
- var switchButton = new Command_Action
- {
- defaultLabel = BuildSwitchButtonLabel(selectedConfig, props),
- defaultDesc = BuildSwitchButtonDescription(selectedConfig, props),
- icon = LoadIcon(selectedConfig?.buttonIconPath ?? props?.defaultIconPath),
- action = ShowSelectionMenu,
- hotKey = KeyBindingDefOf.Misc2
- };
-
- // 如果当前配置不可用(研究未完成),禁用按钮
- if (selectedConfig != null && !selectedConfig.IsResearchComplete)
- {
- if (selectedConfig.requiredResearch != null)
- {
- switchButton.Disable($"Requires research: {selectedConfig.requiredResearch.LabelCap}");
- }
- }
-
- return switchButton;
- }
-
- // 构建切换按钮标签
- private string BuildSwitchButtonLabel(IncubationConfig config, CompProperties_IncubatorData props)
- {
- if (config != null && config.pawnKind != null)
- {
- return (props?.buttonLabel ?? "ARA_OothecaIncubator.IncubateLabel").Translate(config.pawnKind.LabelCap);
- }
- return (props?.buttonLabel ?? "ARA_OothecaIncubator.IncubateLabel").Translate("None");
- }
-
- // 构建切换按钮描述
- private string BuildSwitchButtonDescription(IncubationConfig config, CompProperties_IncubatorData props)
- {
- var builder = new StringBuilder();
-
- // 第一部分:按钮功能说明
- builder.AppendLine((props?.buttonDesc ?? "ARA_OothecaIncubator.ButtonDesc").Translate());
- builder.AppendLine();
-
- if (config != null)
- {
- // 第二部分:当前选择的详细信息
- if (config.pawnKind != null)
- {
- builder.AppendLine($"ARA_OothecaIncubator.ButtonLabel".Translate(config.pawnKind.LabelCap));
- if (!string.IsNullOrEmpty(config.pawnKind.description))
- {
- builder.AppendLine(config.pawnKind.description);
- }
- }
-
- builder.AppendLine($"ARA_OothecaIncubator.IncubationTime".Translate(config.daysRequired));
-
- if (config.requiredResearch != null)
- {
- if (config.requiredResearch.IsFinished)
- builder.AppendLine($"Research: {config.requiredResearch.LabelCap} (Completed)");
- else
- builder.AppendLine($"Research: {config.requiredResearch.LabelCap} (Required)");
- }
- }
- else
- {
- builder.AppendLine("No target selected");
- }
-
- builder.AppendLine();
- builder.AppendLine("ARA_OothecaIncubator.ButtonDesc".Translate());
-
- return builder.ToString().TrimEndNewlines();
- }
-
- // 加载图标
- private Texture2D LoadIcon(string path)
- {
- if (!string.IsNullOrEmpty(path))
- {
- var icon = ContentFinder.Get(path, false);
- if (icon != null) return icon;
- }
- return ContentFinder.Get("UI/Commands/Default", false) ?? BaseContent.BadTex;
- }
-
- // 显示选择菜单
- private void ShowSelectionMenu()
- {
- var configs = IncubatorData?.IncubationConfigs;
- var props = IncubatorData?.props as CompProperties_IncubatorData;
- if (configs == null || configs.Count == 0) return;
-
- var options = new List();
- int currentIndex = IncubatorData.GetSelectedIndex();
-
- for (int i = 0; i < configs.Count; i++)
- {
- int index = i; // 捕获索引
- var config = configs[i];
- if (config == null || config.pawnKind == null) continue;
-
- string label = config.pawnKind.LabelCap;
- string description = config.GetDescription();
-
- // 标记当前选择的项目
- string prefix = (i == currentIndex) ? "✓ " : " ";
-
- // 创建选项
- var option = new FloatMenuOption(
- prefix + label,
- () => SwitchToConfig(index)
- );
-
- // 设置工具提示
- option.tooltip = description;
-
- // 如果研究未完成,禁用选项
- if (!config.IsResearchComplete)
- {
- option.Label = prefix + label;
- option.Disabled = true;
- option.tooltip = description + "\n\n "+ "ARA_OothecaIncubator.ResearchRequired".Translate() + " " + config.requiredResearch.LabelCap;
- }
-
- options.Add(option);
- }
-
- if (options.Count > 0)
- {
- Find.WindowStack.Add(new FloatMenu(options,
- (props?.menuTitle ?? "ARA_OothecaIncubator.MenuTitle").Translate()));
- }
- }
-
- // 切换到特定配置
- private void SwitchToConfig(int index)
- {
- if (IncubatorData != null)
- {
- IncubatorData.SwitchToConfig(index);
- var config = IncubatorData.SelectedConfig;
- if (config != null && config.pawnKind != null)
- {
- Messages.Message($"ARA_OothecaIncubator.TargetSwitched".Translate(config.pawnKind.LabelCap),
- this, MessageTypeDefOf.SilentInput);
- }
- }
- }
-
- // 查找幼虫 - 现在通过种族查找,并考虑搜索半径
- private Pawn FindLarva()
- {
- // 查找地图中属于玩家派系的ArachnaeBase_Race_Larva幼虫
- var map = Map;
- if (map == null) return null;
-
- // 获取搜索半径
- float searchRadius = Ext.larvaSearchRadius;
-
- foreach (var pawn in map.mapPawns.SpawnedPawnsInFaction(Faction))
- {
- // 检查pawn种族是否为幼虫
- if (pawn.def.defName == "ArachnaeBase_Race_Larva")
- {
- // 检查是否在搜索半径内(如果搜索半径小于999)
- if (searchRadius < 999f)
- {
- float distance = pawn.Position.DistanceTo(Position);
- if (distance > searchRadius)
- continue;
- }
-
- // 检查pawn是否能够移动且没有其他重要任务
- if (!pawn.Downed && !pawn.InMentalState &&
- pawn.mindState != null &&
- (pawn.CurJob == null || pawn.CurJob.def != ARA_JobDefOf.ARA_OperateIncubator))
- {
- return pawn;
- }
- }
- }
-
- return null;
- }
-
- // 孵化进度 - 使用override而不是new
-protected override void Tick()
-{
- base.Tick();
-
- // 减少操作剩余时间(仅用于显示)
- if (larvaOperateTicksRemaining > 0)
- {
- larvaOperateTicksRemaining--;
- }
-
- // 只有在孵化过程中才消耗资源和自动调节
- if (isIncubating)
- {
- // 自动模式计算(每250ticks更新一次)
- if (IsAutoMode && Find.TickManager.TicksGame % 250 == 0)
- {
- CalculateAutoFlux();
- }
-
- // 消耗虫蜜(基于孵化活性)
- if (FuelComp != null && neutronFlux > 0.01f)
- {
- // 基础消耗:50虫蜜/天 在100%活性,非线性
- float baseConsumptionPerDay = 50f * FluxEfficiency;
- float consumptionPerTick = baseConsumptionPerDay / 60000f;
- FuelComp.ConsumeFuel(consumptionPerTick);
- }
- }
-
- // 更新孵化进度和质量进度
- if (isIncubating)
- {
- // 更新乘数
- if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
- {
- UpdateSpeedMultiplier();
- UpdateQualityMultiplier();
- }
-
- // 检查是否休眠
- if (IsDormant)
- {
- // 休眠状态:品质进度下降
- float qualityDecayPerTick = 0.1f / 60000f; // 每天下降10%
- qualityProgress = Mathf.Max(0, qualityProgress - qualityDecayPerTick * incubationDuration);
-
- // 品质归零 → 破坏卵
- if (qualityProgress <= 0 && qualityTotal > 0)
- {
- Messages.Message("卵囊因品质归零而损坏!", this, MessageTypeDefOf.NegativeEvent);
- Destroy(DestroyMode.KillFinalize);
- return;
- }
- }
- else
- {
- // 孵化进度:受活性效率影响(100%活性时5倍速度)
- float fluxSpeed = SpeedMultiplier * FluxEfficiency * 5f;
- incubationProgress += fluxSpeed;
-
- // 品质进度:独立增长,低活性时有额外加速
- // - 高活性 = 快速孵化,品质正常速度
- // - 低活性 = 慢速孵化,品质加速累积
- // 加速公式:1 + (1 - flux) * 0.5,即活性0%→1.5x,活性100%→1x
- float qualityBonus = 1f + (1f - neutronFlux) * 0.5f;
- float qualityGain = SpeedMultiplier * QualityMultiplier * qualityBonus;
- qualityProgress = Mathf.Min(qualityProgress + qualityGain, qualityTotal);
- }
-
- if (incubationProgress >= incubationDuration)
- {
- CompleteIncubation();
- }
- }
-}
-// 自动模式:根据选择的模式计算活性
-private void CalculateAutoFlux()
-{
- // 手动模式不自动调节
- if (fluxMode == FluxMode.Manual) return;
-
- float targetFlux = 0.5f;
-
- // 计算进度百分比
- float incubationPercent = incubationDuration > 0 ? incubationProgress / incubationDuration : 0f;
- float qualityPercent = qualityTotal > 0 ? qualityProgress / qualityTotal : 0f;
- float gap = qualityPercent - incubationPercent; // 正=品质领先,负=品质落后
-
- switch (fluxMode)
- {
- case FluxMode.Quality:
- // 品质优先:确保品质始终领先于孵化进度
- // 目标:品质 >= 孵化进度 + 10%
- if (qualityPercent >= 0.98f)
- {
- // 品质已满,全速
- targetFlux = 1.0f;
- }
- else if (gap > 0.2f)
- {
- // 品质大幅领先,可以加速
- targetFlux = 0.6f;
- }
- else if (gap > 0.1f)
- {
- // 品质小幅领先
- targetFlux = 0.45f;
- }
- else if (gap > 0f)
- {
- // 品质略微领先
- targetFlux = 0.35f;
- }
- else
- {
- // 品质落后或持平,放慢追品质
- targetFlux = 0.2f;
- }
- break;
-
- case FluxMode.Speed:
- // 速度优先:尽快完成孵化,允许品质落后
- // 目标:最大化速度,只在品质太低时稍微降速
- if (qualityPercent >= 0.95f)
- {
- // 品质已满
- targetFlux = 1.0f;
- }
- else if (qualityPercent < 0.3f && incubationPercent > 0.5f)
- {
- // 品质太低且孵化过半,稍微降速
- targetFlux = 0.7f;
- }
- else if (qualityPercent < 0.2f && incubationPercent > 0.7f)
- {
- // 品质极低且快完成,降速救品质
- targetFlux = 0.5f;
- }
- else
- {
- // 正常全速
- targetFlux = 1.0f;
- }
- break;
-
- case FluxMode.Balance:
- default:
- // 平衡模式:追求品质和孵化同时完成
- // 目标:gap ≈ 0
- if (qualityPercent >= 0.95f)
- {
- // 品质已满,全速
- targetFlux = 1.0f;
- }
- else if (gap > 0.15f)
- {
- // 品质大幅领先,加速孵化
- targetFlux = 0.8f;
- }
- else if (gap > 0.05f)
- {
- // 品质小幅领先
- targetFlux = 0.6f;
- }
- else if (gap > -0.05f)
- {
- // 接近平衡
- targetFlux = 0.5f;
- }
- else if (gap > -0.15f)
- {
- // 品质小幅落后
- targetFlux = 0.35f;
- }
- else
- {
- // 品质大幅落后,降速追品质
- targetFlux = 0.2f;
- }
- break;
- }
-
- // === 资源紧张保护(所有自动模式通用)===
- if (FuelComp != null && FuelComp.Fuel < 2f)
- {
- targetFlux = Mathf.Min(targetFlux, 0.2f);
- }
-
- if (MaintenanceComp != null)
- {
- float maintenancePercent = MaintenanceComp.CurrentMaintenance / MaintenanceComp.Props.maxMaintenance;
- if (maintenancePercent < 0.15f)
- {
- targetFlux = Mathf.Min(targetFlux, 0.15f);
- }
- }
-
- // 平滑调节
- float adjustSpeed = 0.03f;
- if (neutronFlux < targetFlux)
- neutronFlux = Mathf.Min(neutronFlux + adjustSpeed, targetFlux);
- else if (neutronFlux > targetFlux)
- neutronFlux = Mathf.Max(neutronFlux - adjustSpeed, targetFlux);
-
- neutronFlux = Mathf.Clamp(neutronFlux, 0.1f, 1.0f);
-}
-
- // 应用质量效果到生成的pawn
- private void ApplyQualityEffects(Pawn pawn, float qualityPercent)
- {
- ApplyHediffRewards(pawn, qualityPercent);
- }
- // === 新增:应用Hediff奖励方法 ===
- private void ApplyHediffRewards(Pawn pawn, float qualityPercent)
- {
- var config = IncubatorData?.SelectedConfig;
- if (config == null) return;
-
- // 获取应奖励的Hediff列表
- List rewardHediffs = config.GetRewardHediffs(qualityPercent);
- if (rewardHediffs == null || rewardHediffs.Count == 0) return;
-
- // 应用Hediff
- int appliedCount = 0;
- foreach (var hediffDef in rewardHediffs)
- {
- if (hediffDef == null) continue;
-
- try
- {
- // 检查pawn是否已经有这个Hediff(避免重复)
- if (!pawn.health.hediffSet.HasHediff(hediffDef))
- {
- // 创建并添加Hediff
- Hediff hediff = HediffMaker.MakeHediff(hediffDef, pawn);
- pawn.health.AddHediff(hediff);
- appliedCount++;
- }
- }
- catch (System.Exception ex)
- {
- Log.Error($"Failed to apply hediff {hediffDef?.defName} to pawn {pawn.Label}: {ex.Message}");
- }
- }
-
- // 显示奖励消息
- if (appliedCount > 0)
- {
- string message = config.GetRewardMessage(qualityPercent);
- if (string.IsNullOrEmpty(message))
- {
- message = "ARA_QualityReward_Default".Translate(pawn.LabelShortCap, appliedCount);
- }
-
- Messages.Message(message, pawn, MessageTypeDefOf.PositiveEvent);
- }
- }
-
- // 检查是否在孵化间中
- private bool IsInIncubatorRoom()
- {
- // 如果不要求孵化间,总是返回true
- if (!Ext.requiresIncubatorRoom)
- return true;
-
- var room = this.GetRoom();
- if (room == null) return false;
-
- return room.Role != null && room.Role.defName == "ARA_Incubator_Room";
- }
-
- // 计算周围指定半径内的营养液数量
- private int CountNearbyNutrientSolutions()
- {
- var map = Map;
- if (map == null) return 0;
-
- int count = 0;
- int radius = Ext.NutrientSolutionRadiusInt;
-
- // 检查指定半径范围内的所有单元格
- for (int x = -radius; x <= radius; x++)
- {
- for (int y = -radius; y <= radius; y++)
- {
- IntVec3 cell = Position + new IntVec3(x, 0, y);
-
- // 检查单元格是否在地图内
- if (cell.InBounds(map))
- {
- // 检查地形是否为营养液
- TerrainDef terrain = map.terrainGrid.TerrainAt(cell);
- if (terrain != null && terrain.defName == "ARA_Incubator_Nutrient_Solution")
- {
- count++;
- }
- }
- }
- }
-
- return count;
- }
-
- // 计算房间质量因子
- private float GetRoomQualityFactor()
- {
- // 如果不使用房间质量因子,返回1.0
- if (!Ext.useRoomQualityFactor)
- return 1.0f;
-
- var room = this.GetRoom();
- if (room == null) return 1.0f;
-
- // 获取房间的ARA_IncubatorQualityFactor统计值
- // 如果没有这个统计,返回默认值
- var statDef = DefDatabase.GetNamedSilentFail("ARA_IncubatorQualityFactor");
- if (statDef != null)
- {
- return room.GetStat(statDef);
- }
-
- return 1.0f;
- }
-
- // 计算附近其他Ootheca的数量
- private int CountNearbyOtherOothecas()
- {
- var map = Map;
- if (map == null) return 0;
-
- int count = 0;
- var allBuildings = map.listerThings.ThingsOfDef(this.def);
-
- foreach (var building in allBuildings)
- {
- // 排除自己
- if (building == this) continue;
-
- // 检查是否为ARA_Pawn_Ootheca
- if (building.def.defName == "ARA_Pawn_Ootheca")
- {
- bool isNearby = false;
-
- // 检查是否在同房间内
- if (Ext.checkSameRoomForOotheca)
- {
- var room = building.GetRoom();
- if (room != null && room == this.GetRoom())
- {
- isNearby = true;
- }
- }
-
- // 检查是否在指定半径内
- if (!isNearby)
- {
- float distance = building.Position.DistanceTo(this.Position);
- if (distance <= Ext.nearbyOothecaRadius)
- {
- isNearby = true;
- }
- }
-
- if (isNearby)
- {
- count++;
- }
- }
- }
-
- return count;
- }
-
- // 更新速度乘数(已移除营养液逻辑,后续将使用 CompRefuelableNutrition)
- private void UpdateSpeedMultiplier()
- {
- float multiplier = 1.0f;
-
- // 检查是否在孵化间中(如果要求)
- if (Ext.requiresIncubatorRoom && !IsInIncubatorRoom())
- {
- multiplier *= Ext.speedPenaltyOutsideIncubator;
- }
-
- speedMultiplier = multiplier;
- lastMultiplierUpdateTick = Find.TickManager.TicksGame;
- }
-
- // 更新质量乘数
- private void UpdateQualityMultiplier()
- {
- float multiplier = 1.0f;
-
- // 1. 建筑血量损失百分比(如果启用)
- if (Ext.healthAffectsQuality)
- {
- float healthPercent = (float)HitPoints / MaxHitPoints;
- multiplier *= healthPercent;
- }
-
- // 2. 房间的ARA_IncubatorQualityFactor(如果启用)
- if (Ext.useRoomQualityFactor)
- {
- float roomFactor = GetRoomQualityFactor();
- multiplier *= roomFactor;
- }
-
- // 3. 附近每一个ARA_Pawn_Ootheca的惩罚
- int nearbyOothecaCount = CountNearbyOtherOothecas();
- float oothecaPenalty = Mathf.Max(0f, 1.0f - (nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit));
- multiplier *= oothecaPenalty;
-
- // 确保乘数在合理范围内(0-1)
- qualityMultiplier = Mathf.Clamp(multiplier, 0f, 1.0f);
- }
-
- // 保存/加载
public override void ExposeData()
{
base.ExposeData();
-
Scribe_Values.Look(ref isIncubating, "isIncubating", false);
Scribe_Values.Look(ref incubationProgress, "incubationProgress", 0f);
Scribe_Values.Look(ref incubationDuration, "incubationDuration", 0f);
@@ -1142,12 +438,9 @@ private void CalculateAutoFlux()
Scribe_References.Look(ref assignedLarva, "assignedLarva");
Scribe_Values.Look(ref larvaOperateTicksRemaining, "larvaOperateTicksRemaining", 0);
Scribe_Values.Look(ref speedMultiplier, "speedMultiplier", 1.0f);
- Scribe_Values.Look(ref lastMultiplierUpdateTick, "lastMultiplierUpdateTick", -1);
Scribe_Values.Look(ref qualityMultiplier, "qualityMultiplier", 1.0f);
Scribe_Values.Look(ref qualityProgress, "qualityProgress", 0f);
Scribe_Values.Look(ref qualityTotal, "qualityTotal", 0f);
-
- // 孵化活性系统
Scribe_Values.Look(ref neutronFlux, "neutronFlux", 0.5f);
Scribe_Values.Look(ref fluxMode, "fluxMode", FluxMode.Balance);
}