using System.Collections.Generic; using GUI_2; using HarmonyLib; using UnityEngine; using UnityEngine.Scripting; namespace NecromancerTome { /// /// Урна логиста - латунное кадило (18.10.2026, BACKLOG «Урна логиста - своя модель: кадило»): два вида - на подставке по /// центру клетки (necroCinerariumLogist) и на кронштейне у стены (necroCinerariumLogistWall). Пользователь: «Должна быть /// возможность крепить её и к стене, и по центру какого-нибудь столба... как мы делали с черепом. Только череп у нас изначально /// не блок, а тут должно получиться попроще». Вид выбирается, как у черепа-декора: урна в руке, R - меню блока игры, к нему /// добавлен второй вид; выбор превращает стопку в ячейке пояса в другой блок (тот же счёт). Пустая урна и урна с черепом - /// каждая в свой вид того же рода. /// public static class CinerariumLogistForm { /// Номера наших пунктов в меню блока - вне занятых игрой (-2..10) и черепом (40..42). public const int MenuCenter = 43, MenuWall = 44; static readonly Dictionary Pairs = new Dictionary { { TEFeatureCinerarium.LogistBlockName, (TEFeatureCinerarium.LogistBlockName, TEFeatureCinerarium.LogistWallBlockName) }, { TEFeatureCinerarium.LogistWallBlockName, (TEFeatureCinerarium.LogistBlockName, TEFeatureCinerarium.LogistWallBlockName) }, { TEFeatureCinerarium.LogistSkullBlockName, (TEFeatureCinerarium.LogistSkullBlockName, TEFeatureCinerarium.LogistWallSkullBlockName) }, { TEFeatureCinerarium.LogistWallSkullBlockName, (TEFeatureCinerarium.LogistSkullBlockName, TEFeatureCinerarium.LogistWallSkullBlockName) }, }; public static bool IsLogistItem(ItemClass _item) => _item != null && Pairs.ContainsKey(_item.Name); static void Convert(EntityPlayerLocal _pl, bool _wall) { ItemStack cur = _pl?.inventory?.holdingItemStack; if (cur == null || cur.IsEmpty() || !Pairs.TryGetValue(cur.itemValue.ItemClass.Name, out var pair)) { return; } string name = _wall ? pair.wall : pair.center; if (cur.itemValue.ItemClass.Name == name) { return; } ItemValue iv = ItemClass.GetItem(name); if (iv == null || iv.IsEmpty()) { return; } _pl.inventory.SetItem(_pl.inventory.holdingItemIdx, new ItemStack(iv, cur.count)); } public static void OnBlockMenu(XUiC_Radial _sender, int _commandIndex, XUiC_Radial.RadialContextAbs _context) { EntityPlayerLocal pl = _sender?.xui?.playerUI?.entityPlayer; switch (_commandIndex) { case MenuCenter: Convert(pl, false); return; case MenuWall: Convert(pl, true); return; } _sender.handleBlockShapeCommand(_sender, _commandIndex, _context); } } /// Меню блока с урной логиста в руке: к пунктам игры - второй вид (подставка / у стены). /// 23.10 (#77, «зажимаю R - только вращает»): у блока с одним простым поворотом игра в конце SetupBlockShapeData зовёт /// SetCommonData с одним пунктом - и сразу выполняет его (поворот) и закрывает меню; пункт, добавленный после (постфикс), /// попадал в уже закрытое меню. Поэтому наш пункт и наш обработчик ставятся в самом SetCommonData этого вызова - до /// подсчёта пунктов. Короткое нажатие R по-прежнему поворачивает (пункт «-1» уходит игре). [HarmonyPatch(typeof(XUiC_Radial), nameof(XUiC_Radial.SetupBlockShapeData))] public static class Patch_XUiC_Radial_SetupBlockShapeData_LogistForm { /// Идёт сборка меню блока с урной логиста в руке. public static bool Building; public static void Prefix(XUiC_Radial __instance) { Building = CinerariumLogistForm.IsLogistItem(__instance.xui?.playerUI?.entityPlayer?.inventory?.holdingItem); } public static void Postfix() { Building = false; } } [HarmonyPatch(typeof(XUiC_Radial), nameof(XUiC_Radial.SetCommonData))] public static class Patch_XUiC_Radial_SetCommonData_LogistForm { public static void Prefix(XUiC_Radial __instance, ref XUiC_Radial.CommandHandlerDelegate _commandHandlerFunc) { if (!Patch_XUiC_Radial_SetupBlockShapeData_LogistForm.Building) { return; } Patch_XUiC_Radial_SetupBlockShapeData_LogistForm.Building = false; ItemClass held = __instance.xui.playerUI.entityPlayer.inventory.holdingItem; bool wall = held.Name == TEFeatureCinerarium.LogistWallBlockName || held.Name == TEFeatureCinerarium.LogistWallSkullBlockName; if (wall) { __instance.CreateRadialEntry(CinerariumLogistForm.MenuCenter, TEFeatureCinerarium.LogistBlockName, "ItemIconAtlas", "", Localization.Get("necroCinerariumLogistFormCenter")); } else { __instance.CreateRadialEntry(CinerariumLogistForm.MenuWall, TEFeatureCinerarium.LogistWallBlockName, "ItemIconAtlas", "", Localization.Get("necroCinerariumLogistFormWall")); } _commandHandlerFunc = CinerariumLogistForm.OnBlockMenu; } } /// Мигание урны логиста, когда класть некуда (18.10: «При застревании должно мигать»): свечение модели /// (MaterialPropertyBlock, как у TEFeatureCinerarium.SyncGlow) пульсирует от тёмного до обычного. Выключено - обычное свечение. [Preserve] public class CinerariumBlink : MonoBehaviour { public List Renderers = new List(); public static float Period = 0.8f; static readonly MaterialPropertyBlock _block = new MaterialPropertyBlock(); static readonly int EmissionColorId = Shader.PropertyToID("_EmissionColor"); void Update() { // Резкий переход - мигание, а не дыхание: половину периода горит, половину почти погасла. float k = Mathf.Repeat(Time.time, Period) < Period * 0.5f ? 1f : 0.08f; Set(TEFeatureCinerarium.GlowColor * k); } void OnDisable() { Set(TEFeatureCinerarium.GlowColor); } void Set(Color _c) { foreach (Renderer r in Renderers) { if (r == null) { continue; } r.GetPropertyBlock(_block); _block.SetColor(EmissionColorId, _c); r.SetPropertyBlock(_block); } } } }