This commit is contained in:
Tourswen
2025-12-19 03:08:16 +08:00
parent ad42832aa7
commit 6d7ac8f054
45 changed files with 2187 additions and 1403 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -132,9 +132,15 @@
<Compile Include="Buildings\Building_ResearchBlueprintReader\ResearchBlueprintReaderManager.cs" />
<Compile Include="HarmonyPatches\DestroyRemovesResearch\CompDestroyRemovesResearch.cs" />
<Compile Include="HarmonyPatches\DestroyRemovesResearch\CompProperties_DestroyRemovesResearch.cs" />
<Compile Include="HarmonyPatches\Patch_DraftableAnimals.cs" />
<Compile Include="HarmonyPatches\Patch_ResearchManager_AddRemoveMethod.cs" />
<Compile Include="Pawn_Comps\ARA_DratfableAnimals\CompDratfableAnimals.cs" />
<Compile Include="Pawn_Comps\ARA_DratfableAnimals\CompProperties_DratfableAnimals.cs" />
<Compile Include="Pawn_Comps\ARA_PreventPartLoss\CompProperties_PreventPartLoss.cs" />
<Compile Include="Pawn_Comps\ARA_PreventPartLoss\PreventPartLossPatches.cs" />
<Compile Include="Pawn_Comps\ARA_SwarmMaintainer\CompProperties_SwarmMaintainer.cs" />
<Compile Include="Pawn_Comps\ARA_SwarmMaintainer\Comp_SwarmMaintainer.cs" />
<Compile Include="Pawn_Comps\ARA_DratfableAnimals\BeastUnit.cs" />
<Compile Include="RoomRole\RoomRoleWorker_JellyVat.cs" />
<Compile Include="RoomRole\RoomRoleWorker_Incubator.cs" />
<Compile Include="Buildings\Building_TurretGunHasSpeed.cs" />

View File

