diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index 08dc8cc..40f289a 100644
Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ
diff --git a/Source/ArachnaeSwarm/Abilities/ARA_LaunchMultiProjectile/CompAbilityEffect_LaunchMultiProjectile.cs b/Source/ArachnaeSwarm/Abilities/ARA_LaunchMultiProjectile/CompAbilityEffect_LaunchMultiProjectile.cs
index 51766e4..8aa7503 100644
--- a/Source/ArachnaeSwarm/Abilities/ARA_LaunchMultiProjectile/CompAbilityEffect_LaunchMultiProjectile.cs
+++ b/Source/ArachnaeSwarm/Abilities/ARA_LaunchMultiProjectile/CompAbilityEffect_LaunchMultiProjectile.cs
@@ -13,7 +13,6 @@ namespace ArachnaeSwarm
private int nextProjectileTick;
private int projectilesFired;
private IntVec3? targetCell;
- private bool parametersInitialized;
// 缓存当前状态的参数
private int currentNumProjectiles;
@@ -139,17 +138,20 @@ namespace ArachnaeSwarm
///
private void InitializeParameters()
{
- if (parametersInitialized)
- return;
-
var state = GetCurrentState();
currentNumProjectiles = state?.numProjectiles ?? Props.numProjectiles;
currentProjectileDef = state?.projectileDef ?? Props.projectileDef;
currentOffsetRadius = state?.offsetRadius ?? Props.offsetRadius;
currentShotIntervalTicks = state?.shotIntervalTicks ?? Props.shotIntervalTicks;
-
- parametersInitialized = true;
+ }
+
+ ///
+ /// 强制重新初始化参数(当状态变化时调用)
+ ///
+ public void ForceReinitialize()
+ {
+ InitializeParameters();
}
///
@@ -182,7 +184,31 @@ namespace ArachnaeSwarm
return;
Pawn pawn = parent.pawn;
+
+ // 检查目标有效性(目标可能已移动或消失)
LocalTargetInfo finalTarget = target;
+ if (Props.useSustainedJob && target.Pawn != null)
+ {
+ // 如果是持续模式且有目标Pawn,尝试追踪目标
+ if (target.Pawn.Dead || !target.Pawn.Spawned)
+ {
+ // 目标已死亡或消失,使用最后已知位置
+ if (targetCell.HasValue)
+ {
+ finalTarget = new LocalTargetInfo(targetCell.Value);
+ }
+ else
+ {
+ return; // 无法发射
+ }
+ }
+ else
+ {
+ // 更新目标位置
+ targetCell = target.Pawn.Position;
+ finalTarget = target;
+ }
+ }
// 如果有偏移范围,计算偏移后的目标
if (Props.useRandomOffset && currentOffsetRadius > 0)
@@ -205,7 +231,6 @@ namespace ArachnaeSwarm
nextProjectileTick = 0;
projectilesFired = 0;
targetCell = null;
- parametersInitialized = false;
}
///
diff --git a/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/HediffComp_GestaltNode.cs b/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/HediffComp_GestaltNode.cs
index 6b91cb1..6a612d2 100644
--- a/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/HediffComp_GestaltNode.cs
+++ b/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/HediffComp_GestaltNode.cs
@@ -144,7 +144,7 @@ namespace ArachnaeSwarm
return comp.GestaltTracker;
}
- if (tracker == null && NodeType == GestaltNodeType.OverlordNode)
+ if (tracker == null && NodeType == GestaltNodeType.OverlordNode && pawn != null)
{
tracker = new Pawn_GestaltTracker(pawn);
}
@@ -247,7 +247,7 @@ namespace ArachnaeSwarm
TryFindOverlord();
}
- Log.Message($"[GestaltNode] {pawn.LabelShort} (HiveNode) 延迟初始化完成,当前连接状态: {IsConnectedToOverlord()}");
+ ArachnaeLog.Debug($"[GestaltNode] {pawn.LabelShort} (HiveNode) 延迟初始化完成,当前连接状态: {IsConnectedToOverlord()}");
}
}
@@ -256,6 +256,15 @@ namespace ArachnaeSwarm
///
private void UpdateTransitionState()
{
+ // 检查Pawn是否死亡或无效
+ var pawn = Pawn;
+ if (pawn == null || pawn.Dead)
+ {
+ isTransitioning = false;
+ transitionStartTick = -1;
+ return;
+ }
+
if (!isTransitioning || transitionStartTick < 0)
return;
@@ -267,14 +276,13 @@ namespace ArachnaeSwarm
{
parent.Severity = targetSeverityAfterTransition;
- // 通知Pawn状态改变
- var pawn = Pawn;
+ // 通知Pawn状态改变(使用方法开头声明的pawn变量)
if (pawn?.health != null)
{
pawn.health.Notify_HediffChanged(parent);
}
- Log.Message($"[GestaltNode] {Pawn?.LabelShort} 过渡完成,严重性: {targetSeverityAfterTransition}");
+ ArachnaeLog.Debug($"[GestaltNode] {pawn?.LabelShort} 过渡完成,严重性: {targetSeverityAfterTransition}");
}
// 重置过渡状态
@@ -316,7 +324,7 @@ namespace ArachnaeSwarm
}
}
- Log.Message($"[GestaltNode] {Pawn?.LabelShort} 开始过渡,目标: {(isConnecting ? "连接" : "断开")} ({targetSeverityAfterTransition})");
+ ArachnaeLog.Debug($"[GestaltNode] {Pawn?.LabelShort} 开始过渡,目标: {(isConnecting ? "连接" : "断开")} ({targetSeverityAfterTransition})");
}
///
@@ -434,7 +442,7 @@ namespace ArachnaeSwarm
}
UpdateSeverityBasedOnConnection();
- Log.Message($"[GestaltNode] {pawn.LabelShort} 连接到 Overlord: {bestOverlord.LabelShort}");
+ ArachnaeLog.Debug($"[GestaltNode] {pawn.LabelShort} 连接到 Overlord: {bestOverlord.LabelShort}");
}
}
@@ -466,6 +474,10 @@ namespace ArachnaeSwarm
{
base.CompPostPostRemoved();
+ // 清理过渡状态
+ isTransitioning = false;
+ transitionStartTick = -1;
+
// 当Hediff被移除时,如果连接到Overlord,需要断开连接
if (NodeType == GestaltNodeType.HiveNode && IsConnectedToOverlord())
{
@@ -483,6 +495,20 @@ namespace ArachnaeSwarm
}
}
+ ///
+ /// Pawn 死亡时的处理
+ ///
+ public override void Notify_PawnDied(DamageInfo? dinfo, Hediff culprit = null)
+ {
+ base.Notify_PawnDied(dinfo, culprit);
+
+ // 清理过渡状态
+ isTransitioning = false;
+ transitionStartTick = -1;
+
+ ArachnaeLog.Debug($"[GestaltNode] {Pawn?.LabelShort} 死亡,清理过渡状态");
+ }
+
///
/// 序列化
///
diff --git a/Source/ArachnaeSwarm/Hediffs/ARA_Spawner/HediffComp_Spawner.cs b/Source/ArachnaeSwarm/Hediffs/ARA_Spawner/HediffComp_Spawner.cs
index 16079ec..3db7c6f 100644
--- a/Source/ArachnaeSwarm/Hediffs/ARA_Spawner/HediffComp_Spawner.cs
+++ b/Source/ArachnaeSwarm/Hediffs/ARA_Spawner/HediffComp_Spawner.cs
@@ -350,7 +350,7 @@ namespace ArachnaeSwarm
if (this.Props.animalThing)
{
- // 鍔ㄧ墿鐢熸垚閫昏緫淇濇寔涓嶅彉
+ // 动物生成逻辑保持不变
if (this.Props.spawnMaxAdjacent > 0 && pawn.Map.mapPawns.AllPawns.Where(delegate(Pawn mP)
{
ThingDef defToCompare = this.Props.animalThing ? this.Props.animalToSpawn?.race : this.Props.thingToSpawn;
@@ -402,10 +402,10 @@ namespace ArachnaeSwarm
}
else
{
- // 閲嶆柊璁捐鐗╁搧鐢熸垚閫昏緫锛氭寜浼樺厛绾ч『搴忓皾璇?
+ // 重新设计物品生成逻辑:按优先级顺序尝试
bool success = TrySpawnItemWithPriority(pawn);
- // === 鏂板锛氬鏋滈厤缃簡閿€姣侀殢鏈洪儴浣嶏紝鍦ㄦ垚鍔熺敓鎴愮墿鍝佸悗鎵ц ===
+ // === 新增:如果配置了销毁随机部位,在成功生成物品后执行 ===
if (success && this.Props.destroyRandomBodyPart)
{
DestroyRandomBodyPart(pawn);
@@ -415,7 +415,7 @@ namespace ArachnaeSwarm
}
}
- // 鏂板锛氶攢姣侀殢鏈鸿韩浣撻儴浣嶏紙鍙傝€?CompAbilityEffect_DestroyOwnBodyPart锛?
+ // 新增:销毁随机身体部位(参考 CompAbilityEffect_DestroyOwnBodyPart)
private void DestroyRandomBodyPart(Pawn pawn)
{
try
@@ -426,7 +426,7 @@ namespace ArachnaeSwarm
return;
}
- // 鑾峰彇鎵€鏈夊彲浠ラ攢姣佺殑韬綋閮ㄤ綅
+ // 获取所有可以销毁的身体部位
List possibleParts = GetDestroyableBodyParts(pawn);
if (possibleParts.Count == 0)
@@ -435,7 +435,7 @@ namespace ArachnaeSwarm
return;
}
- // 闅忔満閫夋嫨涓€涓儴浣?
+ // 随机选择一个部位
BodyPartRecord partToDestroy = possibleParts.RandomElement();
if (partToDestroy == null)
@@ -444,10 +444,10 @@ namespace ArachnaeSwarm
return;
}
- // 璁板綍閮ㄤ綅鍚嶇О
- string partName = partToDestroy.def?.label ?? "鏈煡閮ㄤ綅";
+ // 记录部位名称
+ string partName = partToDestroy.def?.label ?? "未知部位";
- // 娣诲姞缂哄け閮ㄤ綅hediff
+ // 添加缺失部位hediff
pawn.health.AddHediff(HediffDefOf.MissingBodyPart, partToDestroy);
Warn($"Destroyed {partName} on {pawn.LabelShort}", this.myDebug);
@@ -458,7 +458,7 @@ namespace ArachnaeSwarm
}
}
- // 鏂板锛氳幏鍙栧彲浠ラ攢姣佺殑韬綋閮ㄤ綅鍒楄〃
+ // 新增:获取可以销毁的身体部位列表
private List GetDestroyableBodyParts(Pawn pawn)
{
List destroyableParts = new List();
@@ -466,20 +466,20 @@ namespace ArachnaeSwarm
if (pawn?.health?.hediffSet == null)
return destroyableParts;
- // 鑾峰彇鎵€鏈夎韩浣撻儴浣?
+ // 获取所有身体部位
List allParts = pawn.RaceProps.body.AllParts;
foreach (BodyPartRecord part in allParts)
{
- // 鎺掗櫎鏍稿績閮ㄤ綅锛堥伩鍏嶆浜★級
+ // 排除核心部位(避免死亡)
if (IsCriticalBodyPart(part))
continue;
- // 鎺掗櫎宸茬粡缂哄け鐨勯儴浣?
+ // 排除已经缺失的部位
if (pawn.health.hediffSet.PartIsMissing(part))
continue;
- // 鎺掗櫎宸茬粡鏈変弗閲嶄激瀹崇殑閮ㄤ綅锛堝彲閫夛級
+ // 排除已经有严重伤害的部位(可选)
if (pawn.health.hediffSet.PartOrAnyAncestorHasDirectlyAddedParts(part))
continue;
@@ -492,34 +492,34 @@ namespace ArachnaeSwarm
return destroyableParts;
}
- // 鏂板锛氭鏌ユ槸鍚︽槸鍏抽敭韬綋閮ㄤ綅
+ // 新增:检查是否是关键身体部位
private bool IsCriticalBodyPart(BodyPartRecord part)
{
if (part == null)
return false;
- // 鏍规嵁鏍囩鍒ゆ柇鏄惁涓哄叧閿儴浣?
+ // 根据标签判断是否为关键部位
if (part.def.tags != null)
{
- // 杩欎簺鏍囩閫氬父琛ㄧず鍏抽敭閮ㄤ綅
- if (part.def.tags.Contains(BodyPartTagDefOf.ConsciousnessSource) || // 鎰忚瘑婧愶紙澶ц剳锛?
- part.def.tags.Contains(BodyPartTagDefOf.BloodPumpingSource) || // 琛€娑叉车婧愶紙蹇冭剰锛?
- part.def.tags.Contains(BodyPartTagDefOf.BreathingSource) || // 鍛煎惛婧愶紙鑲猴級
- part.def.tags.Contains(BodyPartTagDefOf.MetabolismSource) // 浠h阿婧愶紙鑲濊剰锛?
+ // 这些标签通常表示关键部位
+ if (part.def.tags.Contains(BodyPartTagDefOf.ConsciousnessSource) || // 意识源(大脑)
+ part.def.tags.Contains(BodyPartTagDefOf.BloodPumpingSource) || // 血液泵源(心脏)
+ part.def.tags.Contains(BodyPartTagDefOf.BreathingSource) || // 呼吸源(肺)
+ part.def.tags.Contains(BodyPartTagDefOf.MetabolismSource) // 代谢源(肝脏)
)
{
return true;
}
}
- // 鏍规嵁娣卞害鍒ゆ柇锛堟繁搴﹁緝娣辩殑閫氬父鏄唴閮ㄥ櫒瀹橈級
+ // 根据深度判断(深度较深的通常是内部器官)
if (part.depth == BodyPartDepth.Inside && part.parent == null)
return true;
return false;
}
- // 鏂板锛氭寜浼樺厛绾ч『搴忓皾璇曠敓鎴愮墿鍝?
+ // 新增:按优先级顺序尝试生成物品
private bool TrySpawnItemWithPriority(Pawn pawn)
{
Thing thing = ThingMaker.MakeThing(this.Props.thingToSpawn, null);
@@ -739,19 +739,19 @@ namespace ArachnaeSwarm
}
else
{
- // 淇敼杩欓噷锛氬皢鍗婂緞浠?鍑忓皯鍒?锛岃鐢熸垚浣嶇疆鏇撮潬杩憄awn
+ // 修改这里:将半径从3减少到2,让生成位置更靠近Pawn
int searchRadius = 2;
- // 棣栧厛灏濊瘯鍦╬awn鐨勭浉閭诲崟鍏冩牸鐢熸垚锛堝崐寰勪负1锛?
+ // 首先尝试在Pawn的相邻单元格生成(半径为1)
result = CellFinder.RandomClosewalkCellNear(this.pawn.Position, map, 1, null);
- // 濡傛灉鐩搁偦鍗曞厓鏍兼壘涓嶅埌鍚堥€備綅缃紝鍐嶅皾璇曠◢杩滀竴鐐癸紙鍗婂緞涓?锛?
+ // 如果相邻单元格找不到合适位置,再尝试稍远一点(半径为2)
if (!result.IsValid)
{
result = CellFinder.RandomClosewalkCellNear(this.pawn.Position, map, searchRadius, null);
}
- // 濡傛灉杩樻槸鎵句笉鍒帮紝灏濊瘯pawn褰撳墠浣嶇疆锛堜綔涓烘渶鍚庢墜娈碉級
+ // 如果还是找不到,尝试Pawn当前位置(作为最后手段)
if (!result.IsValid && this.pawn.Position.IsValid && this.pawn.Position.Walkable(map))
{
result = this.pawn.Position;
@@ -848,7 +848,7 @@ namespace ArachnaeSwarm
}
}
- // === 鏁村悎鐨?Tools 鏂规硶 ===
+ // === 整合的 Tools 方法 ===
private void DestroyParentHediff(Hediff parentHediff, bool debug = false)
{
@@ -968,7 +968,7 @@ namespace ArachnaeSwarm
}
private void Warn(string warning, bool debug = false)
{
- if (debug)
+ if (debug && Prefs.DevMode)
{
Log.Message($"[HediffComp_Spawner] {warning}");
}
@@ -990,4 +990,3 @@ namespace ArachnaeSwarm
private readonly int errorSpawnCount = 750;
}
}
-
diff --git a/Source/ArachnaeSwarm/Pawn_Comps/ARA_CompHediffGiver/CompHediffGiver.cs b/Source/ArachnaeSwarm/Pawn_Comps/ARA_CompHediffGiver/CompHediffGiver.cs
index d323029..2420fae 100644
--- a/Source/ArachnaeSwarm/Pawn_Comps/ARA_CompHediffGiver/CompHediffGiver.cs
+++ b/Source/ArachnaeSwarm/Pawn_Comps/ARA_CompHediffGiver/CompHediffGiver.cs
@@ -117,8 +117,9 @@ namespace ArachnaeSwarm
ArachnaeLog.Debug($"Added hediff {hediffDef.defName} to {pawn.Label} " +
$"{(bodyPart != null ? $"on {bodyPart.def.defName}" : "to whole body")}");
}
- catch (Exception)
+ catch (Exception ex)
{
+ Log.Warning($"[CompHediffGiver] Failed to add hediff {hediffDef?.defName ?? "null"} to {pawn?.Label ?? "null"}: {ex.Message}");
}
}
@@ -252,27 +253,50 @@ namespace ArachnaeSwarm
///
private bool IsSymmetricalPart(BodyPartRecord part1, BodyPartRecord part2)
{
- // 简单的对称检测逻辑
+ // 优先使用 BodyPartDef 的对称标签检测
+ if (part1.def == part2.def)
+ {
+ // 检查是否有对称标签
+ if (part1.def.tags != null)
+ {
+ // 使用 BodyPartTagDef 进行对称检测
+ bool part1Left = part1.def.tags.Any(t => t.defName.Contains("Left"));
+ bool part1Right = part1.def.tags.Any(t => t.defName.Contains("Right"));
+ bool part2Left = part2.def.tags.Any(t => t.defName.Contains("Left"));
+ bool part2Right = part2.def.tags.Any(t => t.defName.Contains("Right"));
+
+ if ((part1Left && part2Right) || (part1Right && part2Left))
+ return true;
+ }
+ }
+
+ // 备用方案:使用自定义标签检测(支持本地化)
string label1 = part1.customLabel ?? part1.def.label;
string label2 = part2.customLabel ?? part2.def.label;
- // 检查是否包含对称标识符
- string[] symmetryKeywords = { "Left", "Right", "Left", "Right", "Front", "Back", "Upper", "Lower" };
+ // 使用翻译键检测,而非硬编码英文
+ string leftKey = "Left".Translate();
+ string rightKey = "Right".Translate();
- foreach (var keyword in symmetryKeywords)
- {
- if (label1.Contains(keyword) && label2.Contains(keyword))
- {
- // 检查是否是对称的关键词对
- if ((keyword == "Left" && label2.Contains("Right")) ||
- (keyword == "Right" && label2.Contains("Left")) ||
- (keyword == "Front" && label2.Contains("Back")) ||
- (keyword == "Back" && label2.Contains("Front")))
- {
- return true;
- }
- }
- }
+ bool label1HasLeft = label1.Contains(leftKey) || label1.Contains("Left");
+ bool label1HasRight = label1.Contains(rightKey) || label1.Contains("Right");
+ bool label2HasLeft = label2.Contains(leftKey) || label2.Contains("Left");
+ bool label2HasRight = label2.Contains(rightKey) || label2.Contains("Right");
+
+ if ((label1HasLeft && label2HasRight) || (label1HasRight && label2HasLeft))
+ return true;
+
+ // 检查前后对称
+ string frontKey = "Front".Translate();
+ string backKey = "Back".Translate();
+
+ bool label1HasFront = label1.Contains(frontKey) || label1.Contains("Front");
+ bool label1HasBack = label1.Contains(backKey) || label1.Contains("Back");
+ bool label2HasFront = label2.Contains(frontKey) || label2.Contains("Front");
+ bool label2HasBack = label2.Contains(backKey) || label2.Contains("Back");
+
+ if ((label1HasFront && label2HasBack) || (label1HasBack && label2HasFront))
+ return true;
return false;
}
diff --git a/Source/ArachnaeSwarm/Pawn_Comps/ARA_PawnResearchBlueprintReader/Comp_PawnResearchBlueprintReader.cs b/Source/ArachnaeSwarm/Pawn_Comps/ARA_PawnResearchBlueprintReader/Comp_PawnResearchBlueprintReader.cs
index ad56495..af89249 100644
--- a/Source/ArachnaeSwarm/Pawn_Comps/ARA_PawnResearchBlueprintReader/Comp_PawnResearchBlueprintReader.cs
+++ b/Source/ArachnaeSwarm/Pawn_Comps/ARA_PawnResearchBlueprintReader/Comp_PawnResearchBlueprintReader.cs
@@ -345,10 +345,11 @@ namespace ArachnaeSwarm
{
int pointsToAdd = Mathf.FloorToInt(accumulatedResearchPoints);
- // 消耗灵能科研点
+ // 先检查并消耗灵能科研点,再添加进度(确保原子性)
if (Props.usePsychicResearchPoints && spellHolder != null)
{
- if (!spellHolder.ConsumePsychicResearchPoints(pointsToAdd, $"研究 {currentResearch.LabelCap}"))
+ // 先检查是否有足够的灵能科研点
+ if (!spellHolder.HasEnoughPsychicResearchPoints(pointsToAdd))
{
// 灵能科研点不足,停止研究
StopResearching();
@@ -356,9 +357,17 @@ namespace ArachnaeSwarm
Pawn, MessageTypeDefOf.NegativeEvent);
return;
}
+
+ // 消耗灵能科研点
+ if (!spellHolder.ConsumePsychicResearchPoints(pointsToAdd, $"研究 {currentResearch.LabelCap}"))
+ {
+ // 消耗失败(理论上不应该发生,因为已经检查过了)
+ StopResearching();
+ return;
+ }
}
- // 添加到全局研究进度
+ // 添加到全局研究进度(在消耗成功后)
AddResearchProgress(pointsToAdd);
// 减去已添加的点数