This commit is contained in:
2025-12-26 18:45:10 +08:00
parent 592600a6e8
commit 51275a9bdd
11 changed files with 994 additions and 98 deletions

View File

@@ -466,7 +466,7 @@ namespace ArachnaeSwarm
}
float progress = (float)ticksUnderOptimalConditions / _selectedProcess.productionTicks;
float finalQualityPercent = Mathf.Clamp01(progress - temperaturePenaltyPercent);
float finalQualityPercent = Mathf.Clamp(progress - temperaturePenaltyPercent, 0f, 1.5f);
QualityCategory finalQuality = QualityCategory.Awful;
foreach (var threshold in Props.qualityThresholds.OrderByDescending(q => q.threshold))
@@ -766,7 +766,7 @@ namespace ArachnaeSwarm
if (Props.qualityThresholds != null && Props.qualityThresholds.Count > 0)
{
var qualityDetails = GetEstimatedQualityDetails();
float qualityProgress = Mathf.Clamp01(qualityDetails.baseScore - qualityDetails.penalty);
float qualityProgress = Mathf.Clamp(qualityDetails.baseScore - qualityDetails.penalty, 0f, 1.5f);
drawPos.y += 0.2f;

View File

@@ -208,7 +208,7 @@ namespace ArachnaeSwarm
return (QualityCategory.Normal, 0f, 0f);
}
float progress = (order.process.productionTicks > 0) ? (float)order.ticksUnderOptimalConditions / order.process.productionTicks : 1f;
float finalQualityPercent = Mathf.Clamp01(progress - order.temperaturePenaltyPercent);
float finalQualityPercent = Mathf.Clamp(progress - order.temperaturePenaltyPercent, 0f, 1.5f);
QualityCategory finalQuality = QualityCategory.Awful;
foreach (var threshold in Props.qualityThresholds.OrderByDescending(q => q.threshold))
{

View File

@@ -365,18 +365,22 @@ namespace ArachnaeSwarm
private void ApplyQualityEffects(Pawn pawn, float qualityPercent, IncubationConfig config)
{
// 应用 Hediff 奖励
if (config != null && config.extraHediffs != null)
// 使用新的分层奖励系统(优先)或回退到旧系统
List<HediffDef> rewardHediffs = IncubatorDataComp?.DrawRewardsForQuality(qualityPercent);
// 如果新系统没有返回结果,回退到旧系统
if ((rewardHediffs == null || rewardHediffs.Count == 0) && config?.extraHediffs != null)
{
var rewardHediffs = config.GetRewardHediffs(qualityPercent);
if (rewardHediffs != null)
rewardHediffs = config.GetRewardHediffs(qualityPercent);
}
if (rewardHediffs != null)
{
foreach (var hediffDef in rewardHediffs)
{
foreach (var hediffDef in rewardHediffs)
if (hediffDef != null)
{
if (hediffDef != null)
{
pawn.health.AddHediff(hediffDef);
}
pawn.health.AddHediff(hediffDef);
}
}
}

View File

@@ -0,0 +1,263 @@
// File: Building_Comps/ARA_TieredHediffReward/CompTieredIncubatorData.cs
// 分层孵化器数据组件
using System.Collections.Generic;
using System.Text;
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
/// <summary>
/// 分层孵化器组件属性 - 只需引用 TieredHediffRewardDef
/// </summary>
public class CompProperties_TieredIncubatorData : CompProperties
{
// === 孵化配置 ===
/// <summary>孵化配置列表(可孵化的 PawnKind</summary>
public List<IncubationConfig> incubationConfigs = new List<IncubationConfig>();
/// <summary>默认选择索引</summary>
public int defaultIndex = 0;
// === UI 配置 ===
/// <summary>按钮标签翻译键</summary>
public string buttonLabel = "IncubatorButtonLabel";
/// <summary>按钮描述翻译键</summary>
public string buttonDesc = "IncubatorButtonDesc";
/// <summary>菜单标题翻译键</summary>
public string menuTitle = "IncubatorMenuTitle";
/// <summary>默认图标路径</summary>
public string defaultIconPath = "UI/Commands/Default";
// === 引用奖励 Def ===
/// <summary>分层奖励配置的 Def 引用</summary>
public TieredHediffRewardDef rewardDef;
public CompProperties_TieredIncubatorData()
{
compClass = typeof(CompTieredIncubatorData);
}
public override void ResolveReferences(ThingDef parentDef)
{
base.ResolveReferences(parentDef);
if (incubationConfigs == null)
incubationConfigs = new List<IncubationConfig>();
}
public override IEnumerable<string> ConfigErrors(ThingDef parentDef)
{
foreach (var error in base.ConfigErrors(parentDef))
yield return error;
if (rewardDef == null)
yield return "rewardDef is null - no tiered hediff reward configuration";
if (incubationConfigs == null || incubationConfigs.Count == 0)
yield return "incubationConfigs is empty";
}
}
/// <summary>
/// 分层孵化器数据组件 - 管理当前选择状态和奖励抽取
/// </summary>
public class CompTieredIncubatorData : ThingComp
{
// === 属性访问 ===
private CompProperties_TieredIncubatorData Props => (CompProperties_TieredIncubatorData)props;
// === 状态 ===
/// <summary>当前选择的配置索引 (-1 表示未选择)</summary>
private int selectedIndex = -1;
// === 公开属性 ===
/// <summary>获取孵化配置列表</summary>
public List<IncubationConfig> IncubationConfigs => Props?.incubationConfigs ?? new List<IncubationConfig>();
/// <summary>获取奖励 Def</summary>
public TieredHediffRewardDef RewardDef => Props?.rewardDef;
/// <summary>获取当前选中的配置</summary>
public IncubationConfig SelectedConfig
{
get
{
var configs = IncubationConfigs;
if (configs.Count == 0) return null;
// 未选择时返回 null
if (selectedIndex == -1)
return null;
if (selectedIndex < 0 || selectedIndex >= configs.Count)
return null;
return configs[selectedIndex];
}
}
/// <summary>获取当前选择的 PawnKind</summary>
public PawnKindDef SelectedPawnKind => SelectedConfig?.pawnKind;
/// <summary>获取当前选择的索引</summary>
public int GetSelectedIndex() => selectedIndex;
// === 方法 ===
/// <summary>
/// 切换到指定配置 (-1 表示清除选择)
/// </summary>
public void SwitchToConfig(int index)
{
if (index == -1)
{
selectedIndex = -1;
}
else if (index >= 0 && index < IncubationConfigs.Count)
{
selectedIndex = index;
}
}
/// <summary>
/// 检查配置是否可用(研究是否完成)
/// </summary>
public bool IsConfigAvailable(int index)
{
if (index < 0 || index >= IncubationConfigs.Count)
return false;
var config = IncubationConfigs[index];
return config?.IsResearchComplete ?? false;
}
/// <summary>
/// 根据品质抽取 Hediff 奖励
/// </summary>
public List<HediffDef> DrawRewardsForQuality(float quality)
{
if (RewardDef == null)
{
Log.Warning($"[CompTieredIncubatorData] No rewardDef configured for {parent.def.defName}");
return new List<HediffDef>();
}
return RewardDef.DrawRewardsForQuality(quality);
}
/// <summary>
/// 获取品质对应的阶段信息
/// </summary>
public QualityTier GetTierForQuality(float quality)
{
return RewardDef?.GetTierForQuality(quality);
}
/// <summary>
/// 显示目标选择浮动菜单
/// </summary>
public void ShowFloatMenu()
{
var configs = IncubationConfigs;
if (configs == null || configs.Count == 0) return;
List<FloatMenuOption> options = new List<FloatMenuOption>();
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()));
}
/// <summary>
/// 获取奖励系统的描述文本
/// </summary>
public string GetRewardSystemDescription()
{
if (RewardDef == null)
return null;
var builder = new StringBuilder();
builder.AppendLine("ARA_TieredReward_Title".Translate());
builder.AppendLine();
// 显示各阶段信息
if (RewardDef.qualityTiers != null)
{
foreach (var tier in RewardDef.qualityTiers)
{
string qualityRange = $"{tier.minQuality:P0} - {tier.maxQuality:P0}";
builder.AppendLine($" {tier.tierLabel ?? $"{tier.tier}"}: {qualityRange}");
if (tier.drawRules != null)
{
foreach (var rule in tier.drawRules)
{
var pool = RewardDef.GetPool(rule.poolId);
string poolLabel = pool?.label ?? rule.poolId;
builder.AppendLine($" - {poolLabel} x{rule.count}");
}
}
}
}
return builder.ToString().TrimEndNewlines();
}
// === 存档 ===
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref selectedIndex, "selectedIndex", -1);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
// 验证索引有效性
if (selectedIndex >= IncubationConfigs.Count)
selectedIndex = Mathf.Clamp(Props.defaultIndex, 0, IncubationConfigs.Count - 1);
}
}
// === 检查面板 ===
public override string CompInspectStringExtra()
{
var current = SelectedConfig;
if (current != null)
{
string status = "ARA_Status_Target".Translate(current.pawnKind.LabelCap);
if (current.requiredResearch != null && !current.requiredResearch.IsFinished)
{
status += " (" + "ARA_Menu_RequiresResearch".Translate(current.requiredResearch.LabelCap) + ")";
}
return status;
}
return null;
}
}
}

