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); }