Files
ArachnaeSwarm/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs

508 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// File: Building_Ootheca.cs
using RimWorld;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class Building_Ootheca : Building, IFluxController, ILarvaActivatable
{
// === 通量系统字段 ===
private float neutronFlux = 0.5f;
private FluxMode fluxMode = FluxMode.Balance;
// === 孵化核心字段 ===
public bool isIncubating = false;
public float incubationProgress = 0f;
public float incubationDuration = 0f;
public PawnKindDef incubatingPawnKind = null;
// 幼虫交互相关
public Pawn assignedLarva = null;
public int larvaOperateTicksRemaining = 0;
// 乘数系统
private float speedMultiplier = 1.0f;
private float qualityMultiplier = 1.0f;
private float qualityProgress = 0f;
private float qualityTotal = 0f;
// 时间控制
protected int lastMultiplierUpdateTick = -1;
protected const int MultiplierUpdateInterval = 250;
// === 属性 ===
public IFluxController FluxController => this;
public float NeutronFlux => neutronFlux;
public float RawFlux => neutronFlux;
public FluxMode FluxMode => fluxMode;
public FluxMode CurrentFluxMode => fluxMode;
public float FluxEfficiency => IFluxControllerExtensions.GetEfficiency(neutronFlux);
public bool IsAutoMode => fluxMode != FluxMode.Manual;
public bool IsIncubating => isIncubating;
public bool IsDormant => neutronFlux < 0.05f;
public CompRefuelableNutrition FuelComp => GetComp<CompRefuelableNutrition>();
public Comp_SwarmMaintenance MaintenanceComp => GetComp<Comp_SwarmMaintenance>();
public CompIncubatorData IncubatorData => GetComp<CompIncubatorData>();
public void SetNeutronFlux(float value) => neutronFlux = Mathf.Clamp01(value);
public void CycleFluxMode() => fluxMode = (FluxMode)(((int)fluxMode + 1) % 4);
public void SetFluxMode(FluxMode mode) => fluxMode = mode;
public string GetModeName() => fluxMode switch {
FluxMode.Manual => "手动",
FluxMode.Quality => "品质",
FluxMode.Balance => "平衡",
FluxMode.Speed => "速度",
_ => "?"
};
public string GetModeShort() => fluxMode switch {
FluxMode.Manual => "M",
FluxMode.Quality => "Q",
FluxMode.Balance => "B",
FluxMode.Speed => "S",
_ => "?"
};
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 => incubationDuration > 0 ? incubationProgress / incubationDuration : 0f;
// === 描述方法 ===
public string GetSpeedFactorsDescription()
{
var builder = new StringBuilder();
builder.AppendLine("速度因子");
builder.AppendLine();
builder.AppendLine("总速度倍率: " + SpeedMultiplier.ToStringPercent());
return builder.ToString().TrimEndNewlines();
}
public string GetQualityFactorsDescription()
{
var builder = new StringBuilder();
builder.AppendLine("质量因子");
builder.AppendLine();
builder.AppendLine("总质量倍率: " + QualityMultiplier.ToStringPercent());
return builder.ToString().TrimEndNewlines();
}
private string BuildCallLarvaDescription(IncubationConfig config)
{
var builder = new StringBuilder();
builder.AppendLine("ARA_OothecaIncubator.CallLarvaTitle".Translate());
builder.AppendLine();
builder.AppendLine("ARA_OothecaIncubator.LarvaWillCome".Translate());
builder.AppendLine(config.pawnKind.LabelCap);
return builder.ToString().TrimEndNewlines();
}
// === 幼虫交互 ===
public void CallLarva()
{
if (isIncubating || assignedLarva != null) return;
var larva = FindLarva();
if (larva == null)
{
Messages.Message("ARA_OothecaIncubator.NoLarvaeFound".Translate(), MessageTypeDefOf.RejectInput);
return;
}
var job = JobMaker.MakeJob(ARA_JobDefOf.ARA_OperateIncubator, this);
larva.jobs.TryTakeOrderedJob(job, JobTag.MiscWork);
assignedLarva = larva;
Messages.Message("ARA_OothecaIncubator.LarvaCalled".Translate(), MessageTypeDefOf.PositiveEvent);
}
private Pawn FindLarva()
{
float searchRadius = 30f;
Map map = Map;
if (map == null) return null;
foreach (var pawn in map.mapPawns.SpawnedPawnsInFaction(Faction))
{
if (pawn.def.defName == "ArachnaeBase_Race_Larva" && !pawn.Downed && !pawn.Dead)
{
if (Position.DistanceTo(pawn.Position) <= searchRadius)
return pawn;
}
}
return null;
}
public void NotifyLarvaArrived(Pawn larva)
{
larvaOperateTicksRemaining = 180;
assignedLarva = larva;
}
public void NotifyLarvaOperationComplete(Pawn larva)
{
var config = IncubatorData?.SelectedConfig;
if (config == null) return;
incubatingPawnKind = config.pawnKind;
incubationDuration = config.daysRequired * 60000f;
incubationProgress = 0f;
qualityTotal = incubationDuration;
qualityProgress = 0f;
isIncubating = true;
assignedLarva = null;
larvaOperateTicksRemaining = 0;
Messages.Message("ARA_Msg_IncubationStarted".Translate(incubatingPawnKind.LabelCap), MessageTypeDefOf.PositiveEvent);
}
public void CancelIncubation()
{
isIncubating = false;
incubationProgress = 0f;
incubationDuration = 0f;
incubatingPawnKind = null;
Messages.Message("ARA_Msg_IncubationCancelled".Translate(), MessageTypeDefOf.NeutralEvent);
}
private void CompleteIncubation()
{
if (incubatingPawnKind == null) return;
float finalQuality = QualityPercent;
var pawn = PawnGenerator.GeneratePawn(incubatingPawnKind, Faction);
ApplyQualityEffects(pawn, finalQuality);
GenSpawn.Spawn(pawn, Position, Map);
isIncubating = false;
incubatingPawnKind = null;
Messages.Message("ARA_Msg_IncubationComplete".Translate(pawn.LabelCap), MessageTypeDefOf.PositiveEvent);
Destroy();
}
// === Tick方法 ===
protected override void Tick()
{
base.Tick();
// 清理无效的幼虫引用
if (assignedLarva != null)
{
if (assignedLarva.Dead || assignedLarva.Destroyed ||
assignedLarva.CurJobDef == null ||
assignedLarva.CurJobDef.defName != "ARA_OperateIncubator" ||
assignedLarva.CurJob?.targetA.Thing != this)
{
assignedLarva = null;
}
}
if (larvaOperateTicksRemaining > 0)
{
larvaOperateTicksRemaining--;
}
if (isIncubating)
{
if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
{
UpdateSpeedMultiplier();
UpdateQualityMultiplier();
}
if (IsAutoMode && Find.TickManager.TicksGame % 250 == 0)
{
CalculateAutoFlux();
}
if (FuelComp != null && neutronFlux > 0.01f)
{
float fuelPerTick = (50f * FluxEfficiency) / 60000f;
FuelComp.ConsumeFuel(fuelPerTick);
}
if (IsDormant)
{
float qualityDecay = (qualityTotal * 0.1f) / 60000f;
qualityProgress = Mathf.Max(0f, qualityProgress - qualityDecay);
if (qualityProgress <= 0 && qualityTotal > 0)
{
Messages.Message("ARA_Msg_IncubatorBrokenQualityZero".Translate(), this, MessageTypeDefOf.NegativeEvent);
Destroy(DestroyMode.KillFinalize);
return;
}
}
else
{
// 进度增长
float fluxSpeed = SpeedMultiplier * FluxEfficiency * 5f;
incubationProgress += fluxSpeed;
// 品质增长新公式50%通量时与进度同步)
float qualityGain = IncubatorUtils.CalculateQualityGainNew(fluxSpeed, neutronFlux);
qualityProgress = Mathf.Min(qualityProgress + qualityGain, qualityTotal);
}
if (incubationProgress >= incubationDuration)
{
CompleteIncubation();
}
}
}
private void CalculateAutoFlux()
{
if (fluxMode == FluxMode.Manual) return;
float targetFlux = 0.5f;
float incubationPercent = AdjustedProgressPercent;
float gQualityPercent = QualityPercent;
float gap = gQualityPercent - incubationPercent;
switch (fluxMode)
{
case FluxMode.Quality:
if (gQualityPercent >= 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 (gQualityPercent >= 0.95f) targetFlux = 1.0f;
else if (gQualityPercent < 0.3f && incubationPercent > 0.5f) targetFlux = 0.7f;
else if (gQualityPercent < 0.2f && incubationPercent > 0.7f) targetFlux = 0.5f;
else targetFlux = 1.0f;
break;
case FluxMode.Balance:
default:
if (gQualityPercent >= 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);
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);
}
// === 质量效果与奖励 ===
private void ApplyQualityEffects(Pawn pawn, float qualityPercent)
{
ApplyHediffRewards(pawn, qualityPercent);
}
private void ApplyHediffRewards(Pawn pawn, float qualityPercent)
{
var config = IncubatorData?.SelectedConfig;
if (config == null) return;
List<HediffDef> rewardHediffs = config.GetRewardHediffs(qualityPercent);
if (rewardHediffs == null || rewardHediffs.Count == 0) return;
int appliedCount = 0;
foreach (var hediffDef in rewardHediffs)
{
if (hediffDef == null) continue;
if (!pawn.health.hediffSet.HasHediff(hediffDef))
{
pawn.health.AddHediff(HediffMaker.MakeHediff(hediffDef, pawn));
appliedCount++;
}
}
if (appliedCount > 0)
{
string message = config.GetRewardMessage(qualityPercent);
if (string.IsNullOrEmpty(message))
message = "ARA_QualityReward_Default".Translate(pawn.LabelShortCap, appliedCount);
Messages.Message(message, pawn, MessageTypeDefOf.PositiveEvent);
}
}
// === 辅助与UI ===
private void UpdateSpeedMultiplier()
{
speedMultiplier = 1.0f;
lastMultiplierUpdateTick = Find.TickManager.TicksGame;
}
private void UpdateQualityMultiplier()
{
qualityMultiplier = 1.0f;
}
public float GetRemainingTicks()
{
if (!isIncubating) return 0f;
return Mathf.Max(0f, (incubationDuration - incubationProgress) / (SpeedMultiplier * FluxEfficiency * 5f));
}
public float GetRemainingDays() => GetRemainingTicks() / 60000f;
public float GetRemainingHours() => (GetRemainingTicks() % 60000f) / 2500f;
public override string GetInspectString()
{
StringBuilder sb = new StringBuilder();
if (isIncubating && incubatingPawnKind != null)
{
sb.AppendLine("ARA_Status_Incubating".Translate(incubatingPawnKind.LabelCap));
sb.AppendLine("ARA_Status_Progress".Translate(AdjustedProgressPercent.ToStringPercent()));
sb.AppendLine("ARA_Status_RemainingTime".Translate(GetRemainingDays().ToString("F1")));
sb.Append("ARA_Status_SpeedAndQuality".Translate(SpeedMultiplier.ToStringPercent(), QualityMultiplier.ToStringPercent()));
}
else if (assignedLarva != null)
{
sb.Append(larvaOperateTicksRemaining > 0 ? "ARA_Status_LarvaActivating".Translate() : "ARA_Status_LarvaOnTheWay".Translate());
}
else
{
var config = IncubatorData?.SelectedConfig;
if (config != null)
{
sb.AppendLine("ARA_Status_Target".Translate(config.pawnKind.LabelCap));
sb.Append("ARA_Status_Speed".Translate(SpeedMultiplier.ToStringPercent()));
}
}
var baseStr = base.GetInspectString();
if (!string.IsNullOrEmpty(baseStr))
{
if (sb.Length > 0) sb.AppendLine();
sb.Append(baseStr);
}
return sb.ToString().TrimEndNewlines();
}
public override IEnumerable<Gizmo> GetGizmos()
{
foreach (var gizmo in base.GetGizmos())
{
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;
}
// 强制将基础组件(如 Refuelable甚至默认排序为 -100 的东西移到后面
if (gizmo.Order >= -100f && gizmo.Order <= 0f)
{
gizmo.Order = -90f;
}
yield return gizmo;
}
if (Faction == Faction.OfPlayer)
{
yield return new Gizmo_IncubationProgress(this);
yield return new Gizmo_NeutronFlux(this);
var config = IncubatorData?.SelectedConfig;
// 添加订单按钮(未孵化且未选目标,或已选目标但支持切换)
if (!isIncubating && assignedLarva == null)
{
yield return new Command_Action
{
defaultLabel = "ARA_Gizmo_AddOrder".Translate(config != null ? 1 : 0, 1),
defaultDesc = "ARA_Gizmo_AddOrderDesc_Pawn".Translate(),
icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Commands/ARA_NodeSwarmIcon", false),
action = () => IncubatorData?.ShowFloatMenu(),
Order = 100f
};
}
// 呼叫幼虫按钮逻辑
if (!isIncubating && config != null && config.IsResearchComplete)
{
if (assignedLarva == null)
{
// 无幼虫,可以呼叫
yield return new Command_Action
{
defaultLabel = "ARA_Gizmo_CallLarva".Translate(),
defaultDesc = BuildCallLarvaDescription(config),
icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false),
action = CallLarva,
Order = 100f
};
}
else
{
// 幼虫在路上或工作中
string statusText = larvaOperateTicksRemaining > 0
? "ARA_Gizmo_LarvaActivating".Translate()
: "ARA_Gizmo_LarvaOnTheWay".Translate();
yield return new Command_Action
{
defaultLabel = statusText,
defaultDesc = "ARA_Gizmo_LarvaWorkingDesc".Translate(0),
icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Commands/ARA_CallLarva", false),
Disabled = true,
Order = 100f
};
}
}
if (isIncubating)
{
yield return new Command_Action
{
defaultLabel = "ARA_OothecaIncubator.CancelIncubation".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Commands/Cancel", false),
action = CancelIncubation,
Order = 100f
};
}
}
}
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 incubatingPawnKind, "incubatingPawnKind");
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 qualityMultiplier, "qualityMultiplier", 1.0f);
Scribe_Values.Look(ref qualityProgress, "qualityProgress", 0f);
Scribe_Values.Look(ref qualityTotal, "qualityTotal", 0f);
Scribe_Values.Look(ref neutronFlux, "neutronFlux", 0.5f);
Scribe_Values.Look(ref fluxMode, "fluxMode", FluxMode.Balance);
}
}
}