View File

@@ -0,0 +1,280 @@
// File: Defs/TieredHediffRewardDef.cs
// 分层特质奖励系统 - Def 定义
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
/// <summary>
/// 特质池 - 定义一组可抽取的 Hediff
/// </summary>
public class HediffPool
{
/// <summary>池ID如 "bad", "normal", "elite"</summary>
public string poolId;
/// <summary>显示名称,如 "劣质特质"</summary>
public string label;
/// <summary>池中的 Hediff 列表</summary>
public List<HediffDef> hediffs = new List<HediffDef>();
/// <summary>
/// 从池中随机抽取指定数量的 Hediff
/// </summary>
/// <param name="count">抽取数量</param>
/// <param name="allowDuplicates">是否允许重复</param>
/// <returns>抽取的 Hediff 列表</returns>
public List<HediffDef> DrawRandom(int count, bool allowDuplicates = false)
{
var result = new List<HediffDef>();
if (hediffs == null || hediffs.Count == 0 || count <= 0)
return result;
if (allowDuplicates)
{
// 允许重复:直接随机抽取
for (int i = 0; i < count; i++)
{
result.Add(hediffs.RandomElement());
}
}
else
{
// 不允许重复:从可用列表中移除已抽取的
var available = new List<HediffDef>(hediffs);
int actualCount = Mathf.Min(count, available.Count);
for (int i = 0; i < actualCount; i++)
{
var selected = available.RandomElement();
result.Add(selected);
available.Remove(selected);
}
}
return result;
}
}
/// <summary>
/// 从池中抽取的规则
/// </summary>
public class PoolDrawRule
{
/// <summary>从哪个池抽取池ID</summary>
public string poolId;
/// <summary>抽取数量</summary>
public int count = 1;
/// <summary>是否允许重复抽取同一个 Hediff</summary>
public bool allowDuplicates = false;
}
/// <summary>
/// 品质阶段配置 - 定义某个品质范围内的抽取规则
/// </summary>
public class QualityTier
{
/// <summary>阶段编号 (1-7)</summary>
public int tier;
/// <summary>最低品质阈值 (0-1),包含</summary>
public float minQuality;
/// <summary>最高品质阈值 (0-1),不包含(除非是最后一个阶段)</summary>
public float maxQuality;
/// <summary>阶段标签,如 "劣质", "普通", "传奇"</summary>
public string tierLabel;
/// <summary>消息翻译键,孵化完成时显示</summary>
public string messageKey;
/// <summary>该阶段的抽取规则列表(可从多个池抽取)</summary>
public List<PoolDrawRule> drawRules = new List<PoolDrawRule>();
/// <summary>
/// 检查品质值是否属于该阶段
/// </summary>
public bool Contains(float quality)
{
return quality >= minQuality && quality < maxQuality;
}
}
/// <summary>
/// 分层特质奖励配置的 Def 类
/// 定义特质池和品质阶段规则,可被多个建筑共享
/// </summary>
public class TieredHediffRewardDef : Def
{
// === 特质池定义 ===
/// <summary>所有特质池列表</summary>
public List<HediffPool> hediffPools = new List<HediffPool>();
// === 品质阶段定义 ===
/// <summary>所有品质阶段列表</summary>
public List<QualityTier> qualityTiers = new List<QualityTier>();
// === 缓存 ===
private Dictionary<string, HediffPool> poolCache;
/// <summary>
/// 配置错误检查
/// </summary>
public override IEnumerable<string> ConfigErrors()
{
foreach (var error in base.ConfigErrors())
yield return error;
if (hediffPools == null || hediffPools.Count == 0)
yield return "hediffPools is empty";
if (qualityTiers == null || qualityTiers.Count == 0)
yield return "qualityTiers is empty";
// 检查池ID是否重复
var poolIds = new HashSet<string>();
foreach (var pool in hediffPools)
{
if (string.IsNullOrEmpty(pool.poolId))
yield return "A hediffPool has empty poolId";
else if (!poolIds.Add(pool.poolId))
yield return $"Duplicate poolId: {pool.poolId}";
}
// 检查阶段配置
foreach (var tier in qualityTiers)
{
if (tier.minQuality >= tier.maxQuality)
yield return $"Tier {tier.tier}: minQuality ({tier.minQuality}) >= maxQuality ({tier.maxQuality})";
if (tier.drawRules != null)
{
foreach (var rule in tier.drawRules)
{
if (!poolIds.Contains(rule.poolId))
yield return $"Tier {tier.tier}: references unknown poolId '{rule.poolId}'";
}
}
}
}
/// <summary>
/// 解析引用后的初始化
/// </summary>
public override void ResolveReferences()
{
base.ResolveReferences();
// 构建池缓存
poolCache = new Dictionary<string, HediffPool>();
if (hediffPools != null)
{
foreach (var pool in hediffPools)
{
if (!string.IsNullOrEmpty(pool.poolId))
{
poolCache[pool.poolId] = pool;
}
}
}
// 按 minQuality 排序阶段
qualityTiers?.Sort((a, b) => a.minQuality.CompareTo(b.minQuality));
}
/// <summary>
/// 根据池ID获取特质池
/// </summary>
public HediffPool GetPool(string poolId)
{
if (poolCache != null && poolCache.TryGetValue(poolId, out var pool))
return pool;
return hediffPools?.Find(p => p.poolId == poolId);
}
/// <summary>
/// 根据品质百分比获取对应阶段
/// </summary>
public QualityTier GetTierForQuality(float quality)
{
if (qualityTiers == null || qualityTiers.Count == 0)
return null;
// 限制在 0-1.5 范围 (支持150%品质上限)
quality = Mathf.Clamp(quality, 0f, 1.5f);
foreach (var tier in qualityTiers)
{
if (tier.Contains(quality))
return tier;
}
// 如果是 100%,返回最后一个阶段
if (quality >= 1f)
return qualityTiers[qualityTiers.Count - 1];
return null;
}
/// <summary>
/// 根据品质抽取 Hediff 奖励
/// </summary>
/// <param name="quality">品质值 (0-1)</param>
/// <returns>抽取的 Hediff 列表</returns>
public List<HediffDef> DrawRewardsForQuality(float quality)
{
var result = new List<HediffDef>();
var tier = GetTierForQuality(quality);
if (tier?.drawRules == null)
return result;
foreach (var rule in tier.drawRules)
{
var pool = GetPool(rule.poolId);
if (pool == null)
{
Log.Warning($"[TieredHediffReward] Pool '{rule.poolId}' not found in def '{defName}'");
continue;
}
var drawn = pool.DrawRandom(rule.count, rule.allowDuplicates);
result.AddRange(drawn);
}
return result;
}
/// <summary>
/// 获取品质阶段的描述文本
/// </summary>
public string GetTierDescription(float quality)
{
var tier = GetTierForQuality(quality);
if (tier == null)
return null;
return tier.tierLabel ?? $"阶段 {tier.tier}";
}
/// <summary>
/// 获取品质阶段的消息
/// </summary>
public string GetTierMessage(float quality)
{
var tier = GetTierForQuality(quality);
if (tier == null || string.IsNullOrEmpty(tier.messageKey))
return null;
return tier.messageKey.Translate();
}
}
}

