Череп ставится декором прямо из руки: R - меню вида (просто череп / по центру / у стены), ПКМ - поставить

Предмет necroSkull остаётся ItemClass, добавлено Action1 PlaceAsBlock (в ItemValue не пишется -
сейвы целы). R для черепа ловится постфиксом PlayerMoveController.Update, выбор - cvar
necroSkullDecorMode. Поднятый и разрушенный блок - снова череп. Помощник necroSkullDecor спрятан
(CreativeMode None), рецепт «череп -> декор» убран, обратный оставлен для уже сделанных помощников.
Описание черепа дополнено (13 языков), подсказка и пункт меню «Просто череп».

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-15 15:28:04 +03:00
co-authored by Claude Opus 5.5
parent 8920ee01b8
commit a902b44bdf
5 changed files with 131 additions and 6 deletions
+116
View File
@@ -0,0 +1,116 @@
using GUI_2;
using HarmonyLib;
using UnityEngine;
namespace NecromancerTome
{
/// <summary>
/// Череп ставится декором прямо из руки (15.10.2026, указание: «Сейчас держу в руках именно череп. И он нормальный.
/// Но его нельзя поставить как декор. Может сделать возможность через R переключаться в режим декора и сразу
/// выбирать центральный или у стены?»; «А при поднятии снова делать просто черепом»).
/// Удержание R с черепом в руке - круговое меню: «Просто череп» / «Декор: по центру» / «Декор: у стены».
/// ПКМ - ставит блок выбранного вида (Action1 = PlaceAsBlock у necroSkull в items.xml); в «Просто череп» - ничего.
/// Поднятый блок (E) и разрушенный - снова череп-предмет (CanPickup param1 / drop в blocks.xml).
/// Предмет остаётся ItemClass: действие в ItemValue не пишется (ItemValue.Write: тип, износ, качество, Meta,
/// метаданные, статы, моды) - старые стаки черепа читаются как прежде.
/// R для черепа игра сама не обрабатывает (PlayerMoveController открывает меню только оружию, ближнему бою и
/// блокам) - поэтому нажатие ловится постфиксом к PlayerMoveController.Update. Выбор - cvar игрока
/// (necroSkullDecorMode, без точки - пишется в сейв).
/// </summary>
public static class SkullDecorPlace
{
public const string ModeCVar = "necroSkullDecorMode";
public const int Off = 0, Center = 1, Back = 2;
public const string CenterBlock = "necroSkullDecorCenter";
public const string BackBlock = "necroSkullDecorBack";
public static bool IsSkull(ItemClass _item) => _item != null && _item.Name == NecroSkull.ItemName;
public static int Mode(EntityAlive _e)
{
if (_e == null) return Off;
int m = Mathf.RoundToInt(_e.GetCVar(ModeCVar));
return m >= Off && m <= Back ? m : Off;
}
public static void OnRadialCommand(XUiC_Radial _sender, int _commandIndex, XUiC_Radial.RadialContextAbs _context)
{
EntityPlayerLocal pl = _sender?.xui?.playerUI?.entityPlayer;
if (pl != null && _commandIndex >= Off && _commandIndex <= Back)
{
pl.SetCVar(ModeCVar, _commandIndex);
}
}
public static void OpenMenu(PlayerMoveController _pmc, EntityPlayerLocal _pl)
{
XUiC_Radial r = _pmc.playerUI?.xui?.RadialWindow;
if (r == null) return;
int cur = Mode(_pl);
r.Open();
r.ResetRadialEntries();
r.CreateRadialEntry(Off, "ui_game_symbol_hand", "UIAtlas", "", Localization.Get("necroSkullModeOff"), cur == Off);
r.CreateRadialEntry(Center, "ui_game_symbol_skull", "UIAtlas", "", Localization.Get("necroSkullDecorCenter"), cur == Center);
r.CreateRadialEntry(Back, "ui_game_symbol_skull", "UIAtlas", "", Localization.Get("necroSkullDecorBack"), cur == Back);
r.SetCommonData(UIUtils.GetButtonIconForAction(_pmc.playerInput.Reload), OnRadialCommand,
new XUiC_Radial.RadialContextHoldingSlotIndex(_pl.inventory.holdingItemIdx), cur, false,
XUiC_Radial.RadialValidSameHoldingSlotIndex);
}
}
/// <summary>R с черепом в руке - меню вида декора.</summary>
[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.OpenMenu(__instance, pl);
}
}
/// <summary>ПКМ с черепом: ставит выбранный вид; в «Просто череп» не ставит (и подсказывает про R).</summary>
[HarmonyPatch(typeof(ItemActionPlaceAsBlock), nameof(ItemActionPlaceAsBlock.ExecuteAction))]
public static class Patch_ItemActionPlaceAsBlock_ExecuteAction_SkullDecor
{
static float _hintAt;
public static bool Prefix(ItemActionPlaceAsBlock __instance, ItemActionData _actionData, bool _bReleased)
{
if (!SkullDecorPlace.IsSkull(__instance.item))
{
return true;
}
EntityAlive e = _actionData?.invData?.holdingEntity;
int mode = SkullDecorPlace.Mode(e);
if (mode == SkullDecorPlace.Off)
{
if (_bReleased && e is EntityPlayerLocal pl && Time.time - _hintAt > 2f)
{
_hintAt = Time.time;
GameManager.ShowTooltip(pl, Localization.Get("necroSkullDecorHint"));
}
return false;
}
BlockValue bv = Block.GetBlockValue(mode == SkullDecorPlace.Back ? SkullDecorPlace.BackBlock : SkullDecorPlace.CenterBlock);
if (!bv.isair)
{
__instance.blockToPlace = bv;
}
return true;
}
}
}