@@ -1,4 +1,3 @@
// File: Buildings/Building_EquipmentOotheca.cs
using RimWorld;
using System.Collections.Generic;
using System.Text;
@@ -34,23 +33,9 @@ namespace ArachnaeSwarm
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 List<IntVec3> consumedCells = new List<IntVec3>(); // 已消耗的单元格记录
private bool isConsuming = false; // 是否正在消耗营养液
private int consecutiveFailedConsumptions = 0; // 连续失败的消耗次数
private const int MaxConsecutiveFailures = 10; // 最大连续失败次数,超过则暂停检查
private bool nutrientDeficiencyPause = false; // 是否因营养液不足而暂停
// === 新增属性 ===
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 => isConsuming;
// === 简化后的营养液系统:只用于速度加成,不消耗 ===
private int totalNutrientCost = 0; // 总共需要的营养液地块数量(仅用于信息显示)
private int currentNutrientCount = 0; // 当前周围存在的营养液数量
// 缓存的ModExtension
private OothecaIncubatorExtension cachedExtension;
@@ -86,6 +71,10 @@ namespace ArachnaeSwarm
public float QualityProgress => qualityProgress;
public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f;
// 营养液加成属性
public int CurrentNutrientCount => currentNutrientCount;
public float NutrientSpeedBonus => currentNutrientCount * Ext.nutrientSolutionBonusPerTile;
// 进度百分比
public float AdjustedProgressPercent
{
@@ -96,288 +85,30 @@ namespace ArachnaeSwarm
}
}
// === 修复后的初始化营养液消耗方法 ===
private void InitializeNutrientConsumption()
// === 简化的初始化营养液方法 ===
private void InitializeNutrientInfo()
{
if (incubatingThingDef == null)
return;
// 获取孵化成本统计值
// 获取孵化成本统计值(仅用于信息显示)
var costStat = DefDatabase<StatDef>.GetNamedSilentFail("ARA_IncubationCost");
if (costStat != null)
{
totalNutrientCost = Mathf.RoundToInt(incubatingThingDef.GetStatValueAbstract(costStat, null));
Log.Message($"[ARA] 初始化营养液消耗: {incubatingThingDef.defName} 需要 {totalNutrientCost} 个营养液地块");
Log.Message($"[ARA] 孵化 {incubatingThingDef.defName} 建议有 {totalNutrientCost} 个营养液地块以获得最佳速度");
}
else
{
totalNutrientCost = 0;
Log.Message($"[ARA] 孵化 {incubatingThingDef.defName} 不需要营养液");
Log.Message($"[ARA] 孵化 {incubatingThingDef.defName} 不需要营养液加成");
}
consumedNutrientCount = 0;
consumedCells.Clear();
isConsuming = true; // 立即开始消耗
consecutiveFailedConsumptions = 0;
nutrientDeficiencyPause = false;
// 立即检查一次营养液
lastConsumeCheckTick = Find.TickManager.TicksGame - ConsumeCheckInterval; // 强制立即检查
// 立即更新一次营养液计数
UpdateNutrientCount();
}
// === 修复后的检查并消耗营养液方法 ===
private void CheckAndConsumeNutrients()
{
if (!isIncubating || incubatingThingDef == null)
return;
// 如果已经满足需求,停止消耗
if (HasEnoughNutrients)
{
if (isConsuming)
{
Log.Message($"[ARA] 营养液需求已满足: {consumedNutrientCount}/{totalNutrientCost}");
isConsuming = false;
nutrientDeficiencyPause = false; // 重置暂停标志
}
return;
}
// 计算需要消耗的数量(基于当前进度)
float targetProgress = AdjustedProgressPercent;
int targetConsumed = Mathf.RoundToInt(targetProgress * totalNutrientCost);
int toConsume = Mathf.Max(1, targetConsumed - consumedNutrientCount);
if (toConsume <= 0)
return;
// 查找可消耗的营养液单元格
List<IntVec3> availableCells = FindNutrientCells();
if (availableCells.Count == 0)
{
consecutiveFailedConsumptions++;
nutrientDeficiencyPause = true; // 设置为暂停
// 如果没有找到营养液,检查是否应该应用伤害
if (Ext.nutrientDeficiencyDamageEnabled && toConsume > 0)
{
ApplyNutrientDeficiencyDamage();
// 显示消息(频率控制)
if (Find.TickManager.TicksGame % 2000 == 0 && Rand.Chance(0.3f))
{
Messages.Message("ARA_EquipmentIncubator.NoNutrientsFound".Translate(),
this, MessageTypeDefOf.NegativeEvent);
}
Log.Warning($"[ARA] 未找到营养液,连续失败次数: {consecutiveFailedConsumptions}");
}
return;
}
// 重置失败计数和暂停标志
consecutiveFailedConsumptions = 0;
nutrientDeficiencyPause = false; // 有营养液可用,取消暂停
// 开始消耗
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)
{
Log.Message($"[ARA] 消耗了 {consumedThisTick} 个营养液,总计: {consumedNutrientCount}/{totalNutrientCost}");
// 显示消息
if (consumedThisTick > 0 && Find.TickManager.TicksGame % 1000 == 0)
{
Messages.Message($"ARA_EquipmentIncubator.NutrientConsumed".Translate(consumedThisTick, consumedNutrientCount, totalNutrientCost),
this, MessageTypeDefOf.SilentInput);
}
// 如果达到需求,显示完成消息
if (HasEnoughNutrients)
{
Messages.Message("ARA_EquipmentIncubator.NutrientRequirementsMet".Translate(),
this, MessageTypeDefOf.PositiveEvent);
}
}
}
// === 修复后的应用营养液不足伤害方法 ===
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;
TerrainDef nutrientDef = DefDatabase<TerrainDef>.GetNamedSilentFail("ARA_Incubator_Nutrient_Solution");
if (nutrientDef == null)
{
Log.Error("[ARA] 未找到营养液地形定义: ARA_Incubator_Nutrient_Solution");
return availableCells;
}
// 优化搜索:先从内圈开始,逐步扩大
int minRadius = 1; // 最小搜索半径
int currentRadius = Mathf.Min(searchRadius, Mathf.Max(minRadius, consecutiveFailedConsumptions + 1));
for (int x = -currentRadius; x <= currentRadius; x++)
{
for (int z = -currentRadius; z <= currentRadius; z++)
{
IntVec3 cell = Position + new IntVec3(x, 0, z);
// 排除自己的位置和已消耗的单元格
if (cell == Position || consumedCells.Contains(cell))
continue;
// 检查是否在边界内
if (cell.InBounds(map))
{
TerrainDef terrain = map.terrainGrid.TerrainAt(cell);
if (terrain == nutrientDef)
{
availableCells.Add(cell);
}
}
}
}
// 随机排序,避免总是从固定位置开始消耗
if (availableCells.Count > 1)
{
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);
// 创建营养液蓝图
try
{
// 使用PlaceBlueprintForBuild方法创建蓝图
Blueprint_Build blueprint = GenConstruct.PlaceBlueprintForBuild(
nutrientDef,
cell,
map,
Rot4.North,
Faction.OfPlayer,
null
);
if (blueprint != null)
{
// 显示转换效果
if (Find.TickManager.TicksGame % 10 == 0)
{
MoteMaker.ThrowText(cell.ToVector3Shifted(), map,
"ARA_Consumed".Translate(), Color.yellow);
}
return true;
}
}
catch (Exception ex)
{
Log.Error($"Failed to place nutrient solution blueprint at {cell}: {ex.Message}");
// 如果蓝图放置失败,恢复原貌
map.terrainGrid.SetTerrain(cell, originalTerrain);
return false;
}
return false;
}
// === 修复后的Tick方法 ===
// === 简化的Tick方法 ===
protected override void Tick()
{
base.Tick();
@@ -396,39 +127,11 @@ namespace ArachnaeSwarm
UpdateQualityMultiplier();
}
// 检查营养液消耗
if (lastConsumeCheckTick < 0 || Find.TickManager.TicksGame - lastConsumeCheckTick >= ConsumeCheckInterval)
{
lastConsumeCheckTick = Find.TickManager.TicksGame;
CheckAndConsumeNutrients();
}
float currentSpeed = SpeedMultiplier;
// 决定是否应该增加进度
bool shouldProgress = true;
// 检查是否因营养液不足而暂停
if (Ext.stopIncubationWhenNutrientDeficient &&
totalNutrientCost > 0 &&
!HasEnoughNutrients &&
nutrientDeficiencyPause)
{
shouldProgress = false;
// 显示暂停消息(频率控制)
if (Find.TickManager.TicksGame % 2000 == 0 && Rand.Chance(0.2f))
{
Messages.Message("ARA_EquipmentIncubator.IncubationPausedNoNutrients".Translate(incubatingThingDef?.LabelCap ?? "Unknown"),
this, MessageTypeDefOf.NeutralEvent);
}
}
if (shouldProgress)
{
incubationProgress += currentSpeed;
qualityProgress += currentSpeed * QualityMultiplier;
}
// 始终增加进度(不再有营养液不足的暂停)
incubationProgress += currentSpeed;
qualityProgress += currentSpeed * QualityMultiplier;
if (incubationProgress >= incubationDuration)
{
@@ -458,7 +161,7 @@ namespace ArachnaeSwarm
int nutrientSolutionCount = CountNearbyNutrientSolutions();
if (nutrientSolutionCount > 0)
{
builder.AppendLine("ARA_EquipmentIncubator.NutrientSolutions".Translate(
builder.AppendLine("ARA_EquipmentIncubator.NutrientSolutionsBonus".Translate(
nutrientSolutionCount,
nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile * 100));
}
@@ -522,39 +225,34 @@ namespace ArachnaeSwarm
return builder.ToString().TrimEndNewlines();
}
// === 获取营养液消耗描述 ===
public string GetNutrientConsumptionDescription()
// === 获取营养液信息描述 ===
public string GetNutrientInfoDescription()
{
var builder = new StringBuilder();
builder.AppendLine("ARA_EquipmentIncubator.NutrientConsumption".Translate());
builder.AppendLine("ARA_EquipmentIncubator.NutrientInfo".Translate());
builder.AppendLine();
if (totalNutrientCost == 0)
{
builder.AppendLine("ARA_EquipmentIncubator.NoNutrientCost".Translate());
return builder.ToString().TrimEndNewlines();
}
// 显示当前营养液数量和加成
builder.AppendLine("ARA_EquipmentIncubator.CurrentNutrientCount".Translate(currentNutrientCount));
builder.AppendLine("ARA_EquipmentIncubator.NutrientSpeedBonus".Translate(NutrientSpeedBonus.ToStringPercent()));
builder.AppendLine("ARA_EquipmentIncubator.NutrientRequirement".Translate(totalNutrientCost));
builder.AppendLine("ARA_EquipmentIncubator.NutrientConsumed".Translate(consumedNutrientCount));
builder.AppendLine("ARA_EquipmentIncubator.NutrientProgress".Translate(NutrientProgress.ToStringPercent()));
if (HasEnoughNutrients)
// 显示建议的营养液数量
if (totalNutrientCost > 0 && incubatingThingDef != null)
{
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.NutrientRequirementsMet".Translate());
}
else if (isConsuming)
{
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.ConsumingNutrients".Translate());
}
if (nutrientDeficiencyPause)
{
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.NutrientDeficiencyPaused".Translate());
builder.AppendLine("ARA_EquipmentIncubator.RecommendedNutrients".Translate(incubatingThingDef.LabelCap, totalNutrientCost));
// 显示加成效果
if (currentNutrientCount >= totalNutrientCost)
{
builder.AppendLine("ARA_EquipmentIncubator.MaximumBonusActive".Translate());
}
else
{
int needed = totalNutrientCost - currentNutrientCount;
builder.AppendLine("ARA_EquipmentIncubator.AddMoreNutrients".Translate(needed));
}
}
return builder.ToString().TrimEndNewlines();
@@ -684,8 +382,8 @@ namespace ArachnaeSwarm
UpdateQualityMultiplier();
UpdateSpeedMultiplier();
// 初始化营养液消耗
InitializeNutrientConsumption();
// 初始化营养液信息(仅用于显示和建议)
InitializeNutrientInfo();
assignedLarva = null;
larvaOperateTicksRemaining = 0;
@@ -709,8 +407,9 @@ namespace ArachnaeSwarm
qualityProgress = 0f;
qualityTotal = 0f;
// 重置营养液消耗状态
ResetNutrientConsumptionState();
// 重置营养液信息
totalNutrientCost = 0;
currentNutrientCount = 0;
Messages.Message("ARA_EquipmentIncubator.IncubationCancelled".Translate() + " " + "ARA_EquipmentIncubator.ContentsLost".Translate(),
MessageTypeDefOf.NeutralEvent);
@@ -725,16 +424,6 @@ namespace ArachnaeSwarm
float finalQualityPercent = QualityPercent;
// 检查营养液是否满足要求
if (totalNutrientCost > 0 && consumedNutrientCount < totalNutrientCost)
{
Messages.Message("ARA_EquipmentIncubator.IncubationCompleteNutrientDeficient".Translate(incubatingThingDef.LabelCap),
this, MessageTypeDefOf.NegativeEvent);
// 生成物品但应用质量惩罚
finalQualityPercent *= Mathf.Lerp(0.3f, 1.0f, NutrientProgress);
}
// 生成物品
Thing thing = ThingMaker.MakeThing(incubatingThingDef);
ApplyQualityEffects(thing, finalQualityPercent);
@@ -785,18 +474,6 @@ namespace ArachnaeSwarm
}
}
// === 重置营养液消耗状态 ===
private void ResetNutrientConsumptionState()
{
totalNutrientCost = 0;
consumedNutrientCount = 0;
isConsuming = false;
consumedCells.Clear();
consecutiveFailedConsumptions = 0;
nutrientDeficiencyPause = false;
lastConsumeCheckTick = -1;
}
// === 重置孵化状态 ===
private void ResetIncubationState()
{
@@ -806,9 +483,8 @@ namespace ArachnaeSwarm
incubatingThingDef = null;
qualityProgress = 0f;
qualityTotal = 0f;
// 重置营养液消耗状态
ResetNutrientConsumptionState();
totalNutrientCost = 0;
currentNutrientCount = 0;
}
// === 获取剩余时间 ===
@@ -865,34 +541,14 @@ namespace ArachnaeSwarm
}
builder.Append(timeText);
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " +
"ARA_EquipmentIncubator.Quality".Translate() + ": " + QualityMultiplier.ToStringPercent());
// 显示营养液消耗信息
if (totalNutrientCost > 0)
builder.Append("ARA_EquipmentIncubator.Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent());
// 显示营养液加成信息
if (currentNutrientCount > 0)
{
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.NutrientConsumption".Translate() + ": " +
consumedNutrientCount + " / " + totalNutrientCost +
" (" + NutrientProgress.ToStringPercent() + ")");
if (!HasEnoughNutrients)
{
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.NutrientDeficient".Translate());
if (nutrientDeficiencyPause)
{
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.NutrientDeficiencyPaused".Translate());
}
if (consecutiveFailedConsumptions > 0)
{
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.ConsecutiveFailures".Translate(consecutiveFailedConsumptions));
}
}
builder.Append("ARA_EquipmentIncubator.NutrientBonusActive".Translate() + ": " +
currentNutrientCount + " tiles (" + NutrientSpeedBonus.ToStringPercent() + ")");
}
}
else if (assignedLarva != null)
@@ -916,19 +572,15 @@ 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)
builder.Append("ARA_EquipmentIncubator.SpeedMultiplier".Translate() + ": " + SpeedMultiplier.ToStringPercent());
// 显示当前营养液加成
UpdateNutrientCount();
if (currentNutrientCount > 0)
{
int estimatedCost = Mathf.RoundToInt(config.thingDef.GetStatValueAbstract(costStat, null));
if (estimatedCost > 0)
{
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.EstimatedNutrientCost".Translate() + ": " + estimatedCost);
}
builder.AppendLine();
builder.Append("ARA_EquipmentIncubator.CurrentNutrientBonus".Translate() + ": " +
currentNutrientCount + " tiles (" + NutrientSpeedBonus.ToStringPercent() + ")");
}
}
}
@@ -1041,6 +693,18 @@ namespace ArachnaeSwarm
{
builder.AppendLine("ARA_EquipmentIncubator.NoResearchRequired".Translate());
}
// 显示营养液建议
var costStat = DefDatabase<StatDef>.GetNamedSilentFail("ARA_IncubationCost");
if (costStat != null)
{
int recommendedCost = Mathf.RoundToInt(config.thingDef.GetStatValueAbstract(costStat, null));
if (recommendedCost > 0)
{
builder.AppendLine();
builder.AppendLine("ARA_EquipmentIncubator.RecommendedNutrientsForSpeed".Translate(recommendedCost));
}
}
}
builder.AppendLine();
@@ -1220,6 +884,12 @@ namespace ArachnaeSwarm
return count;
}
// === 更新营养液计数 ===
private void UpdateNutrientCount()
{
currentNutrientCount = CountNearbyNutrientSolutions();
}
// === 计算房间质量因子 ===
private float GetRoomQualityFactor()
{
@@ -1293,9 +963,11 @@ namespace ArachnaeSwarm
multiplier *= Ext.speedPenaltyOutsideIncubator;
}
int nutrientSolutionCount = CountNearbyNutrientSolutions();
float nutrientBonus = 1.0f + (nutrientSolutionCount * Ext.nutrientSolutionBonusPerTile);
// 更新营养液计数
UpdateNutrientCount();
// 应用营养液加成
float nutrientBonus = 1.0f + (currentNutrientCount * Ext.nutrientSolutionBonusPerTile);
multiplier *= nutrientBonus;
speedMultiplier = multiplier;
@@ -1342,28 +1014,8 @@ 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 isConsuming, "isConsuming", false);
Scribe_Values.Look(ref consecutiveFailedConsumptions, "consecutiveFailedConsumptions", 0);
Scribe_Values.Look(ref nutrientDeficiencyPause, "nutrientDeficiencyPause", false);
Scribe_Collections.Look(ref consumedCells, "consumedCells", LookMode.Value);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
// 确保列表不为null
if (consumedCells == null)
consumedCells = new List<IntVec3>();
// 确保状态一致性
if (isIncubating && totalNutrientCost > 0 && !isConsuming)
{
isConsuming = true; // 加载后恢复消耗状态
}
}
Scribe_Values.Look(ref currentNutrientCount, "currentNutrientCount", 0);
}
}
}

