Files
necromants-tome-7d2d-3-2/HarmonySrc/RabbitGrab.cs
T
AlexCubeandClaude Opus 5.5 ac4c455e29 Живой кролик в мешке: игрок хватает кролика сразу в рюкзак (#71)
Кролику дан ванильный PickupItem (как у курицы); RabbitGrab.cs кладёт
мешок necroRabbitSack в рюкзак, своя подсказка. ЛКМ с мешком - выпустить,
ПКМ - разделать на мясо 10, кожу 2, кости 5. Выключатель necrorabbit.
Иконка пока временная.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:16:32 +03:00

183 lines
7.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using HarmonyLib;
using UnityEngine;
namespace NecromancerTome
{
/// <summary>
/// Живой кролик в мешке (указание 22.10.2026: «Я бы хватал кролика, но в отличие от курицы пусть он сразу
/// оказывается в сумке»; предмет - «Живой кролик в мешке»). Ваниль даёт схватить любое существо, у которого в
/// описании есть PickupItem (EntityAlive: команда grab голой рукой, предмет кладётся в слот руки, существо
/// уходит из мира через Collect) - так ловят курицу. Кролику PickupItem дан в entityclasses.xml, а здесь его
/// мешок кладётся сразу в рюкзак (нет места - на пояс, иначе под ноги).
///
/// Выпустить - ЛКМ с мешком в руке (ItemActionSpawnEntity, Entity=animalRabbit): ваниль предмет не тратит, тратим
/// здесь. Стервятник кролика по-прежнему несёт под лапами (Possession.Carryable). Выключатель - necrorabbit off:
/// тогда захват идёт ванильно, мешок - в руку, как курица.
/// </summary>
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;
}
/// <summary>Захват кролика - мешок сразу в рюкзак.</summary>
[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;
}
}
/// <summary>Подсказка над кроликом - своя (ваниль пишет «схватить курицу» для любого существа с PickupItem).</summary>
[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");
}
}
}
/// <summary>Выпуск из мешка тратит мешок (ванильный SpawnEntity предмет не трогает).</summary>
[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));
}
}
/// <summary>Разделать кролика из мешка - ПКМ с мешком в руке (указание 22.10: «Кролика можно будет разобрать на кожу,
/// кости и мясо»). Action1 мешка - ванильный Eat, но его ExecuteAction целиком наш: мешок -1, в рюкзак - добыча
/// разделки ножом (entityclasses.xml animalRabbit, butcherHarvest).</summary>
[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<string> _params, CommandSenderInfo _senderInfo)
{
SdtdConsole con = SingletonMonoBehaviour<SdtdConsole>.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"));
}
}
}