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)