View File

@@ -1,4 +1,3 @@
// File: ITabs/ITab_EquipmentOotheca_Incubation.cs
using UnityEngine;
using Verse;
using System.Collections.Generic;
@@ -15,10 +14,10 @@ namespace ArachnaeSwarm
private const float SmallLabelHeight = 20f;
private const float ButtonHeight = 25f;
private const float TabWidth = 320f;
private const float TabHeight = 450f; // 增加高度以容纳更多信息
private const float TabHeight = 450f;
private Vector2 scrollPosition = Vector2.zero;
private const float ViewHeight = 480f; // 增加视图高度
private const float ViewHeight = 480f;
public override bool IsVisible
{
@@ -115,6 +114,7 @@ namespace ArachnaeSwarm
Widgets.Label(targetRect, "ARA_EquipmentIncubator.Target".Translate() + ": " + ootheca.incubatingThingDef.LabelCap);
curY += SmallLabelHeight + 20f;
// 孵化进度条
Rect progressBarRect = new Rect(0f, curY, viewRect.width, BarHeight);
Rect progressLabelRect = new Rect(progressBarRect.x, progressBarRect.y - 20, progressBarRect.width, 18);
Text.Anchor = TextAnchor.MiddleCenter;
@@ -133,6 +133,7 @@ namespace ArachnaeSwarm
curY += BarHeight + 30f;
// 质量进度条
Rect qualityBarRect = new Rect(0f, curY, viewRect.width, BarHeight);
Rect qualityLabelRect = new Rect(qualityBarRect.x, qualityBarRect.y - 20, qualityBarRect.width, 18);
Text.Anchor = TextAnchor.MiddleCenter;
@@ -141,98 +142,8 @@ namespace ArachnaeSwarm
Text.Anchor = TextAnchor.UpperLeft;
GUI.color = Color.white;
Widgets.FillableBar(qualityBarRect, qualityPercent, SolidColorMaterials.NewSolidColorTexture(new Color(0.1f, 0.4f, 0.8f, 0.5f))); curY += BarHeight + 25f;
// === 新增:营养液消耗显示 ===
if (ootheca.TotalNutrientCost > 0)
{
// 标题
Rect nutrientTitleRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight);
Widgets.Label(nutrientTitleRect, "ARA_EquipmentIncubator.NutrientConsumption".Translate());
curY += SmallLabelHeight + 5f;
// 进度条
Rect nutrientBarRect = new Rect(0f, curY, viewRect.width, BarHeight);
float nutrientProgress = ootheca.NutrientProgress;
Widgets.FillableBar(nutrientBarRect, nutrientProgress,
SolidColorMaterials.NewSolidColorTexture(new Color(0.8f, 0.6f, 0.1f, 0.5f)));
// 进度文本
string nutrientText = $"{ootheca.ConsumedNutrientCount} / {ootheca.TotalNutrientCost} ({nutrientProgress:P0})";
Text.Anchor = TextAnchor.MiddleCenter;
Widgets.Label(nutrientBarRect, nutrientText);
Text.Anchor = TextAnchor.UpperLeft;
curY += BarHeight + 10f;
// 状态描述
Rect nutrientStatusRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight);
string statusText;
if (ootheca.HasEnoughNutrients)
{
statusText = "ARA_EquipmentIncubator.NutrientRequirementsMet".Translate();
GUI.color = Color.green;
}
else if (ootheca.IsConsuming)
{
statusText = "ARA_EquipmentIncubator.ConsumingNutrients".Translate();
GUI.color = Color.yellow;
}
else
{
statusText = "ARA_EquipmentIncubator.WaitingForNutrients".Translate();
GUI.color = Color.gray;
}
Widgets.Label(nutrientStatusRect, statusText);
GUI.color = Color.white;
curY += SmallLabelHeight + 15f;
}
// === 新增:营养液不足状态显示 ===
if (ootheca.TotalNutrientCost > 0 && ootheca.IsConsuming)
{
curY += BarHeight + 15f;
// 检查是否有营养液不足的警告
var availableCells = ootheca.FindNutrientCells();
if (availableCells.Count == 0 && !ootheca.HasEnoughNutrients)
{
Rect warningRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 2);
if (ootheca.Ext.stopIncubationWhenNutrientDeficient)
{
Widgets.Label(warningRect, "ARA_EquipmentIncubator.IncubationPausedWarning".Translate());
GUI.color = Color.yellow;
}
else
{
Widgets.Label(warningRect, "ARA_EquipmentIncubator.NutrientDeficiencyWarning".Translate());
GUI.color = Color.red;
}
// 绘制警告背景
Widgets.DrawHighlight(warningRect);
GUI.color = Color.white;
curY += SmallLabelHeight * 2 + 10f;
// 显示血量警告
float healthPercent = (float)ootheca.HitPoints / ootheca.MaxHitPoints;
if (healthPercent < 0.5f)
{
Rect healthWarningRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight);
Widgets.Label(healthWarningRect, "ARA_EquipmentIncubator.LowHealthWarning".Translate(healthPercent.ToStringPercent()));
GUI.color = new Color(1f, 0.5f, 0.5f);
Widgets.DrawHighlight(healthWarningRect);
GUI.color = Color.white;
curY += SmallLabelHeight + 5f;
}
}
}
Widgets.FillableBar(qualityBarRect, qualityPercent, SolidColorMaterials.NewSolidColorTexture(new Color(0.1f, 0.4f, 0.8f, 0.5f)));
string qualityProgressText = $"{qualityPercent:P0}";
Text.Anchor = TextAnchor.MiddleCenter;
Widgets.Label(qualityBarRect, qualityProgressText);
@@ -247,6 +158,7 @@ namespace ArachnaeSwarm
curY += BarHeight + 25f;
// 剩余时间
Rect timeRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight);
string timeText = "ARA_EquipmentIncubator.TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "ARA_EquipmentIncubator.Days".Translate();
if (hoursRemaining > 0.1f && daysRemaining < 1f)

