From 87fad62c62dffa325a6bc02e098a9f5e4ced9e33 Mon Sep 17 00:00:00 2001 From: AlexCube Date: Fri, 16 Oct 2026 23:15:56 +0300 Subject: [PATCH] =?UTF-8?q?necroview:=20=D0=BA=D0=B0=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=D0=B4=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80?= =?UTF-8?q?=D1=84=D0=B5=D0=B9=D1=81=D0=BE=D0=BC;=20=D1=80=D0=B5=D0=B6?= =?UTF-8?q?=D0=B8=D0=BC=20free=20-=20=D0=B2=D1=8B=D0=B9=D1=82=D0=B8=20?= =?UTF-8?q?=D0=B8=D0=B7=20=D1=82=D0=B5=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- HarmonySrc/NecroView.cs | 98 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/HarmonySrc/NecroView.cs b/HarmonySrc/NecroView.cs index dfc2ec9..423e802 100644 --- a/HarmonySrc/NecroView.cs +++ b/HarmonySrc/NecroView.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Globalization; +using HarmonyLib; using UnityEngine; using UnityEngine.Scripting; @@ -29,6 +30,61 @@ namespace NecromancerTome static Quaternion _lockCam; static bool _mouseBroken; + /// «Выйти из тела» (16.10: «временно мог выйти из тела, когда попрошу? Чтобы мог ходить вокруг тела и + /// осматривать»): камера летает свободно - WASD, мышь, пробел/Ctrl - выше/ниже, Shift - быстрее; тело стоит + /// (PlayerMoveController.Update пропускается, как при одержимости), поза рук замерла. Выход - End или necroview off. + public static bool Free; + static Vector3 _freePos; + static float _freeYaw, _freePitch; + const float FlySpeed = 1.5f; + + public static string StartFree() + { + string r = Start(0f, 0f, 0.6f); + Transform pc = HandPose.Camera(); + if (!On || pc == null) + { + return r; + } + Free = true; + _freePos = pc.position; + Vector3 e = pc.eulerAngles; + _freeYaw = e.y; + _freePitch = e.x > 180f ? e.x - 360f : e.x; + return "вне тела: WASD, мышь, пробел/Ctrl - выше/ниже, Shift - быстрее, End - вернуться"; + } + + static void UpdateFree(float _dt) + { + PlayerMoveController pmc = PlayerMoveController.Instance; + Vector2 sens = pmc != null ? pmc.mouseLookSensitivity : Vector2.one * 5f; + if (!_mouseBroken) + { + try + { + _freeYaw += Input.GetAxis("Mouse X") * sens.x; + _freePitch = Mathf.Clamp(_freePitch - Input.GetAxis("Mouse Y") * sens.y, -89f, 89f); + } + catch (System.Exception e) + { + _mouseBroken = true; + Debug.LogWarning("[NecromancerTome] necroview: мышь не читается (" + e.Message + ")"); + } + } + Quaternion rot = Quaternion.Euler(_freePitch, _freeYaw, 0f); + Vector3 move = Vector3.zero; + if (Input.GetKey(KeyCode.W)) move += Vector3.forward; + if (Input.GetKey(KeyCode.S)) move += Vector3.back; + if (Input.GetKey(KeyCode.A)) move += Vector3.left; + if (Input.GetKey(KeyCode.D)) move += Vector3.right; + Vector3 world = rot * move; + if (Input.GetKey(KeyCode.Space)) world += Vector3.up; + if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.C)) world += Vector3.down; + float speed = FlySpeed * (Input.GetKey(KeyCode.LeftShift) ? 4f : 1f); + _freePos += world * speed * _dt; + _cam.transform.SetPositionAndRotation(_freePos, rot); + } + public static string Start(float _yaw, float _pitch, float _dist) { Transform pc = HandPose.Camera(); @@ -50,7 +106,16 @@ namespace NecromancerTome _cam = go.AddComponent(); } _cam.CopyFrom(pl.playerCamera); - _cam.depth = pl.playerCamera.depth + 50f; + // Сразу над камерой игрока, но ПОД камерами интерфейса (16.10: с +50 рисовала поверх меню - «мне из игры не выйти даже»). + float above = float.MaxValue; + foreach (Camera c in Camera.allCameras) + { + if (c != _cam && c.depth > pl.playerCamera.depth && c.depth < above) + { + above = c.depth; + } + } + _cam.depth = above < float.MaxValue ? (pl.playerCamera.depth + above) * 0.5f : pl.playerCamera.depth + 1f; _cam.nearClipPlane = 0.01f; _cam.gameObject.SetActive(true); HandPose.HoldStill = true; @@ -63,6 +128,7 @@ namespace NecromancerTome public static void Stop() { On = false; + Free = false; HandPose.HoldStill = false; if (_cam != null) { @@ -85,6 +151,16 @@ namespace NecromancerTome return; } float dt = Time.unscaledDeltaTime; + if (Free) + { + if (Input.GetKeyDown(KeyCode.End)) + { + Stop(); + return; + } + UpdateFree(dt); + return; + } EntityPlayerLocal pl = GameManager.Instance?.World?.GetPrimaryPlayer(); if (Mouse && pl != null && !_mouseBroken) { @@ -128,7 +204,7 @@ namespace NecromancerTome public class ConsoleCmdNecroView : ConsoleCmdAbstract { public override string[] getCommands() => new[] { "necroview" }; - public override string getDescription() => "NecromancerTome: осмотр рук со стороны - on [вокруг высота расстояние]|off|set вокруг высота расстояние|mouse [off]|show; мышь или цифровой блок 4/6 8/2 +/- 5"; + public override string getDescription() => "NecromancerTome: осмотр рук со стороны - on [вокруг высота расстояние]|off|set вокруг высота расстояние|free (выйти из тела, End - назад)|mouse [off]|show; мышь или цифровой блок 4/6 8/2 +/- 5"; static bool Num(List _p, int _i, out float _v) { @@ -159,6 +235,9 @@ namespace NecromancerTome NecroView.Pitch = p2; NecroView.Dist = d2; break; + case "free": + Out(NecroView.StartFree()); + return; case "mouse": NecroView.Mouse = !(_params.Count > 1 && _params[1] == "off"); Out("мышь " + (NecroView.Mouse ? "облетает руки" : "крутит персонажа, облёт - цифровой блок")); @@ -172,4 +251,19 @@ namespace NecromancerTome Out(NecroView.On ? "вокруг " + NecroView.F(NecroView.Yaw) + ", высота " + NecroView.F(NecroView.Pitch) + ", расстояние " + NecroView.F(NecroView.Dist) : "осмотр выключен"); } } + + /// Вне тела (NecroView.Free) тело не ходит и не крутится - как при одержимости. + [HarmonyPatch(typeof(PlayerMoveController), nameof(PlayerMoveController.Update))] + public static class Patch_PlayerMoveController_Update_NecroView + { + public static bool Prefix(PlayerMoveController __instance) + { + if (!NecroView.Free) + { + return true; + } + __instance.stopMoving(); + return false; + } + } }