using System.Collections.Generic; using HarmonyLib; using UnityEngine; namespace NecromancerTome { /// /// Живой кролик в мешке (указание 22.10.2026: «Я бы хватал кролика, но в отличие от курицы пусть он сразу /// оказывается в сумке»; предмет - «Живой кролик в мешке»). Ваниль даёт схватить любое существо, у которого в /// описании есть PickupItem (EntityAlive: команда grab голой рукой, предмет кладётся в слот руки, существо /// уходит из мира через Collect) - так ловят курицу. Кролику PickupItem дан в entityclasses.xml, а здесь его /// мешок кладётся сразу в рюкзак (нет места - на пояс, иначе под ноги). /// /// Выпустить - ЛКМ с мешком в руке (ItemActionSpawnEntity, Entity=animalRabbit): ваниль предмет не тратит, тратим /// здесь. Стервятник кролика по-прежнему несёт под лапами (Possession.Carryable). Выключатель - necrorabbit off: /// тогда захват идёт ванильно, мешок - в руку, как курица. /// public static class RabbitGrab { public const string SackItem = "necroRabbitSack"; public static bool Enabled = true; public static bool IsSackAnimal(EntityAlive _e) => _e != null && _e.EntityClass != null && _e.EntityClass.PickupItem == SackItem; public static bool IsSack(ItemValue _iv) => _iv != null && _iv.ItemClass != null && _iv.ItemClass.GetItemName() == SackItem; } /// Захват кролика - мешок сразу в рюкзак. [HarmonyPatch(typeof(EntityAlive), nameof(EntityAlive.OnEntityActivated))] public static class Patch_EntityAlive_OnEntityActivated_RabbitGrab { public static bool Prefix(EntityAlive __instance, EntityActivationCommand _command, EntityPlayerLocal _playerFocusing) { if (!RabbitGrab.Enabled || _command.commandId != "grab" || _playerFocusing == null || !RabbitGrab.IsSackAnimal(__instance)) { return true; } ItemClass ic = ItemClass.GetItemClass(RabbitGrab.SackItem); if (ic == null) { Debug.LogWarning("[NecromancerTome] RabbitGrab: нет предмета " + RabbitGrab.SackItem + " - захват ванильный"); return true; } ItemStack sack = new ItemStack(new ItemValue(ic.Id), 1); string where = "рюкзак"; if (_playerFocusing.bag == null || !_playerFocusing.bag.AddItem(sack)) { where = "пояс"; if (_playerFocusing.inventory == null || !_playerFocusing.inventory.AddItem(sack)) { where = "под ноги"; _playerFocusing.world.gameManager.ItemDropServer(sack, _playerFocusing.GetPosition() + Vector3.up * 0.5f, Vector3.zero); } } _playerFocusing.MinEventContext.Other = __instance; _playerFocusing.FireEvent(MinEventTypes.onSelfPickupOther); __instance.MinEventContext.Other = _playerFocusing; __instance.FireEvent(MinEventTypes.onSelfPickedUpByOther); Audio.Manager.PlayInsidePlayerHead(ic.Properties.GetString("SoundPickup")); __instance.Collect(_playerFocusing.entityId); Debug.Log("[NecromancerTome] RabbitGrab: кролик " + __instance.entityId + " пойман, мешок - " + where); return false; } } /// Подсказка над кроликом - своя (ваниль пишет «схватить курицу» для любого существа с PickupItem). [HarmonyPatch(typeof(EntityAlive), nameof(EntityAlive.GetActivationText))] public static class Patch_EntityAlive_GetActivationText_RabbitGrab { public static void Postfix(EntityAlive __instance, ref string __result) { if (RabbitGrab.Enabled && RabbitGrab.IsSackAnimal(__instance)) { __result = Localization.Get("necroRabbitGrab"); } } } /// Выпуск из мешка тратит мешок (ванильный SpawnEntity предмет не трогает). [HarmonyPatch(typeof(ItemActionSpawnEntity), "Spawn", new System.Type[] { typeof(ItemActionData) })] public static class Patch_ItemActionSpawnEntity_Spawn_RabbitSack { public static void Postfix(ItemActionData _actionData) { ItemInventoryData inv = _actionData?.invData; if (inv?.holdingEntity == null || !RabbitGrab.IsSack(inv.itemValue)) { return; } Inventory pack = inv.holdingEntity.inventory; ItemStack held = pack.GetItem(inv.slotIdx); if (held == null || held.IsEmpty() || !RabbitGrab.IsSack(held.itemValue)) { return; } held = held.Clone(); held.count--; pack.SetItem(inv.slotIdx, held.count > 0 ? held : ItemStack.Empty.Clone()); Debug.Log("[NecromancerTome] RabbitGrab: кролик выпущен из мешка, осталось " + Mathf.Max(0, held.count)); } } /// Разделать кролика из мешка - ПКМ с мешком в руке (указание 22.10: «Кролика можно будет разобрать на кожу, /// кости и мясо»). Action1 мешка - ванильный Eat, но его ExecuteAction целиком наш: мешок -1, в рюкзак - добыча /// разделки ножом (entityclasses.xml animalRabbit, butcherHarvest). [HarmonyPatch(typeof(ItemActionEat), "ExecuteAction")] public static class Patch_ItemActionEat_ExecuteAction_RabbitSack { static readonly string[] Yield = { "foodRawMeat", "resourceLeather", "resourceBone" }; static readonly int[] YieldCount = { 10, 2, 5 }; public static bool Prefix(ItemActionData _actionData, bool _bReleased) { ItemInventoryData inv = _actionData?.invData; if (inv == null || !RabbitGrab.IsSack(inv.itemValue)) { return true; } if (!_bReleased || !(inv.holdingEntity is EntityPlayerLocal player)) { return false; } ItemStack held = player.inventory.GetItem(inv.slotIdx); if (held == null || held.IsEmpty() || !RabbitGrab.IsSack(held.itemValue)) { return false; } held = held.Clone(); held.count--; player.inventory.SetItem(inv.slotIdx, held.count > 0 ? held : ItemStack.Empty.Clone()); for (int i = 0; i < Yield.Length; i++) { ItemClass ic = ItemClass.GetItemClass(Yield[i]); if (ic == null) { continue; } ItemStack s = new ItemStack(new ItemValue(ic.Id), YieldCount[i]); if (!player.bag.AddItem(s) && !player.inventory.AddItem(s)) { player.world.gameManager.ItemDropServer(s, player.GetPosition() + Vector3.up * 0.5f, Vector3.zero); } } player.PlayOneShot("rabbitdeath"); Debug.Log("[NecromancerTome] RabbitGrab: кролик разделан, мешков осталось " + Mathf.Max(0, held.count)); return false; } } public class ConsoleCmdNecroRabbit : ConsoleCmdAbstract { public override bool IsExecuteOnClient => true; public override bool AllowedInMainMenu => false; public override string[] getCommands() { return new string[] { "necrorabbit" }; } public override string getDescription() { return "Necromancer's Tome: grabbed rabbit goes straight to the backpack - switch."; } public override string getHelp() { return "necrorabbit - status\n" + "necrorabbit on|off - off: vanilla grab, the sack goes to the hand like a chicken"; } public override void Execute(List _params, CommandSenderInfo _senderInfo) { SdtdConsole con = SingletonMonoBehaviour.Instance; if (_params.Count >= 1 && (_params[0] == "on" || _params[0] == "off")) { RabbitGrab.Enabled = _params[0] == "on"; } con.Output("necrorabbit: " + (RabbitGrab.Enabled ? "on - rabbit sack goes to the backpack" : "off - vanilla grab")); } } }