diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index cbb52f7..17fbb9f 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.pdb b/1.6/1.6/Assemblies/ArachnaeSwarm.pdb index a00d02d..a06cfcb 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.pdb and b/1.6/1.6/Assemblies/ArachnaeSwarm.pdb differ diff --git a/1.6/1.6/Defs/Thing_building/ARA_Building.xml b/1.6/1.6/Defs/Thing_building/ARA_Building.xml index 55794e0..ab9ca02 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_Building.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_Building.xml @@ -52,6 +52,54 @@
  • Insect
  • --> + + ARA_InsectCreepFloor + + 由阿拉克涅虫族所铺设的由真菌、甲壳素分泌物混合得到的地面,性质类似于阿拉克涅菌毯,不过可以在支撑结构上蔓延,以作为虫群的逆重飞船地基。 + ArachnaeSwarm/Terrain/Surfaces/ARA_InsectCreep + (0.95, 0.95, 0.93, 1) + (209, 207, 184) + 0 + FadeRough + 399 + ConstructMetal + true + ARA_Buildings + true + true + Misc9 + +
  • ARA_Base_Technology
  • +
    + +
  • ARA_Creep
  • +
  • Substructure
  • +
    + + 0 + 0.25 + 0 + 0 + 100 + + + 1 + 2 + 2 + + 1000 + true + Walkable + 1 + +
  • ARA_Creep
  • +
  • Substructure
  • +
    + +
  • PlaceWorker_InSubstructureFootprint
  • +
  • PlaceWorker_BuildingsValidOverSubstructure
  • +
    +
    ARA_InsectCreepTile 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 9cb13bf..705fdcd 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml @@ -430,18 +430,24 @@ 0.2 0.5 - +
  • - 0 - 10 - 0 - 5 - true + 25.0
  • ARA_InsectJelly
  • + 虫蜜 + true + true + 5 + true + +
  • + +
  • ARA_NutrientNetworkTower
  • +
  • diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_QueuedIncubator.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_QueuedIncubator.xml index 76daf5d..cfaca0a 100644 --- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_QueuedIncubator.xml +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_QueuedIncubator.xml @@ -41,6 +41,8 @@ 呼叫一只幼虫来激活下一个订单(还有{0}个等待中) 幼虫工作中 一只幼虫正在操作孵化器(还有{0}个订单等待) + 幼虫激活中 + 幼虫赶路中 选择孵化目标 diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompQueuedInteractiveProducerWithFlux.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompQueuedInteractiveProducerWithFlux.cs index e25e34c..968578f 100644 --- a/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompQueuedInteractiveProducerWithFlux.cs +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompQueuedInteractiveProducerWithFlux.cs @@ -8,23 +8,35 @@ using Verse.AI; namespace ArachnaeSwarm { - // 带状态和品质的物品订单(与 Building_Ootheca 统一的累计进度模式) - public class QueuedItemOrder : IExposable + // 带状态和品质的物品订单(实现 IIncubationState 接口) + public class QueuedItemOrder : IExposable, IIncubationState { public ProcessDef process; public string tempThingDefName; public OrderStatus status = OrderStatus.WaitingForLarva; - // 进度系统(累计模式,与 Building_Ootheca 统一) - public float incubationProgress = 0f; - public float incubationDuration = 0f; + // 进度系统 + private float _incubationProgress = 0f; + private float _incubationDuration = 0f; - // 通量品质系统 - public float qualityProgress = 0f; - public float qualityTotal = 0f; + // 品质系统 + private float _qualityProgress = 0f; + private float _qualityTotal = 0f; - public float ProgressPercent => incubationDuration > 0 ? incubationProgress / incubationDuration : 0f; - public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f; + // IIncubationState 接口实现 + public float IncubationProgress { get => _incubationProgress; set => _incubationProgress = value; } + public float IncubationDuration => _incubationDuration; + public float QualityProgress { get => _qualityProgress; set => _qualityProgress = value; } + public float QualityTotal => _qualityTotal; + + public float ProgressPercent => _incubationDuration > 0 ? _incubationProgress / _incubationDuration : 0f; + public float QualityPercent => _qualityTotal > 0 ? _qualityProgress / _qualityTotal : 0f; + + public void SetDuration(float duration) + { + _incubationDuration = duration; + _qualityTotal = duration; + } public void ExposeData() { @@ -34,10 +46,10 @@ namespace ArachnaeSwarm } Scribe_Values.Look(ref tempThingDefName, "thingDefName"); Scribe_Values.Look(ref status, "status", OrderStatus.WaitingForLarva); - Scribe_Values.Look(ref incubationProgress, "incubationProgress", 0f); - Scribe_Values.Look(ref incubationDuration, "incubationDuration", 0f); - Scribe_Values.Look(ref qualityProgress, "qualityProgress", 0f); - Scribe_Values.Look(ref qualityTotal, "qualityTotal", 0f); + Scribe_Values.Look(ref _incubationProgress, "incubationProgress", 0f); + Scribe_Values.Look(ref _incubationDuration, "incubationDuration", 0f); + Scribe_Values.Look(ref _qualityProgress, "qualityProgress", 0f); + Scribe_Values.Look(ref _qualityTotal, "qualityTotal", 0f); } } @@ -259,11 +271,7 @@ namespace ArachnaeSwarm if (waitingOrder != null) { waitingOrder.status = OrderStatus.Incubating; - // 使用累计进度模式(与 Building_Ootheca 统一) - waitingOrder.incubationDuration = waitingOrder.process.productionTicks; - waitingOrder.incubationProgress = 0f; - waitingOrder.qualityTotal = waitingOrder.incubationDuration; - waitingOrder.qualityProgress = 0f; + waitingOrder.SetDuration(waitingOrder.process.productionTicks); } assignedLarvae.Remove(larva); @@ -301,36 +309,20 @@ namespace ArachnaeSwarm FuelComp.ConsumeFuel(fuelPerTick); } - // 进度和品质处理(与 Building_Ootheca 统一) + // 进度和品质处理(调用统一工具方法) + float speedFactor = 1f + (FacilitiesComp?.GetStatOffset(StatDef.Named("ARA_IncubationSpeedFactor")) ?? 0f); + bool isDormant = IsDormant || !hasFuel; + foreach (var order in orders.Where(o => o.status == OrderStatus.Incubating)) { - if (IsDormant) - { - // 休眠时:不推进进度,品质衰减 - float qualityDecay = (order.qualityTotal * 0.1f) / 60000f; - order.qualityProgress = Mathf.Max(0f, order.qualityProgress - qualityDecay); - } - else - { - // 正常工作:推进进度和品质 - float speedFactor = 1f + (FacilitiesComp?.GetStatOffset(StatDef.Named("ARA_IncubationSpeedFactor")) ?? 0f); - float fluxSpeed = speedFactor * FluxEfficiency * 5f; - - // 进度推进(累计模式) - order.incubationProgress += fluxSpeed; - - // 品质增长 - 低活性时品质增长更快 - float qualityBonus = 1f + (1f - neutronFlux) * 0.5f; - float qualityGain = speedFactor * qualityBonus; - order.qualityProgress = Mathf.Min(order.qualityProgress + qualityGain, order.qualityTotal); - } + IncubatorUtils.TickIncubation(order, speedFactor, neutronFlux, isDormant); } - // 完成检查(进度达到持续时间时完成) + // 完成检查 orders.RemoveAll(order => { if (order.status == OrderStatus.Incubating && - order.incubationProgress >= order.incubationDuration) + IncubatorUtils.IsIncubationComplete(order)) { FinishProduction(order); return true; diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_SpawnPawnFromList/CompQueuedPawnSpawnerWithFlux.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_SpawnPawnFromList/CompQueuedPawnSpawnerWithFlux.cs index 7e98a46..27106ad 100644 --- a/Source/ArachnaeSwarm/Building_Comps/ARA_SpawnPawnFromList/CompQueuedPawnSpawnerWithFlux.cs +++ b/Source/ArachnaeSwarm/Building_Comps/ARA_SpawnPawnFromList/CompQueuedPawnSpawnerWithFlux.cs @@ -25,31 +25,43 @@ namespace ArachnaeSwarm public string estimatedQuality; } - // 带状态和品质的督虫订单 - public class QueuedPawnOrder : IExposable + // 带状态和品质的督虫订单(实现 IIncubationState 接口) + public class QueuedPawnOrder : IExposable, IIncubationState { public IncubationConfig config; public OrderStatus status = OrderStatus.WaitingForLarva; // 进度系统(与 Building_Ootheca 统一) - public float incubationProgress = 0f; - public float incubationDuration = 0f; + private float _incubationProgress = 0f; + private float _incubationDuration = 0f; // 品质系统 - public float qualityProgress = 0f; - public float qualityTotal = 0f; + private float _qualityProgress = 0f; + private float _qualityTotal = 0f; - public float ProgressPercent => incubationDuration > 0 ? incubationProgress / incubationDuration : 0f; - public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f; + // IIncubationState 接口实现 + public float IncubationProgress { get => _incubationProgress; set => _incubationProgress = value; } + public float IncubationDuration => _incubationDuration; + public float QualityProgress { get => _qualityProgress; set => _qualityProgress = value; } + public float QualityTotal => _qualityTotal; + + public float ProgressPercent => _incubationDuration > 0 ? _incubationProgress / _incubationDuration : 0f; + public float QualityPercent => _qualityTotal > 0 ? _qualityProgress / _qualityTotal : 0f; + + public void SetDuration(float duration) + { + _incubationDuration = duration; + _qualityTotal = duration; // 品质总量与孵化时间相同 + } public void ExposeData() { Scribe_Deep.Look(ref config, "config"); Scribe_Values.Look(ref status, "status", OrderStatus.WaitingForLarva); - Scribe_Values.Look(ref incubationProgress, "incubationProgress", 0f); - Scribe_Values.Look(ref incubationDuration, "incubationDuration", 0f); - Scribe_Values.Look(ref qualityProgress, "qualityProgress", 0f); - Scribe_Values.Look(ref qualityTotal, "qualityTotal", 0f); + Scribe_Values.Look(ref _incubationProgress, "incubationProgress", 0f); + Scribe_Values.Look(ref _incubationDuration, "incubationDuration", 0f); + Scribe_Values.Look(ref _qualityProgress, "qualityProgress", 0f); + Scribe_Values.Look(ref _qualityTotal, "qualityTotal", 0f); } } @@ -180,8 +192,8 @@ namespace ArachnaeSwarm private float GetRemainingTicks(QueuedPawnOrder order) { - if (order.status != OrderStatus.Incubating || order.incubationDuration <= 0) return 0f; - float remaining = order.incubationDuration - order.incubationProgress; + if (order.status != OrderStatus.Incubating || order.IncubationDuration <= 0) return 0f; + float remaining = order.IncubationDuration - order.IncubationProgress; float speedFactor = 1f + (FacilitiesComp?.GetStatOffset(StatDef.Named("ARA_IncubationSpeedFactor")) ?? 0f); float fluxSpeed = speedFactor * FluxEfficiency * 5f; return fluxSpeed > 0 ? remaining / fluxSpeed : remaining; @@ -275,11 +287,7 @@ namespace ArachnaeSwarm if (waitingOrder != null && waitingOrder.config != null) { waitingOrder.status = OrderStatus.Incubating; - // 使用累计进度模式(与 Building_Ootheca 统一) - waitingOrder.incubationDuration = waitingOrder.config.daysRequired * 60000f; - waitingOrder.incubationProgress = 0f; - waitingOrder.qualityTotal = waitingOrder.incubationDuration; - waitingOrder.qualityProgress = 0f; + waitingOrder.SetDuration(waitingOrder.config.daysRequired * 60000f); } assignedLarvae.Remove(larva); @@ -316,36 +324,20 @@ namespace ArachnaeSwarm FuelComp.ConsumeFuel(fuelPerTick); } - // 进度和品质处理(与 Building_Ootheca 统一) + // 进度和品质处理(调用统一工具方法) + float speedFactor = 1f + (FacilitiesComp?.GetStatOffset(StatDef.Named("ARA_IncubationSpeedFactor")) ?? 0f); + bool isDormant = IsDormant || !hasFuel; + foreach (var order in orders.Where(o => o.status == OrderStatus.Incubating)) { - if (IsDormant) - { - // 休眠时:不推进进度,品质衰减 - float qualityDecay = (order.qualityTotal * 0.1f) / 60000f; - order.qualityProgress = Mathf.Max(0f, order.qualityProgress - qualityDecay); - } - else - { - // 正常工作:推进进度和品质 - float speedFactor = 1f + (FacilitiesComp?.GetStatOffset(StatDef.Named("ARA_IncubationSpeedFactor")) ?? 0f); - float fluxSpeed = speedFactor * FluxEfficiency * 5f; - - // 进度推进(累计模式) - order.incubationProgress += fluxSpeed; - - // 品质增长 - 低活性时品质增长更快 - float qualityBonus = 1f + (1f - neutronFlux) * 0.5f; - float qualityGain = speedFactor * qualityBonus; - order.qualityProgress = Mathf.Min(order.qualityProgress + qualityGain, order.qualityTotal); - } + IncubatorUtils.TickIncubation(order, speedFactor, neutronFlux, isDormant); } - // 完成检查(进度达到持续时间时完成) + // 完成检查 orders.RemoveAll(order => { if (order.status == OrderStatus.Incubating && - order.incubationProgress >= order.incubationDuration) + IncubatorUtils.IsIncubationComplete(order)) { CompleteOrder(order); return true; diff --git a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs index eaa0eda..e425fbe 100644 --- a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs +++ b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs @@ -135,13 +135,12 @@ namespace ArachnaeSwarm } else { - // 正常状态:应用活性效率 + // 进度增长 float fluxSpeed = SpeedMultiplier * FluxEfficiency * 5f; incubationProgress += fluxSpeed; - // 品质独立增长 - float qualityBonus = 1f + (1f - neutronFlux) * 0.5f; - float qualityGain = SpeedMultiplier * QualityMultiplier * qualityBonus; + // 品质增长(新公式:50%通量时与进度同步) + float qualityGain = IncubatorUtils.CalculateQualityGainNew(fluxSpeed, neutronFlux); qualityProgress = Mathf.Min(qualityProgress + qualityGain, qualityTotal); } @@ -404,21 +403,53 @@ namespace ArachnaeSwarm yield return new Gizmo_EquipmentIncubationProgress(this); yield return new Gizmo_NeutronFlux(this); - if (!isIncubating && EquipmentIncubatorData?.SelectedConfig?.IsResearchComplete == true) + var config = EquipmentIncubatorData?.SelectedConfig; + + // 添加订单按钮 + if (!isIncubating && assignedLarva == null) { yield return new Command_Action { - defaultLabel = "呼叫幼虫", - icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false), - action = CallLarva + defaultLabel = "ARA_Gizmo_AddOrder".Translate(config != null ? 1 : 0, 1), + defaultDesc = "ARA_Gizmo_AddOrderDesc_Item".Translate(), + icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_NodeSwarmIcon", false), + action = () => EquipmentIncubatorData?.ShowFloatMenu() }; } + // 呼叫幼虫按钮逻辑 + if (!isIncubating && config != null && config.IsResearchComplete) + { + if (assignedLarva == null) + { + yield return new Command_Action + { + defaultLabel = "ARA_Gizmo_CallLarva".Translate(), + defaultDesc = BuildCallLarvaDescription(config), + icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false), + action = CallLarva + }; + } + else + { + string statusText = larvaOperateTicksRemaining > 0 + ? "ARA_Gizmo_LarvaActivating".Translate() + : "ARA_Gizmo_LarvaOnTheWay".Translate(); + yield return new Command_Action + { + defaultLabel = statusText, + defaultDesc = "ARA_Gizmo_LarvaWorkingDesc".Translate(0), + icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false), + Disabled = true + }; + } + } + if (isIncubating) { yield return new Command_Action { - defaultLabel = "取消孵化", + defaultLabel = "ARA_OothecaIncubator.CancelIncubation".Translate(), icon = ContentFinder.Get("UI/Commands/Cancel", false), action = CancelIncubation }; diff --git a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/CompProperties_EquipmentIncubatorData.cs b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/CompProperties_EquipmentIncubatorData.cs index 2192694..ec87fa8 100644 --- a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/CompProperties_EquipmentIncubatorData.cs +++ b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/CompProperties_EquipmentIncubatorData.cs @@ -320,6 +320,36 @@ namespace ArachnaeSwarm { return selectedIndex; } + + // 显示目标选择菜单 + public void ShowFloatMenu() + { + var configs = IncubationConfigs; + if (configs == null || configs.Count == 0) return; + + List options = new List(); + + for (int i = 0; i < configs.Count; i++) + { + var cfg = configs[i]; + int index = i; + + string label = cfg.thingDef.LabelCap; + if (cfg.requiredResearch != null && !cfg.requiredResearch.IsFinished) + { + label += " (" + "ARA_Menu_RequiresResearch".Translate(cfg.requiredResearch.LabelCap) + ")"; + options.Add(new FloatMenuOption(label, null)); + } + else + { + label += " (" + "ARA_Menu_Days".Translate(cfg.DaysRequired.ToString("F1")) + ")"; + options.Add(new FloatMenuOption(label, () => SwitchToConfig(index))); + } + } + + if (options.Count > 0) + Find.WindowStack.Add(new FloatMenu(options, "ARA_Menu_SelectProductionTarget".Translate())); + } // 存档加载 public override void PostExposeData() diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs index 95e0639..60df022 100644 --- a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs +++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs @@ -240,11 +240,12 @@ namespace ArachnaeSwarm } else { + // 进度增长 float fluxSpeed = SpeedMultiplier * FluxEfficiency * 5f; incubationProgress += fluxSpeed; - float qualityBonus = 1f + (1f - neutronFlux) * 0.5f; - float qualityGain = SpeedMultiplier * QualityMultiplier * qualityBonus; + // 品质增长(新公式:50%通量时与进度同步) + float qualityGain = IncubatorUtils.CalculateQualityGainNew(fluxSpeed, neutronFlux); qualityProgress = Mathf.Min(qualityProgress + qualityGain, qualityTotal); } @@ -408,17 +409,49 @@ namespace ArachnaeSwarm yield return new Gizmo_NeutronFlux(this); var config = IncubatorData?.SelectedConfig; - if (!isIncubating && config != null && config.IsResearchComplete) + + // 添加订单按钮(未孵化且未选目标,或已选目标但支持切换) + if (!isIncubating && assignedLarva == null) { yield return new Command_Action { - defaultLabel = "ARA_OothecaIncubator.CallLarva".Translate(), - defaultDesc = BuildCallLarvaDescription(config), - icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false), - action = CallLarva + defaultLabel = "ARA_Gizmo_AddOrder".Translate(config != null ? 1 : 0, 1), + defaultDesc = "ARA_Gizmo_AddOrderDesc_Pawn".Translate(), + icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_NodeSwarmIcon", false), + action = () => IncubatorData?.ShowFloatMenu() }; } + // 呼叫幼虫按钮逻辑 + if (!isIncubating && config != null && config.IsResearchComplete) + { + if (assignedLarva == null) + { + // 无幼虫,可以呼叫 + yield return new Command_Action + { + defaultLabel = "ARA_Gizmo_CallLarva".Translate(), + defaultDesc = BuildCallLarvaDescription(config), + icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false), + action = CallLarva + }; + } + else + { + // 幼虫在路上或工作中 + string statusText = larvaOperateTicksRemaining > 0 + ? "ARA_Gizmo_LarvaActivating".Translate() + : "ARA_Gizmo_LarvaOnTheWay".Translate(); + yield return new Command_Action + { + defaultLabel = statusText, + defaultDesc = "ARA_Gizmo_LarvaWorkingDesc".Translate(0), + icon = ContentFinder.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false), + Disabled = true + }; + } + } + if (isIncubating) { yield return new Command_Action diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/CompProperties_IncubatorData.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/CompProperties_IncubatorData.cs index ea634f1..19d3700 100644 --- a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/CompProperties_IncubatorData.cs +++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/CompProperties_IncubatorData.cs @@ -384,6 +384,36 @@ namespace ArachnaeSwarm { return selectedIndex; } + + // 显示目标选择菜单 + public void ShowFloatMenu() + { + var configs = IncubationConfigs; + if (configs == null || configs.Count == 0) return; + + List options = new List(); + + for (int i = 0; i < configs.Count; i++) + { + var cfg = configs[i]; + int index = i; + + string label = cfg.pawnKind.LabelCap; + if (cfg.requiredResearch != null && !cfg.requiredResearch.IsFinished) + { + label += " (" + "ARA_Menu_RequiresResearch".Translate(cfg.requiredResearch.LabelCap) + ")"; + options.Add(new FloatMenuOption(label, null)); + } + else + { + label += " (" + "ARA_Menu_Days".Translate(cfg.daysRequired.ToString("F1")) + ")"; + options.Add(new FloatMenuOption(label, () => SwitchToConfig(index))); + } + } + + if (options.Count > 0) + Find.WindowStack.Add(new FloatMenu(options, "ARA_Menu_SelectIncubationTarget".Translate())); + } // 存档加载 public override void PostExposeData() diff --git a/Source/ArachnaeSwarm/Buildings/IncubatorUtils.cs b/Source/ArachnaeSwarm/Buildings/IncubatorUtils.cs index f370a52..42fe6a7 100644 --- a/Source/ArachnaeSwarm/Buildings/IncubatorUtils.cs +++ b/Source/ArachnaeSwarm/Buildings/IncubatorUtils.cs @@ -6,12 +6,67 @@ using Verse; namespace ArachnaeSwarm { + /// + /// 孵化状态数据接口 - 所有孵化系统共用 + /// + public interface IIncubationState + { + float IncubationProgress { get; set; } + float IncubationDuration { get; } + float QualityProgress { get; set; } + float QualityTotal { get; } + } + /// /// 孵化器通用工具类 /// 统一核心算法,保持各孵化器行为一致 /// public static class IncubatorUtils { + // ============================================ + // 核心 Tick 方法(各孵化器调用此方法) + // ============================================ + + /// + /// 执行单tick的孵化进度和品质更新(核心方法) + /// 所有孵化器应调用此方法以保持行为一致 + /// + /// 孵化状态(实现 IIncubationState 接口) + /// 速度倍率(来自设施等) + /// 当前通量值 0-1 + /// 是否休眠(通量过低或无燃料) + /// 本tick的进度增量(用于判断完成) + public static float TickIncubation(IIncubationState state, float speedMultiplier, float neutronFlux, bool isDormant) + { + if (isDormant) + { + // 休眠时:进度不增长,品质衰减 + float qualityDecay = CalculateQualityDecay(state.QualityTotal); + state.QualityProgress = Mathf.Max(0f, state.QualityProgress - qualityDecay); + return 0f; + } + else + { + // 正常工作:进度和品质同步增长 + float fluxEfficiency = GetFluxEfficiency(neutronFlux); + float progressGain = CalculateProgressGain(speedMultiplier, fluxEfficiency); + float qualityGain = CalculateQualityGainNew(progressGain, neutronFlux); + + state.IncubationProgress += progressGain; + state.QualityProgress = Mathf.Min(state.QualityProgress + qualityGain, state.QualityTotal); + + return progressGain; + } + } + + /// + /// 检查孵化是否完成 + /// + public static bool IsIncubationComplete(IIncubationState state) + { + return state.IncubationProgress >= state.IncubationDuration; + } + // ============================================ // 通量系统 // ============================================ @@ -34,16 +89,16 @@ namespace ArachnaeSwarm } /// - /// 获取模式名称 + /// 获取模式名称(使用翻译键) /// public static string GetFluxModeName(FluxMode mode) { return mode switch { - FluxMode.Manual => "手动", - FluxMode.Quality => "品质", - FluxMode.Balance => "平衡", - FluxMode.Speed => "速度", + FluxMode.Manual => "ARA_FluxMode_Manual".Translate(), + FluxMode.Quality => "ARA_FluxMode_Quality".Translate(), + FluxMode.Balance => "ARA_FluxMode_Balance".Translate(), + FluxMode.Speed => "ARA_FluxMode_Speed".Translate(), _ => "?" }; } @@ -64,13 +119,38 @@ namespace ArachnaeSwarm } // ============================================ - // 品质系统 + // 进度系统 // ============================================ /// - /// 计算品质增长(低通量时增长更快) - /// 公式:qualityGain = speedFactor * (1 + (1 - flux) * 0.5) + /// 计算单tick的进度增量 + /// 公式:progressGain = speedMultiplier * fluxEfficiency * 5 /// + public static float CalculateProgressGain(float speedMultiplier, float fluxEfficiency) + { + return speedMultiplier * fluxEfficiency * 5f; + } + + // ============================================ + // 品质系统(新公式:50%通量时与进度同步) + // ============================================ + + /// + /// 计算单tick的品质增量(新公式) + /// 核心:50%通量时品质增长 = 进度增长(1:1同步) + /// 公式:qualityGain = progressGain × 2 × (1 - neutronFlux) + /// + public static float CalculateQualityGainNew(float progressGain, float neutronFlux) + { + // qualityCoeff: 0%通量=2.0, 25%通量=1.5, 50%通量=1.0, 75%通量=0.5, 100%通量=0 + float qualityCoeff = 2f * (1f - neutronFlux); + return progressGain * qualityCoeff; + } + + /// + /// [已废弃] 旧品质计算公式 + /// + [System.Obsolete("Use CalculateQualityGainNew instead")] public static float CalculateQualityGain(float neutronFlux, float speedFactor) { float qualityBonus = 1f + (1f - neutronFlux) * 0.5f;