Пункт «Разобрать» мешку даёт сама игра (материал Mleather, вес 5 как у кожи); разборку перехватываем: вся стопка сразу, на кролика мясо 10, кожа 2, кости 5. ПКМ убран. Исправлены поля немецкой строки подсказки (запятая). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
181 lines
8.1 KiB
C#
181 lines
8.1 KiB
C#
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"));
|
||
}
|
||
}
|
||
}
|