三大系统
This commit is contained in:
@@ -0,0 +1,882 @@
|
||||
// File: Buildings/Building_EquipmentOotheca.cs
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using System;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class Building_EquipmentOotheca : Building
|
||||
{
|
||||
// 引用组件
|
||||
public CompEquipmentIncubatorData EquipmentIncubatorData => this.TryGetComp<CompEquipmentIncubatorData>();
|
||||
|
||||
// 孵化状态
|
||||
public bool isIncubating = false;
|
||||
public float incubationProgress = 0f;
|
||||
public float incubationDuration = 0f;
|
||||
public ThingDef incubatingThingDef = null;
|
||||
|
||||
// 幼虫交互相关
|
||||
public Pawn assignedLarva = null;
|
||||
public int larvaOperateTicksRemaining = 0;
|
||||
|
||||
// 速度乘数系统
|
||||
private float speedMultiplier = 1.0f;
|
||||
private int lastMultiplierUpdateTick = -1;
|
||||
private const int MultiplierUpdateInterval = 250;
|
||||
|
||||
// 质量系统
|
||||
private float qualityMultiplier = 1.0f;
|
||||
private float qualityProgress = 0f;
|
||||
private float qualityTotal = 0f;
|
||||
|
||||
// 缓存的ModExtension
|
||||
private OothecaIncubatorExtension cachedExtension;
|
||||
|
||||
// 获取ModExtension的辅助属性
|
||||
private OothecaIncubatorExtension Ext
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cachedExtension == null)
|
||||
{
|
||||
cachedExtension = def.GetModExtension<OothecaIncubatorExtension>() ?? OothecaIncubatorExtension.Default;
|
||||
}
|
||||
return cachedExtension;
|
||||
}
|
||||
}
|
||||
|
||||
// 属性访问器
|
||||
public float SpeedMultiplier
|
||||
{
|
||||
get
|
||||
{
|
||||
if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
|
||||
{
|
||||
UpdateSpeedMultiplier();
|
||||
}
|
||||
return speedMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
// 质量属性
|
||||
public float QualityMultiplier => qualityMultiplier;
|
||||
public float QualityProgress => qualityProgress;
|
||||
public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f;
|
||||
|
||||
// 进度百分比
|
||||
public float AdjustedProgressPercent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (incubationDuration <= 0) return 0f;
|
||||
return incubationProgress / incubationDuration;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取速度因子描述
|
||||
public string GetSpeedFactorsDescription()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine("ARA_EquipmentIncubator.SpeedFactors".Translate());
|
||||
builder.AppendLine();
|
||||
|
||||
// 1. 检查是否在孵化间中
|
||||
bool inIncubatorRoom = IsInIncubatorRoom();
|
||||
if (Ext.requiresIncubatorRoom)
|
||||
{
|
||||
builder.AppendLine(inIncubatorRoom ?
|
||||
"ARA_EquipmentIncubator.InIncubatorRoom".Translate() :
|
||||
"ARA_EquipmentIncubator.NotInIncubatorRoom".Translate());
|
||||
}
|
||||
|
||||
// 2. 检查营养液数量
|
||||
int nutrientSolutionCount = CountNearbyNutrientSolutions();
|
||||
if (nutrientSolutionCount > 0)
|
||||
{
|
||||
builder.AppendLine("ARA_EquipmentIncubator.NutrientSolutions".Translate(
|
||||
nutrientSolutionCount,
|
||||
nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile * 100));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine("ARA_EquipmentIncubator.NoNutrientSolutionsNearby".Translate());
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("ARA_EquipmentIncubator.NutrientDetectionRadius".Translate(Ext.nutrientSolutionRadius));
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("ARA_EquipmentIncubator.TotalSpeedMultiplier".Translate(SpeedMultiplier.ToStringPercent()));
|
||||
|
||||
return builder.ToString().TrimEndNewlines();
|
||||
}
|
||||
|
||||
// 获取质量因子描述
|
||||
public string GetQualityFactorsDescription()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine("ARA_EquipmentIncubator.QualityFactors".Translate());
|
||||
builder.AppendLine();
|
||||
|
||||
// 1. 建筑血量损失百分比
|
||||
if (Ext.healthAffectsQuality)
|
||||
{
|
||||
float healthPercent = (float)HitPoints / MaxHitPoints;
|
||||
builder.AppendLine("ARA_EquipmentIncubator.BuildingHealth".Translate(healthPercent.ToStringPercent()));
|
||||
}
|
||||
|
||||
// 2. 房间质量因子
|
||||
if (Ext.useRoomQualityFactor)
|
||||
{
|
||||
float roomFactor = GetRoomQualityFactor();
|
||||
builder.AppendLine(roomFactor == 1.0f ?
|
||||
"ARA_EquipmentIncubator.RoomFactorNormal".Translate() :
|
||||
$"{(roomFactor > 1.0f ? "✓" : "✗")} {"ARA_EquipmentIncubator.RoomFactorModified".Translate()}{roomFactor.ToStringPercent()}");
|
||||
}
|
||||
|
||||
// 3. 附近其他卵
|
||||
int nearbyOothecaCount = CountNearbyOtherOothecas();
|
||||
if (nearbyOothecaCount > 0)
|
||||
{
|
||||
builder.AppendLine("ARA_EquipmentIncubator.NearbyOothecas".Translate(
|
||||
nearbyOothecaCount,
|
||||
Mathf.Min(nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit * 100, 100)));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine("ARA_EquipmentIncubator.NoNearbyOothecas".Translate());
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("ARA_EquipmentIncubator.OothecaDetectionRadius".Translate(Ext.nearbyOothecaRadius));
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("ARA_EquipmentIncubator.TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent()));
|
||||
|
||||
return builder.ToString().TrimEndNewlines();
|
||||
}
|
||||
|
||||
// 构建呼叫幼虫描述
|
||||
private string BuildCallLarvaDescription(EquipmentIncubationConfig config)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine("ARA_EquipmentIncubator.CallLarvaTitle".Translate());
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("ARA_EquipmentIncubator.LarvaWillCome".Translate());
|
||||
builder.AppendLine(config.thingDef.LabelCap);
|
||||
builder.AppendLine();
|
||||
|
||||
if (Ext.larvaSearchRadius < 999f)
|
||||
{
|
||||
builder.AppendLine("ARA_EquipmentIncubator.LarvaSearchRadius".Translate(Ext.larvaSearchRadius));
|
||||
}
|
||||
|
||||
return builder.ToString().TrimEndNewlines();
|
||||
}
|
||||
|
||||
// 呼叫幼虫
|
||||
private void CallLarva()
|
||||
{
|
||||
if (isIncubating)
|
||||
{
|
||||
Messages.Message("ARA_EquipmentIncubator.AlreadyIncubating".Translate() + " " + "ARA_EquipmentIncubator.CancelFirst".Translate(),
|
||||
MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
|
||||
if (assignedLarva != null)
|
||||
{
|
||||
Messages.Message("ARA_EquipmentIncubator.LarvaAlreadyOnWay".Translate(),
|
||||
MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
|
||||
var larva = FindLarva();
|
||||
if (larva == null)
|
||||
{
|
||||
Messages.Message("ARA_EquipmentIncubator.NoLarvaeFound".Translate() + " " + "ARA_EquipmentIncubator.LarvaMustBeRace".Translate(),
|
||||
MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
|
||||
var job = JobMaker.MakeJob(ARA_JobDefOf.ARA_OperateEquipmentIncubator, this);
|
||||
job.count = 1;
|
||||
larva.jobs.TryTakeOrderedJob(job, JobTag.MiscWork);
|
||||
|
||||
assignedLarva = larva;
|
||||
|
||||
Messages.Message("ARA_EquipmentIncubator.LarvaCalled".Translate() + " " + "ARA_EquipmentIncubator.ArriveShortly".Translate(),
|
||||
MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
|
||||
// 幼虫到达
|
||||
public void NotifyLarvaArrived(Pawn larva)
|
||||
{
|
||||
if (larva.def.defName != "ArachnaeBase_Race_Larva")
|
||||
{
|
||||
ArachnaeLog.Debug($"Invalid larva arrived: {larva.def.defName}");
|
||||
return;
|
||||
}
|
||||
|
||||
larvaOperateTicksRemaining = 180;
|
||||
assignedLarva = larva;
|
||||
|
||||
Messages.Message("ARA_EquipmentIncubator.LarvaArrived".Translate() + " " + "ARA_EquipmentIncubator.ActivatingOotheca".Translate(),
|
||||
MessageTypeDefOf.SilentInput);
|
||||
}
|
||||
|
||||
// 幼虫完成操作
|
||||
public void NotifyLarvaOperationComplete(Pawn larva)
|
||||
{
|
||||
if (larva != assignedLarva)
|
||||
{
|
||||
ArachnaeLog.Debug("Larva operation complete called with wrong larva.");
|
||||
return;
|
||||
}
|
||||
|
||||
var config = EquipmentIncubatorData?.SelectedConfig;
|
||||
if (config == null)
|
||||
{
|
||||
ArachnaeLog.Debug("No incubation config selected when larva completed operation.");
|
||||
return;
|
||||
}
|
||||
|
||||
incubatingThingDef = config.thingDef;
|
||||
incubationDuration = config.DaysRequired * 60000f;
|
||||
incubationProgress = 0f;
|
||||
isIncubating = true;
|
||||
|
||||
qualityTotal = incubationDuration;
|
||||
qualityProgress = 0f;
|
||||
UpdateQualityMultiplier();
|
||||
|
||||
UpdateSpeedMultiplier();
|
||||
|
||||
assignedLarva = null;
|
||||
larvaOperateTicksRemaining = 0;
|
||||
|
||||
Messages.Message("ARA_EquipmentIncubator.IncubationStarted".Translate() + " " + incubatingThingDef.LabelCap + ". " +
|
||||
"ARA_EquipmentIncubator.ProcessWillComplete".Translate() + " " + config.DaysRequired + " " + "ARA_EquipmentIncubator.DaysBaseTime".Translate(),
|
||||
MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
|
||||
// 取消孵化
|
||||
private void CancelIncubation()
|
||||
{
|
||||
if (!isIncubating) return;
|
||||
|
||||
isIncubating = false;
|
||||
incubationProgress = 0f;
|
||||
incubationDuration = 0f;
|
||||
incubatingThingDef = null;
|
||||
qualityProgress = 0f;
|
||||
qualityTotal = 0f;
|
||||
|
||||
Messages.Message("ARA_EquipmentIncubator.IncubationCancelled".Translate() + " " + "ARA_EquipmentIncubator.ContentsLost".Translate(),
|
||||
MessageTypeDefOf.NeutralEvent);
|
||||
}
|
||||
|
||||
// 完成孵化
|
||||
private void CompleteIncubation()
|
||||
{
|
||||
if (incubatingThingDef == null) return;
|
||||
|
||||
float finalQualityPercent = QualityPercent;
|
||||
|
||||
// 生成物品
|
||||
Thing thing = ThingMaker.MakeThing(incubatingThingDef);
|
||||
|
||||
// 应用质量影响
|
||||
ApplyQualityEffects(thing, finalQualityPercent);
|
||||
|
||||
// 放置物品
|
||||
var spawnPos = Position;
|
||||
GenPlace.TryPlaceThing(thing, spawnPos, Map, ThingPlaceMode.Near);
|
||||
|
||||
// 重置状态
|
||||
isIncubating = false;
|
||||
incubationProgress = 0f;
|
||||
incubationDuration = 0f;
|
||||
incubatingThingDef = null;
|
||||
qualityProgress = 0f;
|
||||
qualityTotal = 0f;
|
||||
|
||||
// 显示消息
|
||||
string qualityText = finalQualityPercent >= 0.9f ? "ARA_EquipmentIncubator.QualityExcellent".Translate() :
|
||||
finalQualityPercent >= 0.7f ? "ARA_EquipmentIncubator.QualityGood".Translate() :
|
||||
finalQualityPercent >= 0.5f ? "ARA_EquipmentIncubator.QualityAverage".Translate() :
|
||||
finalQualityPercent >= 0.3f ? "ARA_EquipmentIncubator.QualityPoor".Translate() : "ARA_EquipmentIncubator.QualityVeryPoor".Translate();
|
||||
|
||||
Messages.Message("ARA_EquipmentIncubator.IncubationComplete".Translate() + " " + thing.LabelCap + " " +
|
||||
"ARA_EquipmentIncubator.HasEmergedWith".Translate() + " " + qualityText + " " +
|
||||
"ARA_EquipmentIncubator.Quality".Translate() + " (" + finalQualityPercent.ToStringPercent() + ").",
|
||||
MessageTypeDefOf.PositiveEvent);
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
// 应用质量效果
|
||||
private void ApplyQualityEffects(Thing thing, float qualityPercent)
|
||||
{
|
||||
// 应用质量效果到装备
|
||||
if (thing.TryGetComp<CompQuality>() is CompQuality compQuality)
|
||||
{
|
||||
// 根据质量百分比设置质量等级
|
||||
QualityCategory qualityCategory = qualityPercent >= 0.99f ? QualityCategory.Legendary :
|
||||
qualityPercent >= 0.75f ? QualityCategory.Masterwork :
|
||||
qualityPercent >= 0.6f ? QualityCategory.Excellent :
|
||||
qualityPercent >= 0.45f ? QualityCategory.Good :
|
||||
qualityPercent >= 0.3f ? QualityCategory.Normal : QualityCategory.Poor;
|
||||
|
||||
compQuality.SetQuality(qualityCategory, ArtGenerationContext.Outsider);
|
||||
}
|
||||
|
||||
// 设置生命值百分比
|
||||
if (qualityPercent < 1.0f)
|
||||
{
|
||||
float healthFactor = Mathf.Lerp(0.5f, 1.0f, qualityPercent);
|
||||
thing.HitPoints = Mathf.RoundToInt(thing.MaxHitPoints * healthFactor);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取剩余时间
|
||||
public float GetRemainingTicks()
|
||||
{
|
||||
if (!isIncubating || incubationDuration <= incubationProgress) return 0f;
|
||||
|
||||
float remainingProgress = incubationDuration - incubationProgress;
|
||||
float currentSpeed = SpeedMultiplier;
|
||||
|
||||
if (currentSpeed <= 0) return float.MaxValue;
|
||||
|
||||
return remainingProgress / currentSpeed;
|
||||
}
|
||||
|
||||
public float GetRemainingDays()
|
||||
{
|
||||
return GetRemainingTicks() / 60000f;
|
||||
}
|
||||
|
||||
public float GetRemainingHours()
|
||||
{
|
||||
float remainingTicks = GetRemainingTicks();
|
||||
return (remainingTicks % 60000f) / 2500f;
|
||||
}
|
||||
|
||||
// 检查字符串
|
||||
public override string GetInspectString()
|
||||
{
|
||||
var baseString = base.GetInspectString();
|
||||
var builder = new StringBuilder();
|
||||
|
||||
if (!string.IsNullOrEmpty(baseString))
|
||||
{
|
||||
builder.Append(baseString);
|
||||
}
|
||||
|
||||
if (isIncubating && incubatingThingDef != null)
|
||||
{
|
||||
float progressPercent = AdjustedProgressPercent;
|
||||
float daysRemaining = GetRemainingDays();
|
||||
float hoursRemaining = GetRemainingHours();
|
||||
|
||||
if (builder.Length > 0) builder.AppendLine();
|
||||
builder.Append("ARA_EquipmentIncubator.Incubating".Translate() + ": " + incubatingThingDef.LabelCap);
|
||||
builder.AppendLine();
|
||||
builder.Append("ARA_EquipmentIncubator.Progress".Translate() + ": " + progressPercent.ToStringPercent());
|
||||
builder.AppendLine();
|
||||
|
||||
string timeText = "ARA_EquipmentIncubator.TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Days".Translate();
|
||||
if (hoursRemaining > 0.1f && daysRemaining < 1f)
|
||||
{
|
||||
timeText += " (" + hoursRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Hours".Translate() + ")";
|
||||
}
|
||||
builder.Append(timeText);
|
||||
|
||||
builder.AppendLine();
|
||||
builder.Append("ARA_EquipmentIncubator.Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
|
||||
"ARA_EquipmentIncubator.Quality".Translate() + ": " + QualityMultiplier.ToStringPercent());
|
||||
}
|
||||
else if (assignedLarva != null)
|
||||
{
|
||||
if (builder.Length > 0) builder.AppendLine();
|
||||
if (larvaOperateTicksRemaining > 0)
|
||||
{
|
||||
float secondsRemaining = larvaOperateTicksRemaining / 60f;
|
||||
builder.Append("ARA_EquipmentIncubator.LarvaOperating".Translate() + ": " + secondsRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.SRemaining".Translate());
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append("ARA_EquipmentIncubator.LarvaOnWay".Translate());
|
||||
}
|
||||
}
|
||||
else if (!isIncubating)
|
||||
{
|
||||
var config = EquipmentIncubatorData?.SelectedConfig;
|
||||
if (config != null)
|
||||
{
|
||||
if (builder.Length > 0) builder.AppendLine();
|
||||
builder.Append("ARA_EquipmentIncubator.Target".Translate() + ": " + config.thingDef.LabelCap);
|
||||
|
||||
builder.AppendLine();
|
||||
builder.Append("ARA_EquipmentIncubator.SpeedMultiplier".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
|
||||
"ARA_EquipmentIncubator.QualityMultiplier".Translate() + ": " + QualityMultiplier.ToStringPercent());
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToString().TrimEndNewlines();
|
||||
}
|
||||
|
||||
// Gizmos
|
||||
public override IEnumerable<Gizmo> GetGizmos()
|
||||
{
|
||||
foreach (var gizmo in base.GetGizmos())
|
||||
{
|
||||
yield return gizmo;
|
||||
}
|
||||
|
||||
if (Faction == Faction.OfPlayer)
|
||||
{
|
||||
if (!isIncubating && EquipmentIncubatorData?.IncubationConfigs?.Count > 0)
|
||||
{
|
||||
yield return CreateTargetSwitchGizmo();
|
||||
}
|
||||
|
||||
var config = EquipmentIncubatorData?.SelectedConfig;
|
||||
if (!isIncubating && config != null && config.IsResearchComplete)
|
||||
{
|
||||
yield return new Command_Action
|
||||
{
|
||||
defaultLabel = "ARA_EquipmentIncubator.CallLarva".Translate(),
|
||||
defaultDesc = BuildCallLarvaDescription(config),
|
||||
icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false) ?? BaseContent.BadTex,
|
||||
action = CallLarva,
|
||||
hotKey = KeyBindingDefOf.Misc3
|
||||
};
|
||||
}
|
||||
|
||||
if (isIncubating)
|
||||
{
|
||||
yield return new Command_Action
|
||||
{
|
||||
defaultLabel = "ARA_EquipmentIncubator.CancelIncubation".Translate(),
|
||||
defaultDesc = "ARA_EquipmentIncubator.CancelIncubationDesc".Translate(),
|
||||
icon = ContentFinder<Texture2D>.Get("UI/Commands/Cancel", false) ?? TexCommand.ClearPrioritizedWork,
|
||||
action = CancelIncubation
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建切换目标Gizmo - 现在使用装备的图标
|
||||
private Gizmo CreateTargetSwitchGizmo()
|
||||
{
|
||||
var configs = EquipmentIncubatorData?.IncubationConfigs;
|
||||
if (configs == null || configs.Count == 0) return null;
|
||||
|
||||
var props = EquipmentIncubatorData?.props as CompProperties_EquipmentIncubatorData;
|
||||
var selectedConfig = EquipmentIncubatorData?.SelectedConfig;
|
||||
|
||||
var switchButton = new Command_Action
|
||||
{
|
||||
defaultLabel = BuildSwitchButtonLabel(selectedConfig, props),
|
||||
defaultDesc = BuildSwitchButtonDescription(selectedConfig, props),
|
||||
icon = GetConfigIcon(selectedConfig),
|
||||
action = ShowSelectionMenu,
|
||||
hotKey = KeyBindingDefOf.Misc2
|
||||
};
|
||||
|
||||
if (selectedConfig != null && !selectedConfig.IsResearchComplete)
|
||||
{
|
||||
if (selectedConfig.requiredResearch != null)
|
||||
{
|
||||
switchButton.Disable($"Requires research: {selectedConfig.requiredResearch.LabelCap}");
|
||||
}
|
||||
}
|
||||
|
||||
return switchButton;
|
||||
}
|
||||
|
||||
// 获取配置图标 - 现在直接从ThingDef获取
|
||||
private Texture2D GetConfigIcon(EquipmentIncubationConfig config)
|
||||
{
|
||||
if (config == null)
|
||||
return BaseContent.BadTex;
|
||||
|
||||
// 如果配置中没有缓存图标,尝试直接获取ThingDef的uiIcon
|
||||
if (config.thingDef?.uiIcon != null)
|
||||
return config.thingDef.uiIcon;
|
||||
|
||||
// 回退到默认图标
|
||||
return ContentFinder<Texture2D>.Get("UI/Commands/Default", false) ?? BaseContent.BadTex;
|
||||
}
|
||||
|
||||
private string BuildSwitchButtonLabel(EquipmentIncubationConfig config, CompProperties_EquipmentIncubatorData props)
|
||||
{
|
||||
if (config != null && config.thingDef != null)
|
||||
{
|
||||
return (props?.buttonLabel ?? "ARA_EquipmentIncubator.IncubateLabel").Translate(config.thingDef.LabelCap);
|
||||
}
|
||||
return (props?.buttonLabel ?? "ARA_EquipmentIncubator.IncubateLabel").Translate("None");
|
||||
}
|
||||
|
||||
private string BuildSwitchButtonDescription(EquipmentIncubationConfig config, CompProperties_EquipmentIncubatorData props)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine((props?.buttonDesc ?? "ARA_EquipmentIncubator.ButtonDesc").Translate());
|
||||
builder.AppendLine();
|
||||
|
||||
if (config != null)
|
||||
{
|
||||
if (config.thingDef != null)
|
||||
{
|
||||
builder.AppendLine($"ARA_EquipmentIncubator.ButtonLabel".Translate(config.thingDef.LabelCap));
|
||||
if (!string.IsNullOrEmpty(config.thingDef.description))
|
||||
{
|
||||
builder.AppendLine(config.thingDef.description);
|
||||
}
|
||||
}
|
||||
|
||||
builder.AppendLine($"ARA_EquipmentIncubator.IncubationTime".Translate(config.DaysRequired));
|
||||
|
||||
if (config.requiredResearch != null)
|
||||
{
|
||||
if (config.requiredResearch.IsFinished)
|
||||
builder.AppendLine($"Research: {config.requiredResearch.LabelCap} (Completed)");
|
||||
else
|
||||
builder.AppendLine($"Research: {config.requiredResearch.LabelCap} (Required)");
|
||||
}
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("ARA_EquipmentIncubator.ButtonDesc".Translate());
|
||||
|
||||
return builder.ToString().TrimEndNewlines();
|
||||
}
|
||||
|
||||
private void ShowSelectionMenu()
|
||||
{
|
||||
var configs = EquipmentIncubatorData?.IncubationConfigs;
|
||||
var props = EquipmentIncubatorData?.props as CompProperties_EquipmentIncubatorData;
|
||||
if (configs == null || configs.Count == 0) return;
|
||||
|
||||
var options = new List<FloatMenuOption>();
|
||||
int currentIndex = EquipmentIncubatorData.GetSelectedIndex();
|
||||
|
||||
for (int i = 0; i < configs.Count; i++)
|
||||
{
|
||||
int index = i;
|
||||
var config = configs[i];
|
||||
if (config == null || config.thingDef == null) continue;
|
||||
|
||||
string label = config.thingDef.LabelCap;
|
||||
string description = config.GetDescription();
|
||||
|
||||
string prefix = (i == currentIndex) ? "✓ " : " ";
|
||||
|
||||
// 使用原版FloatMenuOption的构造函数,直接传入图标
|
||||
FloatMenuOption option;
|
||||
|
||||
// 尝试获取ThingDef的图标
|
||||
Texture2D icon = config.thingDef.uiIcon;
|
||||
|
||||
if (icon != null)
|
||||
{
|
||||
// 使用带有Texture2D图标的构造函数
|
||||
option = new FloatMenuOption(
|
||||
prefix + label,
|
||||
() => SwitchToConfig(index),
|
||||
icon,
|
||||
Color.white,
|
||||
MenuOptionPriority.Default,
|
||||
null, // mouseoverGuiAction
|
||||
null, // revalidateClickTarget
|
||||
0f, // extraPartWidth
|
||||
null, // extraPartOnGUI
|
||||
null, // revalidateWorldClickTarget
|
||||
true, // playSelectionSound
|
||||
0, // orderInPriority
|
||||
HorizontalJustification.Left, // iconJustification
|
||||
false // extraPartRightJustified
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有图标,使用普通构造函数
|
||||
option = new FloatMenuOption(
|
||||
prefix + label,
|
||||
() => SwitchToConfig(index)
|
||||
);
|
||||
}
|
||||
|
||||
// 设置工具提示
|
||||
option.tooltip = description;
|
||||
|
||||
// 如果研究未完成,禁用选项
|
||||
if (!config.IsResearchComplete)
|
||||
{
|
||||
option.Label = prefix + label;
|
||||
option.Disabled = true;
|
||||
option.tooltip = description + "\n\n " + "ARA_EquipmentIncubator.ResearchRequired".Translate() + " " + config.requiredResearch.LabelCap;
|
||||
}
|
||||
|
||||
options.Add(option);
|
||||
}
|
||||
|
||||
if (options.Count > 0)
|
||||
{
|
||||
Find.WindowStack.Add(new FloatMenu(options,
|
||||
(props?.menuTitle ?? "ARA_EquipmentIncubator.MenuTitle").Translate()));
|
||||
}
|
||||
}
|
||||
|
||||
private void SwitchToConfig(int index)
|
||||
{
|
||||
if (EquipmentIncubatorData != null)
|
||||
{
|
||||
EquipmentIncubatorData.SwitchToConfig(index);
|
||||
var config = EquipmentIncubatorData.SelectedConfig;
|
||||
if (config != null && config.thingDef != null)
|
||||
{
|
||||
Messages.Message($"ARA_EquipmentIncubator.TargetSwitched".Translate(config.thingDef.LabelCap),
|
||||
this, MessageTypeDefOf.SilentInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找幼虫
|
||||
private Pawn FindLarva()
|
||||
{
|
||||
var map = Map;
|
||||
if (map == null) return null;
|
||||
|
||||
float searchRadius = Ext.larvaSearchRadius;
|
||||
|
||||
foreach (var pawn in map.mapPawns.SpawnedPawnsInFaction(Faction))
|
||||
{
|
||||
if (pawn.def.defName == "ArachnaeBase_Race_Larva")
|
||||
{
|
||||
if (searchRadius < 999f)
|
||||
{
|
||||
float distance = pawn.Position.DistanceTo(Position);
|
||||
if (distance > searchRadius)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pawn.Downed && !pawn.InMentalState &&
|
||||
pawn.mindState != null &&
|
||||
(pawn.CurJob == null || pawn.CurJob.def != ARA_JobDefOf.ARA_OperateEquipmentIncubator))
|
||||
{
|
||||
return pawn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 每tick更新
|
||||
protected override void Tick()
|
||||
{
|
||||
base.Tick();
|
||||
|
||||
if (larvaOperateTicksRemaining > 0)
|
||||
{
|
||||
larvaOperateTicksRemaining--;
|
||||
}
|
||||
|
||||
if (isIncubating)
|
||||
{
|
||||
if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
|
||||
{
|
||||
UpdateSpeedMultiplier();
|
||||
UpdateQualityMultiplier();
|
||||
}
|
||||
|
||||
float currentSpeed = SpeedMultiplier;
|
||||
incubationProgress += currentSpeed;
|
||||
|
||||
qualityProgress += currentSpeed * QualityMultiplier;
|
||||
|
||||
if (incubationProgress >= incubationDuration)
|
||||
{
|
||||
CompleteIncubation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否在孵化间中
|
||||
private bool IsInIncubatorRoom()
|
||||
{
|
||||
if (!Ext.requiresIncubatorRoom)
|
||||
return true;
|
||||
|
||||
var room = this.GetRoom();
|
||||
if (room == null) return false;
|
||||
|
||||
return room.Role != null && room.Role.defName == "ARA_Incubator_Room";
|
||||
}
|
||||
|
||||
// 计算营养液数量
|
||||
private int CountNearbyNutrientSolutions()
|
||||
{
|
||||
var map = Map;
|
||||
if (map == null) return 0;
|
||||
|
||||
int count = 0;
|
||||
int radius = Ext.NutrientSolutionRadiusInt;
|
||||
|
||||
for (int x = -radius; x <= radius; x++)
|
||||
{
|
||||
for (int y = -radius; y <= radius; y++)
|
||||
{
|
||||
IntVec3 cell = Position + new IntVec3(x, 0, y);
|
||||
|
||||
if (cell.InBounds(map))
|
||||
{
|
||||
TerrainDef terrain = map.terrainGrid.TerrainAt(cell);
|
||||
if (terrain != null && terrain.defName == "ARA_Incubator_Nutrient_Solution")
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// 计算房间质量因子
|
||||
private float GetRoomQualityFactor()
|
||||
{
|
||||
if (!Ext.useRoomQualityFactor)
|
||||
return 1.0f;
|
||||
|
||||
var room = this.GetRoom();
|
||||
if (room == null) return 1.0f;
|
||||
|
||||
var statDef = DefDatabase<RoomStatDef>.GetNamedSilentFail("ARA_IncubatorQualityFactor");
|
||||
if (statDef != null)
|
||||
{
|
||||
return room.GetStat(statDef);
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// 计算附近其他卵的数量
|
||||
private int CountNearbyOtherOothecas()
|
||||
{
|
||||
var map = Map;
|
||||
if (map == null) return 0;
|
||||
|
||||
int count = 0;
|
||||
var allBuildings = map.listerThings.ThingsOfDef(this.def);
|
||||
|
||||
foreach (var building in allBuildings)
|
||||
{
|
||||
if (building == this) continue;
|
||||
|
||||
if (building.def.defName == "ARA_Pawn_Ootheca" || building.def.defName == "ARA_Equipment_Ootheca")
|
||||
{
|
||||
bool isNearby = false;
|
||||
|
||||
if (Ext.checkSameRoomForOotheca)
|
||||
{
|
||||
var room = building.GetRoom();
|
||||
if (room != null && room == this.GetRoom())
|
||||
{
|
||||
isNearby = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNearby)
|
||||
{
|
||||
float distance = building.Position.DistanceTo(this.Position);
|
||||
if (distance <= Ext.nearbyOothecaRadius)
|
||||
{
|
||||
isNearby = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNearby)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// 更新速度乘数
|
||||
private void UpdateSpeedMultiplier()
|
||||
{
|
||||
float multiplier = 1.0f;
|
||||
|
||||
if (Ext.requiresIncubatorRoom && !IsInIncubatorRoom())
|
||||
{
|
||||
multiplier *= Ext.speedPenaltyOutsideIncubator;
|
||||
}
|
||||
|
||||
int nutrientSolutionCount = CountNearbyNutrientSolutions();
|
||||
float nutrientBonus = 1.0f + (nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile);
|
||||
|
||||
multiplier *= nutrientBonus;
|
||||
|
||||
speedMultiplier = multiplier;
|
||||
lastMultiplierUpdateTick = Find.TickManager.TicksGame;
|
||||
}
|
||||
|
||||
// 更新质量乘数
|
||||
private void UpdateQualityMultiplier()
|
||||
{
|
||||
float multiplier = 1.0f;
|
||||
|
||||
if (Ext.healthAffectsQuality)
|
||||
{
|
||||
float healthPercent = (float)HitPoints / MaxHitPoints;
|
||||
multiplier *= healthPercent;
|
||||
}
|
||||
|
||||
if (Ext.useRoomQualityFactor)
|
||||
{
|
||||
float roomFactor = GetRoomQualityFactor();
|
||||
multiplier *= roomFactor;
|
||||
}
|
||||
|
||||
int nearbyOothecaCount = CountNearbyOtherOothecas();
|
||||
float oothecaPenalty = Mathf.Max(0f, 1.0f - (nearbyOothecaCount * Ext.nearbyOothecaPenaltyPerUnit));
|
||||
multiplier *= oothecaPenalty;
|
||||
|
||||
qualityMultiplier = Mathf.Clamp(multiplier, 0f, 1.0f);
|
||||
}
|
||||
|
||||
// 保存/加载
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
|
||||
Scribe_Values.Look(ref isIncubating, "isIncubating", false);
|
||||
Scribe_Values.Look(ref incubationProgress, "incubationProgress", 0f);
|
||||
Scribe_Values.Look(ref incubationDuration, "incubationDuration", 0f);
|
||||
Scribe_Defs.Look(ref incubatingThingDef, "incubatingThingDef");
|
||||
Scribe_References.Look(ref assignedLarva, "assignedLarva");
|
||||
Scribe_Values.Look(ref larvaOperateTicksRemaining, "larvaOperateTicksRemaining", 0);
|
||||
Scribe_Values.Look(ref speedMultiplier, "speedMultiplier", 1.0f);
|
||||
Scribe_Values.Look(ref lastMultiplierUpdateTick, "lastMultiplierUpdateTick", -1);
|
||||
Scribe_Values.Look(ref qualityMultiplier, "qualityMultiplier", 1.0f);
|
||||
Scribe_Values.Look(ref qualityProgress, "qualityProgress", 0f);
|
||||
Scribe_Values.Look(ref qualityTotal, "qualityTotal", 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user