11
This commit is contained in:
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
if (DebugEnabled)
|
||||
{
|
||||
Log.Message(message);
|
||||
ArachnaeLog.Debug(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// File: CompDelayedTerrainSpawn.cs
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
@@ -44,27 +46,80 @@ namespace ArachnaeSwarm
|
||||
// The core logic: iterate through nearby cells and change their terrain.
|
||||
foreach (IntVec3 current in GenRadial.RadialCellsAround(parent.Position, Props.spawnRadius, true))
|
||||
{
|
||||
if (current.InBounds(parent.Map) && current.Walkable(parent.Map))
|
||||
if (!current.InBounds(parent.Map) || !current.Walkable(parent.Map))
|
||||
continue;
|
||||
|
||||
TerrainDef currentTerrain = parent.Map.terrainGrid.TerrainAt(current);
|
||||
if (currentTerrain == null)
|
||||
continue;
|
||||
|
||||
// === 新增:使用黑名单检查 ===
|
||||
if (ShouldSkipTerrain(currentTerrain, current))
|
||||
{
|
||||
// 添加:检查当前地形是否有 ARA_Creep 标签
|
||||
TerrainDef currentTerrain = parent.Map.terrainGrid.TerrainAt(current);
|
||||
if (currentTerrain != null && HasCreepTag(currentTerrain))
|
||||
{
|
||||
continue; // 跳过有 ARA_Creep 标签的地面
|
||||
}
|
||||
|
||||
parent.Map.terrainGrid.SetTerrain(current, Props.terrainToSpawn);
|
||||
continue; // 跳过有排除tag的地面
|
||||
}
|
||||
|
||||
parent.Map.terrainGrid.SetTerrain(current, Props.terrainToSpawn);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查地形是否具有 ARA_Creep 标签
|
||||
/// === 新增:检查是否应该跳过此地形 ===
|
||||
/// 基于tag黑名单、允许性和其他条件
|
||||
/// </summary>
|
||||
/// <returns>如果地形有 ARA_Creep 标签则返回 true</returns>
|
||||
private bool HasCreepTag(TerrainDef terrain)
|
||||
private bool ShouldSkipTerrain(TerrainDef terrain, IntVec3 cell)
|
||||
{
|
||||
return terrain.tags != null && terrain.tags.Contains("ARA_Creep") || terrain.tags.Contains("ARA_Incubator_Nutrient_Solution");
|
||||
if (terrain == null)
|
||||
return false;
|
||||
|
||||
// 1. 检查tag黑名单
|
||||
if (Props.IsTerrainExcluded(terrain))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. 检查允许性(如果启用)
|
||||
if (Props.checkAffordances && terrain.affordances != null && Props.excludeAffordances != null)
|
||||
{
|
||||
foreach (var affordance in terrain.affordances)
|
||||
{
|
||||
if (affordance != null && Props.excludeAffordances.Contains(affordance.defName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 检查优先地形(如果启用智能覆盖)
|
||||
if (Props.smartOverlay && Props.preferredTerrains != null)
|
||||
{
|
||||
if (Props.preferredTerrains.Contains(terrain))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 检查路径(如果启用路径保护)
|
||||
if (Props.preservePaths)
|
||||
{
|
||||
if (IsPathCell(cell, parent.Map))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:检查是否是路径单元格 ===
|
||||
/// </summary>
|
||||
private bool IsPathCell(IntVec3 cell, Map map)
|
||||
{
|
||||
// 检查是否有路径标记
|
||||
// 这里可以根据需要扩展,检查设计者路径标记等
|
||||
// 暂时返回false,因为需要更多信息
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTICE: There is NO CompTick() method here. This component does not perform any updates after it has spawned.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// File: CompProperties_DelayedTerrainSpawn.cs
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
@@ -10,11 +12,75 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
public TerrainDef terrainToSpawn;
|
||||
public float spawnRadius = 0f;
|
||||
|
||||
// === 新增:tag黑名单 ===
|
||||
public List<string> excludeTerrainTags = null; // 要排除的tag列表
|
||||
public bool useDefaultExclusions = true; // 是否使用默认排除项(ARA_Creep和ARA_Incubator_Nutrient_Solution)
|
||||
|
||||
// === 新增:扩展检查选项 ===
|
||||
public bool checkAffordances = false; // 是否检查地形允许性(affordances)
|
||||
public List<string> excludeAffordances = null; // 要排除的允许性列表
|
||||
|
||||
// === 新增:智能覆盖选项 ===
|
||||
public bool smartOverlay = false; // 是否智能覆盖(仅覆盖"较差"的地形)
|
||||
public List<TerrainDef> preferredTerrains = null; // 优先地形列表,这些不会被覆盖
|
||||
public bool preservePaths = false; // 是否保留路径
|
||||
|
||||
public CompProperties_DelayedTerrainSpawn()
|
||||
{
|
||||
// The component logic now runs instantly, but the class name is kept for compatibility.
|
||||
compClass = typeof(CompDelayedTerrainSpawn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取要排除的tag列表(包括默认值)
|
||||
/// </summary>
|
||||
public List<string> GetExcludedTags()
|
||||
{
|
||||
var tags = new List<string>();
|
||||
|
||||
// 如果使用默认排除项,添加默认tag
|
||||
if (useDefaultExclusions)
|
||||
{
|
||||
if (!tags.Contains("ARA_Creep"))
|
||||
tags.Add("ARA_Creep");
|
||||
if (!tags.Contains("ARA_Incubator_Nutrient_Solution"))
|
||||
tags.Add("ARA_Incubator_Nutrient_Solution");
|
||||
}
|
||||
|
||||
// 添加自定义排除项
|
||||
if (excludeTerrainTags != null)
|
||||
{
|
||||
foreach (var tag in excludeTerrainTags)
|
||||
{
|
||||
if (!tags.Contains(tag))
|
||||
tags.Add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查地形是否被排除
|
||||
/// </summary>
|
||||
public bool IsTerrainExcluded(TerrainDef terrain)
|
||||
{
|
||||
if (terrain == null || terrain.tags == null)
|
||||
return false;
|
||||
|
||||
var excludedTags = GetExcludedTags();
|
||||
if (excludedTags.Count == 0)
|
||||
return false;
|
||||
|
||||
// 检查地形是否有排除tag
|
||||
foreach (var tag in terrain.tags)
|
||||
{
|
||||
if (excludedTags.Contains(tag))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +492,7 @@ namespace ArachnaeSwarm
|
||||
// 显示消息(仅开发模式)
|
||||
if (Prefs.DevMode)
|
||||
{
|
||||
Log.Message($"[CorpseConverter] Converted {corpseName} at {corpsePosition} to {convertedThing.LabelCap}");
|
||||
ArachnaeLog.Debug($"[CorpseConverter] Converted {corpseName} at {corpsePosition} to {convertedThing.LabelCap}");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -542,7 +542,7 @@ namespace ArachnaeSwarm
|
||||
// 显示消息(仅开发模式)
|
||||
if (Prefs.DevMode)
|
||||
{
|
||||
Log.Message($"[CorpseConverter] Marked building at {markingTargetBuilding.Position} ({markingTargetBuilding.LabelCap}) for deconstruction");
|
||||
ArachnaeLog.Debug($"[CorpseConverter] Marked building at {markingTargetBuilding.Position} ({markingTargetBuilding.LabelCap}) for deconstruction");
|
||||
}
|
||||
|
||||
ResetMarkingState();
|
||||
|
||||
@@ -463,7 +463,7 @@ namespace ArachnaeSwarm
|
||||
// 显示消息(可选)
|
||||
if (Prefs.DevMode)
|
||||
{
|
||||
Log.Message($"[TerrainChanger] Changed terrain at {targetCell} from {previousTerrain?.defName ?? "null"} to {Props.targetTerrain.defName}");
|
||||
ArachnaeLog.Debug($"[TerrainChanger] Changed terrain at {targetCell} from {previousTerrain?.defName ?? "null"} to {Props.targetTerrain.defName}");
|
||||
}
|
||||
|
||||
ResetWorkState();
|
||||
@@ -510,7 +510,7 @@ namespace ArachnaeSwarm
|
||||
// 显示消息(可选)
|
||||
if (Prefs.DevMode)
|
||||
{
|
||||
Log.Message($"[TerrainChanger] Marked terrain at {markingTargetCell} ({currentTerrain.defName}) for removal");
|
||||
ArachnaeLog.Debug($"[TerrainChanger] Marked terrain at {markingTargetCell} ({currentTerrain.defName}) for removal");
|
||||
}
|
||||
|
||||
ResetMarkingState();
|
||||
|
||||
@@ -96,12 +96,12 @@ namespace ArachnaeSwarm
|
||||
if (costStat != null)
|
||||
{
|
||||
totalNutrientCost = Mathf.RoundToInt(incubatingThingDef.GetStatValueAbstract(costStat, null));
|
||||
Log.Message($"[ARA] 孵化 {incubatingThingDef.defName} 建议有 {totalNutrientCost} 个营养液地块以获得最佳速度");
|
||||
ArachnaeLog.Debug($"[ARA] 孵化 {incubatingThingDef.defName} 建议有 {totalNutrientCost} 个营养液地块以获得最佳速度");
|
||||
}
|
||||
else
|
||||
{
|
||||
totalNutrientCost = 0;
|
||||
Log.Message($"[ARA] 孵化 {incubatingThingDef.defName} 不需要营养液加成");
|
||||
ArachnaeLog.Debug($"[ARA] 孵化 {incubatingThingDef.defName} 不需要营养液加成");
|
||||
}
|
||||
|
||||
// 立即更新一次营养液计数
|
||||
@@ -237,24 +237,6 @@ namespace ArachnaeSwarm
|
||||
builder.AppendLine("ARA_EquipmentIncubator.CurrentNutrientCount".Translate(currentNutrientCount));
|
||||
builder.AppendLine("ARA_EquipmentIncubator.NutrientSpeedBonus".Translate(NutrientSpeedBonus.ToStringPercent()));
|
||||
|
||||
// 显示建议的营养液数量
|
||||
if (totalNutrientCost > 0 && incubatingThingDef != null)
|
||||
{
|
||||
builder.AppendLine();
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -398,7 +380,7 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
if (!isIncubating) return;
|
||||
|
||||
Log.Message($"[ARA] 取消孵化: {incubatingThingDef?.defName}");
|
||||
ArachnaeLog.Debug($"[ARA] 取消孵化: {incubatingThingDef?.defName}");
|
||||
|
||||
isIncubating = false;
|
||||
incubationProgress = 0f;
|
||||
@@ -420,7 +402,7 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
if (incubatingThingDef == null) return;
|
||||
|
||||
Log.Message($"[ARA] 完成孵化: {incubatingThingDef.defName}");
|
||||
ArachnaeLog.Debug($"[ARA] 完成孵化: {incubatingThingDef.defName}");
|
||||
|
||||
float finalQualityPercent = QualityPercent;
|
||||
|
||||
|
||||
@@ -293,7 +293,7 @@ namespace ArachnaeSwarm
|
||||
// 按物品名称排序
|
||||
cachedConfigs.Sort((a, b) => string.Compare(a.thingDef?.label ?? "", b.thingDef?.label ?? ""));
|
||||
|
||||
Log.Message($"Built {cachedConfigs.Count} equipment incubation configs for {parent.def.defName}");
|
||||
ArachnaeLog.Debug($"Built {cachedConfigs.Count} equipment incubation configs for {parent.def.defName}");
|
||||
}
|
||||
|
||||
// 切换到特定索引
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace ArachnaeSwarm
|
||||
|
||||
selfDestructInitiated = true;
|
||||
pawnMissingTickCount = 0;
|
||||
Log.Message($"RefuelingVat at {Position}: Pawn missing from container. Self-destruct initiated. Will destroy in {MissingTicksBeforeDestruction} ticks.");
|
||||
ArachnaeLog.Debug($"RefuelingVat at {Position}: Pawn missing from container. Self-destruct initiated. Will destroy in {MissingTicksBeforeDestruction} ticks.");
|
||||
|
||||
// 发送警告消息
|
||||
Messages.Message("RefuelingVat_ContainmentBreach".Translate(), this, MessageTypeDefOf.NegativeEvent);
|
||||
@@ -132,7 +132,7 @@ namespace ArachnaeSwarm
|
||||
// === 新增方法:执行建筑销毁 ===
|
||||
private void ExecuteSelfDestruct()
|
||||
{
|
||||
Log.Message($"RefuelingVat at {Position}: Self-destruct sequence complete. Destroying building.");
|
||||
ArachnaeLog.Debug($"RefuelingVat at {Position}: Self-destruct sequence complete. Destroying building.");
|
||||
|
||||
// 显示爆炸效果
|
||||
if (!Destroyed)
|
||||
@@ -172,7 +172,7 @@ namespace ArachnaeSwarm
|
||||
}
|
||||
else if (pawnMissingTickCount % 30 == 0) // 每0.5秒报告一次
|
||||
{
|
||||
Log.Message($"RefuelingVat at {Position}: Pawn still missing. Self-destruct in {MissingTicksBeforeDestruction - pawnMissingTickCount} ticks.");
|
||||
ArachnaeLog.Debug($"RefuelingVat at {Position}: Pawn still missing. Self-destruct in {MissingTicksBeforeDestruction - pawnMissingTickCount} ticks.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,7 +181,7 @@ namespace ArachnaeSwarm
|
||||
// Pawn在容器内,重置自毁系统
|
||||
if (selfDestructInitiated)
|
||||
{
|
||||
Log.Message($"RefuelingVat at {Position}: Pawn returned to container. Self-destruct cancelled.");
|
||||
ArachnaeLog.Debug($"RefuelingVat at {Position}: Pawn returned to container. Self-destruct cancelled.");
|
||||
selfDestructInitiated = false;
|
||||
pawnMissingTickCount = 0;
|
||||
Messages.Message("RefuelingVat_ContainmentRestored".Translate(), this, MessageTypeDefOf.PositiveEvent);
|
||||
@@ -249,7 +249,7 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
if (selectedPawn != null && innerContainer.Contains(selectedPawn))
|
||||
{
|
||||
Log.Message($"RefuelingVat despawned with pawn inside, forcing ejection.");
|
||||
ArachnaeLog.Debug($"RefuelingVat despawned with pawn inside, forcing ejection.");
|
||||
Finish(); // 使用修改后的Finish方法
|
||||
}
|
||||
}
|
||||
@@ -308,7 +308,7 @@ namespace ArachnaeSwarm
|
||||
// 检查是否是被建筑杀死的
|
||||
if (pawnsKilledByVat.Contains(pawn))
|
||||
{
|
||||
Log.Message($"Pawn {pawn.Label} killed by RefuelingVat.");
|
||||
ArachnaeLog.Debug($"Pawn {pawn.Label} killed by RefuelingVat.");
|
||||
|
||||
// 注意:现在不再需要从容器中移除Pawn或销毁尸体
|
||||
// 因为建筑会被销毁,Pawn会自然弹出
|
||||
@@ -375,7 +375,7 @@ namespace ArachnaeSwarm
|
||||
else
|
||||
{
|
||||
// 其他原因的死亡 - 启动自毁系统
|
||||
Log.Message($"Pawn {selectedPawn.Label} died unexpectedly. Starting self-destruct.");
|
||||
ArachnaeLog.Debug($"Pawn {selectedPawn.Label} died unexpectedly. Starting self-destruct.");
|
||||
InitiateSelfDestruct();
|
||||
}
|
||||
return;
|
||||
@@ -465,7 +465,7 @@ namespace ArachnaeSwarm
|
||||
selfDestructInitiated = false;
|
||||
pawnMissingTickCount = 0;
|
||||
|
||||
Log.Message($"Pawn {pawn.Label} inserted into RefuelingVat at {Position}. Self-destruct monitoring active.");
|
||||
ArachnaeLog.Debug($"Pawn {pawn.Label} inserted into RefuelingVat at {Position}. Self-destruct monitoring active.");
|
||||
}
|
||||
if (deselected)
|
||||
{
|
||||
@@ -482,7 +482,7 @@ namespace ArachnaeSwarm
|
||||
// 检查pawn是否还活着,如果已经死亡且是被建筑杀死的,则启动自毁
|
||||
if (selectedPawn.Dead && pawnsKilledByVat.Contains(selectedPawn))
|
||||
{
|
||||
Log.Message($"Pawn {selectedPawn.Label} killed by vat. Starting self-destruct.");
|
||||
ArachnaeLog.Debug($"Pawn {selectedPawn.Label} killed by vat. Starting self-destruct.");
|
||||
InitiateSelfDestruct();
|
||||
return;
|
||||
}
|
||||
@@ -514,7 +514,7 @@ namespace ArachnaeSwarm
|
||||
// 方法3:强制移除(仅对活着的pawn)
|
||||
if (!ejected && innerContainer.Contains(selectedPawn) && !selectedPawn.Dead)
|
||||
{
|
||||
Log.Message($"Forcing removal of pawn {selectedPawn} from RefuelingVat");
|
||||
ArachnaeLog.Debug($"Forcing removal of pawn {selectedPawn} from RefuelingVat");
|
||||
innerContainer.Remove(selectedPawn);
|
||||
GenPlace.TryPlaceThing(selectedPawn, this.Position, base.Map, ThingPlaceMode.Near);
|
||||
ejected = true;
|
||||
@@ -523,11 +523,11 @@ namespace ArachnaeSwarm
|
||||
|
||||
if (ejected)
|
||||
{
|
||||
Log.Message($"Successfully ejected {selectedPawn} using method: {ejectionMethod}");
|
||||
ArachnaeLog.Debug($"Successfully ejected {selectedPawn} using method: {ejectionMethod}");
|
||||
}
|
||||
else if (!selectedPawn.Dead) // 只有活着的pawn弹出失败才报错
|
||||
{
|
||||
Log.Message($"Failed to eject {selectedPawn} from RefuelingVat");
|
||||
ArachnaeLog.Debug($"Failed to eject {selectedPawn} from RefuelingVat");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -551,7 +551,7 @@ namespace ArachnaeSwarm
|
||||
// 确保pawn不在容器中(除非是被建筑杀死的)
|
||||
if (innerContainer.Contains(selectedPawn) && !(selectedPawn.Dead && pawnsKilledByVat.Contains(selectedPawn)))
|
||||
{
|
||||
Log.Message($"Pawn {selectedPawn} still in container during OnStop, forcing removal.");
|
||||
ArachnaeLog.Debug($"Pawn {selectedPawn} still in container during OnStop, forcing removal.");
|
||||
innerContainer.Remove(selectedPawn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace ArachnaeSwarm
|
||||
manager.RegisterResearch(this, storedResearch);
|
||||
}
|
||||
|
||||
Log.Message($"[ResearchBlueprintReader] Building spawned with ID: {ThingID}, UniqueId: {UniqueId}");
|
||||
ArachnaeLog.Debug($"[ResearchBlueprintReader] Building spawned with ID: {ThingID}, UniqueId: {UniqueId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -177,7 +177,7 @@ namespace ArachnaeSwarm
|
||||
Messages.Message("ResearchBlueprintReader_ResearchCompleted".Translate(storedResearch.LabelCap),
|
||||
MessageTypeDefOf.PositiveEvent);
|
||||
|
||||
Log.Message($"[ResearchBlueprintReader] Research completed: {storedResearch.defName}");
|
||||
ArachnaeLog.Debug($"[ResearchBlueprintReader] Research completed: {storedResearch.defName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -449,21 +449,6 @@ namespace ArachnaeSwarm
|
||||
else
|
||||
{
|
||||
builder.AppendLine("<color=#ff9900>" + "ResearchBlueprintReader_StatusResearching".Translate(storedResearch.LabelCap) + "</color>");
|
||||
builder.AppendLine("ResearchBlueprintReader_BuildingProgress".Translate(
|
||||
progress, storedResearch.baseCost, (progress / storedResearch.baseCost * 100)));
|
||||
builder.Append("ResearchBlueprintReader_GlobalProgress".Translate(
|
||||
Find.ResearchManager.GetProgress(storedResearch), storedResearch.baseCost));
|
||||
|
||||
// 显示研究速度
|
||||
builder.AppendLine();
|
||||
builder.Append("ResearchBlueprintReader_ResearchSpeed".Translate(ResearchSpeed));
|
||||
|
||||
if (researchStartTime > 0)
|
||||
{
|
||||
int days = (Find.TickManager.TicksGame - researchStartTime) / 60000;
|
||||
builder.AppendLine();
|
||||
builder.Append("ResearchBlueprintReader_ResearchTime".Translate(days));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -27,11 +27,16 @@ namespace ArachnaeSwarm
|
||||
private List<ResearchProjectDef> serializedProjects;
|
||||
private List<List<Building_ResearchBlueprintReader>> serializedBuildings;
|
||||
|
||||
// === 新增:科技丢失检查计时器 ===
|
||||
private int lostResearchCheckTimer;
|
||||
private const int LostResearchCheckInterval = 5000; // 每5秒检查一次
|
||||
|
||||
public ResearchBlueprintReaderManager(Game game) : base()
|
||||
{
|
||||
instance = this;
|
||||
allReaders = new List<Building_ResearchBlueprintReader>();
|
||||
researchBuildings = new Dictionary<ResearchProjectDef, List<Building_ResearchBlueprintReader>>();
|
||||
lostResearchCheckTimer = 0;
|
||||
}
|
||||
|
||||
public static ResearchBlueprintReaderManager Instance => instance;
|
||||
@@ -107,13 +112,16 @@ namespace ArachnaeSwarm
|
||||
}
|
||||
}
|
||||
|
||||
Log.Message($"[ResearchManager] Loaded {allReaders.Count} buildings, {researchBuildings.Count} research projects");
|
||||
ArachnaeLog.Debug($"[ResearchManager] Loaded {allReaders.Count} buildings, {researchBuildings.Count} research projects");
|
||||
}
|
||||
else if (Scribe.mode == LoadSaveMode.PostLoadInit)
|
||||
{
|
||||
// 后加载初始化:清理所有无效数据
|
||||
CleanupInvalidData();
|
||||
}
|
||||
|
||||
// 保存和加载计时器
|
||||
Scribe_Values.Look(ref lostResearchCheckTimer, "lostResearchCheckTimer", 0);
|
||||
}
|
||||
|
||||
public override void GameComponentTick()
|
||||
@@ -126,6 +134,92 @@ namespace ArachnaeSwarm
|
||||
CleanupInvalidData();
|
||||
cleanupTimer = 0;
|
||||
}
|
||||
|
||||
// === 新增:定期检查是否有科技因建筑损失而丢失 ===
|
||||
lostResearchCheckTimer++;
|
||||
if (lostResearchCheckTimer >= LostResearchCheckInterval)
|
||||
{
|
||||
CheckForLostResearch();
|
||||
lostResearchCheckTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:检查因建筑损失而丢失的科技 ===
|
||||
/// </summary>
|
||||
private void CheckForLostResearch()
|
||||
{
|
||||
if (researchBuildings == null || researchBuildings.Count == 0)
|
||||
return;
|
||||
|
||||
ArachnaeLog.Debug($"[ResearchManager] Checking for lost research projects...");
|
||||
|
||||
// 获取需要检查的项目列表(复制以避免修改时遍历)
|
||||
var projectsToCheck = new List<ResearchProjectDef>(researchBuildings.Keys);
|
||||
|
||||
foreach (var project in projectsToCheck)
|
||||
{
|
||||
if (project == null)
|
||||
continue;
|
||||
|
||||
if (!researchBuildings.ContainsKey(project))
|
||||
continue;
|
||||
|
||||
var buildings = researchBuildings[project];
|
||||
if (buildings == null)
|
||||
{
|
||||
researchBuildings.Remove(project);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 清理无效建筑
|
||||
buildings.RemoveAll(b =>
|
||||
b == null || b.Destroyed || !b.Spawned || b.Map == null);
|
||||
|
||||
// 如果已经没有建筑了
|
||||
if (buildings.Count == 0)
|
||||
{
|
||||
researchBuildings.Remove(project);
|
||||
|
||||
// 调用Patch创建的移除方法来丢失科技
|
||||
OnResearchLostDueToBuildingLoss(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:科技因建筑损失而丢失的处理 ===
|
||||
/// </summary>
|
||||
private void OnResearchLostDueToBuildingLoss(ResearchProjectDef project)
|
||||
{
|
||||
if (project == null)
|
||||
return;
|
||||
|
||||
ArachnaeLog.Debug($"[ResearchManager] Research project lost due to building loss: {project.defName}");
|
||||
|
||||
try
|
||||
{
|
||||
// 使用ResearchRemover类来移除科技
|
||||
if (ResearchRemover.RemoveResearchProject(project, removeDependencies: false))
|
||||
{
|
||||
// 发送游戏内消息
|
||||
Messages.Message(
|
||||
"ResearchManager_ResearchLost".Translate(project.LabelCap),
|
||||
MessageTypeDefOf.NegativeEvent
|
||||
);
|
||||
|
||||
// 发送日志
|
||||
ArachnaeLog.Debug($"[ResearchManager] Research project '{project.defName}' has been removed due to loss of all research buildings.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"[ResearchManager] Failed to remove research project: {project.defName}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"[ResearchManager] Error removing research project {project.defName}: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -141,7 +235,10 @@ namespace ArachnaeSwarm
|
||||
removedCount += allReaders.RemoveAll(b =>
|
||||
b == null || b.Destroyed || !b.Spawned || b.Map == null);
|
||||
|
||||
Log.Message($"[ResearchManager] Cleaned up {removedCount} invalid buildings from allReaders");
|
||||
if (removedCount > 0)
|
||||
{
|
||||
ArachnaeLog.Debug($"[ResearchManager] Cleaned up {removedCount} invalid buildings from allReaders");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -175,9 +272,18 @@ namespace ArachnaeSwarm
|
||||
foreach (var project in projectsToRemove)
|
||||
{
|
||||
researchBuildings.Remove(project);
|
||||
|
||||
// 如果项目存在且有效,触发丢失逻辑
|
||||
if (project != null)
|
||||
{
|
||||
OnResearchLostDueToBuildingLoss(project);
|
||||
}
|
||||
}
|
||||
|
||||
Log.Message($"[ResearchManager] Cleaned up {projectsToRemove.Count} empty research projects");
|
||||
if (projectsToRemove.Count > 0)
|
||||
{
|
||||
ArachnaeLog.Debug($"[ResearchManager] Cleaned up {projectsToRemove.Count} empty research projects");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -202,7 +308,7 @@ namespace ArachnaeSwarm
|
||||
if (!allReaders.Contains(reader))
|
||||
{
|
||||
allReaders.Add(reader);
|
||||
Log.Message($"[ResearchManager] Registered reader: {reader.ThingID} at position {reader.Position}");
|
||||
ArachnaeLog.Debug($"[ResearchManager] Registered reader: {reader.ThingID} at position {reader.Position}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +331,7 @@ namespace ArachnaeSwarm
|
||||
if (!researchBuildings[project].Contains(reader))
|
||||
{
|
||||
researchBuildings[project].Add(reader);
|
||||
Log.Message($"[ResearchManager] Registered research: {project.defName} at building {reader.Position}");
|
||||
ArachnaeLog.Debug($"[ResearchManager] Registered research: {project.defName} at building {reader.Position}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,23 +342,26 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
Log.Message("[ResearchManager] Project is null, cannot process building destruction");
|
||||
ArachnaeLog.Debug("[ResearchManager] Project is null, cannot process building destruction");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Message($"[ResearchManager] Processing building destruction for project: {project.defName}");
|
||||
ArachnaeLog.Debug($"[ResearchManager] Processing building destruction for project: {project.defName}");
|
||||
|
||||
// 从列表中移除
|
||||
if (researchBuildings != null && researchBuildings.ContainsKey(project))
|
||||
{
|
||||
researchBuildings[project].Remove(building);
|
||||
Log.Message($"[ResearchManager] Removed building from project list. Remaining buildings: {researchBuildings[project].Count}");
|
||||
ArachnaeLog.Debug($"[ResearchManager] Removed building from project list. Remaining buildings: {researchBuildings[project].Count}");
|
||||
|
||||
// 检查是否还有建筑
|
||||
if (researchBuildings[project].Count == 0)
|
||||
{
|
||||
researchBuildings.Remove(project);
|
||||
Log.Message($"[ResearchManager] No buildings left for project: {project.defName}");
|
||||
ArachnaeLog.Debug($"[ResearchManager] No buildings left for project: {project.defName}");
|
||||
|
||||
// 触发科技丢失检查
|
||||
OnResearchLostDueToBuildingLoss(project);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +372,50 @@ namespace ArachnaeSwarm
|
||||
}
|
||||
}
|
||||
|
||||
// ... 其余方法保持不变 ...
|
||||
/// <summary>
|
||||
/// === 新增:获取指定科技的建筑数量 ===
|
||||
/// </summary>
|
||||
public int GetBuildingCountForResearch(ResearchProjectDef project)
|
||||
{
|
||||
if (project == null || !researchBuildings.ContainsKey(project))
|
||||
return 0;
|
||||
|
||||
return researchBuildings[project]?.Count(b => b != null && !b.Destroyed && b.Spawned) ?? 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:手动触发科技丢失检查(用于调试) ===
|
||||
/// </summary>
|
||||
public void DebugTriggerLostResearchCheck()
|
||||
{
|
||||
ArachnaeLog.Debug("[ResearchManager] Manual trigger of lost research check");
|
||||
CheckForLostResearch();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:强制移除某个科技(用于调试) ===
|
||||
/// </summary>
|
||||
public void DebugForceRemoveResearch(ResearchProjectDef project)
|
||||
{
|
||||
if (project == null)
|
||||
return;
|
||||
|
||||
ArachnaeLog.Debug($"[ResearchManager] Debug force remove research: {project.defName}");
|
||||
|
||||
// 从字典中移除
|
||||
if (researchBuildings.ContainsKey(project))
|
||||
{
|
||||
researchBuildings.Remove(project);
|
||||
}
|
||||
|
||||
// 使用ResearchRemover移除科技
|
||||
ResearchRemover.RemoveResearchProject(project, removeDependencies: false);
|
||||
|
||||
Messages.Message(
|
||||
$"Debug: Research '{project.LabelCap}' has been forcibly removed.",
|
||||
MessageTypeDefOf.NeutralEvent
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调试命令
|
||||
@@ -272,13 +424,13 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
Log.Message("[ResearchManager] No instance found");
|
||||
ArachnaeLog.Debug("[ResearchManager] No instance found");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Message("=== Research Manager Status ===");
|
||||
Log.Message($"Total buildings: {Instance.allReaders?.Count ?? 0}");
|
||||
Log.Message($"Active research projects: {Instance.researchBuildings?.Count ?? 0}");
|
||||
ArachnaeLog.Debug("=== Research Manager Status ===");
|
||||
ArachnaeLog.Debug($"Total buildings: {Instance.allReaders?.Count ?? 0}");
|
||||
ArachnaeLog.Debug($"Active research projects: {Instance.researchBuildings?.Count ?? 0}");
|
||||
|
||||
if (Instance.researchBuildings != null)
|
||||
{
|
||||
@@ -287,7 +439,7 @@ namespace ArachnaeSwarm
|
||||
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");
|
||||
ArachnaeLog.Debug($" - {kvp.Key.defName}: {activeBuildings} active buildings");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Linq;
|
||||
using Verse;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ArachnaeSwarm.Comps
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompDestroyRemovesResearch : ThingComp
|
||||
{
|
||||
@@ -70,7 +70,7 @@ namespace ArachnaeSwarm.Comps
|
||||
int removedCount = 0;
|
||||
foreach (var project in projectsToRemove)
|
||||
{
|
||||
if (Utilities.ResearchRemover.RemoveResearchProject(project, false))
|
||||
if (ResearchRemover.RemoveResearchProject(project, false))
|
||||
{
|
||||
removedCount++;
|
||||
}
|
||||
@@ -124,7 +124,7 @@ namespace ArachnaeSwarm.Comps
|
||||
Messages.Message(message, MessageTypeDefOf.NegativeEvent);
|
||||
|
||||
// 同时在日志中记录
|
||||
Log.Message($"[ResearchRemover] Building {parent.LabelCap} destroyed, removed {removedCount} research projects: " +
|
||||
ArachnaeLog.Debug($"[ResearchRemover] Building {parent.LabelCap} destroyed, removed {removedCount} research projects: " +
|
||||
string.Join(", ", projectsRemoved.Select(p => p.defName)));
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace ArachnaeSwarm.Comps
|
||||
int removedCount = 0;
|
||||
foreach (var project in projectsToRemove)
|
||||
{
|
||||
if (Utilities.ResearchRemover.RemoveResearchProject(project, false))
|
||||
if (ResearchRemover.RemoveResearchProject(project, false))
|
||||
{
|
||||
removedCount++;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm.Comps
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_DestroyRemovesResearch : CompProperties
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm.Utilities
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public static class ResearchRemover
|
||||
{
|
||||
@@ -61,7 +61,7 @@ namespace ArachnaeSwarm.Utilities
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.Message($"[ResearchRemover] Removing research project: {projectDef.defName}");
|
||||
ArachnaeLog.Debug($"[ResearchRemover] Removing research project: {projectDef.defName}");
|
||||
|
||||
// 获取字段值
|
||||
var progress = (Dictionary<ResearchProjectDef, float>)progressField.GetValue(manager);
|
||||
@@ -81,21 +81,21 @@ namespace ArachnaeSwarm.Utilities
|
||||
if (progress != null && progress.ContainsKey(projectDef))
|
||||
{
|
||||
progress.Remove(projectDef);
|
||||
Log.Message($" Removed from progress dictionary");
|
||||
ArachnaeLog.Debug($" Removed from progress dictionary");
|
||||
}
|
||||
|
||||
// 2. 从科技碎片字典中移除
|
||||
if (techprints != null && techprints.ContainsKey(projectDef))
|
||||
{
|
||||
techprints.Remove(projectDef);
|
||||
Log.Message($" Removed from techprints dictionary");
|
||||
ArachnaeLog.Debug($" Removed from techprints dictionary");
|
||||
}
|
||||
|
||||
// 3. 从异常知识字典中移除
|
||||
if (anomalyKnowledge != null && anomalyKnowledge.ContainsKey(projectDef))
|
||||
{
|
||||
anomalyKnowledge.Remove(projectDef);
|
||||
Log.Message($" Removed from anomalyKnowledge dictionary");
|
||||
ArachnaeLog.Debug($" Removed from anomalyKnowledge dictionary");
|
||||
}
|
||||
|
||||
// 4. 如果这是当前项目,停止它
|
||||
@@ -103,7 +103,7 @@ namespace ArachnaeSwarm.Utilities
|
||||
{
|
||||
manager.StopProject(projectDef);
|
||||
currentProjField.SetValue(manager, null);
|
||||
Log.Message($" Stopped current project");
|
||||
ArachnaeLog.Debug($" Stopped current project");
|
||||
}
|
||||
|
||||
// 5. 从异常知识项目中移除
|
||||
@@ -119,7 +119,7 @@ namespace ArachnaeSwarm.Utilities
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
if (removed) Log.Message($" Removed from anomaly knowledge projects");
|
||||
if (removed) ArachnaeLog.Debug($" Removed from anomaly knowledge projects");
|
||||
}
|
||||
|
||||
// 6. 如果设置了移除依赖项,递归移除依赖于此科技的项目
|
||||
@@ -132,7 +132,7 @@ namespace ArachnaeSwarm.Utilities
|
||||
// 7. 重新应用所有mod(取消该科技的效果)
|
||||
manager.ReapplyAllMods();
|
||||
|
||||
Log.Message($"[ResearchRemover] Successfully removed research project: {projectDef.defName}");
|
||||
ArachnaeLog.Debug($"[ResearchRemover] Successfully removed research project: {projectDef.defName}");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -184,7 +184,7 @@ namespace ArachnaeSwarm.Utilities
|
||||
|
||||
foreach (var dependent in dependentProjects)
|
||||
{
|
||||
Log.Message($" Removing dependent project: {dependent.defName}");
|
||||
ArachnaeLog.Debug($" Removing dependent project: {dependent.defName}");
|
||||
|
||||
// 递归移除依赖项
|
||||
RemoveDependentProjects(dependent, progress, techprints, anomalyKnowledge,
|
||||
@@ -246,7 +246,7 @@ namespace ArachnaeSwarm.Utilities
|
||||
.Where(p => p.IsFinished)
|
||||
.ToList();
|
||||
|
||||
Log.Message($"[ResearchRemover] Removing all {allFinishedProjects.Count} finished research projects");
|
||||
ArachnaeLog.Debug($"[ResearchRemover] Removing all {allFinishedProjects.Count} finished research projects");
|
||||
|
||||
// 批量移除所有科技
|
||||
RemoveMultipleProjects(allFinishedProjects, false);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using RimWorld;
|
||||
// File: CompHediffTerrainSpawn.cs
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using Verse.Sound;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
@@ -13,6 +16,11 @@ namespace ArachnaeSwarm
|
||||
|
||||
private int ticksUntilNextSpawn;
|
||||
private bool initialized = false;
|
||||
|
||||
// === 新增:缓存数据 ===
|
||||
private int lastCellCount = 0;
|
||||
private float lastSpawnTime = 0f;
|
||||
private List<IntVec3> affectedCells = new List<IntVec3>();
|
||||
|
||||
public override void CompPostTick(ref float severityAdjustment)
|
||||
{
|
||||
@@ -55,6 +63,14 @@ namespace ArachnaeSwarm
|
||||
|
||||
if (Props.onlyWhenMoving && (parent.pawn.pather == null || !parent.pawn.pather.Moving))
|
||||
return false;
|
||||
|
||||
// === 新增:检查是否在Creep上 ===
|
||||
if (Props.onlyWhenOnCreep)
|
||||
{
|
||||
var terrain = parent.pawn.Map.terrainGrid.TerrainAt(parent.pawn.Position);
|
||||
if (terrain == null || terrain.tags == null || !terrain.tags.Contains("ARA_Creep"))
|
||||
return false;
|
||||
}
|
||||
|
||||
// 确保pawn在地图内
|
||||
if (!parent.pawn.Position.InBounds(parent.pawn.Map))
|
||||
@@ -72,20 +88,59 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
Map map = parent.pawn.Map;
|
||||
IntVec3 center = parent.pawn.Position;
|
||||
|
||||
// 清空受影响单元格列表
|
||||
affectedCells.Clear();
|
||||
int spawnedCount = 0;
|
||||
|
||||
foreach (IntVec3 current in GenRadial.RadialCellsAround(center, Props.spawnRadius, true))
|
||||
{
|
||||
if (current.InBounds(map) && current.Walkable(map))
|
||||
if (!current.InBounds(map) || !current.Walkable(map))
|
||||
continue;
|
||||
|
||||
// === 新增:检查是否被占用 ===
|
||||
if (Props.ignoreOccupiedCells && IsCellOccupied(current, map))
|
||||
continue;
|
||||
|
||||
TerrainDef currentTerrain = map.terrainGrid.TerrainAt(current);
|
||||
if (currentTerrain == null)
|
||||
continue;
|
||||
|
||||
// === 新增:使用黑名单检查 ===
|
||||
if (ShouldSkipTerrain(currentTerrain, current, map))
|
||||
{
|
||||
// 检查当前地形是否有 ARA_Creep 标签
|
||||
TerrainDef currentTerrain = map.terrainGrid.TerrainAt(current);
|
||||
if (currentTerrain != null && HasCreepTag(currentTerrain))
|
||||
{
|
||||
continue; // 跳过有 ARA_Creep 标签的地面
|
||||
}
|
||||
|
||||
map.terrainGrid.SetTerrain(current, Props.terrainToSpawn);
|
||||
continue; // 跳过有排除tag的地面
|
||||
}
|
||||
// === 新增:检查是否只影响自己的Creep ===
|
||||
if (Props.affectOwnCreepOnly && currentTerrain.tags != null &&
|
||||
currentTerrain.tags.Contains("ARA_Creep"))
|
||||
{
|
||||
// 这里可以添加派系检查逻辑
|
||||
// 例如,只覆盖自己派系的Creep
|
||||
}
|
||||
|
||||
// 应用地形变化
|
||||
map.terrainGrid.SetTerrain(current, Props.terrainToSpawn);
|
||||
affectedCells.Add(current);
|
||||
spawnedCount++;
|
||||
|
||||
// === 新增:播放效果 ===
|
||||
TryPlayEffects(current, map, spawnedCount);
|
||||
}
|
||||
|
||||
lastCellCount = spawnedCount;
|
||||
lastSpawnTime = Find.TickManager.TicksGame;
|
||||
|
||||
// 播放声音
|
||||
if (Props.spawnSound != null && Rand.Chance(Props.soundChance))
|
||||
{
|
||||
Props.spawnSound.PlayOneShot(new TargetInfo(center, map));
|
||||
}
|
||||
|
||||
// 调试日志
|
||||
if (spawnedCount > 0)
|
||||
{
|
||||
ArachnaeLog.Debug($"[HediffTerrainSpawn] Spawned {spawnedCount} terrain cells for {parent.pawn.LabelShort}");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
@@ -93,13 +148,135 @@ namespace ArachnaeSwarm
|
||||
ArachnaeLog.Debug($"Error in CompHediffTerrainSpawn.DoTerrainSpawn: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:检查单元格是否被占用 ===
|
||||
/// </summary>
|
||||
private bool IsCellOccupied(IntVec3 cell, Map map)
|
||||
{
|
||||
var thingList = cell.GetThingList(map);
|
||||
foreach (var thing in thingList)
|
||||
{
|
||||
// 忽略建筑和植物
|
||||
if (thing is Building || thing is Plant)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 忽略有生命的生物
|
||||
if (thing is Pawn pawn && !pawn.Dead)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查地形是否具有 ARA_Creep 标签
|
||||
/// === 新增:检查是否应该跳过此地形 ===
|
||||
/// 基于tag黑名单、允许性和其他条件
|
||||
/// </summary>
|
||||
private bool HasCreepTag(TerrainDef terrain)
|
||||
private bool ShouldSkipTerrain(TerrainDef terrain, IntVec3 cell, Map map)
|
||||
{
|
||||
return terrain.tags != null && terrain.tags.Contains("ARA_Creep");
|
||||
if (terrain == null)
|
||||
return false;
|
||||
|
||||
// 1. 检查tag黑名单
|
||||
if (Props.IsTerrainExcluded(terrain))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. 检查允许性(如果启用)
|
||||
if (Props.checkAffordances && terrain.affordances != null && Props.excludeAffordances != null)
|
||||
{
|
||||
foreach (var affordance in terrain.affordances)
|
||||
{
|
||||
if (affordance != null && Props.excludeAffordances.Contains(affordance.defName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 检查优先地形(如果启用智能覆盖)
|
||||
if (Props.smartOverlay && Props.IsPreferredTerrain(terrain))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. 检查路径(如果启用路径保护)
|
||||
if (Props.preservePaths)
|
||||
{
|
||||
if (IsPathCell(cell, map))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 检查是否是水或其他特殊地形
|
||||
if (terrain.IsWater || terrain.defName.Contains("Water") || terrain.defName.Contains("Marsh"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:检查是否应该覆盖此地形(智能覆盖模式) ===
|
||||
/// </summary>
|
||||
private bool ShouldOverlayTerrain(TerrainDef terrain)
|
||||
{
|
||||
if (terrain == null)
|
||||
return false;
|
||||
|
||||
// 默认逻辑:仅覆盖"较差"的地形
|
||||
|
||||
// 如果地形已经有我们要生成的类型,跳过
|
||||
if (terrain == Props.terrainToSpawn)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否是肥沃土壤
|
||||
if (terrain.fertility > 0.5f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:检查是否是路径单元格 ===
|
||||
/// </summary>
|
||||
private bool IsPathCell(IntVec3 cell, Map map)
|
||||
{
|
||||
// 检查是否有路径标记
|
||||
// 这里可以根据需要扩展,检查设计者路径标记等
|
||||
// 暂时返回false,因为需要更多信息
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:尝试播放效果 ===
|
||||
/// </summary>
|
||||
private void TryPlayEffects(IntVec3 cell, Map map, int spawnedCount)
|
||||
{
|
||||
if (Props.spawnEffecter == null || !Rand.Chance(Props.effectChance))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var effecter = Props.spawnEffecter.Spawn();
|
||||
effecter.Trigger(new TargetInfo(cell, map), new TargetInfo(cell, map));
|
||||
effecter.Cleanup();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
ArachnaeLog.Debug($"Error playing effect at {cell}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public override void CompExposeData()
|
||||
@@ -107,6 +284,8 @@ namespace ArachnaeSwarm
|
||||
base.CompExposeData();
|
||||
Scribe_Values.Look(ref ticksUntilNextSpawn, "ticksUntilNextSpawn", Props.intervalTicks);
|
||||
Scribe_Values.Look(ref initialized, "initialized", false);
|
||||
Scribe_Values.Look(ref lastCellCount, "lastCellCount", 0);
|
||||
Scribe_Values.Look(ref lastSpawnTime, "lastSpawnTime", 0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -120,7 +299,28 @@ namespace ArachnaeSwarm
|
||||
return $"Next spawn in: {ticksUntilNextSpawn} ticks\n" +
|
||||
$"Interval: {Props.intervalTicks} ticks\n" +
|
||||
$"Radius: {Props.spawnRadius}\n" +
|
||||
$"Terrain: {Props.terrainToSpawn?.defName ?? "None"}";
|
||||
$"Terrain: {Props.terrainToSpawn?.defName ?? "None"}\n" +
|
||||
$"Last spawned: {lastCellCount} cells at tick {lastSpawnTime}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:获取受影响的单元格(用于调试) ===
|
||||
/// </summary>
|
||||
public List<IntVec3> GetAffectedCells()
|
||||
{
|
||||
return new List<IntVec3>(affectedCells);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:获取黑名单信息 ===
|
||||
/// </summary>
|
||||
public string GetBlacklistInfo()
|
||||
{
|
||||
var excludedTags = Props.GetExcludedTags();
|
||||
if (excludedTags.Count == 0)
|
||||
return "No tags excluded";
|
||||
|
||||
return $"Excluded tags: {string.Join(", ", excludedTags)}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// File: CompProperties_HediffTerrainSpawn.cs
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
@@ -16,9 +18,95 @@ namespace ArachnaeSwarm
|
||||
public bool onlyWhenDowned = false;
|
||||
public bool onlyWhenMoving = false;
|
||||
|
||||
// === 新增:tag黑名单 ===
|
||||
public List<string> excludedTerrainTags = null; // 要排除的tag列表
|
||||
public bool useDefaultExclusions = true; // 是否使用默认排除项(ARA_Creep和ARA_Incubator_Nutrient_Solution)
|
||||
|
||||
// === 新增:扩展检查选项 ===
|
||||
public bool checkAffordances = false; // 是否检查地形允许性(affordances)
|
||||
public List<string> excludeAffordances = null; // 要排除的允许性列表
|
||||
|
||||
// === 新增:智能覆盖选项 ===
|
||||
public bool smartOverlay = false; // 是否智能覆盖(仅覆盖"较差"的地形)
|
||||
public List<TerrainDef> preferredTerrains = null; // 优先地形列表,这些不会被覆盖
|
||||
public bool preservePaths = false; // 是否保留路径
|
||||
|
||||
// === 新增:行为选项 ===
|
||||
public bool onlyWhenOnCreep = false; // 是否只在Creep上生效
|
||||
public bool ignoreOccupiedCells = true; // 是否忽略被占用的单元格
|
||||
public bool affectOwnCreepOnly = false; // 是否只影响自己的Creep(如果有派系)
|
||||
|
||||
// === 新增:视觉效果选项 ===
|
||||
public EffecterDef spawnEffecter = null; // 生成地形时的效果
|
||||
public float effectChance = 0.3f; // 效果播放几率
|
||||
public SoundDef spawnSound = null; // 生成地形时的声音
|
||||
public float soundChance = 0.1f; // 声音播放几率
|
||||
|
||||
public CompProperties_HediffTerrainSpawn()
|
||||
{
|
||||
compClass = typeof(CompHediffTerrainSpawn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取要排除的tag列表(包括默认值)
|
||||
/// </summary>
|
||||
public List<string> GetExcludedTags()
|
||||
{
|
||||
var tags = new List<string>();
|
||||
|
||||
// 如果使用默认排除项,添加默认tag
|
||||
if (useDefaultExclusions)
|
||||
{
|
||||
if (!tags.Contains("ARA_Creep"))
|
||||
tags.Add("ARA_Creep");
|
||||
if (!tags.Contains("ARA_Incubator_Nutrient_Solution"))
|
||||
tags.Add("ARA_Incubator_Nutrient_Solution");
|
||||
}
|
||||
|
||||
// 添加自定义排除项
|
||||
if (excludedTerrainTags != null)
|
||||
{
|
||||
foreach (var tag in excludedTerrainTags)
|
||||
{
|
||||
if (!tags.Contains(tag))
|
||||
tags.Add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查地形是否被排除
|
||||
/// </summary>
|
||||
public bool IsTerrainExcluded(TerrainDef terrain)
|
||||
{
|
||||
if (terrain == null || terrain.tags == null)
|
||||
return false;
|
||||
|
||||
var excludedTags = GetExcludedTags();
|
||||
if (excludedTags.Count == 0)
|
||||
return false;
|
||||
|
||||
// 检查地形是否有排除tag
|
||||
foreach (var tag in terrain.tags)
|
||||
{
|
||||
if (excludedTags.Contains(tag))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查地形是否在优先列表中(智能覆盖)
|
||||
/// </summary>
|
||||
public bool IsPreferredTerrain(TerrainDef terrain)
|
||||
{
|
||||
if (terrain == null || preferredTerrains == null)
|
||||
return false;
|
||||
|
||||
return preferredTerrains.Contains(terrain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using RimWorld;
|
||||
@@ -44,10 +44,53 @@ namespace ArachnaeSwarm
|
||||
if (!Props.allowDuplicates && pawn.health.hediffSet.HasHediff(hediffDef))
|
||||
continue;
|
||||
|
||||
// === 新增:获取应应用的部位 ===
|
||||
BodyPartDef bodyPartDef = Props.GetBodyPartForHediff(hediffDef);
|
||||
BodyPartRecord bodyPartRecord = null;
|
||||
|
||||
if (bodyPartDef != null)
|
||||
{
|
||||
bodyPartRecord = GetFirstMatchingBodyPart(pawn, bodyPartDef);
|
||||
}
|
||||
|
||||
// 添加hediff
|
||||
pawn.health.AddHediff(hediffDef);
|
||||
if (bodyPartRecord != null)
|
||||
{
|
||||
pawn.health.AddHediff(hediffDef, bodyPartRecord);
|
||||
}
|
||||
else
|
||||
{
|
||||
pawn.health.AddHediff(hediffDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取第一个匹配的身体部位记录
|
||||
/// </summary>
|
||||
private BodyPartRecord GetFirstMatchingBodyPart(Pawn pawn, BodyPartDef bodyPartDef)
|
||||
{
|
||||
if (pawn == null || bodyPartDef == null || pawn.RaceProps?.body == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// 获取所有匹配的身体部位
|
||||
List<BodyPartRecord> matchingParts = pawn.RaceProps.body.GetPartsWithDef(bodyPartDef);
|
||||
|
||||
if (matchingParts != null && matchingParts.Count > 0)
|
||||
{
|
||||
// 返回第一个可用的部位
|
||||
return matchingParts[0];
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ArachnaeLog.Debug($"Error getting body part for {bodyPartDef.defName}: {ex.Message}");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 新增:序列化hediffsApplied标记
|
||||
public override void PostExposeData()
|
||||
@@ -66,5 +109,33 @@ namespace ArachnaeSwarm
|
||||
ArachnaeLog.Debug($"Debug: Applied hediffs to {pawn.Label}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// === 新增:获取已应用的hediff信息(用于调试) ===
|
||||
/// </summary>
|
||||
public string GetAppliedHediffInfo()
|
||||
{
|
||||
if (!hediffsApplied || !(this.parent is Pawn pawn))
|
||||
return "No hediffs applied";
|
||||
|
||||
var result = new System.Text.StringBuilder();
|
||||
result.AppendLine("Applied hediffs:");
|
||||
|
||||
foreach (var hediffDef in Props.hediffs)
|
||||
{
|
||||
var hediff = pawn.health.hediffSet.GetFirstHediffOfDef(hediffDef);
|
||||
if (hediff != null)
|
||||
{
|
||||
string partInfo = hediff.Part?.def?.defName ?? "No specific part";
|
||||
result.AppendLine($"- {hediffDef.defName} on {partInfo}");
|
||||
}
|
||||
else
|
||||
{
|
||||
result.AppendLine($"- {hediffDef.defName} (not applied)");
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
@@ -14,10 +13,39 @@ namespace ArachnaeSwarm
|
||||
|
||||
// 是否允许重复添加相同的hediff
|
||||
public bool allowDuplicates = false;
|
||||
|
||||
// === 新增:优先应用部位设置 ===
|
||||
public bool useDefaultInstallPart = true; // 是否使用HediffDef的defaultInstallPart
|
||||
|
||||
// === 新增:自定义部位映射 ===
|
||||
public Dictionary<HediffDef, BodyPartDef> customBodyPartMapping = null;
|
||||
|
||||
public CompProperties_HediffGiver()
|
||||
{
|
||||
this.compClass = typeof(CompHediffGiver);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取Hediff应该应用的部位
|
||||
/// </summary>
|
||||
public BodyPartDef GetBodyPartForHediff(HediffDef hediffDef)
|
||||
{
|
||||
if (hediffDef == null)
|
||||
return null;
|
||||
|
||||
// 首先检查自定义映射
|
||||
if (customBodyPartMapping != null && customBodyPartMapping.ContainsKey(hediffDef))
|
||||
{
|
||||
return customBodyPartMapping[hediffDef];
|
||||
}
|
||||
|
||||
// 然后检查是否使用默认安装部位
|
||||
if (useDefaultInstallPart && hediffDef.defaultInstallPart != null)
|
||||
{
|
||||
return hediffDef.defaultInstallPart;
|
||||
}
|
||||
|
||||
return null; // 没有指定部位
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace ArachnaeSwarm
|
||||
}
|
||||
|
||||
// 智能溅射:次要目标的敌对状态必须与主目标一致
|
||||
if (secondaryTargetPawn.HostileTo(casterPawn))
|
||||
if (secondaryTargetPawn.Faction == casterPawn.Faction)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user