View File

@@ -6,34 +6,47 @@ using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class Building_ResearchBlueprintReader : Building
{
// 储存的科技
private ResearchProjectDef storedResearch;
// 当前研究进度
private float progress;
// 管理器引用
private ResearchBlueprintReaderManager manager;
// 是否正在研究
private bool isResearching = false;
// 电力组件
private CompPowerTrader powerComp;
// 锁定信息
private int researchStartTime;
// === 新增唯一ID防止重复注册 ===
private string uniqueId = null;
public string UniqueId
{
get
{
if (uniqueId == null)
{
uniqueId = $"ResearchBlueprintReader_{Guid.NewGuid():N}";
}
return uniqueId;
}
}
public ResearchProjectDef StoredResearch => storedResearch;
public float Progress => progress;
public bool IsLocked => storedResearch != null;
public bool IsResearching => isResearching && storedResearch != null && !storedResearch.IsFinished;
// 获取研究速度
private float ResearchSpeed
{
@@ -43,44 +56,54 @@ namespace ArachnaeSwarm
return ext?.researchSpeed ?? 10f;
}
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Defs.Look(ref storedResearch, "storedResearch");
Scribe_Values.Look(ref progress, "progress", 0f);
Scribe_Values.Look(ref isResearching, "isResearching", false);
Scribe_Values.Look(ref researchStartTime, "researchStartTime", 0);
Scribe_Values.Look(ref uniqueId, "uniqueId");
if (Scribe.mode == LoadSaveMode.LoadingVars)
{
// 重建管理器连接
manager = ResearchBlueprintReaderManager.Instance;
}
}
public override void SpawnSetup(Map map, bool respawningAfterLoad)
{
base.SpawnSetup(map, respawningAfterLoad);
// 获取管理器
manager = ResearchBlueprintReaderManager.Instance;
if (manager != null)
try
{
manager.RegisterReader(this);
base.SpawnSetup(map, respawningAfterLoad);
// 获取管理器
manager = ResearchBlueprintReaderManager.Instance;
if (manager != null)
{
manager.RegisterReader(this);
}
// 获取电力组件
powerComp = GetComp<CompPowerTrader>();
// 如果加载时有储存的科技,确保注册到管理器
if (storedResearch != null && manager != null)
{
manager.RegisterResearch(this, storedResearch);
}
Log.Message($"[ResearchBlueprintReader] Building spawned with ID: {ThingID}, UniqueId: {UniqueId}");
}
// 获取电力组件
powerComp = GetComp<CompPowerTrader>();
// 如果加载时有储存的科技,确保注册到管理器
if (storedResearch != null && manager != null)
catch (Exception ex)
{
manager.RegisterResearch(this, storedResearch);
Log.Error($"[ResearchBlueprintReader] Error in SpawnSetup: {ex}");
}
}
public override void Destroy(DestroyMode mode = DestroyMode.Vanish)
{
// 通知管理器建筑被摧毁
@@ -88,10 +111,10 @@ namespace ArachnaeSwarm
{
manager.OnBuildingDestroyed(this, storedResearch);
}
base.Destroy(mode);
}
protected override void Tick()
{
base.Tick();
@@ -207,38 +230,6 @@ namespace ArachnaeSwarm
MessageTypeDefOf.NeutralEvent);
}
/// <summary>
/// 解锁建筑(释放储存的科技)
/// </summary>
public void UnlockBuilding()
{
if (storedResearch != null && manager != null)
{
var project = storedResearch;
// 如果科技未完成,需要移除进度
if (!project.IsFinished)
{
// 计算当前建筑的贡献
float contributedProgress = progress;
float globalProgress = Find.ResearchManager.GetProgress(project);
// 移除这个建筑的贡献(简化处理:减去当前建筑的进度)
// 注意:这里可能有多个建筑同时研究,所以不能简单减去
// 我们让管理器来处理复杂的逻辑
manager.OnBuildingUnlocked(this, project);
}
storedResearch = null;
progress = 0f;
isResearching = false;
researchStartTime = 0;
Messages.Message("ResearchBlueprintReader_BuildingUnlocked".Translate(project.LabelCap),
MessageTypeDefOf.NeutralEvent);
}
}
/// <summary>
/// 强制完成研究(用于调试)
/// </summary>
@@ -258,22 +249,6 @@ namespace ArachnaeSwarm
yield return gizmo;
}
// 如果已锁定,显示解锁按钮
if (IsLocked)
{
var unlockCmd = new Command_Action();
unlockCmd.defaultLabel = "ResearchBlueprintReader_UnlockBuilding".Translate();
unlockCmd.defaultDesc = "ResearchBlueprintReader_UnlockBuildingDesc".Translate();
unlockCmd.icon = ContentFinder<Texture2D>.Get("ArachnaeSwarm/UI/Abilities/ARA_Ability_Morph", false);
unlockCmd.action = delegate
{
UnlockBuilding();
};
yield return unlockCmd;
yield break; // 锁定状态下不显示其他按钮
}
// 选择研究按钮
var selectCmd = new Command_Action();
selectCmd.defaultLabel = "ResearchBlueprintReader_SelectProject".Translate();

