diff --git a/HarmonySrc/HandPosePose.cs b/HarmonySrc/HandPosePose.cs index 38e8d88..9dd82fd 100644 --- a/HarmonySrc/HandPosePose.cs +++ b/HarmonySrc/HandPosePose.cs @@ -76,6 +76,8 @@ namespace NecromancerTome /// положение ВСЕХ костей под Hips, и каждый кадр они возвращаются на место, а поверх - поза. /// Снимок только в покое аниматора (ItemActionIndex = -1), иначе застыл бы кадр удара. public static bool Freeze = true; + /// Замереть без своего хвата - на время осмотра со стороны (NecroView). + public static bool HoldStill; static readonly List _frozen = new List(); static readonly List _frozenPos = new List(); static readonly List _frozenRot = new List(); @@ -87,6 +89,7 @@ namespace NecromancerTome { Enabled = false; KnifeToLeft = false; + NecroView.Stop(); Overrides.Clear(); CamOverrides.Clear(); _jabStart = -1f; @@ -143,10 +146,18 @@ namespace NecromancerTome { if (!Enabled || Overrides.Count == 0 && CamOverrides.Count == 0) { - if (_frozen.Count > 0) + if (HoldStill) + { + FreezeBones(); // осмотр со стороны (necroview): поза замирает и без своего хвата + } + else if (_frozen.Count > 0) { Unfreeze(); } + if (KnifeToLeft && Enabled) + { + ApplyKnifeToLeft(); + } return; } if (Freeze) @@ -435,6 +446,7 @@ namespace NecromancerTome HandHolds.Update(); HandPose.Apply(); HeldTweak.Apply(); + NecroView.Update(); } } diff --git a/HarmonySrc/NecroView.cs b/HarmonySrc/NecroView.cs new file mode 100644 index 0000000..45c6e85 --- /dev/null +++ b/HarmonySrc/NecroView.cs @@ -0,0 +1,146 @@ +using System.Collections.Generic; +using System.Globalization; +using UnityEngine; +using UnityEngine.Scripting; + +namespace NecromancerTome +{ + /// + /// Осмотр рук от первого лица со стороны (16.10: «зафиксировать командой позу игрока и осматривать его камерой... хват + /// от первого лица не позволяет видеть всех нюансов»). Руки первого лица (baseRigFP) - отдельная модель под камерой + /// игрока, в виде от третьего лица их нет, а у тела свои анимации. Поэтому вид остаётся от первого лица, а поверх + /// рисует своя камера, которая облетает руки. Её место задано в осях камеры игрока: повернёшь голову - вид на руки + /// не меняется. Поза замирает (снимок всех костей, как у хватов мода). + /// Управление - цифровой блок: 4/6 вокруг, 8/2 выше/ниже, +/- ближе/дальше, 5 - сначала; или командой necroview. + /// + public static class NecroView + { + public static bool On; + public static float Yaw, Pitch, Dist = 0.6f; + /// Точка, вокруг которой летает камера (между кистями), - в осях камеры игрока. + static Vector3 _target; + static Camera _cam; + const float TurnSpeed = 60f, ZoomSpeed = 0.4f; + + public static string Start(float _yaw, float _pitch, float _dist) + { + Transform pc = HandPose.Camera(); + EntityPlayerLocal pl = GameManager.Instance?.World?.GetPrimaryPlayer(); + if (pc == null || pl?.playerCamera == null) + { + return "нет камеры игрока"; + } + Transform l = HandPose.Bone("LeftHand"), r = HandPose.Bone("RightHand"); + Vector3 mid = l != null && r != null ? (l.position + r.position) * 0.5f : pc.position + pc.forward * 0.4f; + _target = pc.InverseTransformPoint(mid); + Yaw = _yaw; + Pitch = _pitch; + Dist = _dist; + if (_cam == null) + { + GameObject go = new GameObject("NecroViewCam"); + Object.DontDestroyOnLoad(go); + _cam = go.AddComponent(); + } + _cam.CopyFrom(pl.playerCamera); + _cam.depth = pl.playerCamera.depth + 50f; + _cam.nearClipPlane = 0.01f; + _cam.gameObject.SetActive(true); + HandPose.HoldStill = true; + On = true; + return "осмотр включён: вокруг " + Yaw + ", высота " + Pitch + ", расстояние " + Dist; + } + + public static void Stop() + { + On = false; + HandPose.HoldStill = false; + if (_cam != null) + { + Object.Destroy(_cam.gameObject); + _cam = null; + } + } + + /// Из HandPoseDriver.LateUpdate - после позы. + public static void Update() + { + if (!On) + { + return; + } + Transform pc = HandPose.Camera(); + if (pc == null || _cam == null) + { + Stop(); + return; + } + float dt = Time.unscaledDeltaTime; + if (Input.GetKey(KeyCode.Keypad4)) Yaw -= TurnSpeed * dt; + if (Input.GetKey(KeyCode.Keypad6)) Yaw += TurnSpeed * dt; + if (Input.GetKey(KeyCode.Keypad8)) Pitch = Mathf.Min(85f, Pitch + TurnSpeed * dt); + if (Input.GetKey(KeyCode.Keypad2)) Pitch = Mathf.Max(-85f, Pitch - TurnSpeed * dt); + if (Input.GetKey(KeyCode.KeypadPlus)) Dist = Mathf.Max(0.1f, Dist - ZoomSpeed * dt); + if (Input.GetKey(KeyCode.KeypadMinus)) Dist = Mathf.Min(3f, Dist + ZoomSpeed * dt); + if (Input.GetKeyDown(KeyCode.Keypad5)) + { + Yaw = 0f; + Pitch = 0f; + Dist = 0.6f; + } + // Yaw 0, Pitch 0 - смотрим на руки со стороны глаз игрока (сзади), 180 - спереди. + Quaternion local = Quaternion.Euler(Pitch, Yaw, 0f); + Vector3 offset = local * new Vector3(0f, 0f, -Dist); + Vector3 t = pc.TransformPoint(_target); + _cam.transform.SetPositionAndRotation(t + pc.rotation * offset, pc.rotation * local); + } + + public static string F(float _v) => _v.ToString("0.#", CultureInfo.InvariantCulture); + } + + /// necroview on [вокруг высота расстояние] | off | set вокруг высота расстояние | show + [Preserve] + public class ConsoleCmdNecroView : ConsoleCmdAbstract + { + public override string[] getCommands() => new[] { "necroview" }; + public override string getDescription() => "NecromancerTome: осмотр рук со стороны - on [вокруг высота расстояние]|off|set вокруг высота расстояние|show; цифровой блок 4/6 8/2 +/- 5"; + + 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); + } + + void Out(string _s) + { + SdtdConsole.Instance.Output(_s); + Debug.Log("[NecromancerTome] necroview: " + _s); + } + + public override void Execute(List _params, CommandSenderInfo _senderInfo) + { + string cmd = _params.Count > 0 ? _params[0] : ""; + switch (cmd) + { + case "on": + Out(NecroView.Start(Num(_params, 1, out float y) ? y : 150f, Num(_params, 2, out float p) ? p : 10f, Num(_params, 3, out float d) ? d : 0.6f)); + return; + case "off": + NecroView.Stop(); + Out("осмотр выключен"); + return; + case "set" when NecroView.On && Num(_params, 1, out float y2) && Num(_params, 2, out float p2) && Num(_params, 3, out float d2): + NecroView.Yaw = y2; + NecroView.Pitch = p2; + NecroView.Dist = d2; + break; + case "show": + break; + default: + Out(getDescription()); + return; + } + Out(NecroView.On ? "вокруг " + NecroView.F(NecroView.Yaw) + ", высота " + NecroView.F(NecroView.Pitch) + ", расстояние " + NecroView.F(NecroView.Dist) : "осмотр выключен"); + } + } +} diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll index e2e0662..fa44ea2 100644 Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