using GUI_2; using HarmonyLib; using UnityEngine; namespace NecromancerTome { /// /// Череп - предмет или декор (15.10.2026, вторая версия). Указание после теста: «Посмотри как ставится мебель. Например, /// горшки с цветами... Три режима. Предмет, и два декора. Если выбран декор, хват меняется на тот который при установке /// строительных блоков или мебели, и я начинаю видеть типа превью того как блок будет стоять»; поворот - как у блоков. /// Череп-предмет в руке, удержание R - меню: «Просто череп» / «Череп: по центру» / «Череп: у стены». Выбор декора /// превращает всю стопку в ячейке пояса в блок этого вида: в руке настоящий блок - превью, повороты блока. /// Блок-декор в руке, R - обычное меню блока (короткое нажатие - поворот), к нему добавлены «Просто череп» (стопка /// обратно в черепа) и второй вид декора. /// Поднятый (E) и разрушенный декор - снова череп-предмет (blocks.xml: CanPickup param1 / drop). /// Предмет necroSkull не меняется (ItemClass, без действий) - сейвы с черепом целы; блоки-декор - отдельные блоки. /// R для предмета игра не обрабатывает (PlayerMoveController открывает меню только оружию, ближнему бою и блокам) - /// нажатие ловится постфиксом к PlayerMoveController.Update. /// public static class SkullDecorPlace { public const int ItemIdx = 0, CenterIdx = 1, BackIdx = 2; /// Номера наших пунктов в меню блока - вне занятых игрой (-2..10). public const int BlockMenuItem = 40, BlockMenuCenter = 41, BlockMenuBack = 42; public const string CenterBlock = "necroSkullDecorCenter"; public const string BackBlock = "necroSkullDecorBack"; public static bool IsSkull(ItemClass _item) => _item != null && _item.Name == NecroSkull.ItemName; public static bool IsDecor(ItemClass _item) => _item != null && (_item.Name == CenterBlock || _item.Name == BackBlock); /// Стопка в руке превращается в _name (тот же счёт). public static void Convert(EntityPlayerLocal _pl, string _name) { if (_pl?.inventory == null) return; ItemStack cur = _pl.inventory.holdingItemStack; if (cur == null || cur.IsEmpty() || !(IsSkull(cur.itemValue.ItemClass) || IsDecor(cur.itemValue.ItemClass))) return; 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 OnItemMenu(XUiC_Radial _sender, int _commandIndex, XUiC_Radial.RadialContextAbs _context) { EntityPlayerLocal pl = _sender?.xui?.playerUI?.entityPlayer; if (_commandIndex == CenterIdx) Convert(pl, CenterBlock); else if (_commandIndex == BackIdx) Convert(pl, BackBlock); } public static void OpenItemMenu(PlayerMoveController _pmc, EntityPlayerLocal _pl) { XUiC_Radial r = _pmc.playerUI?.xui?.RadialWindow; if (r == null) return; r.Open(); r.ResetRadialEntries(); r.CreateRadialEntry(ItemIdx, "necroSkull", "ItemIconAtlas", "", Localization.Get("necroSkullModeOff"), true); r.CreateRadialEntry(CenterIdx, "ui_game_symbol_skull", "UIAtlas", "", Localization.Get("necroSkullDecorCenter")); r.CreateRadialEntry(BackIdx, "ui_game_symbol_skull", "UIAtlas", "", Localization.Get("necroSkullDecorBack")); r.SetCommonData(UIUtils.GetButtonIconForAction(_pmc.playerInput.Reload), OnItemMenu, new XUiC_Radial.RadialContextHoldingSlotIndex(_pl.inventory.holdingItemIdx), ItemIdx, false, XUiC_Radial.RadialValidSameHoldingSlotIndex); } /// Меню блока с декором в руке: наши пункты - сами, остальное - игре. public static void OnBlockMenu(XUiC_Radial _sender, int _commandIndex, XUiC_Radial.RadialContextAbs _context) { EntityPlayerLocal pl = _sender?.xui?.playerUI?.entityPlayer; switch (_commandIndex) { case BlockMenuItem: Convert(pl, NecroSkull.ItemName); return; case BlockMenuCenter: Convert(pl, CenterBlock); return; case BlockMenuBack: Convert(pl, BackBlock); return; } _sender.handleBlockShapeCommand(_sender, _commandIndex, _context); } } /// R с черепом-предметом в руке - меню «предмет / два декора». [HarmonyPatch(typeof(PlayerMoveController), nameof(PlayerMoveController.Update))] public static class Patch_PlayerMoveController_Update_SkullDecor { public static void Postfix(PlayerMoveController __instance) { EntityPlayerLocal pl = __instance.entityPlayerLocal; if (pl == null || pl.inventory == null || !SkullDecorPlace.IsSkull(pl.inventory.holdingItem) || pl.IsDead()) { return; } PlayerActionsLocal inp = __instance.playerInput; if (inp == null || !(inp.Reload.WasPressed || inp.PermanentActions.Reload.WasPressed) || pl.inventory.IsHandSwitching()) { return; } if (__instance.playerUI == null || __instance.playerUI.windowManager.IsModalWindowOpen()) { return; } SkullDecorPlace.OpenItemMenu(__instance, pl); } } /// Меню блока с черепом-декором в руке: к пунктам игры - «Просто череп» и второй вид. [HarmonyPatch(typeof(XUiC_Radial), nameof(XUiC_Radial.SetupBlockShapeData))] public static class Patch_XUiC_Radial_SetupBlockShapeData_SkullDecor { public static void Postfix(XUiC_Radial __instance) { EntityPlayerLocal pl = __instance.xui?.playerUI?.entityPlayer; ItemClass held = pl?.inventory?.holdingItem; if (!SkullDecorPlace.IsDecor(held)) { return; } __instance.CreateRadialEntry(SkullDecorPlace.BlockMenuItem, "necroSkull", "ItemIconAtlas", "", Localization.Get("necroSkullModeOff")); if (held.Name == SkullDecorPlace.CenterBlock) __instance.CreateRadialEntry(SkullDecorPlace.BlockMenuBack, "ui_game_symbol_skull", "UIAtlas", "", Localization.Get("necroSkullDecorBack")); else __instance.CreateRadialEntry(SkullDecorPlace.BlockMenuCenter, "ui_game_symbol_skull", "UIAtlas", "", Localization.Get("necroSkullDecorCenter")); __instance.SetCommonData(UIUtils.GetButtonIconForAction(__instance.xui.playerUI.playerInput.Activate), SkullDecorPlace.OnBlockMenu, new XUiC_Radial.RadialContextHoldingSlotIndex(pl.inventory.holdingItemIdx), -1, true, XUiC_Radial.RadialValidSameHoldingSlotIndex); } } }