diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index 02b820c..eeffb18 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 49bfb77..0627f59 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_NutrientNetworkBuilding.xml b/1.6/1.6/Defs/Thing_building/ARA_NutrientNetworkBuilding.xml index a097ed0..ae73e4b 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_NutrientNetworkBuilding.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_NutrientNetworkBuilding.xml @@ -61,6 +61,7 @@
  • CatastropheMissileSilo
  • ARA_AutoSniperCannon
  • ARA_Pawn_Ootheca
  • +
  • ARA_Equipment_Ootheca
  • 80 10 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 e051f02..49d69a4 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml @@ -111,42 +111,6 @@ ARA_Incubator_Room - - -
  • - - 5 - - 5 - - false - - - - - - - - - - - - - - - - - true - 2 - Burn - true - -
  • -
    - - - 25 @@ -454,44 +418,10 @@ ARA_Incubator_Room - - -
  • - - 3 - - 3 - - false - - 0.03 - - - - - - - - - - - - -
  • -
    - 25 - - -
  • ArachnaeSwarm.ITab_EquipmentOotheca_Incubation
  • -
    - - -
  • ArachnaeSwarm.PlaceWorker_CustomRadius
  • -
  • 100 @@ -500,14 +430,18 @@ 0.2 0.5
  • -
  • - 3 - (0.5, 1, 1) - 0 - true - - 这个卵在孵化过程中的吸收半径,确保这些地格中铺满阿拉克涅营养液,并且没有其他的卵,以获得最佳的孵化速度和孵化质量。 - false + +
  • + 0 + 10 + 0 + 5 + true + + +
  • ARA_InsectJelly
  • + +
  • @@ -585,4 +519,266 @@
  • + + + + ARA_BioforgeIncubator_Thing + + 一个大型的、需要消耗大量营养物质的孵化设施,可以同时孵化多个物品,并能通过链接外部设备来提高效率。 + + ArachnaeSwarm/Building/ARA_BioforgeIncubator_Thing + Graphic_Single + CutoutComplex + false + (5.5,6.5) + (0, 2, 0.5) + + (2.75, 1.2, 2) + (0.2,0,-1.15) + + + (5,5) + false + Normal + + 0 + + 200 + 40 + 10 + + false + 0 + Building + PassThroughOnly + ARA_Creep + 50 + + 20000 + 250 + 2800 + 1.0 + + +
  • PlaceWorker_PreventInteractionSpotOverlap
  • +
    + 0.8 + (0,0,-1) + true + ARA_Buildings + 2600 + Item + + Laboratory + 0.8 + +
  • ARA_InsectCreep
  • +
    +
    + +
  • ARA_Technology_4NPT
  • +
    + + +
  • + + 3 + 1.0 + +
  • ArachnaeNode_Race_WeaponSmith
  • + + + + 10 + 30 + 0.00001 + +
  • + Legendary + 0.99 +
  • +
  • + Masterwork + 0.90 +
  • +
  • + Excellent + 0.70 +
  • +
  • + Good + 0.50 +
  • +
  • + Normal + 0.20 +
  • +
  • + Poor + 0.10 +
  • +
    + + + +
  • + 300.0 + + +
  • ARA_InsectJelly
  • + + + 虫蜜 + true + true + + + +
  • + +
  • ARA_NutrientNetworkTower
  • +
  • ARA_GrowthVat
  • + + +
  • + ARA_InsectCreep + 8 +
  • +
    +
    + + ARA_BioforgeIncubator + + 一个大型的、需要消耗大量营养物质的孵化设施,可以同时孵化多个单位,并能通过链接外部设备来提高效率。 + + ArachnaeSwarm/Building/ARA_BioforgeIncubator + Graphic_Single + CutoutComplex + (5.5,6.5) + false + (0, 2, 0.5) + + (2.75, 1.4, 2) + (0,0,-1.25) + + + (5,5) + Normal + false + + 0 + + 200 + 40 + 10 + + false + 0 + Building + PassThroughOnly + ARA_Creep + ARA_Buildings + 50 + +
  • ARA_Technology_4NPT
  • +
    + + 20000 + 250 + 2800 + 1.0 + + +
  • PlaceWorker_PreventInteractionSpotOverlap
  • +
    + 0.8 + (0,0,-1) + true + + 2600 + Item + + Laboratory + 0.8 + +
  • ARA_InsectCreep
  • +
    +
    + + +
  • + 5 + 0.5 + +
  • ARA_ArachnaeQueen
  • + + +
  • + ArachnaeNode_Race_Myrmecocystus + 240000 + 100.0 +
  • +
  • + ArachnaeNode_Race_ShieldHead + 180000 + 40.0 +
  • +
  • + ArachnaeNode_Race_WeaponSmith + 180000 + 40.0 +
  • +
  • + ArachnaeNode_Race_Fighter + 90000 + 20.0 + ARA_Technology_1KYC +
  • +
  • + ArachnaeNode_Race_Smokepop + 180000 + 60.0 + ARA_Technology_5KYC +
  • +
  • + ArachnaeNode_Race_Skyraider + 120000 + 80.0 + ARA_Technology_2KYC +
  • +
  • + ARA_MimicNematodeShamblerSwarmer + 600 + 10.0 + ARA_Technology_6MEN +
  • +
    + + + +
  • + 20.0 + + +
  • ARA_InsectJelly
  • + + + 虫蜜 + true + true + + + +
  • + +
  • ARA_NutrientNetworkTower
  • +
  • ARA_GrowthVat
  • + + +
  • + ARA_InsectCreep + 8 +
  • +
    +
    \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index a762a77..56b0905 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -118,9 +118,11 @@ + + diff --git a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs index 072f906..9f37ef8 100644 --- a/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs +++ b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs @@ -8,7 +8,7 @@ using System; namespace ArachnaeSwarm { - public class Building_EquipmentOotheca : Building + public class Building_EquipmentOotheca : Building, IFluxController { // 引用组件 public CompEquipmentIncubatorData EquipmentIncubatorData => this.TryGetComp(); @@ -33,27 +33,75 @@ namespace ArachnaeSwarm private float qualityProgress = 0f; private float qualityTotal = 0f; - // === 简化后的营养液系统:只用于速度加成,不消耗 === - private int totalNutrientCost = 0; // 总共需要的营养液地块数量(仅用于信息显示) - private int currentNutrientCount = 0; // 当前周围存在的营养液数量 + // ======= 孵化活性系统 ======= + private float neutronFlux = 0.5f; // 0-1, 默认50% + private FluxMode fluxMode = FluxMode.Balance; // 默认平衡模式 + private CompRefuelableNutrition fuelComp; // 燃料组件缓存 + private Comp_SwarmMaintenance maintenanceComp; // 维护组件缓存 - // 缓存的ModExtension - private OothecaIncubatorExtension cachedExtension; + // 孵化活性公开属性 + public float NeutronFlux => isIncubating ? neutronFlux : 0f; + public float RawFlux => neutronFlux; + public FluxMode CurrentFluxMode => fluxMode; + public bool IsAutoMode => fluxMode != FluxMode.Manual; + public bool IsIncubating => isIncubating; - // 获取ModExtension的辅助属性 - public OothecaIncubatorExtension Ext + // 获取燃料组件 + 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) { - get + 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) { - if (cachedExtension == null) - { - cachedExtension = def.GetModExtension() ?? OothecaIncubatorExtension.Default; - } - return cachedExtension; + 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 float IncubationProgress => incubationDuration > 0 ? incubationProgress / incubationDuration : 0f; + public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f; + + // 速度乘数 public float SpeedMultiplier { get @@ -69,21 +117,27 @@ namespace ArachnaeSwarm // 质量属性 public float QualityMultiplier => qualityMultiplier; public float QualityProgress => qualityProgress; - public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f; - // 营养液加成属性 - public int CurrentNutrientCount => currentNutrientCount; - public float NutrientSpeedBonus => currentNutrientCount * Ext.nutrientSolutionBonusPerTile; + // 临时保留:营养液相关字段(后续清理) + private int totalNutrientCost = 0; + private int currentNutrientCount = 0; + private OothecaIncubatorExtension cachedExtension; - // 进度百分比 - public float AdjustedProgressPercent + public OothecaIncubatorExtension Ext { get { - if (incubationDuration <= 0) return 0f; - return incubationProgress / incubationDuration; + 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() @@ -108,7 +162,7 @@ namespace ArachnaeSwarm UpdateNutrientCount(); } - // === 简化的Tick方法 === + // === Tick方法(带活性系统)=== protected override void Tick() { base.Tick(); @@ -126,12 +180,45 @@ namespace ArachnaeSwarm UpdateSpeedMultiplier(); UpdateQualityMultiplier(); } - - float currentSpeed = SpeedMultiplier; - - // 始终增加进度(不再有营养液不足的暂停) - incubationProgress += currentSpeed; - qualityProgress += currentSpeed * QualityMultiplier; + + // 自动模式计算 + if (IsAutoMode && Find.TickManager.TicksGame % 250 == 0) + { + CalculateAutoFlux(); + } + + // 消耗虫蜜(基于孵化活性) + if (FuelComp != null && neutronFlux > 0.01f) + { + float fuelPerTick = (50f * FluxEfficiency) / 60000f; + FuelComp.ConsumeFuel(fuelPerTick); + } + + // 休眠状态处理 + if (IsDormant) + { + // 休眠时品质下降(10%/天) + float qualityDecay = (qualityTotal * 0.1f) / 60000f; + qualityProgress = Mathf.Max(0f, qualityProgress - qualityDecay); + + 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) { @@ -139,6 +226,57 @@ namespace ArachnaeSwarm } } } + + // 自动模式算法 + 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: + 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: + 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 && MaintenanceComp.CurrentMaintenance / MaintenanceComp.Props.maxMaintenance < 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); + } // === 获取速度因子描述 === public string GetSpeedFactorsDescription() @@ -565,18 +703,30 @@ namespace ArachnaeSwarm // === 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")) + continue; + } yield return gizmo; } + // 只有玩家派系才显示Gizmo if (Faction == Faction.OfPlayer) { - if (!isIncubating && EquipmentIncubatorData?.IncubationConfigs?.Count > 0) - { - yield return CreateTargetSwitchGizmo(); - } + // 始终显示双进度条Gizmo + yield return new Gizmo_EquipmentIncubationProgress(this); + // 始终显示活性 Gizmo + yield return new Gizmo_NeutronFlux(this); + + // 不在孵化中且研究完成时才显示呼叫按钮 var config = EquipmentIncubatorData?.SelectedConfig; if (!isIncubating && config != null && config.IsResearchComplete) { @@ -590,6 +740,7 @@ namespace ArachnaeSwarm }; } + // 如果正在孵化,显示取消按钮 if (isIncubating) { yield return new Command_Action @@ -978,6 +1129,10 @@ namespace ArachnaeSwarm 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_EquipmentOotheca/Gizmo_EquipmentIncubationProgress.cs b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Gizmo_EquipmentIncubationProgress.cs new file mode 100644 index 0000000..a0c7bc8 --- /dev/null +++ b/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Gizmo_EquipmentIncubationProgress.cs @@ -0,0 +1,239 @@ +// File: Gizmo_EquipmentIncubationProgress.cs +// 装备孵化进度 Gizmo - 与 Gizmo_IncubationProgress 样式完全一致 +using System.Collections.Generic; +using RimWorld; +using UnityEngine; +using Verse; + +namespace ArachnaeSwarm +{ + [StaticConstructorOnStartup] + public class Gizmo_EquipmentIncubationProgress : Gizmo + { + private readonly Building_EquipmentOotheca building; + + // 尺寸常量(与 Gizmo_IncubationProgress 相同) + private const float Width = 180f; + private const float BarHeight = 14f; + private const float LabelHeight = 14f; + private const float Spacing = 4f; + private const float Padding = 8f; + + // 进度条材质(与 Gizmo_IncubationProgress 相同) + private static readonly Texture2D IncubationBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.2f, 0.7f, 0.2f, 0.8f)); + private static readonly Texture2D QualityBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.2f, 0.5f, 0.9f, 0.8f)); + private static readonly Texture2D EmptyBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.1f, 0.1f, 0.1f, 0.5f)); + + public Gizmo_EquipmentIncubationProgress(Building_EquipmentOotheca building) + { + this.building = building; + Order = -99f; // 在通量 Gizmo 之后显示 + } + + public override float GetWidth(float maxWidth) + { + return Mathf.Min(Width, maxWidth); + } + + public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms) + { + // 主矩形区域 + Rect rect = new Rect(topLeft.x, topLeft.y - 25f, GetWidth(maxWidth), 100f); + Widgets.DrawWindowBackground(rect); + + // 内部区域 + Rect innerRect = rect.ContractedBy(Padding); + float curY = innerRect.y; + + // 状态判断 + bool isIncubating = building.isIncubating; + bool hasLarva = building.assignedLarva != null; + var config = building.EquipmentIncubatorData?.SelectedConfig; + + // === 标题(可点击切换目标) === + Text.Font = GameFont.Small; + Rect titleRect = new Rect(innerRect.x, curY, innerRect.width, Text.LineHeight); + + string title; + if (isIncubating && building.incubatingThingDef != null) + title = building.incubatingThingDef.LabelCap; + else if (config != null) + title = config.thingDef.LabelCap; + else + title = "选择孵化目标..."; + + // 标题按钮(只有非孵化状态可点击) + bool canSwitch = !isIncubating && !hasLarva && building.EquipmentIncubatorData?.IncubationConfigs?.Count > 0; + if (canSwitch) + { + // 绘制按钮背景 + if (Mouse.IsOver(titleRect)) + { + Widgets.DrawHighlight(titleRect); + } + + if (Widgets.ButtonInvisible(titleRect)) + { + ShowTargetSwitchMenu(); + } + + // 带下划线的标题(表示可点击) + GUI.color = new Color(0.7f, 0.9f, 1f); + Widgets.Label(titleRect, title.Truncate(titleRect.width - 10f) + " ▼"); + GUI.color = Color.white; + } + else + { + Widgets.Label(titleRect, title.Truncate(titleRect.width)); + } + curY += Text.LineHeight + Spacing; + + // === 内容绘制 === + if (isIncubating) + { + // 孵化进度标签 + DrawLabeledProgressBar(ref curY, innerRect.x, innerRect.width, + "孵化进度:", building.IncubationProgress, IncubationBarTex); + + curY += Spacing; + + // 品质进度标签 + DrawLabeledProgressBar(ref curY, innerRect.x, innerRect.width, + "品质进度:", building.QualityPercent, QualityBarTex); + } + else if (hasLarva) + { + Text.Font = GameFont.Tiny; + GUI.color = new Color(0.9f, 0.9f, 0.5f); + string statusText = building.larvaOperateTicksRemaining > 0 ? "幼虫激活中..." : "幼虫赶路中..."; + Widgets.Label(new Rect(innerRect.x, curY, innerRect.width, 30f), statusText); + GUI.color = Color.white; + } + else + { + Text.Font = GameFont.Tiny; + GUI.color = new Color(0.7f, 0.7f, 0.7f); + string statusText = (config != null && config.IsResearchComplete) ? "就绪 - 点击上方切换目标" : "需要研究"; + Widgets.Label(new Rect(innerRect.x, curY, innerRect.width, 20f), statusText); + curY += 20f; + + string stats = $"速度:{building.SpeedMultiplier.ToStringPercent()} 质量:{building.QualityMultiplier.ToStringPercent()}"; + Widgets.Label(new Rect(innerRect.x, curY, innerRect.width, 16f), stats); + GUI.color = Color.white; + } + + // === 工具提示 === + if (Mouse.IsOver(rect) && !Mouse.IsOver(titleRect)) + { + Widgets.DrawHighlight(rect); + TooltipHandler.TipRegion(rect, GetTooltip()); + } + + Text.Font = GameFont.Small; + return new GizmoResult(GizmoState.Clear); + } + + private void ShowTargetSwitchMenu() + { + var configs = building.EquipmentIncubatorData?.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; + + // 获取物品图标 + Texture2D icon = cfg.thingDef?.uiIcon; + + string label = cfg.thingDef.LabelCap; + if (cfg.requiredResearch != null && !cfg.requiredResearch.IsFinished) + { + label += $" (需要: {cfg.requiredResearch.LabelCap})"; + options.Add(new FloatMenuOption(label, null, icon, Color.white)); // 禁用但有图标 + } + else + { + label += $" ({cfg.daysRequired}天)"; + options.Add(new FloatMenuOption(label, () => + { + building.EquipmentIncubatorData.SwitchToConfig(index); + }, icon, Color.white)); + } + } + + Find.WindowStack.Add(new FloatMenu(options)); + } + + // 带标签的进度条 + private void DrawLabeledProgressBar(ref float curY, float x, float width, string label, float percent, Texture2D barTex) + { + // 标签 + Text.Font = GameFont.Tiny; + GUI.color = new Color(0.8f, 0.8f, 0.8f); + Widgets.Label(new Rect(x, curY, width, LabelHeight), label); + GUI.color = Color.white; + curY += LabelHeight; + + // 进度条 + Rect barRect = new Rect(x, curY, width, BarHeight); + Widgets.FillableBar(barRect, percent, barTex, EmptyBarTex, true); + + // 百分比文字 + Text.Font = GameFont.Tiny; + Text.Anchor = TextAnchor.MiddleCenter; + Widgets.Label(barRect, percent.ToStringPercent("0")); + Text.Anchor = TextAnchor.UpperLeft; + curY += BarHeight; + } + + private string GetTooltip() + { + var sb = new System.Text.StringBuilder(); + bool isIncubating = building.isIncubating; + + if (isIncubating && building.incubatingThingDef != null) + { + // 孵化中状态 + sb.AppendLine("【孵化中】"); + sb.AppendLine("目标: " + building.incubatingThingDef.LabelCap); + sb.AppendLine("孵化进度: " + building.IncubationProgress.ToStringPercent()); + sb.AppendLine("品质进度: " + building.QualityPercent.ToStringPercent()); + + float remaining = building.incubationDuration - building.incubationProgress; + if (remaining > 0) + { + sb.AppendLine("剩余时间: " + ((int)remaining).ToStringTicksToPeriod()); + } + + sb.AppendLine(); + sb.AppendLine("速度: " + building.SpeedMultiplier.ToStringPercent()); + sb.AppendLine("质量: " + building.QualityMultiplier.ToStringPercent()); + } + else + { + // 空闲状态 + var config = building.EquipmentIncubatorData?.SelectedConfig; + if (config != null) + { + sb.AppendLine("【就绪】"); + sb.AppendLine("目标: " + config.thingDef.LabelCap); + sb.AppendLine("孵化时间: " + config.daysRequired + " 天"); + } + else + { + sb.AppendLine("【未选择目标】"); + sb.AppendLine("点击上方标题选择孵化目标"); + } + + sb.AppendLine(); + sb.AppendLine("当前速度加成: " + building.SpeedMultiplier.ToStringPercent()); + sb.AppendLine("当前质量加成: " + building.QualityMultiplier.ToStringPercent()); + } + + return sb.ToString().TrimEndNewlines(); + } + } +} diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs index 2368112..eccba9a 100644 --- a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs +++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs @@ -8,7 +8,7 @@ using Verse.AI; namespace ArachnaeSwarm { - public class Building_Ootheca : Building + public class Building_Ootheca : Building, IFluxController { // 引用组件 public CompIncubatorData IncubatorData => this.TryGetComp(); @@ -19,7 +19,7 @@ namespace ArachnaeSwarm public float incubationDuration = 0f; public PawnKindDef incubatingPawnKind = null; - // 幼虫交互相关 - 设为public以便ITab访问 + // 幼虫交互相关 public Pawn assignedLarva = null; public int larvaOperateTicksRemaining = 0; @@ -39,15 +39,6 @@ namespace ArachnaeSwarm private CompRefuelableNutrition fuelComp; // 燃料组件缓存 private Comp_SwarmMaintenance maintenanceComp; // 维护组件缓存 - // 活性模式枚举 - public enum FluxMode - { - Manual, // 手动:完全手动控制 - Quality, // 品质优先:低活性,追求高品质 - Balance, // 平衡:自动平衡速度和品质 - Speed // 速度优先:高活性,快速孵化 - } - // 孵化活性公开属性(非孵化时锁定为0) public float NeutronFlux => isIncubating ? neutronFlux : 0f; public float RawFlux => neutronFlux; // 原始值用于 Gizmo 显示 diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_NeutronFlux.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_NeutronFlux.cs index e1ec7db..c086b1e 100644 --- a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_NeutronFlux.cs +++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_NeutronFlux.cs @@ -1,5 +1,6 @@ // File: Gizmo_NeutronFlux.cs -// 竖向滑动条样式的孵化活性调节 Gizmo(带刻度和可拖动光标) +// 竖向滑动条样式的孵化活性调节 Gizmo(带刻度) +// 支持实现 IFluxController 接口的所有建筑 using UnityEngine; using Verse; @@ -8,11 +9,12 @@ namespace ArachnaeSwarm [StaticConstructorOnStartup] public class Gizmo_NeutronFlux : Gizmo { - private readonly Building_Ootheca ootheca; + private readonly IFluxController controller; + private readonly Thing parentThing; // 用于获取 Thing 属性 // 尺寸常量 private const float Width = 100f; - private const float GizmoHeight = 140f; // 增加高度与信息面板齐平 + private const float GizmoHeight = 140f; private const float Padding = 6f; // 材质 @@ -23,10 +25,12 @@ namespace ArachnaeSwarm private static readonly Texture2D TickMarkTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.4f, 0.4f, 0.4f, 0.8f)); private static readonly Texture2D CursorTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.9f, 0.9f, 0.9f, 1f)); - public Gizmo_NeutronFlux(Building_Ootheca ootheca) + // 构造函数接受接口 + public Gizmo_NeutronFlux(IFluxController controller) { - this.ootheca = ootheca; - Order = -100f; // 最先显示 + this.controller = controller; + this.parentThing = controller as Thing; + Order = -100f; } public override float GetWidth(float maxWidth) @@ -54,12 +58,12 @@ namespace ArachnaeSwarm // 根据模式显示不同颜色 Color modeColor; - switch (ootheca.CurrentFluxMode) + switch (controller.CurrentFluxMode) { - case Building_Ootheca.FluxMode.Manual: modeColor = new Color(0.5f, 0.5f, 0.5f, 0.9f); break; - case Building_Ootheca.FluxMode.Quality: modeColor = new Color(0.3f, 0.6f, 0.9f, 0.9f); break; - case Building_Ootheca.FluxMode.Balance: modeColor = new Color(0.2f, 0.7f, 0.2f, 0.9f); break; - case Building_Ootheca.FluxMode.Speed: modeColor = new Color(0.9f, 0.5f, 0.2f, 0.9f); break; + case FluxMode.Manual: modeColor = new Color(0.5f, 0.5f, 0.5f, 0.9f); break; + case FluxMode.Quality: modeColor = new Color(0.3f, 0.6f, 0.9f, 0.9f); break; + case FluxMode.Balance: modeColor = new Color(0.2f, 0.7f, 0.2f, 0.9f); break; + case FluxMode.Speed: modeColor = new Color(0.9f, 0.5f, 0.2f, 0.9f); break; default: modeColor = Color.gray; break; } @@ -69,11 +73,11 @@ namespace ArachnaeSwarm Text.Font = GameFont.Tiny; Text.Anchor = TextAnchor.MiddleCenter; GUI.color = Color.white; - Widgets.Label(modeRect, ootheca.GetModeShort()); + Widgets.Label(modeRect, controller.GetModeShort()); if (Widgets.ButtonInvisible(modeRect)) { - ootheca.CycleFluxMode(); + controller.CycleFluxMode(); } // === 竖向计量条 === @@ -84,14 +88,14 @@ namespace ArachnaeSwarm Rect barRect = new Rect(barX, barY, barWidth, barHeight); // 非孵化时显示灰色锁定状态 - bool isLocked = !ootheca.IsIncubating; + bool isLocked = !controller.IsIncubating; // 绘制背景 GUI.color = isLocked ? new Color(0.3f, 0.3f, 0.3f, 0.5f) : Color.white; GUI.DrawTexture(barRect, EmptyBarTex); // 绘制填充(从底部向上)- 使用 RawFlux 显示预设值 - float displayFlux = ootheca.RawFlux; + float displayFlux = controller.RawFlux; float fillHeight = barRect.height * displayFlux; Rect fillRect = new Rect(barRect.x, barRect.yMax - fillHeight, barRect.width, fillHeight); GUI.color = isLocked ? new Color(0.5f, 0.35f, 0.1f, 0.5f) : Color.white; @@ -150,9 +154,9 @@ namespace ArachnaeSwarm Text.Anchor = TextAnchor.MiddleLeft; // 百分比(大字) - GUI.color = ootheca.IsDormant ? Color.red : Color.white; + GUI.color = controller.IsDormant ? Color.red : Color.white; Rect percentRect = new Rect(infoX, barRect.y, infoWidth, 20f); - Widgets.Label(percentRect, (ootheca.NeutronFlux * 100f).ToString("0") + "%"); + Widgets.Label(percentRect, (controller.NeutronFlux * 100f).ToString("0") + "%"); Text.Font = GameFont.Tiny; @@ -162,11 +166,11 @@ namespace ArachnaeSwarm Widgets.Label(effRect, "效率"); Rect effValRect = new Rect(infoX, barRect.y + 36f, infoWidth, 14f); GUI.color = Color.white; - Widgets.Label(effValRect, (ootheca.FluxEfficiency * 100f).ToString("0") + "%"); + Widgets.Label(effValRect, (controller.FluxEfficiency * 100f).ToString("0") + "%"); // 速度倍率 GUI.color = new Color(0.7f, 0.7f, 0.7f); - float speedMultiplier = ootheca.FluxEfficiency * 5f; + float speedMultiplier = controller.FluxEfficiency * 5f; Rect speedRect = new Rect(infoX, barRect.y + 54f, infoWidth, 14f); Widgets.Label(speedRect, "速度"); Rect speedValRect = new Rect(infoX, barRect.y + 68f, infoWidth, 14f); @@ -190,7 +194,7 @@ namespace ArachnaeSwarm private void UpdateFluxFromMouse(Rect barRect, float mouseY) { float percent = 1f - (mouseY - barRect.y) / barRect.height; - ootheca.SetNeutronFlux(Mathf.Clamp01(percent)); + controller.SetNeutronFlux(Mathf.Clamp01(percent)); } private string GetTooltip() @@ -206,24 +210,24 @@ namespace ArachnaeSwarm sb.AppendLine(); // 当前状态 - sb.AppendLine($"当前活性: {(ootheca.RawFlux * 100f):0}%"); - sb.AppendLine($"孵化速度: {(ootheca.FluxEfficiency * 5f):0.0}x"); + sb.AppendLine($"当前活性: {(controller.RawFlux * 100f):0}%"); + sb.AppendLine($"孵化速度: {(controller.FluxEfficiency * 5f):0.0}x"); sb.AppendLine(); // 模式说明 - sb.AppendLine($"模式: {ootheca.GetModeName()}"); - switch (ootheca.CurrentFluxMode) + sb.AppendLine($"模式: {controller.GetModeName()}"); + switch (controller.CurrentFluxMode) { - case Building_Ootheca.FluxMode.Manual: + case FluxMode.Manual: sb.AppendLine(" 手动调节活性"); break; - case Building_Ootheca.FluxMode.Quality: + case FluxMode.Quality: sb.AppendLine(" 低活性,追求品质"); break; - case Building_Ootheca.FluxMode.Balance: + case FluxMode.Balance: sb.AppendLine(" 自动平衡速度和品质"); break; - case Building_Ootheca.FluxMode.Speed: + case FluxMode.Speed: sb.AppendLine(" 高活性,快速孵化"); break; } @@ -232,7 +236,7 @@ namespace ArachnaeSwarm // 操作 sb.AppendLine("点击拖动调节 | 右上角切换模式"); - if (ootheca.IsDormant) + if (controller.IsDormant) { sb.AppendLine(); sb.AppendLine("⚠ 休眠中,品质下降!"); diff --git a/Source/ArachnaeSwarm/Buildings/IFluxController.cs b/Source/ArachnaeSwarm/Buildings/IFluxController.cs new file mode 100644 index 0000000..50f8d91 --- /dev/null +++ b/Source/ArachnaeSwarm/Buildings/IFluxController.cs @@ -0,0 +1,37 @@ +namespace ArachnaeSwarm +{ + /// + /// 共享接口:孵化活性控制器 + /// Building_Ootheca 和 Building_EquipmentOotheca 都实现此接口 + /// + public interface IFluxController + { + // 活性值 + float NeutronFlux { get; } + float RawFlux { get; } + float FluxEfficiency { get; } + + // 模式 + FluxMode CurrentFluxMode { get; } + bool IsAutoMode { get; } + bool IsIncubating { get; } + bool IsDormant { get; } + + // 方法 + void SetNeutronFlux(float value); + void CycleFluxMode(); + string GetModeName(); + string GetModeShort(); + } + + /// + /// 活性模式枚举(共享) + /// + public enum FluxMode + { + Manual, // 手动:完全手动控制 + Quality, // 品质优先:低活性,追求高品质 + Balance, // 平衡:自动平衡速度和品质 + Speed // 速度优先:高活性,快速孵化 + } +}