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); // 减去已添加的点数