View File

@@ -23,6 +23,10 @@ namespace ArachnaeSwarm
private int cleanupTimer;
private const int CleanupInterval = 2500;
// === 新增:用于序列化的临时字段 ===
private List<ResearchProjectDef> serializedProjects;
private List<List<Building_ResearchBlueprintReader>> serializedBuildings;
public ResearchBlueprintReaderManager(Game game) : base()
{
instance = this;
@@ -36,34 +40,79 @@ namespace ArachnaeSwarm
{
base.ExposeData();
Scribe_Collections.Look(ref allReaders, "allReaders", LookMode.Reference);
// 序列化研究建筑映射
// 修复字典序列化问题
if (Scribe.mode == LoadSaveMode.Saving)
{
List<ResearchProjectDef> keys = researchBuildings.Keys.ToList();
List<List<Building_ResearchBlueprintReader>> values = researchBuildings.Values.ToList();
Scribe_Collections.Look(ref keys, "researchKeys", LookMode.Def);
Scribe_Collections.Look(ref values, "researchValues", LookMode.Deep);
// 保存时:将字典转换为两个列表
serializedProjects = new List<ResearchProjectDef>();
serializedBuildings = new List<List<Building_ResearchBlueprintReader>>();
foreach (var kvp in researchBuildings)
{
// 只保存有效的项目和有建筑的项目
if (kvp.Key != null && kvp.Value != null && kvp.Value.Count > 0)
{
// 过滤掉已被摧毁的建筑
var validBuildings = kvp.Value.Where(b => b != null && !b.Destroyed).ToList();
if (validBuildings.Count > 0)
{
serializedProjects.Add(kvp.Key);
serializedBuildings.Add(validBuildings);
}
}
}
Scribe_Collections.Look(ref serializedProjects, "serializedProjects", LookMode.Def);
Scribe_Collections.Look(ref serializedBuildings, "serializedBuildings", LookMode.Reference);
}
else if (Scribe.mode == LoadSaveMode.LoadingVars)
{
allReaders = allReaders ?? new List<Building_ResearchBlueprintReader>();
List<ResearchProjectDef> keys = null;
List<List<Building_ResearchBlueprintReader>> values = null;
Scribe_Collections.Look(ref keys, "researchKeys", LookMode.Def);
Scribe_Collections.Look(ref values, "researchValues", LookMode.Deep);
// 加载时:清空现有数据
allReaders = new List<Building_ResearchBlueprintReader>();
researchBuildings = new Dictionary<ResearchProjectDef, List<Building_ResearchBlueprintReader>>();
if (keys != null && values != null && keys.Count == values.Count)
serializedProjects = null;
serializedBuildings = null;
Scribe_Collections.Look(ref serializedProjects, "serializedProjects", LookMode.Def);
Scribe_Collections.Look(ref serializedBuildings, "serializedBuildings", LookMode.Reference);
if (serializedProjects != null && serializedBuildings != null &&
serializedProjects.Count == serializedBuildings.Count)
{
for (int i = 0; i < keys.Count; i++)
for (int i = 0; i < serializedProjects.Count; i++)
{
if (keys[i] != null)
researchBuildings[keys[i]] = values[i] ?? new List<Building_ResearchBlueprintReader>();
var project = serializedProjects[i];
var buildings = serializedBuildings[i];
if (project != null && buildings != null)
{
// 清理空引用
buildings.RemoveAll(b => b == null);
if (buildings.Count > 0)
{
researchBuildings[project] = buildings;
// 添加到所有建筑列表
foreach (var building in buildings)
{
if (!allReaders.Contains(building))
{
allReaders.Add(building);
}
}
}
}
}
}
Log.Message($"[ResearchManager] Loaded {allReaders.Count} buildings, {researchBuildings.Count} research projects");
}
else if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
// 后加载初始化:清理所有无效数据
CleanupInvalidData();
}
}
@@ -74,21 +123,86 @@ namespace ArachnaeSwarm
cleanupTimer++;
if (cleanupTimer >= CleanupInterval)
{
CleanupDestroyedBuildings();
CleanupInvalidData();
cleanupTimer = 0;
}
}
/// <summary>
/// 清理无效数据
/// </summary>
private void CleanupInvalidData()
{
int removedCount = 0;
// 清理所有建筑列表
if (allReaders != null)
{
removedCount += allReaders.RemoveAll(b =>
b == null || b.Destroyed || !b.Spawned || b.Map == null);
Log.Message($"[ResearchManager] Cleaned up {removedCount} invalid buildings from allReaders");
}
else
{
allReaders = new List<Building_ResearchBlueprintReader>();
}
// 清理研究建筑映射
if (researchBuildings != null)
{
var projectsToRemove = new List<ResearchProjectDef>();
foreach (var kvp in researchBuildings)
{
if (kvp.Key == null)
{
projectsToRemove.Add(kvp.Key);
continue;
}
// 清理无效建筑
kvp.Value.RemoveAll(b =>
b == null || b.Destroyed || !b.Spawned || b.Map == null);
if (kvp.Value.Count == 0)
{
projectsToRemove.Add(kvp.Key);
}
}
// 移除空项目
foreach (var project in projectsToRemove)
{
researchBuildings.Remove(project);
}
Log.Message($"[ResearchManager] Cleaned up {projectsToRemove.Count} empty research projects");
}
else
{
researchBuildings = new Dictionary<ResearchProjectDef, List<Building_ResearchBlueprintReader>>();
}
}
/// <summary>
/// 注册建筑
/// </summary>
public void RegisterReader(Building_ResearchBlueprintReader reader)
{
if (reader == null || reader.Destroyed) return;
if (reader == null || reader.Destroyed || !reader.Spawned)
return;
// 防止重复注册
if (allReaders == null)
{
allReaders = new List<Building_ResearchBlueprintReader>();
}
if (!allReaders.Contains(reader))
{
allReaders.Add(reader);
Log.Message($"[ResearchManager] Registered reader: {reader.ThingID} at position {reader.Position}");
}
}
@@ -97,15 +211,22 @@ namespace ArachnaeSwarm
/// </summary>
public void RegisterResearch(Building_ResearchBlueprintReader reader, ResearchProjectDef project)
{
if (reader == null || project == null) return;
if (reader == null || project == null)
return;
if (researchBuildings == null)
{
researchBuildings = new Dictionary<ResearchProjectDef, List<Building_ResearchBlueprintReader>>();
}
if (!researchBuildings.ContainsKey(project))
researchBuildings[project] = new List<Building_ResearchBlueprintReader>();
if (!researchBuildings[project].Contains(reader))
{
researchBuildings[project].Add(reader);
Log.Message($"[ResearchManager] Registered research: {project.defName} at building {reader.Position}");
Log.Message($"[ResearchManager] Registered research: {project.defName} at building {reader.Position}");
}
}
/// <summary>
@@ -122,159 +243,27 @@ namespace ArachnaeSwarm
Log.Message($"[ResearchManager] Processing building destruction for project: {project.defName}");
// 从列表中移除
if (researchBuildings.ContainsKey(project))
if (researchBuildings != null && researchBuildings.ContainsKey(project))
{
researchBuildings[project].Remove(building);
Log.Message($"[ResearchManager] Removed building from project list. Remaining buildings: {researchBuildings[project].Count}");
// 检查是否还有建筑
CheckResearchStatus(project);
}
// 从所有建筑列表中移除
allReaders.Remove(building);
}
/// <summary>
/// 检查科技状态
/// </summary>
private void CheckResearchStatus(ResearchProjectDef project)
{
if (project == null) return;
// 检查是否还有建筑研究这个科技
bool hasBuildings = false;
if (researchBuildings.ContainsKey(project))
{
// 清理列表中的空引用
researchBuildings[project].RemoveAll(b => b == null || b.Destroyed);
hasBuildings = researchBuildings[project].Count > 0;
if (!hasBuildings)
if (researchBuildings[project].Count == 0)
{
researchBuildings.Remove(project);
Log.Message($"[ResearchManager] No buildings left for project: {project.defName}");
}
}
// 如果没有建筑了,移除科技
if (!hasBuildings)
// 从所有建筑列表中移除
if (allReaders != null)
{
RemoveResearchProject(project);
allReaders.Remove(building);
}
}
/// <summary>
/// 移除科技
/// </summary>
private void RemoveResearchProject(ResearchProjectDef project)
{
if (project == null) return;
Log.Message($"[ResearchManager] Removing research project: {project.defName}");
try
{
// 移除科技完成状态
Utilities.ResearchRemover.RemoveResearchProject(project, false);
// 发送消息
Messages.Message("ResearchManager_ResearchLost".Translate(project.LabelCap),
MessageTypeDefOf.NegativeEvent);
Log.Message($"[ResearchManager] Successfully removed research project: {project.defName}");
}
catch (Exception ex)
{
Log.Error($"[ResearchManager] Error removing research project {project.defName}: {ex}");
}
}
/// <summary>
/// 建筑解锁时的处理
/// </summary>
public void OnBuildingUnlocked(Building_ResearchBlueprintReader building, ResearchProjectDef project)
{
if (project == null) return;
Log.Message($"[ResearchManager] Processing building unlock for project: {project.defName}");
// 从研究中移除
if (researchBuildings.ContainsKey(project))
{
researchBuildings[project].Remove(building);
Log.Message($"[ResearchManager] Removed building from project list due to unlock. Remaining buildings: {researchBuildings[project].Count}");
// 检查是否还有建筑
CheckResearchStatus(project);
}
}
/// <summary>
/// 检查科技是否还有建筑研究
/// </summary>
public bool HasResearchBuildings(ResearchProjectDef project)
{
if (project == null) return false;
if (researchBuildings.ContainsKey(project))
{
researchBuildings[project].RemoveAll(b => b == null || b.Destroyed);
return researchBuildings[project].Count > 0;
}
return false;
}
/// <summary>
/// 获取研究某个科技的建筑数量
/// </summary>
public int GetResearchBuildingCount(ResearchProjectDef project)
{
if (researchBuildings.ContainsKey(project))
{
researchBuildings[project].RemoveAll(b => b == null || b.Destroyed);
return researchBuildings[project].Count;
}
return 0;
}
/// <summary>
/// 清理被摧毁的建筑
/// </summary>
private void CleanupDestroyedBuildings()
{
int removedCount = 0;
// 清理所有建筑列表
allReaders.RemoveAll(b => b == null || b.Destroyed);
// 清理研究建筑映射,并检查科技状态
List<ResearchProjectDef> projectsToCheck = new List<ResearchProjectDef>();
foreach (var kvp in researchBuildings.ToList())
{
kvp.Value.RemoveAll(b => b == null || b.Destroyed);
removedCount += kvp.Value.Count(b => b == null || b.Destroyed);
if (kvp.Value.Count == 0)
{
projectsToCheck.Add(kvp.Key);
}
}
// 检查需要处理的科技
foreach (var project in projectsToCheck)
{
CheckResearchStatus(project);
}
if (removedCount > 0)
{
Log.Message($"[ResearchManager] Cleanup: removed {removedCount} destroyed buildings");
}
}
// ... 其余方法保持不变 ...
/// <summary>
/// 调试命令
@@ -287,28 +276,19 @@ namespace ArachnaeSwarm
return;
}
Log.Message("ResearchManager_StatusTitle".Translate());
Log.Message("ResearchManager_TotalBuildings".Translate(Instance.allReaders.Count));
Log.Message("ResearchManager_ActiveResearch".Translate(Instance.researchBuildings.Count));
Log.Message("=== Research Manager Status ===");
Log.Message($"Total buildings: {Instance.allReaders?.Count ?? 0}");
Log.Message($"Active research projects: {Instance.researchBuildings?.Count ?? 0}");
foreach (var kvp in Instance.researchBuildings)
if (Instance.researchBuildings != null)
{
int activeBuildings = kvp.Value.Count(b => b != null && !b.Destroyed);
Log.Message("ResearchManager_ProjectStatus".Translate(
kvp.Key.defName, activeBuildings, kvp.Value.Count));
}
}
/// <summary>
/// 强制检查所有科技状态(调试用)
/// </summary>
public static void ForceCheckAllResearch()
{
if (Instance == null) return;
foreach (var project in Instance.researchBuildings.Keys.ToList())
{
Instance.CheckResearchStatus(project);
foreach (var kvp in Instance.researchBuildings)
{
if (kvp.Key == null) continue;
int activeBuildings = kvp.Value?.Count(b => b != null && !b.Destroyed && b.Spawned) ?? 0;
Log.Message($" - {kvp.Key.defName}: {activeBuildings} active buildings");
}
}
}
}

