This commit is contained in:
2025-12-16 11:52:30 +08:00
parent 4f46620e26
commit dbbaf1802f
15 changed files with 1330 additions and 1409 deletions

View File

@@ -33,12 +33,303 @@ namespace ArachnaeSwarm
private float qualityMultiplier = 1.0f;
private float qualityProgress = 0f;
private float qualityTotal = 0f;
// === 新增:营养液消耗相关字段 ===
private int totalNutrientCost = 0; // 总共需要的营养液地块数量
private int consumedNutrientCount = 0; // 已消耗的营养液地块数量
private int lastConsumeCheckTick = -1; // 上次检查消耗的时间
private const int ConsumeCheckInterval = 250; // 检查间隔tick
private const int ConsumeRadius = 5; // 搜索半径
private List<IntVec3> consumedCells = new List<IntVec3>(); // 已消耗的单元格记录
private bool hasStartedConsumption = false; // 是否已开始消耗
private float consumeProgress = 0f; // 消耗进度0-1
// === 新增属性 ===
public int TotalNutrientCost => totalNutrientCost;
public int ConsumedNutrientCount => consumedNutrientCount;
public float NutrientProgress => totalNutrientCost > 0 ? (float)consumedNutrientCount / totalNutrientCost : 0f;
public bool HasEnoughNutrients => consumedNutrientCount >= totalNutrientCost;
public bool IsConsuming => hasStartedConsumption && !HasEnoughNutrients && isIncubating;
// === 新增:营养液消耗方法 ===
// 初始化营养液消耗需求
private void InitializeNutrientConsumption()
{
if (incubatingThingDef == null)
return;
// 获取孵化成本统计值
var costStat = DefDatabase<StatDef>.GetNamedSilentFail("ARA_IncubationCost");
if (costStat != null)
{
totalNutrientCost = Mathf.RoundToInt(incubatingThingDef.GetStatValueAbstract(costStat, null));
}
else
{
totalNutrientCost = 0; // 如果统计不存在,则不需要消耗
}
consumedNutrientCount = 0;
consumedCells.Clear();
hasStartedConsumption = false;
consumeProgress = 0f;
}
// 检查并消耗营养液
private void CheckAndConsumeNutrients()
{
if (!isIncubating || incubatingThingDef == null || HasEnoughNutrients)
return;
// 第一次检查时标记开始消耗
if (!hasStartedConsumption)
{
hasStartedConsumption = true;
Messages.Message("ARA_EquipmentIncubator.StartedNutrientConsumption".Translate(),
this, MessageTypeDefOf.SilentInput);
}
// 计算本次应该消耗的数量
// 基于孵化进度来动态计算
float targetProgress = AdjustedProgressPercent;
int targetConsumed = Mathf.RoundToInt(targetProgress * totalNutrientCost);
int toConsume = targetConsumed - consumedNutrientCount;
if (toConsume <= 0)
return;
// 查找可消耗的营养液单元格
List<IntVec3> availableCells = FindNutrientCells();
int consumedThisTick = 0;
for (int i = 0; i < Mathf.Min(toConsume, availableCells.Count); i++)
{
if (ConsumeNutrientCell(availableCells[i]))
{
consumedThisTick++;
consumedNutrientCount++;
// 添加到已消耗列表
consumedCells.Add(availableCells[i]);
if (HasEnoughNutrients)
break;
}
}
if (consumedThisTick > 0)
{
consumeProgress = NutrientProgress;
// 如果达到需求,显示消息
if (HasEnoughNutrients)
{
Messages.Message("ARA_EquipmentIncubator.NutrientRequirementsMet".Translate(),
this, MessageTypeDefOf.PositiveEvent);
}
}
else if (Ext.nutrientDeficiencyDamageEnabled && toConsume > 0)
{
// === 新增:没有找到营养液,造成伤害 ===
ApplyNutrientDeficiencyDamage();
// === 新增:如果设置停止孵化,则暂停进度 ===
if (Ext.stopIncubationWhenNutrientDeficient)
{
// 暂停孵化进度,直到找到营养液
// 我们通过不增加incubationProgress来实现
// 但这里需要特殊处理我们在Tick方法中处理
}
}
}
// === 新增:应用营养液不足的伤害 ===
private void ApplyNutrientDeficiencyDamage()
{
if (Ext.nutrientDeficiencyDamageAmount <= 0f || Ext.nutrientDamageType == null)
return;
try
{
// 计算实际伤害量
float damageAmount = Ext.nutrientDeficiencyDamageAmount;
// 如果建筑血量已经很低,减少伤害以避免立即摧毁
float healthPercent = (float)HitPoints / MaxHitPoints;
if (healthPercent < 0.3f)
{
damageAmount *= 0.5f; // 血量低于30%时,伤害减半
}
if (healthPercent < 0.1f)
{
damageAmount *= 0.2f; // 血量低于10%时伤害减为20%
}
// 应用伤害
DamageInfo damageInfo = new DamageInfo(
Ext.nutrientDamageType,
damageAmount,
armorPenetration: 0,
angle: -1f,
instigator: null,
hitPart: null,
weapon: null,
category: DamageInfo.SourceCategory.ThingOrUnknown,
intendedTarget: this
);
TakeDamage(damageInfo);
// 显示伤害消息(几率性)
if (Ext.showDamageMessages && Rand.Chance(Ext.damageMessageChance))
{
Messages.Message("ARA_EquipmentIncubator.NutrientDeficiencyDamage".Translate(damageAmount.ToString("F1")),
this, MessageTypeDefOf.NegativeEvent);
}
// 更新质量乘数(因为血量变化会影响质量)
UpdateQualityMultiplier();
}
catch (Exception ex)
{
Log.Error($"Failed to apply nutrient deficiency damage: {ex.Message}");
}
}
// 查找可消耗的营养液单元格
public List<IntVec3> FindNutrientCells()
{
List<IntVec3> availableCells = new List<IntVec3>();
var map = Map;
if (map == null)
return availableCells;
// === 修改使用ModExtension中定义的营养液检测半径 ===
int searchRadius = Ext.NutrientSolutionRadiusInt; // 使用ModExtension中的半径
TerrainDef nutrientDef = DefDatabase<TerrainDef>.GetNamedSilentFail("ARA_Incubator_Nutrient_Solution");
if (nutrientDef == null)
return availableCells;
// 搜索矩形区域
for (int x = -searchRadius; x <= searchRadius; x++)
{
for (int z = -searchRadius; z <= searchRadius; z++)
{
IntVec3 cell = Position + new IntVec3(x, 0, z);
// 排除自己的位置
if (cell == Position)
continue;
// 检查是否在边界内且未被消耗过
if (cell.InBounds(map) && !consumedCells.Contains(cell))
{
TerrainDef terrain = map.terrainGrid.TerrainAt(cell);
if (terrain == nutrientDef)
{
availableCells.Add(cell);
}
}
}
}
// 随机排序,避免总是从固定位置开始消耗
availableCells.Shuffle();
return availableCells;
}
// 消耗单个营养液单元格
private bool ConsumeNutrientCell(IntVec3 cell)
{
var map = Map;
if (map == null)
return false;
// 获取目标地貌定义
TerrainDef insectCreepDef = DefDatabase<TerrainDef>.GetNamedSilentFail("ARA_InsectCreep");
TerrainDef nutrientDef = DefDatabase<TerrainDef>.GetNamedSilentFail("ARA_Incubator_Nutrient_Solution");
if (insectCreepDef == null || nutrientDef == null)
return false;
// 记录原来的地貌
TerrainDef originalTerrain = map.terrainGrid.TerrainAt(cell);
// 转换为昆虫爬行地貌
map.terrainGrid.SetTerrain(cell, insectCreepDef);
// 检查是否有物品需要移除(如果有的话)
List<Thing> thingsAtCell = map.thingGrid.ThingsListAt(cell);
foreach (Thing thing in thingsAtCell)
{
// 如果物品阻碍了地貌转换,可能需要处理
if (thing.def.passability == Traversability.Impassable)
{
// 可以根据需要处理阻碍物
continue;
}
}
// 创建营养液蓝图
try
{
// 使用PlaceBlueprintForBuild方法创建蓝图
Blueprint_Build blueprint = GenConstruct.PlaceBlueprintForBuild(
nutrientDef,
cell,
map,
Rot4.North, // 地貌没有旋转,使用默认
Faction.OfPlayer,
null // 地貌没有stuff
);
if (blueprint != null)
{
// 显示转换效果
if (Find.TickManager.TicksGame % 10 == 0) // 每10次消耗显示一次效果
{
MoteMaker.ThrowText(cell.ToVector3Shifted(), map,
"ARA_Consumed".Translate(), Color.yellow);
}
}
}
catch (Exception ex)
{
Log.Error($"Failed to place nutrient solution blueprint at {cell}: {ex.Message}");
// 如果蓝图放置失败,恢复原貌
map.terrainGrid.SetTerrain(cell, originalTerrain);
return false;
}
return true;
}
// 获取营养液消耗描述
public string GetNutrientConsumptionDescription()
{
var builder = new StringBuilder();
builder.AppendLine("ARA_EquipmentIncubator.NutrientConsumption".Translate());
builder.AppendLine();
if (totalNutrientCost == 0)
{
builder.AppendLine("ARA_EquipmentIncubator.NoNutrientCost".Translate());
return builder.ToString().TrimEndNewlines();
}
builder.AppendLine("ARA_EquipmentIncubator.NutrientRequirement".Translate(totalNutrientCost));
builder.AppendLine("ARA_EquipmentIncubator.NutrientConsumed".Translate(consumedNutrientCount));
builder.AppendLine("ARA_EquipmentIncubator.NutrientProgress".Translate(NutrientProgress.ToStringPercent()));
if (HasEnoughNutrients)
{
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.NutrientRequirementsMet".Translate());
}
else if (IsConsuming)
{
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.ConsumingNutrients".Translate());
}
return builder.ToString().TrimEndNewlines();
}
// 缓存的ModExtension
private OothecaIncubatorExtension cachedExtension;
// 获取ModExtension的辅助属性
private OothecaIncubatorExtension Ext
public OothecaIncubatorExtension Ext
{
get
{
@@ -254,7 +545,7 @@ namespace ArachnaeSwarm
Messages.Message("ARA_EquipmentIncubator.LarvaArrived".Translate() + " " + "ARA_EquipmentIncubator.ActivatingOotheca".Translate(),
MessageTypeDefOf.SilentInput);
}
// 幼虫完成操作
public void NotifyLarvaOperationComplete(Pawn larva)
{
@@ -263,38 +554,34 @@ namespace ArachnaeSwarm
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();
// === 新增:初始化营养液消耗 ===
InitializeNutrientConsumption();
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;
@@ -302,27 +589,29 @@ namespace ArachnaeSwarm
qualityProgress = 0f;
qualityTotal = 0f;
// === 新增:重置营养液消耗 ===
totalNutrientCost = 0;
consumedNutrientCount = 0;
hasStartedConsumption = false;
consumeProgress = 0f;
consumedCells.Clear();
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;
@@ -331,20 +620,25 @@ namespace ArachnaeSwarm
qualityProgress = 0f;
qualityTotal = 0f;
// === 新增:清理营养液消耗状态 ===
totalNutrientCost = 0;
consumedNutrientCount = 0;
hasStartedConsumption = false;
consumeProgress = 0f;
consumedCells.Clear();
// 显示消息
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)
{
@@ -392,40 +686,50 @@ namespace ArachnaeSwarm
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());
// === 新增:显示营养液消耗信息 ===
if (totalNutrientCost > 0)
{
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.NutrientConsumption".Translate() + ": " +
consumedNutrientCount + " / " + totalNutrientCost +
" (" + NutrientProgress.ToStringPercent() + ")");
if (!HasEnoughNutrients && IsConsuming)
{
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.ConsumingNutrients".Translate());
}
}
}
else if (assignedLarva != null)
{
@@ -447,16 +751,26 @@ namespace ArachnaeSwarm
{
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());
// === 新增:显示预计的营养液需求 ===
var costStat = DefDatabase<StatDef>.GetNamedSilentFail("ARA_IncubationCost");
if (costStat != null)
{
int estimatedCost = Mathf.RoundToInt(config.thingDef.GetStatValueAbstract(costStat, null));
if (estimatedCost > 0)
{
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.EstimatedNutrientCost".Translate() + ": " + estimatedCost);
}
}
}
}
return builder.ToString().TrimEndNewlines();
}
// Gizmos
public override IEnumerable<Gizmo> GetGizmos()
{
@@ -718,17 +1032,18 @@ namespace ArachnaeSwarm
return null;
}
// 每tick更新
// 修改Tick方法处理营养液不足时停止孵化的逻辑
protected override void Tick()
{
base.Tick();
if (larvaOperateTicksRemaining > 0)
{
larvaOperateTicksRemaining--;
}
if (isIncubating)
{
if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval)
@@ -736,19 +1051,57 @@ namespace ArachnaeSwarm
UpdateSpeedMultiplier();
UpdateQualityMultiplier();
}
float currentSpeed = SpeedMultiplier;
incubationProgress += currentSpeed;
qualityProgress += currentSpeed * QualityMultiplier;
// === 新增:检查是否因营养液不足而停止孵化 ===
bool shouldProgress = true;
if (Ext.stopIncubationWhenNutrientDeficient &&
!HasEnoughNutrients &&
isIncubating &&
hasStartedConsumption)
{
// 检查是否有可用的营养液
var availableCells = FindNutrientCells();
if (availableCells.Count == 0 && totalNutrientCost > 0 && consumedNutrientCount < totalNutrientCost)
{
shouldProgress = false;
// 显示停止消息(第一次或偶尔显示)
if (Find.TickManager.TicksGame % 1000 == 0 && Rand.Chance(0.1f))
{
Messages.Message("ARA_EquipmentIncubator.IncubationPausedNoNutrients".Translate(incubatingThingDef?.LabelCap ?? "Unknown"),
this, MessageTypeDefOf.NeutralEvent);
}
}
}
if (shouldProgress)
{
incubationProgress += currentSpeed;
qualityProgress += currentSpeed * QualityMultiplier;
}
else
{
// 孵化暂停,但仍然需要检查营养液消耗(可能营养液又出现了)
// 并且仍然应用伤害
}
// === 新增:检查营养液消耗 ===
if (lastConsumeCheckTick < 0 || Find.TickManager.TicksGame - lastConsumeCheckTick >= ConsumeCheckInterval)
{
lastConsumeCheckTick = Find.TickManager.TicksGame;
CheckAndConsumeNutrients();
}
if (incubationProgress >= incubationDuration)
{
CompleteIncubation();
}
}
}
// 检查是否在孵化间中
private bool IsInIncubatorRoom()
{
@@ -895,12 +1248,12 @@ namespace ArachnaeSwarm
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);
@@ -912,6 +1265,21 @@ namespace ArachnaeSwarm
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 totalNutrientCost, "totalNutrientCost", 0);
Scribe_Values.Look(ref consumedNutrientCount, "consumedNutrientCount", 0);
Scribe_Values.Look(ref lastConsumeCheckTick, "lastConsumeCheckTick", -1);
Scribe_Values.Look(ref hasStartedConsumption, "hasStartedConsumption", false);
Scribe_Values.Look(ref consumeProgress, "consumeProgress", 0f);
Scribe_Collections.Look(ref consumedCells, "consumedCells", LookMode.Value);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
// 确保列表不为null
if (consumedCells == null)
consumedCells = new List<IntVec3>();
}
}
}
}