necroview: камера под интерфейсом; режим free - выйти из тела

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-16 23:15:56 +03:00
co-authored by Claude Opus 5.5
parent 606f9c3816
commit 87fad62c62
+96 -2
View File
@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using HarmonyLib;
using UnityEngine; using UnityEngine;
using UnityEngine.Scripting; using UnityEngine.Scripting;
@@ -29,6 +30,61 @@ namespace NecromancerTome
static Quaternion _lockCam; static Quaternion _lockCam;
static bool _mouseBroken; static bool _mouseBroken;
/// <summary>«Выйти из тела» (16.10: «временно мог выйти из тела, когда попрошу? Чтобы мог ходить вокруг тела и
/// осматривать»): камера летает свободно - WASD, мышь, пробел/Ctrl - выше/ниже, Shift - быстрее; тело стоит
/// (PlayerMoveController.Update пропускается, как при одержимости), поза рук замерла. Выход - End или necroview off.</summary>
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) public static string Start(float _yaw, float _pitch, float _dist)
{ {
Transform pc = HandPose.Camera(); Transform pc = HandPose.Camera();
@@ -50,7 +106,16 @@ namespace NecromancerTome
_cam = go.AddComponent<Camera>(); _cam = go.AddComponent<Camera>();
} }
_cam.CopyFrom(pl.playerCamera); _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.nearClipPlane = 0.01f;
_cam.gameObject.SetActive(true); _cam.gameObject.SetActive(true);
HandPose.HoldStill = true; HandPose.HoldStill = true;
@@ -63,6 +128,7 @@ namespace NecromancerTome
public static void Stop() public static void Stop()
{ {
On = false; On = false;
Free = false;
HandPose.HoldStill = false; HandPose.HoldStill = false;
if (_cam != null) if (_cam != null)
{ {
@@ -85,6 +151,16 @@ namespace NecromancerTome
return; return;
} }
float dt = Time.unscaledDeltaTime; float dt = Time.unscaledDeltaTime;
if (Free)
{
if (Input.GetKeyDown(KeyCode.End))
{
Stop();
return;
}
UpdateFree(dt);
return;
}
EntityPlayerLocal pl = GameManager.Instance?.World?.GetPrimaryPlayer(); EntityPlayerLocal pl = GameManager.Instance?.World?.GetPrimaryPlayer();
if (Mouse && pl != null && !_mouseBroken) if (Mouse && pl != null && !_mouseBroken)
{ {
@@ -128,7 +204,7 @@ namespace NecromancerTome
public class ConsoleCmdNecroView : ConsoleCmdAbstract public class ConsoleCmdNecroView : ConsoleCmdAbstract
{ {
public override string[] getCommands() => new[] { "necroview" }; 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<string> _p, int _i, out float _v) static bool Num(List<string> _p, int _i, out float _v)
{ {
@@ -159,6 +235,9 @@ namespace NecromancerTome
NecroView.Pitch = p2; NecroView.Pitch = p2;
NecroView.Dist = d2; NecroView.Dist = d2;
break; break;
case "free":
Out(NecroView.StartFree());
return;
case "mouse": case "mouse":
NecroView.Mouse = !(_params.Count > 1 && _params[1] == "off"); NecroView.Mouse = !(_params.Count > 1 && _params[1] == "off");
Out("мышь " + (NecroView.Mouse ? "облетает руки" : "крутит персонажа, облёт - цифровой блок")); Out("мышь " + (NecroView.Mouse ? "облетает руки" : "крутит персонажа, облёт - цифровой блок"));
@@ -172,4 +251,19 @@ namespace NecromancerTome
Out(NecroView.On ? "вокруг " + NecroView.F(NecroView.Yaw) + ", высота " + NecroView.F(NecroView.Pitch) + ", расстояние " + NecroView.F(NecroView.Dist) : "осмотр выключен"); Out(NecroView.On ? "вокруг " + NecroView.F(NecroView.Yaw) + ", высота " + NecroView.F(NecroView.Pitch) + ", расстояние " + NecroView.F(NecroView.Dist) : "осмотр выключен");
} }
} }
/// <summary>Вне тела (NecroView.Free) тело не ходит и не крутится - как при одержимости.</summary>
[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;
}
}
} }