View File

@@ -0,0 +1,194 @@
using HarmonyLib;
using RimWorld;
using System;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
[HarmonyPatch(typeof(Pawn), "get_CanTakeOrder")]
public class Patch_CanTakeOrder
{
[HarmonyPostfix]
public static void postfix(ref bool __result, Pawn __instance)
{
if (__instance.HasComp<CompDraftableAnimals>() && __instance.Drafted)
{
__result = true;
}
}
}
[HarmonyPatch(typeof(PawnComponentsUtility), "AddAndRemoveDynamicComponents")]
public class Patch_PawnTracer
{
[HarmonyPostfix]
public static void postfix(Pawn pawn)
{
CompDraftableAnimals comp = pawn.TryGetComp<CompDraftableAnimals>();
if (comp != null && comp.Props.draftable)
{
pawn.drafter = new Pawn_DraftController(pawn);
}
}
}
[HarmonyPatch(typeof(FloatMenuOptionProvider), "SelectedPawnValid")]
public class Patch_GetSingleOption
{
[HarmonyPostfix]
public static void postfix(ref bool __result, Pawn pawn)
{
if (pawn.HasComp<CompDraftableAnimals>() && pawn.Drafted)
{
__result = true;
}
}
}
[HarmonyPatch(typeof(FloatMenuUtility), "UseRangedAttack")]
public static class Patch_FloatMenuUtility_UseRangedAttack
{
[HarmonyPrefix]
public static bool Prefix(Pawn pawn, ref bool __result)
{
if (pawn.equipment == null)
{
__result = false;
return false;
}
return true;
}
}
[HarmonyPatch(typeof(FloatMenuUtility), nameof(FloatMenuUtility.GetMeleeAttackAction))]
public static class Patch_FloatMenuUtility_GetMeleeAttackAction_Simple
{
[HarmonyPrefix]
public static bool Prefix(Pawn pawn, LocalTargetInfo target, out string failStr, ref Action __result, bool ignoreControlled = false)
{
failStr = "";
// 检查是否有 CompDraftableAnimals
var comp = pawn?.GetComp<CompDraftableAnimals>();
if (comp != null)
{
// 直接返回 true 跳过控制检查
ignoreControlled = true;
// 这里我们直接调用修改后的方法
__result = ModifiedGetMeleeAttackAction(pawn, target, out failStr, ignoreControlled);
return false; // 跳过原始方法
}
return true; // 没有组件,继续执行原始方法
}
private static Action ModifiedGetMeleeAttackAction(Pawn pawn, LocalTargetInfo target, out string failStr, bool ignoreControlled = false)
{
failStr = "";
try
{
// 直接使用原始代码,但跳过控制检查
if (!pawn.Drafted && !ignoreControlled)
{
failStr = "IsNotDraftedLower".Translate(pawn.LabelShort, pawn);
}
else if (target.IsValid && !pawn.CanReach(target, PathEndMode.Touch, Danger.Deadly))
{
failStr = "NoPath".Translate();
}
else if (pawn.WorkTagIsDisabled(WorkTags.Violent))
{
failStr = "IsIncapableOfViolenceLower".Translate(pawn.LabelShort, pawn);
}
else if (pawn.meleeVerbs?.TryGetMeleeVerb(target.Thing) == null)
{
failStr = "Incapable".Translate();
}
else if (pawn == target.Thing)
{
failStr = "CannotAttackSelf".Translate();
}
else if (target.Thing is Pawn targetPawn && (pawn.InSameExtraFaction(targetPawn, ExtraFactionType.HomeFaction) || pawn.InSameExtraFaction(targetPawn, ExtraFactionType.MiniFaction)))
{
failStr = "CannotAttackSameFactionMember".Translate();
}
else
{
if (!(target.Thing is Pawn pawn2) || !pawn2.RaceProps.Animal || !HistoryEventUtility.IsKillingInnocentAnimal(pawn, pawn2) || new HistoryEvent(HistoryEventDefOf.KilledInnocentAnimal, pawn.Named(HistoryEventArgsNames.Doer)).DoerWillingToDo())
{
return delegate
{
Job job = JobMaker.MakeJob(JobDefOf.AttackMelee, target);
if (target.Thing is Pawn pawn3)
{
job.killIncappedTarget = pawn3.Downed;
}
pawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
};
}
failStr = "IdeoligionForbids".Translate();
}
failStr = failStr.CapitalizeFirst();
return null;
}
catch (Exception ex)
{
Log.Error($"[ARA] Error in ModifiedGetMeleeAttackAction: {ex}");
failStr = "Cannot attack";
return null;
}
}
}
// 阻止有 ARA_HiveMindBeast 的动物判断应该逃跑
[HarmonyPatch(typeof(FleeUtility), "ShouldAnimalFleeDanger")]
public static class Patch_FleeUtility_ShouldAnimalFleeDanger
{
[HarmonyPostfix]
public static void Postfix(Pawn pawn, ref bool __result)
{
try
{
if (pawn != null && pawn.health?.hediffSet != null && __result)
{
var hiveMindBeastDef = HediffDef.Named("ARA_HiveMindBeast");
if (hiveMindBeastDef != null && pawn.health.hediffSet.HasHediff(hiveMindBeastDef))
{
// 如果有这个 hediff动物不应该逃跑
__result = false;
}
}
}
catch (Exception ex)
{
Log.Error($"[ARA] Error in ShouldAnimalFleeDanger postfix: {ex}");
}
}
}
// 阻止有 ARA_HiveMindBeast 的动物判断应该从特定事物逃跑
[HarmonyPatch(typeof(FleeUtility), "ShouldFleeFrom")]
public static class Patch_FleeUtility_ShouldFleeFrom
{
[HarmonyPostfix]
public static void Postfix(Thing t, Pawn pawn, ref bool __result)
{
try
{
if (pawn != null && pawn.health?.hediffSet != null && __result)
{
var hiveMindBeastDef = HediffDef.Named("ARA_HiveMindBeast");
if (hiveMindBeastDef != null && pawn.health.hediffSet.HasHediff(hiveMindBeastDef))
{
// 如果有这个 hediff动物不应该逃跑
__result = false;
}
}
}
catch (Exception ex)
{
Log.Error($"[ARA] Error in ShouldFleeFrom postfix: {ex}");
}
}
}
}