View File

@@ -341,7 +341,8 @@ namespace ArachnaeSwarm
// 品质增长新公式50%通量时与进度同步)
float qualityGain = IncubatorUtils.CalculateQualityGainNew(fluxSpeed, neutronFlux);
qualityProgress = Mathf.Min(qualityProgress + qualityGain, qualityTotal);
// 不限制上限允许超过100%最高150%
qualityProgress += qualityGain;
}
if (incubationProgress >= incubationDuration)
@@ -405,10 +406,8 @@ namespace ArachnaeSwarm
private void ApplyHediffRewards(Pawn pawn, float qualityPercent)
{
var config = IncubatorData?.SelectedConfig;
if (config == null) return;
List<HediffDef> rewardHediffs = config.GetRewardHediffs(qualityPercent);
// 使用新的分层奖励系统(优先)或回退到旧系统
List<HediffDef> rewardHediffs = IncubatorData?.DrawRewardsForQuality(qualityPercent);
if (rewardHediffs == null || rewardHediffs.Count == 0) return;
int appliedCount = 0;
@@ -424,9 +423,17 @@ namespace ArachnaeSwarm
if (appliedCount > 0)
{
string message = config.GetRewardMessage(qualityPercent);
// 使用分层系统的消息
var tier = IncubatorData?.GetTierForQuality(qualityPercent);
string message = null;
if (tier != null && !string.IsNullOrEmpty(tier.messageKey))
{
message = tier.messageKey.Translate(pawn.LabelShortCap, appliedCount);
}
if (string.IsNullOrEmpty(message))
{
message = "ARA_QualityReward_Default".Translate(pawn.LabelShortCap, appliedCount);
}
Messages.Message(message, pawn, MessageTypeDefOf.PositiveEvent);
}
}

