From 533e6711d14a53e1d0f9a0a46fd626a53ebd0c19 Mon Sep 17 00:00:00 2001 From: AlexCube Date: Tue, 13 Oct 2026 14:12:54 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=82=D0=B7=D1=8B=D0=B2=20=D0=BF=D0=B8?= =?UTF-8?q?=D1=82=D0=BE=D0=BC=D1=86=D0=B0:=20E=20=D1=81=20=D0=BA=D0=BD?= =?UTF-8?q?=D0=B8=D0=B3=D0=BE=D0=B9=20=D0=BE=D1=82=D0=B7=D1=8B=D0=B2=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=20=D0=B8=20=D0=BD=D0=B8=D1=87=D0=B5=D0=B9=D0=BD?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D0=BF=D0=B8=D1=82=D0=BE=D0=BC=D1=86=D0=B0?= =?UTF-8?q?=20=D1=82=D0=BE=D0=B3=D0=BE=20=D0=B6=D0=B5=20=D0=B2=D0=B8=D0=B4?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Стервятник 14620 из старого сейва летал у игрока, но в списке владения не был, и отзыв его не видел. Если своего питомца нет - отзывается ближайший ничейный того же вида в 60 м. Co-Authored-By: Claude Opus 5.5 --- HarmonySrc/SummonPatch.cs | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/HarmonySrc/SummonPatch.cs b/HarmonySrc/SummonPatch.cs index 267d52a..63b26d7 100644 --- a/HarmonySrc/SummonPatch.cs +++ b/HarmonySrc/SummonPatch.cs @@ -467,16 +467,75 @@ namespace NecromancerTome return; } List owned = holdingEntity.GetOwnedEntities(petClassId); + EntityAlive orphan; if (owned.Count > 0) { RecallPet(holdingEntity, owned[0].Id, tooltips.RecalledKey, tooltips.ConsumesBook ? tooltips.SummonItemName : null); } + else if ((orphan = NearestOrphan(holdingEntity, petClassId)) != null) + { + // Питомец без хозяина (13.10: «летает могильный стервятник, которого я не могу отозвать» - 14620 из + // старого сейва, за игроком не записан): отзываем ближайшего ничейного того же вида. + Debug.Log("[NecromancerTome] SummonPatch: отзыв ничейного " + orphan.EntityName + " (" + orphan.entityId + "), " + + (orphan.position - holdingEntity.position).magnitude.ToString("F0") + " м"); + RecallPet(holdingEntity, orphan.entityId, tooltips.RecalledKey, null); + } else if (holdingEntity.world != null) { GameManager.ShowTooltip(GameManager.Instance.World.GetPrimaryPlayer(), tooltips.NothingToRecallKey); } } + public const float OrphanRecallRange = 60f; + + /// Ближайший живой питомец этого вида не дальше OrphanRecallRange, которого нет в списке владения + /// ни у одного игрока. + static EntityAlive NearestOrphan(EntityAlive _holder, int _classId) + { + World world = _holder.world; + if (world == null) + { + return null; + } + EntityAlive best = null; + float bestSq = OrphanRecallRange * OrphanRecallRange; + for (int i = 0; i < world.EntityAlives.Count; i++) + { + EntityAlive e = world.EntityAlives[i]; + if (e == null || e.entityClass != _classId || e.IsDead()) + { + continue; + } + float sq = (e.position - _holder.position).sqrMagnitude; + if (sq > bestSq || OwnedByAnyPlayer(world, e.entityId, _classId)) + { + continue; + } + best = e; + bestSq = sq; + } + return best; + } + + static bool OwnedByAnyPlayer(World _world, int _entityId, int _classId) + { + foreach (EntityPlayer p in _world.Players.list) + { + if (p == null) + { + continue; + } + foreach (OwnedEntityData d in p.GetOwnedEntities(_classId)) + { + if (d.Id == _entityId) + { + return true; + } + } + } + return false; + } + public static void RecallPet(EntityAlive owner, int petEntityId, string recalledTooltipKey, string summonItemName) { if (Possession.Active && Possession.Zombie != null && Possession.Zombie.entityId == petEntityId)