View File

@@ -0,0 +1,70 @@
using System.Collections.Generic;
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public class BeastUnit : Pawn
{
public override void DrawExtraSelectionOverlays()
{
base.DrawExtraSelectionOverlays();
pather.curPath?.DrawPath(this);
jobs.DrawLinesBetweenTargets();
}
public override IEnumerable<Gizmo> GetGizmos()
{
foreach (Gizmo gizmo in base.GetGizmos())
{
yield return gizmo;
}
if (drafter == null)
{
yield break;
}
foreach (Gizmo draftGizmo in GetDraftGizmos())
{
yield return draftGizmo;
}
}
public IEnumerable<Gizmo> GetDraftGizmos()
{
if (!drafter.ShowDraftGizmo)
{
yield break;
}
Command_Toggle command_Toggle = new Command_Toggle
{
hotKey = KeyBindingDefOf.Command_ColonistDraft,
isActive = () => base.Drafted,
toggleAction = delegate
{
drafter.Drafted = !drafter.Drafted;
PlayerKnowledgeDatabase.KnowledgeDemonstrated(ConceptDefOf.Drafting, KnowledgeAmount.SpecificInteraction);
if (base.Drafted)
{
LessonAutoActivator.TeachOpportunity(ConceptDefOf.QueueOrders, OpportunityType.GoodToKnow);
}
},
defaultDesc = "CommandToggleDraftDesc".Translate(),
icon = TexCommand.Draft,
turnOnSound = SoundDefOf.DraftOn,
turnOffSound = SoundDefOf.DraftOff,
groupKeyIgnoreContent = 81729172,
defaultLabel = (base.Drafted ? "CommandUndraftLabel" : "CommandDraftLabel").Translate()
};
if (base.Downed)
{
command_Toggle.Disable("IsIncapped".Translate(LabelShort, this));
}
if (base.Deathresting)
{
command_Toggle.Disable("IsDeathresting".Translate(this.Named("PAWN")));
}
command_Toggle.tutorTag = ((!base.Drafted) ? "Draft" : "Undraft");
yield return command_Toggle;
}
}
}