View File

@@ -247,7 +247,10 @@ namespace ArachnaeSwarm
public string menuTitle = "IncubatorMenuTitle"; // 菜单标题翻译键
public string defaultIconPath = "UI/Commands/Default";
// === 新增:全局Hediff奖励所有配置共享===
// === 新增:分层奖励Def引用推荐使用===
public TieredHediffRewardDef rewardDef;
// === 旧版全局Hediff奖励保留向后兼容===
public List<HediffDef> globalExtraHediffs = new List<HediffDef>();
public List<QualityHediffReward> globalHediffRewards = new List<QualityHediffReward>();
@@ -336,6 +339,9 @@ namespace ArachnaeSwarm
// 公开获取孵化配置列表的方法
public List<IncubationConfig> IncubationConfigs => Props?.incubationConfigs ?? new List<IncubationConfig>();
// 获取奖励 Def新系统
public TieredHediffRewardDef RewardDef => Props?.rewardDef;
// 获取当前选择的配置
public IncubationConfig SelectedConfig
{
@@ -493,5 +499,73 @@ namespace ArachnaeSwarm
return builder.ToString().TrimEndNewlines();
}
// === 新增:使用分层系统抽取奖励 ===
/// <summary>
/// 根据品质抽取 Hediff 奖励(优先使用新的分层系统)
/// </summary>
/// <param name="quality">品质值 (0-1.5支持150%上限)</param>
/// <returns>抽取的 Hediff 列表</returns>
public List<HediffDef> DrawRewardsForQuality(float quality)
{
// 优先使用新的分层奖励系统
if (RewardDef != null)
{
return RewardDef.DrawRewardsForQuality(quality);
}
// 回退到旧系统
var config = SelectedConfig;
if (config != null)
{
return config.GetRewardHediffs(quality);
}
return new List<HediffDef>();
}
/// <summary>
/// 获取品质对应的阶段信息(仅新系统可用)
/// </summary>
public QualityTier GetTierForQuality(float quality)
{
return RewardDef?.GetTierForQuality(quality);
}
/// <summary>
/// 获取奖励系统的描述文本
/// </summary>
public string GetRewardSystemDescription()
{
if (RewardDef == null)
return GetHediffRewardDescription(); // 回退到旧描述
var builder = new StringBuilder();
builder.AppendLine("ARA_TieredReward_Title".Translate());
builder.AppendLine();
// 显示各阶段信息
if (RewardDef.qualityTiers != null)
{
foreach (var tier in RewardDef.qualityTiers)
{
string qualityRange = $"{tier.minQuality:P0} - {tier.maxQuality:P0}";
builder.AppendLine($" {tier.tierLabel ?? $"{tier.tier}"}: {qualityRange}");
if (tier.drawRules != null)
{
foreach (var rule in tier.drawRules)
{
var pool = RewardDef.GetPool(rule.poolId);
string poolLabel = pool?.label ?? rule.poolId;
builder.AppendLine($" - {poolLabel} x{rule.count}");
}
}
}
}
return builder.ToString().TrimEndNewlines();
}
}
}