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

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

181 lines
8.1 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: «Кролика можно будет разобрать на кожу, кости и
/// мясо»; «Разделывать будем по команде сломать»). Пункт ваниль показывает сама: мешок из кожи (Mleather, forge_category
/// leather), вес не меньше кожи. Но ванильная разборка даёт один материал через очередь крафта - у мешка она наша:
/// вся стопка сразу, на каждого кролика добыча разделки ножом (entityclasses.xml animalRabbit, butcherHarvest).</summary>
[HarmonyPatch(typeof(ItemActionEntryScrap), nameof(ItemActionEntryScrap.OnActivated))]
public static class Patch_ItemActionEntryScrap_OnActivated_RabbitSack
{
static readonly string[] Yield = { "foodRawMeat", "resourceLeather", "resourceBone" };
static readonly int[] YieldCount = { 10, 2, 5 };
public static bool Prefix(ItemActionEntryScrap __instance)
{
if (!(__instance.ItemController is IItemStackController isc))
{
return true;
}
ItemStack st = isc.ItemStack;
if (st == null || st.IsEmpty() || !RabbitGrab.IsSack(st.itemValue))
{
return true;
}
EntityPlayerLocal player = __instance.ItemController.xui.playerUI.entityPlayer;
XUiM_PlayerInventory pack = __instance.ItemController.xui.PlayerInventory;
int n = st.count;
isc.ItemStack = 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] * n);
if (!pack.AddItem(s))
{
GameManager.Instance.ItemDropServer(s, player.GetPosition(), new Vector3(0.5f, 0f, 0.5f));
}
}
isc.WindowGroup?.Controller?.SetAllChildrenDirty();
Audio.Manager.PlayInsidePlayerHead("rabbitdeath");
Debug.Log("[NecromancerTome] RabbitGrab: разобрано мешков с кроликами - " + n);
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"));
}
}
}