diff --git a/HarmonySrc/HandPosePose.cs b/HarmonySrc/HandPosePose.cs
index 631104e..6422981 100644
--- a/HarmonySrc/HandPosePose.cs
+++ b/HarmonySrc/HandPosePose.cs
@@ -323,6 +323,7 @@ namespace NecromancerTome
{
HandHolds.Update();
HandPose.Apply();
+ HeldTweak.Apply();
}
}
diff --git a/HarmonySrc/HeldTweak.cs b/HarmonySrc/HeldTweak.cs
new file mode 100644
index 0000000..a3b461c
--- /dev/null
+++ b/HarmonySrc/HeldTweak.cs
@@ -0,0 +1,139 @@
+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;
+
+ /// Исходные локальные значения детей модели (по экземпляру узла).
+ static readonly Dictionary _base = new Dictionary();
+
+ public static Transform Model()
+ {
+ EntityPlayerLocal pl = GameManager.Instance?.World?.GetPrimaryPlayer();
+ return pl?.inventory?.GetHoldingItemTransform();
+ }
+
+ public static void Apply()
+ {
+ Transform m = Model();
+ if (m == null)
+ {
+ 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);
+ }
+ }
+ }
+
+ 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 | 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|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 "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")
+ {
+ HeldTweak.Enabled = true;
+ }
+ Transform m = HeldTweak.Model();
+ Out("модель " + (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);
+ }
+ }
+}
diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll
index 2a6a242..a770afa 100644
Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