Files
ArachnaeSwarm/Source/ArachnaeSwarm/Buildings/Building_EquipmentOotheca/Building_EquipmentOotheca.cs

497 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.
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, IFluxController, ILarvaActivatable
{
// === 通量系统字段 ===
private float neutronFlux = 0.5f;
private FluxMode fluxMode = FluxMode.Balance;
// === 孵化核心字段 ===
public bool isIncubating = false;
public ThingDef incubatingThingDef = null;
public float incubationProgress = 0f;
public float incubationDuration = 0f;
public float qualityProgress = 0f;
public float qualityTotal = 0f;
public float qualityMultiplier = 1.0f;
public float speedMultiplier = 1.0f;
// 幼虫相关
public Pawn assignedLarva = null;
public int larvaOperateTicksRemaining = 0;
// 时间控制
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 CompEquipmentIncubatorData EquipmentIncubatorData => GetComp<CompEquipmentIncubatorData>();
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 IncubationProgress => incubationDuration > 0 ? incubationProgress / incubationDuration : 0f;
public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f;
public float AdjustedProgressPercent => IncubationProgress;
public float SpeedMultiplier
{
get
{
if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
{
UpdateSpeedMultiplier();
}
return speedMultiplier;
}
}
public float QualityMultiplier => qualityMultiplier;
private void InitializeNutrientInfo() { } // 清理完毕
// === 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)
{
// 休眠时品质下降10%/天)
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 = incubationDuration > 0 ? incubationProgress / incubationDuration : 0f;
float gQualityPercent = qualityTotal > 0 ? qualityProgress / qualityTotal : 0f;
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);
}
// === 获取描述 ===
public string GetSpeedFactorsDescription()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("ARA_Gizmo_SpeedFactors".Translate());
sb.AppendLine();
sb.Append("ARA_Gizmo_TotalSpeedMultiplier".Translate(SpeedMultiplier.ToStringPercent()));
return sb.ToString().TrimEndNewlines();
}
public string GetQualityFactorsDescription()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("ARA_Gizmo_QualityFactors".Translate());
sb.AppendLine();
sb.Append("ARA_Gizmo_TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent()));
return sb.ToString().TrimEndNewlines();
}
private string BuildCallLarvaDescription(EquipmentIncubationConfig config)
{
return "ARA_Gizmo_CallLarvaActivationDesc".Translate(config.thingDef.LabelCap);
}
// === 幼虫交互 ===
public void CallLarva()
{
if (isIncubating || assignedLarva != null) return;
var larva = FindLarva();
if (larva == null)
{
Messages.Message("ARA_Msg_NoLarvaFound".Translate(), MessageTypeDefOf.RejectInput);
return;
}
var job = JobMaker.MakeJob(ARA_JobDefOf.ARA_OperateIncubator, this);
larva.jobs.TryTakeOrderedJob(job, JobTag.MiscWork);
assignedLarva = larva;
Messages.Message("ARA_Msg_LarvaCalled".Translate(), MessageTypeDefOf.PositiveEvent);
}
private Pawn FindLarva()
{
float searchRadius = 30f;
Map map = Map;
if (map == null) return null;
foreach (Pawn pawn in map.mapPawns.AllPawnsSpawned)
{
if (pawn.def.defName == "ArachnaeBase_Race_Larva" && !pawn.Downed && !pawn.Dead && pawn.Faction == Faction.OfPlayer)
{
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 = EquipmentIncubatorData?.SelectedConfig;
if (config == null) return;
incubatingThingDef = config.thingDef;
incubationDuration = config.daysRequired * 60000f;
incubationProgress = 0f;
qualityTotal = incubationDuration;
qualityProgress = 0f;
isIncubating = true;
assignedLarva = null;
larvaOperateTicksRemaining = 0;
Messages.Message("ARA_Msg_IncubationStarted".Translate(incubatingThingDef.LabelCap), MessageTypeDefOf.PositiveEvent);
}
public void CancelIncubation()
{
isIncubating = false;
incubatingThingDef = null;
incubationProgress = 0f;
Messages.Message("ARA_Msg_IncubationCancelled".Translate(), MessageTypeDefOf.NeutralEvent);
}
private void CompleteIncubation()
{
if (incubatingThingDef == null) return;
float finalQuality = QualityPercent;
Thing thing = ThingMaker.MakeThing(incubatingThingDef);
// 应用质量
CompQuality compQuality = thing.TryGetComp<CompQuality>();
if (compQuality != null)
{
QualityCategory qc = finalQuality switch {
>= 0.95f => QualityCategory.Legendary,
>= 0.85f => QualityCategory.Masterwork,
>= 0.70f => QualityCategory.Excellent,
>= 0.50f => QualityCategory.Good,
>= 0.30f => QualityCategory.Normal,
>= 0.15f => QualityCategory.Poor,
_ => QualityCategory.Awful
};
compQuality.SetQuality(qc, ArtGenerationContext.Colony);
}
GenSpawn.Spawn(thing, Position, Map);
isIncubating = false;
incubatingThingDef = null;
Messages.Message("ARA_Msg_IncubationComplete".Translate(thing.LabelCap), MessageTypeDefOf.PositiveEvent);
Destroy();
}
// === 辅助方法 ===
private void UpdateSpeedMultiplier()
{
float multiplier = 1.0f;
var facilityComp = GetComp<CompAffectedByFacilities>();
if (facilityComp != null)
{
var speedStat = DefDatabase<StatDef>.GetNamedSilentFail("ARA_IncubationSpeedFactor") ?? StatDefOf.WorkTableWorkSpeedFactor;
multiplier += facilityComp.GetStatOffset(speedStat);
}
speedMultiplier = multiplier;
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 && incubatingThingDef != null)
{
sb.AppendLine("ARA_Status_Incubating".Translate(incubatingThingDef.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 = EquipmentIncubatorData?.SelectedConfig;
if (config != null)
{
sb.AppendLine("ARA_Status_Target".Translate(config.thingDef.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_EquipmentIncubationProgress(this);
yield return new Gizmo_NeutronFlux(this);
var config = EquipmentIncubatorData?.SelectedConfig;
// 添加订单按钮
if (!isIncubating && assignedLarva == null)
{
yield return new Command_Action
{
defaultLabel = "ARA_Gizmo_AddOrder".Translate(config != null ? 1 : 0, 1),
defaultDesc = "ARA_Gizmo_AddOrderDesc_Item".Translate(),
icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Commands/ARA_NodeSwarmIcon", false),
action = () => EquipmentIncubatorData?.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 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 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);
}
}
}