Живой кролик в мешке: игрок хватает кролика сразу в рюкзак (#71)
Кролику дан ванильный PickupItem (как у курицы); RabbitGrab.cs кладёт мешок necroRabbitSack в рюкзак, своя подсказка. ЛКМ с мешком - выпустить, ПКМ - разделать на мясо 10, кожу 2, кости 5. Выключатель necrorabbit. Иконка пока временная. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0999fd3afc
commit
ac4c455e29
@@ -549,3 +549,6 @@ necroSkullDecorCenter,blocks,Block,,,,Skull: in the middle,,Schädel: in der Mit
|
||||
necroSkullDecorBack,blocks,Block,,,,Skull: against the wall,,Schädel: an der Wand,Cráneo: contra la pared,Crâne : contre le mur,Teschio: contro il muro,頭蓋骨:壁際,두개골: 벽에 붙여,Czaszka: przy ścianie,Crânio: encostado na parede,Череп: у стены,Kafatası: duvara dayalı,头骨:靠墙,頭骨:靠牆
|
||||
necroSkullDecorDesc,blocks,Block,,,,"A skull ready to be placed as decor. Hold [action:permanent:Reload][action:local:Reload] for block rotation, the other look, or to turn it back into a skull. Picked up, it is a skull again. A skull on display also relays a cinerarium's ghostly thread: pass a thread through it to reach farther.",,"Ein Schädel, bereit zum Platzieren als Deko. Halte [action:permanent:Reload][action:local:Reload] für Blockdrehung, die andere Ansicht oder um ihn wieder zum Schädel zu machen. Aufgehoben ist er wieder ein Schädel. Ein ausgestellter Schädel leitet auch den Geisterfaden eines Cinerariums weiter: Führe einen Faden hindurch, um weiter zu reichen.","Un cráneo listo para colocarse como decoración. Mantén [action:permanent:Reload][action:local:Reload] para girar el bloque, cambiar de aspecto o volver a convertirlo en cráneo. Al recogerlo vuelve a ser un cráneo. Un cráneo expuesto también transmite el hilo fantasmal de un cinerario: pasa un hilo por él para llegar más lejos.","Un crâne prêt à être posé en décor. Maintiens [action:permanent:Reload][action:local:Reload] pour pivoter le bloc, changer d'aspect ou le reconvertir en crâne. Ramassé, il redevient un crâne. Un crâne exposé relaie aussi le fil fantomatique d'un cinéraire : fais-y passer un fil pour aller plus loin.","Un teschio pronto da posizionare come decorazione. Tieni premuto [action:permanent:Reload][action:local:Reload] per ruotare il blocco, cambiare aspetto o farlo tornare un teschio. Raccolto, torna un teschio. Un teschio esposto trasmette anche il filo spettrale di un cinerario: fai passare un filo attraverso di esso per arrivare più lontano.",飾りとして置ける頭蓋骨。[action:permanent:Reload][action:local:Reload]長押しでブロックの回転、もう一つの見た目、頭蓋骨への戻しを選ぶ。拾うと頭蓋骨に戻る。飾った頭蓋骨は骨壺の霊の糸を中継する――糸を通せばさらに遠くまで届く。,"장식으로 놓을 수 있는 두개골. [action:permanent:Reload][action:local:Reload]을 길게 눌러 블록 회전, 다른 모양, 두개골로 되돌리기를 고른다. 주우면 다시 두개골이 된다. 장식한 두개골은 유골함의 유령 실을 이어 준다 - 실을 통과시켜 더 멀리 닿게 하라.","Czaszka gotowa do postawienia jako ozdoba. Przytrzymaj [action:permanent:Reload][action:local:Reload], by obrócić blok, zmienić wygląd albo z powrotem zrobić z niej czaszkę. Podniesiona znów jest czaszką. Wystawiona czaszka przekazuje też widmową nić cinerarium: przeprowadź przez nią nić, by sięgnąć dalej.","Um crânio pronto para ser colocado como decoração. Segure [action:permanent:Reload][action:local:Reload] para girar o bloco, mudar o visual ou voltar a ser crânio. Recolhido, vira crânio de novo. Um crânio exposto também retransmite o fio fantasmagórico de um cinerário: passe um fio por ele para ir mais longe.","Череп, готовый стать декором. Удерживай [action:permanent:Reload][action:local:Reload]: поворот блока, другой вид или обратно в череп. Поднятый - снова череп. Выставленный череп ещё и передаёт призрачную нить цинерария: проведи нить через него, чтобы дотянуться дальше.","Süs olarak konmaya hazır bir kafatası. Blok döndürme, diğer görünüm ya da tekrar kafatasına çevirmek için [action:permanent:Reload][action:local:Reload] basılı tut. Toplanınca yine kafatası olur. Sergilenen kafatası bir küllüğün hayalet ipliğini de aktarır: daha uzağa ulaşmak için ipliği içinden geçir.",可作为装饰摆放的头骨。长按[action:permanent:Reload][action:local:Reload]旋转方块、切换另一种外观或变回头骨。拾起后又是头骨。摆出的头骨还能中继骨灰瓮的幽灵丝线:让丝线从它这里穿过,就能连到更远处。,可作為裝飾擺放的頭骨。長按[action:permanent:Reload][action:local:Reload]旋轉方塊、切換另一種外觀或變回頭骨。撿起後又是頭骨。擺出的頭骨還能中繼骨灰甕的幽靈絲線:讓絲線從它這裡穿過,就能連到更遠處。
|
||||
necroSkullModeOff,items,Item,,,,Just a skull,,Nur ein Schädel,Solo un cráneo,Simple crâne,Solo un teschio,ただの頭蓋骨,그냥 두개골,Zwykła czaszka,Só um crânio,Просто череп,Sadece kafatası,只是头骨,只是頭骨
|
||||
necroRabbitSack,items,Item,,,,Live Rabbit in a Sack,,Lebender Hase im Sack,Conejo vivo en un saco,Lapin vivant dans un sac,Coniglio vivo nel sacco,袋の中の生きたウサギ,자루 속 살아있는 토끼,Żywy królik w worku,Coelho vivo no saco,Живой кролик в мешке,Çuvalda canlı tavşan,袋中的活兔子,袋中的活兔子
|
||||
necroRabbitSackDesc,items,Item,,,,"A rabbit you caught with your bare hands. It kicks inside the sack.\n\nLeft click - let it go. Right click - butcher it for meat, leather and bones.",,"Ein Hase, mit bloßen Händen gefangen. Er strampelt im Sack.\n\nLinksklick - freilassen. Rechtsklick - zerlegen: Fleisch, Leder und Knochen.","Un conejo atrapado con las manos desnudas. Patalea dentro del saco.\n\nClic izquierdo: soltarlo. Clic derecho: despiezarlo en carne, cuero y huesos.","Un lapin attrapé à mains nues. Il se débat dans le sac.\n\nClic gauche : le relâcher. Clic droit : le dépecer pour la viande, le cuir et les os.","Un coniglio preso a mani nude. Scalcia nel sacco.\n\nClic sinistro: liberarlo. Clic destro: macellarlo per carne, cuoio e ossa.","素手で捕まえたウサギ。袋の中で暴れている。\n\n左クリック - 放す。右クリック - 解体して肉、革、骨を得る。","맨손으로 잡은 토끼. 자루 안에서 발버둥친다.\n\n좌클릭 - 풀어준다. 우클릭 - 도축해 고기, 가죽, 뼈를 얻는다.","Królik złapany gołymi rękami. Wierzga w worku.\n\nLewy przycisk - wypuść. Prawy przycisk - oprawić na mięso, skórę i kości.","Um coelho pego com as próprias mãos. Ele esperneia no saco.\n\nClique esquerdo - soltar. Clique direito - abater para carne, couro e ossos.","Кролик, пойманный голыми руками. Брыкается в мешке.\n\nЛКМ - выпустить. ПКМ - разделать на мясо, кожу и кости.","Çıplak elle yakalanmış bir tavşan. Çuvalın içinde tepiniyor.\n\nSol tık - serbest bırak. Sağ tık - et, deri ve kemik için kes.","徒手抓到的兔子,在袋子里乱蹬。\n\n左键 - 放走。右键 - 宰杀,得到肉、皮革和骨头。","徒手抓到的兔子,在袋子裡亂蹬。\n\n左鍵 - 放走。右鍵 - 宰殺,得到肉、皮革和骨頭。"
|
||||
necroRabbitGrab,PlayerMoveController.cs,Label,,,,Press [action:local:Activate][action:permanent:Activate] to Grab the Rabbit,,[action:local:Activate][action:permanent:Activate] drücken, um den Hasen zu packen,Pulsa [action:local:Activate][action:permanent:Activate] para atrapar el conejo,Appuyez sur [action:local:Activate][action:permanent:Activate] pour attraper le lapin,Premi [action:local:Activate][action:permanent:Activate] per afferrare il coniglio,[action:local:Activate][action:permanent:Activate]でウサギを捕まえる,[action:local:Activate][action:permanent:Activate]을(를) 눌러 토끼 잡기,"Naciśnij [action:local:Activate][action:permanent:Activate], aby złapać królika",Pressione [action:local:Activate][action:permanent:Activate] para pegar o coelho,"Нажмите [action:local:Activate][action:permanent:Activate], чтобы схватить кролика",Tavşanı yakalamak için [action:local:Activate][action:permanent:Activate] tuşuna bas,按[action:local:Activate][action:permanent:Activate]抓住兔子,按[action:local:Activate][action:permanent:Activate]抓住兔子
|
||||
|
||||
|
@@ -469,4 +469,9 @@
|
||||
<property name="Faction" value="none"/>
|
||||
</entity_class>
|
||||
</append>
|
||||
<!-- Живой кролик в мешке (22.10.2026, RabbitGrab.cs): «Я бы хватал кролика, но в отличие от курицы пусть он сразу
|
||||
оказывается в сумке». PickupItem - ванильный механизм захвата курицы (голой рукой, E); мешок кладёт в рюкзак код. -->
|
||||
<append xpath="/entity_classes/entity_class[@name='animalRabbit']">
|
||||
<property name="PickupItem" value="necroRabbitSack"/>
|
||||
</append>
|
||||
</config>
|
||||
|
||||
@@ -2071,4 +2071,38 @@
|
||||
<property name="Group" value="Resources"/>
|
||||
</item>
|
||||
</append>
|
||||
<!-- Живой кролик в мешке (22.10.2026, RabbitGrab.cs). Пойманный игроком кролик сразу в рюкзаке. ЛКМ с мешком в руке -
|
||||
выпустить (ванильный SpawnEntity, мешок тратит код), ПКМ - разделать: мясо 10, кожа 2, кости 5, как ножом
|
||||
(указание 22.10: «Кролика можно будет разобрать на кожу, кости и мясо»). Мешок и поза в руке - как у кожи.
|
||||
Иконка пока временная - кожа с подкраской. -->
|
||||
<append xpath="/items">
|
||||
<item name="necroRabbitSack">
|
||||
<property name="Tags" value="noMods"/>
|
||||
<property name="HoldType" value="45"/>
|
||||
<property name="Meshfile" value="@:Other/Items/Misc/sackPrefab.prefab"/>
|
||||
<property name="DropMeshfile" value="@:Other/Items/Misc/sack_droppedPrefab.prefab"/>
|
||||
<property name="Material" value="Mleather"/>
|
||||
<property name="CustomIcon" value="resourceLeather"/>
|
||||
<property name="CustomIconTint" value="c8a070"/>
|
||||
<property name="DescriptionKey" value="necroRabbitSackDesc"/>
|
||||
<property name="Stacknumber" value="10"/>
|
||||
<property name="Weight" value="3"/>
|
||||
<property name="EconomicValue" value="0"/>
|
||||
<property name="SellableToTrader" value="false"/>
|
||||
<property name="Group" value="Resources"/>
|
||||
<property name="SoundPickup" value="rabbitpain"/>
|
||||
<property class="Action0">
|
||||
<property name="Class" value="SpawnEntity"/>
|
||||
<property name="AnimType" value="4"/>
|
||||
<property name="AnimWait" value="0.3"/>
|
||||
<property name="SoundWarn" value="rabbitpain"/>
|
||||
<property name="Entity" value="animalRabbit"/>
|
||||
<property name="EntityOffset" value="0, -1, 1.2"/>
|
||||
</property>
|
||||
<property class="Action1">
|
||||
<property name="Class" value="Eat"/>
|
||||
<property name="Delay" value="1"/>
|
||||
</property>
|
||||
</item>
|
||||
</append>
|
||||
</config>
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user