diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index 995e1cc..c1336e9 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 38f6111..ad94408 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/ResearchProjectDefs/ARA_ResearchProjects.xml b/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects.xml
index 0545910..4a45731 100644
--- a/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects.xml
+++ b/1.6/1.6/Defs/ResearchProjectDefs/ARA_ResearchProjects.xml
@@ -487,6 +487,18 @@
ARA_Technology_2NPT
+
+ ARA_Technology_4NPT
+
+ <color=#887E78><i>阿拉克涅虫群-主巢触须\n主巢触须的进化路径是包含于每一支虫群中的通用进化路径,它们奠定了虫群在生物学上的优越性。</i></color>\n\n允许虫族建造孵化池。一种专用于批量生产虫族的孵化场地。孵化池的孵化效率比孵化茧更高。
+ 1800
+ 12.00
+ 2.10
+ ARA_ResearchBench
+
+ ARA_Technology_1NPT
+
+
ARA_Technology_8CPE
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 ed68cb9..9cb13bf 100644
--- a/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml
+++ b/1.6/1.6/Defs/Thing_building/ARA_Ootheca.xml
@@ -701,89 +701,124 @@
-
+
5
0.5
ArachnaeBase_Race_Larva
+
-
- 10
- 30
- 0.00001
-
-
- Legendary
- 0.99
-
-
- Masterwork
- 0.90
-
-
- Excellent
- 0.70
-
-
- Good
- 0.50
-
-
- Normal
- 0.20
-
-
- Poor
- 0.10
-
-
-
-
-
- ArachnaeNode_Race_Myrmecocystus
- 240000
- 100.0
-
+
+
+
ArachnaeNode_Race_ShieldHead
- 180000
- 40.0
+ 3
+
+ ARA_Incubator_1_Reward_Hediffs
+ ARA_Incubator_2_Reward_Hediffs
+ ARA_Incubator_3_Reward_Hediffs
+ ARA_Incubator_4_Reward_Hediffs
+ ARA_Incubator_5_Reward_Hediffs
+ ARA_Incubator_6_Reward_Hediffs
+ ARA_Incubator_7_Reward_Hediffs
+ ARA_Incubator_8_Reward_Hediffs
+
ArachnaeNode_Race_WeaponSmith
- 180000
- 40.0
+ 3
+
+ ARA_Incubator_1_Reward_Hediffs
+ ARA_Incubator_2_Reward_Hediffs
+ ARA_Incubator_3_Reward_Hediffs
+ ARA_Incubator_4_Reward_Hediffs
+ ARA_Incubator_5_Reward_Hediffs
+ ARA_Incubator_6_Reward_Hediffs
+ ARA_Incubator_7_Reward_Hediffs
+ ARA_Incubator_8_Reward_Hediffs
+
ArachnaeNode_Race_Fighter
- 90000
- 20.0
+ 1.5
ARA_Technology_1KYC
+
+ ARA_Incubator_1_Reward_Hediffs
+ ARA_Incubator_2_Reward_Hediffs
+ ARA_Incubator_3_Reward_Hediffs
+ ARA_Incubator_4_Reward_Hediffs
+ ARA_Incubator_5_Reward_Hediffs
+ ARA_Incubator_6_Reward_Hediffs
+ ARA_Incubator_7_Reward_Hediffs
+ ARA_Incubator_8_Reward_Hediffs
+
+
+
+ ArachnaeNode_Race_Myrmecocystus
+ 4
+
+ ARA_Incubator_1_Reward_Hediffs
+ ARA_Incubator_2_Reward_Hediffs
+ ARA_Incubator_3_Reward_Hediffs
+ ARA_Incubator_4_Reward_Hediffs
+ ARA_Incubator_5_Reward_Hediffs
+ ARA_Incubator_6_Reward_Hediffs
+ ARA_Incubator_7_Reward_Hediffs
+ ARA_Incubator_8_Reward_Hediffs
+
ArachnaeNode_Race_Smokepop
- 180000
- 60.0
+ 3
ARA_Technology_5KYC
+
+ ARA_Incubator_1_Reward_Hediffs
+ ARA_Incubator_2_Reward_Hediffs
+ ARA_Incubator_3_Reward_Hediffs
+ ARA_Incubator_4_Reward_Hediffs
+ ARA_Incubator_5_Reward_Hediffs
+ ARA_Incubator_6_Reward_Hediffs
+ ARA_Incubator_7_Reward_Hediffs
+ ARA_Incubator_8_Reward_Hediffs
+
ArachnaeNode_Race_Skyraider
- 120000
- 80.0
+ 2
ARA_Technology_2KYC
+
+ ARA_Incubator_1_Reward_Hediffs
+ ARA_Incubator_2_Reward_Hediffs
+ ARA_Incubator_3_Reward_Hediffs
+ ARA_Incubator_4_Reward_Hediffs
+ ARA_Incubator_5_Reward_Hediffs
+ ARA_Incubator_6_Reward_Hediffs
+ ARA_Incubator_7_Reward_Hediffs
+ ARA_Incubator_8_Reward_Hediffs
+
ARA_MimicNematodeShamblerSwarmer
- 600
- 10.0
+ 0.01
ARA_Technology_6MEN
+
+ ARA_Incubator_1_Reward_Hediffs
+ ARA_Incubator_2_Reward_Hediffs
+ ARA_Incubator_3_Reward_Hediffs
+ ARA_Incubator_4_Reward_Hediffs
+ ARA_Incubator_5_Reward_Hediffs
+ ARA_Incubator_6_Reward_Hediffs
+ ARA_Incubator_7_Reward_Hediffs
+ ARA_Incubator_8_Reward_Hediffs
+
-
+
-
+
300.0
@@ -796,7 +831,7 @@
true
-
+
ARA_NutrientNetworkTower
diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
index 4b9deeb..bd7fa47 100644
--- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
+++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
@@ -124,6 +124,7 @@
+
diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompQueuedInteractiveProducerWithFlux.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompQueuedInteractiveProducerWithFlux.cs
index bc4a678..2c828e9 100644
--- a/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompQueuedInteractiveProducerWithFlux.cs
+++ b/Source/ArachnaeSwarm/Building_Comps/ARA_CompInteractiveProducer/CompQueuedInteractiveProducerWithFlux.cs
@@ -401,6 +401,8 @@ namespace ArachnaeSwarm
}
}
+ public void ShowOrderMenuPublic() => ShowOrderMenu();
+
private void ShowOrderMenu()
{
var options = new List();
diff --git a/Source/ArachnaeSwarm/Building_Comps/ARA_SpawnPawnFromList/CompQueuedPawnSpawnerWithFlux.cs b/Source/ArachnaeSwarm/Building_Comps/ARA_SpawnPawnFromList/CompQueuedPawnSpawnerWithFlux.cs
index 9005f18..92415ec 100644
--- a/Source/ArachnaeSwarm/Building_Comps/ARA_SpawnPawnFromList/CompQueuedPawnSpawnerWithFlux.cs
+++ b/Source/ArachnaeSwarm/Building_Comps/ARA_SpawnPawnFromList/CompQueuedPawnSpawnerWithFlux.cs
@@ -11,8 +11,8 @@ namespace ArachnaeSwarm
// 订单状态
public enum OrderStatus
{
- WaitingForLarva = 0, // 等待幼虫激活
- Incubating = 1, // 正在孵化
+ WaitingForLarva = 0,
+ Incubating = 1,
}
// 用于 Gizmo 显示的订单信息
@@ -25,10 +25,10 @@ namespace ArachnaeSwarm
public string estimatedQuality;
}
- // 带状态和品质的订单
+ // 带状态和品质的督虫订单
public class QueuedPawnOrder : IExposable
{
- public QueuedPawnSpawnEntry entry;
+ public IncubationConfig config; // 使用 IncubationConfig 而不是 QueuedPawnSpawnEntry
public OrderStatus status = OrderStatus.WaitingForLarva;
public int spawnUntilTick = -1;
@@ -40,7 +40,7 @@ namespace ArachnaeSwarm
public void ExposeData()
{
- Scribe_Deep.Look(ref entry, "entry");
+ Scribe_Deep.Look(ref config, "config");
Scribe_Values.Look(ref status, "status", OrderStatus.WaitingForLarva);
Scribe_Values.Look(ref spawnUntilTick, "spawnUntilTick", -1);
Scribe_Values.Look(ref qualityProgress, "qualityProgress", 0f);
@@ -50,15 +50,11 @@ namespace ArachnaeSwarm
public class CompProperties_QueuedPawnSpawnerWithFlux : CompProperties
{
- public List spawnablePawns;
- public List whitelist;
+ public List whitelist; // 允许激活的 pawn 类型(幼虫)
public int productionQueueLimit = 5;
public float minNutritionToStart = 0.1f;
// 质量系统
- public float minSafeTemperature = 7f;
- public float maxSafeTemperature = 32f;
- public float penaltyPerDegreePerTick = 0.00001f;
public List qualityThresholds;
public CompProperties_QueuedPawnSpawnerWithFlux()
@@ -82,10 +78,15 @@ namespace ArachnaeSwarm
// === 组件引用 ===
private CompRefuelableNutrition _fuelComp;
private CompAffectedByFacilities _facilitiesComp;
+ private CompIncubatorData _incubatorDataComp;
public CompProperties_QueuedPawnSpawnerWithFlux Props => (CompProperties_QueuedPawnSpawnerWithFlux)props;
private CompRefuelableNutrition FuelComp => _fuelComp ?? (_fuelComp = parent.GetComp());
private CompAffectedByFacilities FacilitiesComp => _facilitiesComp ?? (_facilitiesComp = parent.GetComp());
+ private CompIncubatorData IncubatorDataComp => _incubatorDataComp ?? (_incubatorDataComp = parent.GetComp());
+
+ // 获取可孵化配置列表(从 CompIncubatorData)
+ public List IncubationConfigs => IncubatorDataComp?.IncubationConfigs ?? new List();
// === IFluxController 接口实现 ===
public float NeutronFlux => neutronFlux;
@@ -120,17 +121,18 @@ namespace ArachnaeSwarm
base.Initialize(props);
_fuelComp = parent.GetComp();
_facilitiesComp = parent.GetComp();
+ _incubatorDataComp = parent.GetComp();
}
// === 订单管理 ===
- public void AddOrder(QueuedPawnSpawnEntry entry)
+ public void AddOrder(IncubationConfig config)
{
if (orders.Count >= Props.productionQueueLimit)
{
Messages.Message("队列已满!", MessageTypeDefOf.RejectInput);
return;
}
- orders.Add(new QueuedPawnOrder { entry = entry, status = OrderStatus.WaitingForLarva });
+ orders.Add(new QueuedPawnOrder { config = config, status = OrderStatus.WaitingForLarva });
}
public void RemoveOrder(QueuedPawnOrder order) => orders.Remove(order);
@@ -148,14 +150,14 @@ namespace ArachnaeSwarm
float prodProgress = GetProgress(order);
result.Add(new PawnOrderDisplayInfo
{
- label = order.entry.pawnKind.LabelCap,
+ label = order.config?.pawnKind?.LabelCap ?? "?",
status = order.status,
progress = prodProgress,
qualityProgress = order.QualityPercent,
remainingTime = order.status == OrderStatus.Incubating && order.spawnUntilTick > 0
? (order.spawnUntilTick - Find.TickManager.TicksGame).ToStringTicksToPeriod()
: "等待中",
- estimatedQuality = GetEstimatedQuality(order).GetLabel()
+ estimatedQuality = GetEstimatedQuality(order.QualityPercent)
});
}
return result;
@@ -163,24 +165,21 @@ namespace ArachnaeSwarm
private float GetProgress(QueuedPawnOrder order)
{
- if (order.status != OrderStatus.Incubating || order.spawnUntilTick <= 0) return 0f;
- int totalTicks = order.entry.delayTicks;
+ if (order.status != OrderStatus.Incubating || order.spawnUntilTick <= 0 || order.config == null) return 0f;
+ int totalTicks = Mathf.RoundToInt(order.config.daysRequired * 60000);
int startTick = order.spawnUntilTick - totalTicks;
int elapsed = Find.TickManager.TicksGame - startTick;
return totalTicks > 0 ? Mathf.Clamp01((float)elapsed / totalTicks) : 0f;
}
- private QualityCategory GetEstimatedQuality(QueuedPawnOrder order)
+ private string GetEstimatedQuality(float qualityPercent)
{
- if (Props.qualityThresholds.NullOrEmpty()) return QualityCategory.Normal;
- float qualityPercent = order.QualityPercent;
- foreach (var threshold in Props.qualityThresholds.OrderByDescending(q => q.threshold))
- {
- if (qualityPercent >= threshold.threshold) return threshold.quality;
- }
- return Props.qualityThresholds.Any()
- ? Props.qualityThresholds.OrderBy(q => q.threshold).First().quality
- : QualityCategory.Awful;
+ if (qualityPercent >= 0.99f) return "传奇";
+ if (qualityPercent >= 0.90f) return "杰作";
+ if (qualityPercent >= 0.70f) return "优秀";
+ if (qualityPercent >= 0.50f) return "良好";
+ if (qualityPercent >= 0.20f) return "普通";
+ return "较差";
}
// === 幼虫激活逻辑 ===
@@ -194,9 +193,14 @@ namespace ArachnaeSwarm
}
int called = 0;
- var availableLarvae = FindAvailableLarvae(neededLarvae);
+ int found = 0;
+ var availableLarvae = FindAvailableLarvae(neededLarvae * 2);
+ found = availableLarvae.Count;
+
foreach (var larva in availableLarvae)
{
+ if (called >= neededLarvae) break;
+
var job = JobMaker.MakeJob(DefDatabase.GetNamed("ARA_OperateIncubator"), parent);
if (larva.jobs.TryTakeOrderedJob(job, JobTag.Misc))
{
@@ -206,28 +210,32 @@ namespace ArachnaeSwarm
}
if (called > 0)
- Messages.Message($"已呼叫 {called} 只幼虫", MessageTypeDefOf.PositiveEvent);
+ Messages.Message($"已呼叫 {called} 只幼虫 (找到{found}只)", MessageTypeDefOf.PositiveEvent);
else
- Messages.Message("未找到可用的幼虫!", MessageTypeDefOf.RejectInput);
+ Messages.Message($"未找到可用的幼虫! (需要{neededLarvae}只,找到{found}只)", MessageTypeDefOf.RejectInput);
}
private List FindAvailableLarvae(int maxCount)
{
var result = new List();
- float searchRadius = 50f;
if (parent.Map == null) return result;
foreach (var pawn in parent.Map.mapPawns.AllPawnsSpawned)
{
if (result.Count >= maxCount) break;
- if (pawn.def.defName == "ArachnaeBase_Race_Larva" &&
- !pawn.Downed && !pawn.Dead &&
- pawn.Faction == parent.Faction &&
- !assignedLarvae.Contains(pawn) &&
- parent.Position.DistanceTo(pawn.Position) <= searchRadius)
- {
- result.Add(pawn);
- }
+
+ if (pawn.def.defName != "ArachnaeBase_Race_Larva") continue;
+ if (pawn.Downed || pawn.Dead) continue;
+ if (pawn.Faction != parent.Faction) continue;
+ if (assignedLarvae.Contains(pawn)) continue;
+
+ bool isBusy = pawn.CurJobDef != null &&
+ pawn.CurJobDef != JobDefOf.Wait_Wander &&
+ pawn.CurJobDef != JobDefOf.GotoWander &&
+ pawn.CurJobDef.defName != "Wait";
+ if (isBusy) continue;
+
+ result.Add(pawn);
}
return result;
}
@@ -237,11 +245,12 @@ namespace ArachnaeSwarm
public void NotifyLarvaOperationComplete(Pawn larva)
{
var waitingOrder = orders.FirstOrDefault(o => o.status == OrderStatus.WaitingForLarva);
- if (waitingOrder != null)
+ if (waitingOrder != null && waitingOrder.config != null)
{
waitingOrder.status = OrderStatus.Incubating;
- waitingOrder.spawnUntilTick = Find.TickManager.TicksGame + waitingOrder.entry.delayTicks;
- waitingOrder.qualityTotal = waitingOrder.entry.delayTicks;
+ int totalTicks = Mathf.RoundToInt(waitingOrder.config.daysRequired * 60000);
+ waitingOrder.spawnUntilTick = Find.TickManager.TicksGame + totalTicks;
+ waitingOrder.qualityTotal = totalTicks;
waitingOrder.qualityProgress = 0f;
}
assignedLarvae.Remove(larva);
@@ -254,22 +263,19 @@ namespace ArachnaeSwarm
assignedLarvae.RemoveAll(l => l == null || l.Dead || l.Destroyed);
- bool hasFuel = FuelComp?.HasFuel ?? true;
+ bool hasFuel = (FuelComp?.Fuel ?? 10f) > 0.01f;
- // 自动模式调节
if (IsAutoMode && parent.IsHashIntervalTick(250) && IsIncubating)
{
CalculateAutoFlux();
}
- // 消耗燃料
if (IsIncubating && FuelComp != null && neutronFlux > 0.01f)
{
float fuelPerTick = (50f * FluxEfficiency * IncubatingCount) / 60000f;
FuelComp.ConsumeFuel(fuelPerTick);
}
- // 处理正在孵化的订单
if (hasFuel && !IsDormant)
{
float speedFactor = 1f + (FacilitiesComp?.GetStatOffset(StatDef.Named("ARA_IncubationSpeedFactor")) ?? 0f);
@@ -277,7 +283,6 @@ namespace ArachnaeSwarm
foreach (var order in orders.Where(o => o.status == OrderStatus.Incubating))
{
- // 进度推进
float extraProgress = fluxSpeed - 1f;
if (extraProgress > 0)
{
@@ -286,14 +291,12 @@ namespace ArachnaeSwarm
order.spawnUntilTick -= extraTicks;
}
- // 品质累积(低通量时累积更快)
float qualityBonus = 1f + (1f - neutronFlux) * 0.5f;
float qualityGain = speedFactor * qualityBonus;
order.qualityProgress = Mathf.Min(order.qualityProgress + qualityGain, order.qualityTotal);
}
}
- // 完成订单
orders.RemoveAll(order =>
{
if (order.status == OrderStatus.Incubating &&
@@ -309,30 +312,39 @@ namespace ArachnaeSwarm
private void CompleteOrder(QueuedPawnOrder order)
{
- Pawn pawn = PawnGenerator.GeneratePawn(new PawnGenerationRequest(order.entry.pawnKind, parent.Faction));
+ if (order.config?.pawnKind == null) return;
+
+ Pawn pawn = PawnGenerator.GeneratePawn(new PawnGenerationRequest(order.config.pawnKind, parent.Faction));
if (pawn != null)
{
- // 应用品质效果到 Pawn
- ApplyQualityEffects(pawn, order.QualityPercent);
+ ApplyQualityEffects(pawn, order.QualityPercent, order.config);
GenPlace.TryPlaceThing(pawn, parent.Position, parent.Map, ThingPlaceMode.Near);
}
}
- private void ApplyQualityEffects(Pawn pawn, float qualityPercent)
+ private void ApplyQualityEffects(Pawn pawn, float qualityPercent, IncubationConfig config)
{
- // 基于品质百分比调整能力
- // 0.0 = 最差, 1.0 = 最佳
- float statBonus = qualityPercent * 0.3f; // 最多+30%
-
- // 可以在这里添加特定的品质效果
- // 例如:pawn.health, pawn.skills 等
+ // 应用 Hediff 奖励
+ if (config != null && config.extraHediffs != null)
+ {
+ var rewardHediffs = config.GetRewardHediffs(qualityPercent);
+ if (rewardHediffs != null)
+ {
+ foreach (var hediffDef in rewardHediffs)
+ {
+ if (hediffDef != null)
+ {
+ pawn.health.AddHediff(hediffDef);
+ }
+ }
+ }
+ }
}
private void CalculateAutoFlux()
{
if (fluxMode == FluxMode.Manual) return;
- // 找到品质最低的订单来决定通量
var incubating = orders.Where(o => o.status == OrderStatus.Incubating).ToList();
if (!incubating.Any()) return;
@@ -367,13 +379,9 @@ namespace ArachnaeSwarm
foreach (var g in base.CompGetGizmosExtra()) yield return g;
if (parent.Faction != Faction.OfPlayer) yield break;
- // 通量控制 Gizmo
yield return new Gizmo_NeutronFlux(this);
-
- // 进度 Gizmo
yield return new Gizmo_PawnProgressBar(this);
- // 添加订单按钮
if (orders.Count < Props.productionQueueLimit)
{
yield return new Command_Action
@@ -385,7 +393,6 @@ namespace ArachnaeSwarm
};
}
- // 呼叫幼虫按钮
int needed = WaitingForLarvaCount - assignedLarvae.Count;
if (needed > 0)
{
@@ -399,26 +406,31 @@ namespace ArachnaeSwarm
}
}
+ public void ShowOrderMenuPublic() => ShowOrderMenu();
+
private void ShowOrderMenu()
{
var options = new List();
+ var configs = IncubationConfigs;
- foreach (var entry in Props.spawnablePawns)
+ foreach (var config in configs)
{
- if (entry.requiredResearch != null && !entry.requiredResearch.IsFinished)
+ if (config?.pawnKind == null) continue;
+
+ if (config.requiredResearch != null && !config.requiredResearch.IsFinished)
{
options.Add(new FloatMenuOption(
- entry.pawnKind.LabelCap + " (需要研究: " + entry.requiredResearch.LabelCap + ")",
+ config.pawnKind.LabelCap + " (需要研究: " + config.requiredResearch.LabelCap + ")",
null
));
}
else
{
- var capturedEntry = entry;
+ var capturedConfig = config;
options.Add(new FloatMenuOption(
- entry.pawnKind.LabelCap,
+ config.pawnKind.LabelCap,
() => {
- AddOrder(capturedEntry);
+ AddOrder(capturedConfig);
if (orders.Count < Props.productionQueueLimit)
ShowOrderMenu();
}
@@ -429,7 +441,7 @@ namespace ArachnaeSwarm
if (options.Count > 0)
Find.WindowStack.Add(new FloatMenu(options, "选择孵化目标"));
else
- Messages.Message("没有可用的孵化选项", MessageTypeDefOf.RejectInput);
+ Messages.Message("没有可用的孵化选项(检查 CompIncubatorData 配置)", MessageTypeDefOf.RejectInput);
}
public override void PostExposeData()
diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_DualProgressBar.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_DualProgressBar.cs
index c4f97e8..2e948d0 100644
--- a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_DualProgressBar.cs
+++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_DualProgressBar.cs
@@ -7,16 +7,20 @@ namespace ArachnaeSwarm
{
///
/// 双向进度条 Gizmo - 用于物品孵化池
- /// 从中间向两边生长:左边品质进度,右边生产进度
- /// 支持滚动显示多个订单
+ /// 样式与旧版 Gizmo_IncubationProgress 统一
///
+ [StaticConstructorOnStartup]
public class Gizmo_DualProgressBar : Gizmo
{
- private const float BarHeight = 22f;
+ private const float Width = 200f;
+ private const float BarHeight = 18f;
private const float Spacing = 4f;
private const float Padding = 8f;
- private const float TitleHeight = 26f;
- private const float MaxVisibleOrders = 4;
+ private const float MaxVisibleOrders = 3;
+
+ private static readonly Texture2D ProgressBarTex = 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));
private readonly CompQueuedInteractiveProducerWithFlux comp;
private float scrollPosition = 0f;
@@ -27,7 +31,7 @@ namespace ArachnaeSwarm
this.Order = -98f;
}
- public override float GetWidth(float maxWidth) => 220f;
+ public override float GetWidth(float maxWidth) => Mathf.Min(Width, maxWidth);
public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms)
{
@@ -36,42 +40,68 @@ namespace ArachnaeSwarm
// 计算高度
int visibleCount = Mathf.Min(orderCount, (int)MaxVisibleOrders);
- float contentHeight = TitleHeight + Spacing;
- contentHeight += Mathf.Max(1, visibleCount) * (BarHeight + Spacing);
- contentHeight += Padding;
-
+ float contentHeight = Padding * 2 + Text.LineHeight + Spacing;
+ contentHeight += Mathf.Max(1, visibleCount) * (BarHeight + Spacing + 14f);
float totalHeight = Mathf.Max(75f, contentHeight);
- Rect gizmoRect = new Rect(topLeft.x, topLeft.y - (totalHeight - 75f), GetWidth(maxWidth), totalHeight);
- Widgets.DrawWindowBackground(gizmoRect);
+ Rect rect = new Rect(topLeft.x, topLeft.y - (totalHeight - 75f), GetWidth(maxWidth), totalHeight);
+ Widgets.DrawWindowBackground(rect);
- float curY = gizmoRect.y + Padding;
+ Rect innerRect = rect.ContractedBy(Padding);
+ float curY = innerRect.y;
- // 标题
- Rect titleRect = new Rect(gizmoRect.x + Padding, curY, gizmoRect.width - Padding * 2, TitleHeight);
+ // === 标题区域(可点击打开菜单) ===
Text.Font = GameFont.Small;
- Text.Anchor = TextAnchor.MiddleCenter;
-
- string title = $"孵化进度 ({orderCount}/{comp.Props.productionQueueLimit})";
- Widgets.Label(titleRect, title);
-
- curY += TitleHeight + Spacing;
- Text.Anchor = TextAnchor.UpperLeft;
-
- // 订单列表区域
- Rect listRect = new Rect(gizmoRect.x + Padding, curY, gizmoRect.width - Padding * 2, visibleCount * (BarHeight + Spacing));
-
+ Rect titleRect = new Rect(innerRect.x, curY, innerRect.width, Text.LineHeight);
+
+ string title;
+ bool canAdd = orderCount < comp.Props.productionQueueLimit;
+
if (orderCount > 0)
{
- // 滚动处理
- if (orderCount > MaxVisibleOrders)
+ title = orders[0].label;
+ if (orderCount > 1) title += $" (+{orderCount - 1})";
+ }
+ else
+ {
+ title = "选择生产目标...";
+ }
+
+ if (canAdd)
+ {
+ if (Mouse.IsOver(titleRect))
{
- float scrollMax = (orderCount - MaxVisibleOrders) * (BarHeight + Spacing);
- if (Mouse.IsOver(listRect))
- {
- scrollPosition -= Event.current.delta.y * 0.5f;
- scrollPosition = Mathf.Clamp(scrollPosition, 0f, scrollMax);
- }
+ Widgets.DrawHighlight(titleRect);
+ }
+
+ if (Widgets.ButtonInvisible(titleRect))
+ {
+ comp.ShowOrderMenuPublic();
+ }
+
+ GUI.color = new Color(0.7f, 0.9f, 1f);
+ Widgets.Label(titleRect, title.Truncate(titleRect.width - 20f) + " ▼");
+ GUI.color = Color.white;
+ }
+ else
+ {
+ GUI.color = new Color(0.5f, 0.5f, 0.5f);
+ Widgets.Label(titleRect, title.Truncate(titleRect.width) + " (满)");
+ GUI.color = Color.white;
+ }
+ curY += Text.LineHeight + Spacing;
+
+ // === 订单列表 ===
+ if (orderCount > 0)
+ {
+ float listHeight = Mathf.Min(visibleCount, orderCount) * (BarHeight + Spacing + 14f);
+ Rect listRect = new Rect(innerRect.x, curY, innerRect.width, listHeight);
+
+ if (orderCount > MaxVisibleOrders && Mouse.IsOver(listRect))
+ {
+ float scrollMax = (orderCount - MaxVisibleOrders) * (BarHeight + Spacing + 14f);
+ scrollPosition -= Event.current.delta.y * 0.5f;
+ scrollPosition = Mathf.Clamp(scrollPosition, 0f, scrollMax);
}
GUI.BeginClip(listRect);
@@ -80,103 +110,87 @@ namespace ArachnaeSwarm
for (int i = 0; i < orderCount; i++)
{
var order = orders[i];
- Rect barRect = new Rect(0, drawY, listRect.width, BarHeight);
+ float itemHeight = BarHeight + 14f;
+ Rect itemRect = new Rect(0, drawY, listRect.width, itemHeight);
- if (barRect.yMax > 0 && barRect.y < listRect.height)
+ if (itemRect.yMax > 0 && itemRect.y < listRect.height)
{
- DrawOrderBar(barRect, order, i);
+ DrawOrderItem(itemRect, order, i);
}
- drawY += BarHeight + Spacing;
+ drawY += itemHeight + Spacing;
}
GUI.EndClip();
-
- // 滚动条指示
- if (orderCount > MaxVisibleOrders)
- {
- float scrollMax = (orderCount - MaxVisibleOrders) * (BarHeight + Spacing);
- float scrollBarHeight = listRect.height * (MaxVisibleOrders / (float)orderCount);
- float scrollBarY = curY + (scrollPosition / scrollMax) * (listRect.height - scrollBarHeight);
-
- Rect scrollBarRect = new Rect(gizmoRect.xMax - 6f, scrollBarY, 4f, scrollBarHeight);
- Widgets.DrawBoxSolid(scrollBarRect, new Color(1f, 1f, 1f, 0.3f));
- }
}
else
{
- Rect emptyRect = new Rect(gizmoRect.x + Padding, curY, gizmoRect.width - Padding * 2, BarHeight);
Text.Font = GameFont.Tiny;
- GUI.color = Color.gray;
- Text.Anchor = TextAnchor.MiddleCenter;
- Widgets.Label(emptyRect, "无订单 - 点击添加");
+ GUI.color = new Color(0.7f, 0.7f, 0.7f);
+ Widgets.Label(new Rect(innerRect.x, curY, innerRect.width, 20f), "就绪 - 点击上方选择目标");
GUI.color = Color.white;
}
Text.Font = GameFont.Small;
- Text.Anchor = TextAnchor.UpperLeft;
-
return new GizmoResult(GizmoState.Clear);
}
- private void DrawOrderBar(Rect rect, OrderDisplayInfo order, int index)
+ private void DrawOrderItem(Rect rect, OrderDisplayInfo order, int index)
{
- // 背景
- Widgets.DrawBoxSolid(rect, new Color(0.08f, 0.08f, 0.1f, 0.9f));
-
+ float labelHeight = 14f;
+
+ Text.Font = GameFont.Tiny;
+ Rect labelRect = new Rect(rect.x, rect.y, rect.width - 20f, labelHeight);
+
if (order.status == OrderStatus.WaitingForLarva)
{
- // 等待幼虫状态 - 显示标签
- Text.Font = GameFont.Tiny;
- Text.Anchor = TextAnchor.MiddleLeft;
- Rect labelRect = new Rect(rect.x + 4f, rect.y, rect.width - 28f, rect.height);
GUI.color = new Color(1f, 0.8f, 0.4f);
- Widgets.Label(labelRect, $"🐛 {order.label} [等待幼虫]");
- GUI.color = Color.white;
+ Widgets.Label(labelRect, $"{order.label} [等待幼虫]");
}
else
{
- // 正在孵化 - 双向进度条
- float midX = rect.x + rect.width / 2f;
- float halfWidth = (rect.width - 28f) / 2f; // 留出取消按钮空间
-
- // 品质进度(向左生长,青色)
- float qualityWidth = halfWidth * order.qualityProgress;
- Rect qualityRect = new Rect(midX - qualityWidth, rect.y + 2f, qualityWidth, rect.height - 4f);
- Color qualityColor = Color.Lerp(new Color(0.2f, 0.5f, 0.6f), new Color(0.3f, 0.8f, 0.9f), order.qualityProgress);
- Widgets.DrawBoxSolid(qualityRect, qualityColor);
-
- // 生产进度(向右生长,绿色)
- float progressWidth = halfWidth * order.productionProgress;
- Rect progressRect = new Rect(midX, rect.y + 2f, progressWidth, rect.height - 4f);
- Color progressColor = Color.Lerp(new Color(0.3f, 0.5f, 0.3f), new Color(0.4f, 0.9f, 0.4f), order.productionProgress);
- Widgets.DrawBoxSolid(progressRect, progressColor);
-
- // 中线
- Widgets.DrawLineVertical(midX, rect.y + 2f, rect.height - 4f);
-
- // 标签
- Text.Font = GameFont.Tiny;
- Text.Anchor = TextAnchor.MiddleCenter;
- Rect labelRect = new Rect(rect.x, rect.y, rect.width - 24f, rect.height);
- string displayText = $"{order.label} {order.productionProgress.ToStringPercent("F0")}";
GUI.color = Color.white;
- Widgets.Label(labelRect, displayText);
-
- // 品质提示
- string tooltip = $"{order.label}\n生产进度: {order.productionProgress.ToStringPercent()}\n品质进度: {order.qualityProgress.ToStringPercent()}\n预计品质: {order.estimatedQuality}";
- TooltipHandler.TipRegion(rect, tooltip);
+ Widgets.Label(labelRect, $"{order.label} {order.productionProgress.ToStringPercent("F0")}");
}
+ GUI.color = Color.white;
- // 取消按钮(右侧X)
- Rect cancelRect = new Rect(rect.xMax - 20f, rect.y + 2f, 18f, rect.height - 4f);
- if (Widgets.ButtonText(cancelRect, "X", false))
+ Rect cancelRect = new Rect(rect.xMax - 16f, rect.y, 16f, labelHeight);
+ if (Widgets.ButtonText(cancelRect, "×", false))
{
comp.RemoveOrderByIndex(index);
}
- // 边框
- Widgets.DrawBox(rect);
- Text.Anchor = TextAnchor.UpperLeft;
+ Rect barRect = new Rect(rect.x, rect.y + labelHeight, rect.width, BarHeight);
+
+ if (order.status == OrderStatus.Incubating)
+ {
+ float midX = barRect.x + barRect.width / 2f;
+ float halfWidth = barRect.width / 2f;
+
+ GUI.DrawTexture(barRect, EmptyBarTex);
+
+ float qualityWidth = halfWidth * order.qualityProgress;
+ Rect qualityRect = new Rect(midX - qualityWidth, barRect.y, qualityWidth, barRect.height);
+ GUI.DrawTexture(qualityRect, QualityBarTex);
+
+ float progressWidth = halfWidth * order.productionProgress;
+ Rect progressRect = new Rect(midX, barRect.y, progressWidth, barRect.height);
+ GUI.DrawTexture(progressRect, ProgressBarTex);
+
+ Widgets.DrawLineVertical(midX, barRect.y, barRect.height);
+
+ string tooltip = $"{order.label}\n品质: {order.qualityProgress.ToStringPercent()} → {order.estimatedQuality}\n进度: {order.productionProgress.ToStringPercent()}";
+ TooltipHandler.TipRegion(barRect, tooltip);
+ }
+ else
+ {
+ GUI.DrawTexture(barRect, EmptyBarTex);
+ Text.Font = GameFont.Tiny;
+ Text.Anchor = TextAnchor.MiddleCenter;
+ GUI.color = new Color(0.8f, 0.6f, 0.2f);
+ Widgets.Label(barRect, "等待幼虫激活");
+ GUI.color = Color.white;
+ Text.Anchor = TextAnchor.UpperLeft;
+ }
}
}
}
diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_PawnProgressBar.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_PawnProgressBar.cs
index 1c1aabb..4ca140c 100644
--- a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_PawnProgressBar.cs
+++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Gizmo_PawnProgressBar.cs
@@ -7,16 +7,20 @@ namespace ArachnaeSwarm
{
///
/// 双向进度条 Gizmo - 用于督虫孵化池
- /// 从中间向两边生长:左边品质进度,右边生产进度
- /// 支持滚动显示多个订单
+ /// 样式与旧版 Gizmo_IncubationProgress 统一
///
+ [StaticConstructorOnStartup]
public class Gizmo_PawnProgressBar : Gizmo
{
- private const float BarHeight = 22f;
+ private const float Width = 200f;
+ private const float BarHeight = 18f;
private const float Spacing = 4f;
private const float Padding = 8f;
- private const float TitleHeight = 26f;
- private const float MaxVisibleOrders = 4;
+ private const float MaxVisibleOrders = 3;
+
+ private static readonly Texture2D ProgressBarTex = 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));
private readonly CompQueuedPawnSpawnerWithFlux comp;
private float scrollPosition = 0f;
@@ -27,48 +31,81 @@ namespace ArachnaeSwarm
this.Order = -98f;
}
- public override float GetWidth(float maxWidth) => 220f;
+ public override float GetWidth(float maxWidth) => Mathf.Min(Width, maxWidth);
public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms)
{
var orders = comp.GetOrdersForGizmo();
int orderCount = orders.Count;
+ // 计算高度
int visibleCount = Mathf.Min(orderCount, (int)MaxVisibleOrders);
- float contentHeight = TitleHeight + Spacing;
- contentHeight += Mathf.Max(1, visibleCount) * (BarHeight + Spacing);
- contentHeight += Padding;
-
+ float contentHeight = Padding * 2 + Text.LineHeight + Spacing; // 标题
+ contentHeight += Mathf.Max(1, visibleCount) * (BarHeight + Spacing + 14f); // 订单(进度条+标签)
float totalHeight = Mathf.Max(75f, contentHeight);
- Rect gizmoRect = new Rect(topLeft.x, topLeft.y - (totalHeight - 75f), GetWidth(maxWidth), totalHeight);
- Widgets.DrawWindowBackground(gizmoRect);
+ Rect rect = new Rect(topLeft.x, topLeft.y - (totalHeight - 75f), GetWidth(maxWidth), totalHeight);
+ Widgets.DrawWindowBackground(rect);
- float curY = gizmoRect.y + Padding;
+ Rect innerRect = rect.ContractedBy(Padding);
+ float curY = innerRect.y;
- // 标题
- Rect titleRect = new Rect(gizmoRect.x + Padding, curY, gizmoRect.width - Padding * 2, TitleHeight);
+ // === 标题区域(可点击打开菜单) ===
Text.Font = GameFont.Small;
- Text.Anchor = TextAnchor.MiddleCenter;
-
- string title = $"孵化进度 ({orderCount}/{comp.Props.productionQueueLimit})";
- Widgets.Label(titleRect, title);
-
- curY += TitleHeight + Spacing;
- Text.Anchor = TextAnchor.UpperLeft;
-
- Rect listRect = new Rect(gizmoRect.x + Padding, curY, gizmoRect.width - Padding * 2, visibleCount * (BarHeight + Spacing));
-
+ Rect titleRect = new Rect(innerRect.x, curY, innerRect.width, Text.LineHeight);
+
+ string title;
+ bool canAdd = orderCount < comp.Props.productionQueueLimit;
+
if (orderCount > 0)
{
- if (orderCount > MaxVisibleOrders)
+ // 显示第一个订单名称
+ title = orders[0].label;
+ if (orderCount > 1) title += $" (+{orderCount - 1})";
+ }
+ else
+ {
+ title = "选择孵化目标...";
+ }
+
+ // 标题按钮
+ if (canAdd)
+ {
+ if (Mouse.IsOver(titleRect))
{
- float scrollMax = (orderCount - MaxVisibleOrders) * (BarHeight + Spacing);
- if (Mouse.IsOver(listRect))
- {
- scrollPosition -= Event.current.delta.y * 0.5f;
- scrollPosition = Mathf.Clamp(scrollPosition, 0f, scrollMax);
- }
+ Widgets.DrawHighlight(titleRect);
+ }
+
+ if (Widgets.ButtonInvisible(titleRect))
+ {
+ comp.ShowOrderMenuPublic();
+ }
+
+ // 带下拉箭头的标题
+ GUI.color = new Color(0.7f, 0.9f, 1f);
+ Widgets.Label(titleRect, title.Truncate(titleRect.width - 20f) + " ▼");
+ GUI.color = Color.white;
+ }
+ else
+ {
+ GUI.color = new Color(0.5f, 0.5f, 0.5f);
+ Widgets.Label(titleRect, title.Truncate(titleRect.width) + " (满)");
+ GUI.color = Color.white;
+ }
+ curY += Text.LineHeight + Spacing;
+
+ // === 订单列表 ===
+ if (orderCount > 0)
+ {
+ float listHeight = Mathf.Min(visibleCount, orderCount) * (BarHeight + Spacing + 14f);
+ Rect listRect = new Rect(innerRect.x, curY, innerRect.width, listHeight);
+
+ // 滚动支持
+ if (orderCount > MaxVisibleOrders && Mouse.IsOver(listRect))
+ {
+ float scrollMax = (orderCount - MaxVisibleOrders) * (BarHeight + Spacing + 14f);
+ scrollPosition -= Event.current.delta.y * 0.5f;
+ scrollPosition = Mathf.Clamp(scrollPosition, 0f, scrollMax);
}
GUI.BeginClip(listRect);
@@ -77,97 +114,98 @@ namespace ArachnaeSwarm
for (int i = 0; i < orderCount; i++)
{
var order = orders[i];
- Rect barRect = new Rect(0, drawY, listRect.width, BarHeight);
+ float itemHeight = BarHeight + 14f;
+ Rect itemRect = new Rect(0, drawY, listRect.width, itemHeight);
- if (barRect.yMax > 0 && barRect.y < listRect.height)
+ if (itemRect.yMax > 0 && itemRect.y < listRect.height)
{
- DrawOrderBar(barRect, order, i);
+ DrawOrderItem(itemRect, order, i);
}
- drawY += BarHeight + Spacing;
+ drawY += itemHeight + Spacing;
}
GUI.EndClip();
-
- if (orderCount > MaxVisibleOrders)
- {
- float scrollMax = (orderCount - MaxVisibleOrders) * (BarHeight + Spacing);
- float scrollBarHeight = listRect.height * (MaxVisibleOrders / (float)orderCount);
- float scrollBarY = curY + (scrollPosition / scrollMax) * (listRect.height - scrollBarHeight);
-
- Rect scrollBarRect = new Rect(gizmoRect.xMax - 6f, scrollBarY, 4f, scrollBarHeight);
- Widgets.DrawBoxSolid(scrollBarRect, new Color(1f, 1f, 1f, 0.3f));
- }
}
else
{
- Rect emptyRect = new Rect(gizmoRect.x + Padding, curY, gizmoRect.width - Padding * 2, BarHeight);
+ // 无订单时的提示
Text.Font = GameFont.Tiny;
- GUI.color = Color.gray;
- Text.Anchor = TextAnchor.MiddleCenter;
- Widgets.Label(emptyRect, "无订单 - 点击添加");
+ GUI.color = new Color(0.7f, 0.7f, 0.7f);
+ Widgets.Label(new Rect(innerRect.x, curY, innerRect.width, 20f), "就绪 - 点击上方选择目标");
GUI.color = Color.white;
}
Text.Font = GameFont.Small;
- Text.Anchor = TextAnchor.UpperLeft;
-
return new GizmoResult(GizmoState.Clear);
}
- private void DrawOrderBar(Rect rect, PawnOrderDisplayInfo order, int index)
+ private void DrawOrderItem(Rect rect, PawnOrderDisplayInfo order, int index)
{
- Widgets.DrawBoxSolid(rect, new Color(0.08f, 0.08f, 0.1f, 0.9f));
-
+ float labelHeight = 14f;
+
+ // 标签行
+ Text.Font = GameFont.Tiny;
+ Rect labelRect = new Rect(rect.x, rect.y, rect.width - 20f, labelHeight);
+
if (order.status == OrderStatus.WaitingForLarva)
{
- Text.Font = GameFont.Tiny;
- Text.Anchor = TextAnchor.MiddleLeft;
- Rect labelRect = new Rect(rect.x + 4f, rect.y, rect.width - 28f, rect.height);
GUI.color = new Color(1f, 0.8f, 0.4f);
- Widgets.Label(labelRect, $"🐛 {order.label} [等待幼虫]");
- GUI.color = Color.white;
+ Widgets.Label(labelRect, $"{order.label} [等待幼虫]");
}
else
{
- // 双向进度条
- float midX = rect.x + rect.width / 2f;
- float halfWidth = (rect.width - 28f) / 2f;
-
- // 品质进度(向左生长,青色)
- float qualityWidth = halfWidth * order.qualityProgress;
- Rect qualityRect = new Rect(midX - qualityWidth, rect.y + 2f, qualityWidth, rect.height - 4f);
- Color qualityColor = Color.Lerp(new Color(0.2f, 0.5f, 0.6f), new Color(0.3f, 0.8f, 0.9f), order.qualityProgress);
- Widgets.DrawBoxSolid(qualityRect, qualityColor);
-
- // 生产进度(向右生长,绿色)
- float progressWidth = halfWidth * order.progress;
- Rect progressRect = new Rect(midX, rect.y + 2f, progressWidth, rect.height - 4f);
- Color progressColor = Color.Lerp(new Color(0.3f, 0.5f, 0.3f), new Color(0.4f, 0.9f, 0.4f), order.progress);
- Widgets.DrawBoxSolid(progressRect, progressColor);
-
- // 中线
- Widgets.DrawLineVertical(midX, rect.y + 2f, rect.height - 4f);
-
- // 标签
- Text.Font = GameFont.Tiny;
- Text.Anchor = TextAnchor.MiddleCenter;
- Rect labelRect = new Rect(rect.x, rect.y, rect.width - 24f, rect.height);
GUI.color = Color.white;
- Widgets.Label(labelRect, $"{order.label} {order.progress.ToStringPercent("F0")}");
-
- // 品质提示
- string tooltip = $"{order.label}\n生产进度: {order.progress.ToStringPercent()}\n品质进度: {order.qualityProgress.ToStringPercent()}\n预计品质: {order.estimatedQuality}\n剩余时间: {order.remainingTime}";
- TooltipHandler.TipRegion(rect, tooltip);
+ Widgets.Label(labelRect, $"{order.label} {order.progress.ToStringPercent("F0")}");
}
+ GUI.color = Color.white;
- Rect cancelRect = new Rect(rect.xMax - 20f, rect.y + 2f, 18f, rect.height - 4f);
- if (Widgets.ButtonText(cancelRect, "X", false))
+ // 取消按钮
+ Rect cancelRect = new Rect(rect.xMax - 16f, rect.y, 16f, labelHeight);
+ if (Widgets.ButtonText(cancelRect, "×", false))
{
comp.RemoveOrderByIndex(index);
}
- Widgets.DrawBox(rect);
- Text.Anchor = TextAnchor.UpperLeft;
+ // 进度条
+ Rect barRect = new Rect(rect.x, rect.y + labelHeight, rect.width, BarHeight);
+
+ if (order.status == OrderStatus.Incubating)
+ {
+ // 双向进度条:品质向左,进度向右
+ float midX = barRect.x + barRect.width / 2f;
+ float halfWidth = barRect.width / 2f;
+
+ // 背景
+ GUI.DrawTexture(barRect, EmptyBarTex);
+
+ // 品质进度(向左)
+ float qualityWidth = halfWidth * order.qualityProgress;
+ Rect qualityRect = new Rect(midX - qualityWidth, barRect.y, qualityWidth, barRect.height);
+ GUI.DrawTexture(qualityRect, QualityBarTex);
+
+ // 生产进度(向右)
+ float progressWidth = halfWidth * order.progress;
+ Rect progressRect = new Rect(midX, barRect.y, progressWidth, barRect.height);
+ GUI.DrawTexture(progressRect, ProgressBarTex);
+
+ // 中线
+ Widgets.DrawLineVertical(midX, barRect.y, barRect.height);
+
+ // Tooltip
+ string tooltip = $"{order.label}\n品质: {order.qualityProgress.ToStringPercent()} → {order.estimatedQuality}\n进度: {order.progress.ToStringPercent()}\n剩余: {order.remainingTime}";
+ TooltipHandler.TipRegion(barRect, tooltip);
+ }
+ else
+ {
+ // 等待幼虫状态:显示等待指示
+ GUI.DrawTexture(barRect, EmptyBarTex);
+ Text.Font = GameFont.Tiny;
+ Text.Anchor = TextAnchor.MiddleCenter;
+ GUI.color = new Color(0.8f, 0.6f, 0.2f);
+ Widgets.Label(barRect, "等待幼虫激活");
+ GUI.color = Color.white;
+ Text.Anchor = TextAnchor.UpperLeft;
+ }
}
}
}
diff --git a/Source/ArachnaeSwarm/Buildings/IncubatorUtils.cs b/Source/ArachnaeSwarm/Buildings/IncubatorUtils.cs
new file mode 100644
index 0000000..f370a52
--- /dev/null
+++ b/Source/ArachnaeSwarm/Buildings/IncubatorUtils.cs
@@ -0,0 +1,275 @@
+using RimWorld;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ ///
+ /// 孵化器通用工具类
+ /// 统一核心算法,保持各孵化器行为一致
+ ///
+ public static class IncubatorUtils
+ {
+ // ============================================
+ // 通量系统
+ // ============================================
+
+ ///
+ /// 计算通量效率(0-1之间,通量越高效率越高)
+ ///
+ public static float GetFluxEfficiency(float neutronFlux)
+ {
+ // 与 IFluxControllerExtensions.GetEfficiency 保持一致
+ return Mathf.Pow(neutronFlux, 0.7f);
+ }
+
+ ///
+ /// 判断是否处于休眠状态
+ ///
+ public static bool IsDormant(float neutronFlux)
+ {
+ return neutronFlux < 0.05f;
+ }
+
+ ///
+ /// 获取模式名称
+ ///
+ public static string GetFluxModeName(FluxMode mode)
+ {
+ return mode switch
+ {
+ FluxMode.Manual => "手动",
+ FluxMode.Quality => "品质",
+ FluxMode.Balance => "平衡",
+ FluxMode.Speed => "速度",
+ _ => "?"
+ };
+ }
+
+ ///
+ /// 获取模式简称
+ ///
+ public static string GetFluxModeShort(FluxMode mode)
+ {
+ return mode switch
+ {
+ FluxMode.Manual => "M",
+ FluxMode.Quality => "Q",
+ FluxMode.Balance => "B",
+ FluxMode.Speed => "S",
+ _ => "?"
+ };
+ }
+
+ // ============================================
+ // 品质系统
+ // ============================================
+
+ ///
+ /// 计算品质增长(低通量时增长更快)
+ /// 公式:qualityGain = speedFactor * (1 + (1 - flux) * 0.5)
+ ///
+ public static float CalculateQualityGain(float neutronFlux, float speedFactor)
+ {
+ float qualityBonus = 1f + (1f - neutronFlux) * 0.5f;
+ return speedFactor * qualityBonus;
+ }
+
+ ///
+ /// 计算休眠时品质衰减(10%/天)
+ ///
+ public static float CalculateQualityDecay(float qualityTotal)
+ {
+ return (qualityTotal * 0.1f) / 60000f;
+ }
+
+ ///
+ /// 根据品质百分比获取品质等级
+ ///
+ public static QualityCategory GetQualityFromPercent(float qualityPercent, List thresholds)
+ {
+ if (thresholds.NullOrEmpty()) return QualityCategory.Normal;
+
+ foreach (var threshold in thresholds.OrderByDescending(q => q.threshold))
+ {
+ if (qualityPercent >= threshold.threshold)
+ return threshold.quality;
+ }
+
+ return thresholds.OrderBy(q => q.threshold).First().quality;
+ }
+
+ ///
+ /// 默认品质阈值
+ ///
+ public static List GetDefaultQualityThresholds()
+ {
+ return new List
+ {
+ new QualityThreshold { quality = QualityCategory.Legendary, threshold = 0.99f },
+ new QualityThreshold { quality = QualityCategory.Masterwork, threshold = 0.90f },
+ new QualityThreshold { quality = QualityCategory.Excellent, threshold = 0.70f },
+ new QualityThreshold { quality = QualityCategory.Good, threshold = 0.50f },
+ new QualityThreshold { quality = QualityCategory.Normal, threshold = 0.20f },
+ new QualityThreshold { quality = QualityCategory.Poor, threshold = 0.10f },
+ new QualityThreshold { quality = QualityCategory.Awful, threshold = 0f }
+ };
+ }
+
+ // ============================================
+ // 幼虫系统
+ // ============================================
+
+ ///
+ /// 搜索可用幼虫
+ ///
+ public static Pawn FindLarva(Map map, IntVec3 position, Faction faction, float searchRadius = 50f)
+ {
+ if (map == null) return null;
+
+ foreach (var pawn in map.mapPawns.AllPawnsSpawned)
+ {
+ if (pawn.def.defName == "ArachnaeBase_Race_Larva" &&
+ !pawn.Downed && !pawn.Dead &&
+ pawn.Faction == faction &&
+ position.DistanceTo(pawn.Position) <= searchRadius)
+ {
+ return pawn;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// 搜索多个可用幼虫
+ ///
+ public static List FindLarvae(Map map, IntVec3 position, Faction faction, int maxCount, float searchRadius = 50f, List exclude = null)
+ {
+ var result = new List();
+ if (map == null) return result;
+
+ foreach (var pawn in map.mapPawns.AllPawnsSpawned)
+ {
+ if (result.Count >= maxCount) break;
+ if (pawn.def.defName == "ArachnaeBase_Race_Larva" &&
+ !pawn.Downed && !pawn.Dead &&
+ pawn.Faction == faction &&
+ (exclude == null || !exclude.Contains(pawn)) &&
+ position.DistanceTo(pawn.Position) <= searchRadius)
+ {
+ result.Add(pawn);
+ }
+ }
+ return result;
+ }
+
+ // ============================================
+ // 自动通量计算
+ // ============================================
+
+ ///
+ /// 计算自动通量目标值
+ ///
+ /// 通量模式
+ /// 当前品质百分比
+ /// 当前进度百分比
+ /// 当前燃料量
+ /// 目标通量值
+ public static float CalculateAutoFlux(FluxMode mode, float qualityPercent, float progressPercent, float currentFuel)
+ {
+ if (mode == FluxMode.Manual) return -1f; // 手动模式不计算
+
+ float gap = qualityPercent - progressPercent;
+ float targetFlux;
+
+ switch (mode)
+ {
+ 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 && progressPercent > 0.5f) targetFlux = 0.7f;
+ else if (qualityPercent < 0.2f && progressPercent > 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 (currentFuel < 20f)
+ targetFlux = Mathf.Min(targetFlux, 0.3f);
+ else if (currentFuel < 50f)
+ targetFlux = Mathf.Min(targetFlux, 0.5f);
+
+ return targetFlux;
+ }
+
+ ///
+ /// 平滑调节通量(用于自动模式)
+ ///
+ public static float SmoothFlux(float currentFlux, float targetFlux, float lerpSpeed = 0.05f)
+ {
+ return Mathf.Lerp(currentFlux, targetFlux, lerpSpeed);
+ }
+
+ // ============================================
+ // 燃料消耗
+ // ============================================
+
+ ///
+ /// 计算每tick燃料消耗
+ ///
+ /// 每天基础消耗
+ /// 通量效率
+ /// 正在处理的订单数
+ public static float CalculateFuelConsumption(float baseFuelPerDay, float fluxEfficiency, int orderCount = 1)
+ {
+ return (baseFuelPerDay * fluxEfficiency * orderCount) / 60000f;
+ }
+
+ // ============================================
+ // 速度计算
+ // ============================================
+
+ ///
+ /// 计算实际进度速度
+ ///
+ public static float CalculateProgressSpeed(float fluxEfficiency, float speedFactor, float baseMultiplier = 5f)
+ {
+ return speedFactor * fluxEfficiency * baseMultiplier;
+ }
+
+ ///
+ /// 计算需要减少的ticks(带随机)
+ ///
+ public static int CalculateExtraTicks(float progressSpeed)
+ {
+ float extra = progressSpeed - 1f;
+ if (extra <= 0) return 0;
+
+ int extraTicks = Mathf.FloorToInt(extra);
+ if (Rand.Value < (extra - extraTicks)) extraTicks++;
+ return extraTicks;
+ }
+ }
+}