using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
using UnityEngine.Scripting;
namespace NecromancerTome
{
///
/// Подбор посадки модели предмета в руке прямо в игре (10.10.2026, череп): как necropose для жестов. Сдвиг, поворот
/// и масштаб применяются к дочерним узлам модели в руке (корень модели держит игра) каждый кадр - переживают смену
/// слота. Итог - строкой в лог, затем переносится в настройку модели (NecroSkullSetup и т.п.) и бандл пересобирается
/// при закрытой игре.
///
public static class HeldTweak
{
public static bool Enabled;
public static Vector3 Pos; // метры, в осях модели
public static Vector3 Rot; // градусы, добавка к исходному повороту
public static float Scale = 1f;
/// Предмет, для которого подбирается посадка (тот, что был в руке при первой команде). Другие предметы
/// не трогаются: 10.10 без этого поворот черепа применился и к книге, взятой следом.
public static string Item;
/// Исходные локальные значения детей модели (по экземпляру узла).
static readonly Dictionary _base = new Dictionary();
public static Transform Model()
{
EntityPlayerLocal pl = GameManager.Instance?.World?.GetPrimaryPlayer();
return pl?.inventory?.GetHoldingItemTransform();
}
public static string HeldName()
{
EntityPlayerLocal pl = GameManager.Instance?.World?.GetPrimaryPlayer();
return pl?.inventory?.holdingItem?.Name;
}
public static void Apply()
{
Transform m = Model();
if (m == null || (Enabled && HeldName() != Item))
{
return;
}
for (int i = 0; i < m.childCount; i++)
{
Transform c = m.GetChild(i);
int id = c.GetInstanceID();
if (!_base.TryGetValue(id, out var b))
{
if (!Enabled)
{
continue;
}
b = (c.localPosition, c.localRotation, c.localScale);
_base[id] = b;
}
if (Enabled)
{
c.localPosition = Scale * b.p + Pos;
c.localRotation = Quaternion.Euler(Rot) * b.r;
c.localScale = b.s * Scale;
}
else
{
c.localPosition = b.p;
c.localRotation = b.r;
c.localScale = b.s;
_base.Remove(id);
}
}
}
/// Сменить хват (HoldType) предмета в руке и пересобрать его в руке, как BloodTide (16.10: «давай попробуем в
/// режиме нитей разные хваты. Ты будешь перебирать их по моей просьбе»). Меняется класс предмета - до перезапуска
/// игры или следующей команды; итог переносится в items.xml.
public static string SetHold(int _hold)
{
EntityPlayerLocal pl = GameManager.Instance?.World?.GetPrimaryPlayer();
Inventory inv = pl?.inventory;
ItemInventoryData held = inv?.holdingItemData;
if (held?.item == null)
{
return "в руке ничего";
}
if (pl.Hand != null && pl.Hand.isSwitching || inv.IsHoldingItemActionRunning())
{
return "рука занята - повторите";
}
int was = held.item.HoldType.Value;
held.item.HoldType.Value = _hold;
ItemValue pv = (held.physicalItemValue ?? held.itemValue).Clone();
pv.Seed = (ushort)(pv.Seed + 1); // «собранный» предмет устарел - соберётся заново (NeedsRebuild)
held.physicalItemValue = pv;
inv.SetHoldingItemIdx(inv.holdingItemIdx);
return held.item.Name + ": хват " + was + " -> " + _hold;
}
public static string F(float _v) => _v.ToString("0.###", CultureInfo.InvariantCulture);
public static string F(Vector3 _v) => F(_v.x) + " " + F(_v.y) + " " + F(_v.z);
}
///
/// necroheld - посадка модели в руке:
/// pos x y z (см) | add x y z (см) | rot x y z (градусы) | scale s | hold N (хват, HoldType) | off | show
///
[Preserve]
public class ConsoleCmdNecroHeld : ConsoleCmdAbstract
{
public override string[] getCommands() => new[] { "necroheld" };
public override string getDescription() => "NecromancerTome: посадка модели в руке - pos x y z (см)|add x y z (см)|rot x y z|scale s|hold N|off|show";
static bool Num(List _p, int _i, out float _v)
{
_v = 0f;
return _p.Count > _i && float.TryParse(_p[_i].Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out _v);
}
static bool Vec(List _p, out Vector3 _v)
{
bool ok = Num(_p, 1, out float x) & Num(_p, 2, out float y) & Num(_p, 3, out float z);
_v = new Vector3(x, y, z);
return ok;
}
public override void Execute(List _params, CommandSenderInfo _senderInfo)
{
string cmd = _params.Count > 0 ? _params[0] : "show";
Vector3 v;
switch (cmd)
{
case "pos" when Vec(_params, out v):
HeldTweak.Pos = v / 100f;
break;
case "add" when Vec(_params, out v):
HeldTweak.Pos += v / 100f;
break;
case "rot" when Vec(_params, out v):
HeldTweak.Rot = v;
break;
case "scale" when Num(_params, 1, out float s) && s > 0f:
HeldTweak.Scale = s;
break;
case "tphold" when Num(_params, 1, out float th):
ThreadKnifeBody.TpHold = (int)th;
Out("хват тела для ножа-для-нитей: " + ThreadKnifeBody.TpHold + (ThreadKnifeBody.TpHold > 0 ? "" : " (выкл - как у рук)") + "; место в руке - при следующем доставании");
return;
case "hold" when Num(_params, 1, out float h):
Out(HeldTweak.SetHold((int)h));
return;
case "hold":
Out("хват предмета в руке: " + (GameManager.Instance?.World?.GetPrimaryPlayer()?.inventory?.holdingItem?.HoldType.Value.ToString() ?? "нет"));
return;
case "off":
HeldTweak.Enabled = false;
HeldTweak.Pos = Vector3.zero;
HeldTweak.Rot = Vector3.zero;
HeldTweak.Scale = 1f;
Out("посадка сброшена");
return;
case "show":
break;
default:
Out(getDescription());
return;
}
if (cmd != "show")
{
if (!HeldTweak.Enabled)
{
HeldTweak.Item = HeldTweak.HeldName();
}
HeldTweak.Enabled = true;
}
Transform m = HeldTweak.Model();
Out("предмет " + HeldTweak.Item + ", модель " + (m != null ? m.name : "нет") + ": сдвиг (см) " + HeldTweak.F(HeldTweak.Pos * 100f) + ", поворот " +
HeldTweak.F(HeldTweak.Rot) + ", масштаб " + HeldTweak.F(HeldTweak.Scale) + (HeldTweak.Enabled ? "" : " (выключено)"));
}
static void Out(string _s)
{
SdtdConsole.Instance.Output(_s);
Debug.Log("[NecromancerTome] necroheld: " + _s);
}
}
}