diff --git a/HarmonySrc/CinerariumLogist.cs b/HarmonySrc/CinerariumLogist.cs index 495e12a..ffdf499 100644 --- a/HarmonySrc/CinerariumLogist.cs +++ b/HarmonySrc/CinerariumLogist.cs @@ -84,6 +84,28 @@ namespace NecromancerTome } __instance.SetCommonData(UIUtils.GetButtonIconForAction(__instance.xui.playerUI.playerInput.Activate), CinerariumLogistForm.OnBlockMenu, new XUiC_Radial.RadialContextHoldingSlotIndex(pl.inventory.holdingItemIdx), -1, true, XUiC_Radial.RadialValidSameHoldingSlotIndex); + Debug.Log("[NecromancerTome] LogistForm: меню блока - " + held.Name + ", пунктов " + __instance.currentEnabledEntriesCount()); + } + } + + /// Диагностика #77 (23.10.2026: «зажимаю R - меню не открывается, только поворот»): на нажатие R с урной-логистом в + /// руке - что видит игра (блок ли в руке, не считает ли предмет оружием). Убрать, когда причина найдена. + [HarmonyPatch(typeof(PlayerMoveController), nameof(PlayerMoveController.Update))] + public static class Patch_PlayerMoveController_Update_LogistFormDiag + { + public static void Postfix(PlayerMoveController __instance) + { + EntityPlayerLocal pl = __instance.entityPlayerLocal; + PlayerActionsLocal inp = __instance.playerInput; + if (pl?.inventory == null || inp == null || !CinerariumLogistForm.IsLogistItem(pl.inventory.holdingItem) || + !(inp.Reload.WasPressed || inp.PermanentActions.Reload.WasPressed)) + { + return; + } + XUiC_Radial r = __instance.playerUI?.xui?.RadialWindow; + Debug.Log("[NecromancerTome] LogistForm: R - " + pl.inventory.holdingItem.Name + ", блок " + (pl.inventory.GetHoldingBlock() != null) + + ", оружие " + pl.inventory.IsHoldingGun() + "/" + pl.inventory.IsHoldingDynamicMelee() + ", смена руки " + pl.inventory.IsHandSwitching() + + ", меню " + (r != null && r.IsOpen) + ", модальное " + (__instance.playerUI?.windowManager?.IsModalWindowOpen() ?? false)); } } diff --git a/HarmonySrc/PetFarRecall.cs b/HarmonySrc/PetFarRecall.cs new file mode 100644 index 0000000..0513214 --- /dev/null +++ b/HarmonySrc/PetFarRecall.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace NecromancerTome +{ + /// + /// Дальний питомец после одержимости (указание 23.10.2026, #35: «Если питомец далеко от хозяина и хозяин вышел из режима + /// одержимости, то питомец должен сразу быть отозван. Скажем, если больше 100 блоков, то отзовём, если меньше, то + /// возвращается своим ходом»). Отзыв - тот же, что книгой (RecallPet: подсказка «… отозван», книга назад, если призыв её + /// тратит). Далеко ушедший питомец иначе остаётся в выгруженном участке, и PetFollowPatch снимает с него владение. + /// + public static class PetFarRecall + { + /// Порог в блоках; 0 - выключено (питомец всегда идёт своим ходом, как раньше). + public static float Distance = 100f; + + /// true - питомец отозван (дальше ничего с ним не делать). + public static bool TryRecall(EntityPlayer _owner, EntityAlive _pet) + { + if (Distance <= 0f || _owner == null || _pet == null || _pet.IsDead()) + { + return false; + } + float d = Vector3.Distance(_owner.position, _pet.position); + string cls = EntityClass.list.TryGetValue(_pet.entityClass, out EntityClass ec) ? ec.entityClassName : null; + if (d <= Distance || cls == null || + !Patch_ItemActionSpawnEntity_Spawn_PetLimit.LimitedPets.TryGetValue(cls, out Patch_ItemActionSpawnEntity_Spawn_PetLimit.PetInfo info) || + info.RecalledKey == null) + { + return false; + } + Debug.Log("[NecromancerTome] PetFarRecall: " + cls + " " + _pet.entityId + " в " + Mathf.RoundToInt(d) + " блоках от хозяина - отозван"); + Patch_ItemActionSpawnEntity_Spawn_PetLimit.RecallPet(_owner, _pet.entityId, info.RecalledKey, info.ConsumesBook ? info.SummonItemName : null); + return true; + } + } + + public class ConsoleCmdNecroPetFar : ConsoleCmdAbstract + { + public override bool IsExecuteOnClient => true; + + public override bool AllowedInMainMenu => false; + + public override string[] getCommands() + { + return new string[] { "necropetfar" }; + } + + public override string getDescription() + { + return "Necromancer's Tome: recall a far pet when leaving possession - distance."; + } + + public override string getHelp() + { + return "necropetfar - status\n" + + "necropetfar - recall the pet if it is farther than this from you\n" + + "necropetfar off - never recall, the pet always walks back"; + } + + public override void Execute(List _params, CommandSenderInfo _senderInfo) + { + SdtdConsole con = SingletonMonoBehaviour.Instance; + if (_params.Count >= 1) + { + if (_params[0] == "off") + { + PetFarRecall.Distance = 0f; + } + else if (float.TryParse(_params[0], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float v) && v >= 0f) + { + PetFarRecall.Distance = v; + } + } + con.Output("necropetfar: " + (PetFarRecall.Distance > 0f ? "recall farther than " + PetFarRecall.Distance + " blocks" : "off - the pet always walks back")); + } + } +} diff --git a/HarmonySrc/Possession.cs b/HarmonySrc/Possession.cs index 865f14b..c17bb53 100644 --- a/HarmonySrc/Possession.cs +++ b/HarmonySrc/Possession.cs @@ -449,10 +449,12 @@ namespace NecromancerTome } } // Питомец просто отпущен: его ИИ (следование, охрана, поводок) сам вернёт его к хозяину. + // Далеко ушедший - сразу отозван (23.10, #35: PetFarRecall). if (pet) { z.SetMoveForwardWithModifiers(0f, 0f, 0f, false); z.SetAttackTarget(null, 0); + PetFarRecall.TryRecall(player, z); return; } // Сам зомби в ужасе бежит от тела игрока (решение 03.10: «пугается и убегает как при отрицании»). diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll index 28e596a..26ce755 100644 Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ diff --git a/NecromancerHarmony.pdb b/NecromancerHarmony.pdb index f7507a6..584c1de 100644 Binary files a/NecromancerHarmony.pdb and b/NecromancerHarmony.pdb differ