View File

@@ -0,0 +1,14 @@
using RimWorld;
using UnityEngine;
using Verse;
using Verse.AI.Group;
using Verse.Sound;
using static HarmonyLib.Code;
namespace ArachnaeSwarm
{
public class CompDraftableAnimals : ThingComp
{
public CompProperties_DraftableAnimals Props => (CompProperties_DraftableAnimals)props;
}
}

View File

@@ -0,0 +1,17 @@
using RimWorld;
using System.Collections.Generic;
using Verse;
namespace ArachnaeSwarm
{
public class CompProperties_DraftableAnimals : CompProperties
{
public bool draftable = true;
public CompProperties_DraftableAnimals()
{
compClass = typeof(CompDraftableAnimals);
}
}
}

View File

@@ -75,7 +75,7 @@ namespace ArachnaeSwarm
}
// 智能溅射:次要目标的敌对状态必须与主目标一致
if (secondaryTargetPawn.HostileTo(casterPawn) != mainTargetIsHostile)
if (secondaryTargetPawn.HostileTo(casterPawn))
{
continue;
}

View File

@@ -83,7 +83,7 @@ namespace ArachnaeSwarm
}
// 智能溅射:次要目标的敌对状态必须与主目标一致
if (secondaryTargetPawn.HostileTo(casterPawn) != mainTargetIsHostile)
if (secondaryTargetPawn.Faction == casterPawn.Faction)
{
continue;
}

View File

@@ -130,7 +130,7 @@ namespace ArachnaeSwarm
// 检查是否有食物需求
private bool HasFoodNeed(Pawn pawn)
{
return pawn.needs?.TryGetNeed<Need_Food>() != null;
return pawn.needs?.TryGetNeed<Need_Food>() != null && pawn.needs?.TryGetNeed<Need_Food>().CurLevelPercentage <= 0.25f;
}
// 检查是否是虫族成员