193 lines
5.7 KiB
C#
193 lines
5.7 KiB
C#
using System;
|
||
using Sandbox;
|
||
using Sandbox.Citizen;
|
||
using Sasalka;
|
||
using ShrimpleCharacterController;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
|
||
public sealed partial class Dedugan : Component, IUseContext, Component.INetworkSpawn
|
||
{
|
||
[RequireComponent] public ShrimpleCharacterController.ShrimpleCharacterController Controller { get; set; }
|
||
[RequireComponent] public CitizenAnimationHelper AnimationHelper { get; set; }
|
||
[Property] public SkinnedModelRenderer Renderer { get; set; }
|
||
[Property] public GameObject Camera { get; set; }
|
||
[Property] public GameObject CameraPivot { get; set; }
|
||
[Property] public GameObject Eyes { get; set; }
|
||
[Property] public GameObject InventoryUI { get; set; }
|
||
|
||
[Property] public Vector3 CamOffset { get; set; }
|
||
[Property] [Range( 50f, 1200f, 10f )] public float WalkSpeed { get; set; } = 100f;
|
||
[Property] [Range( 100f, 1500f, 20f )] public float RunSpeed { get; set; } = 300f;
|
||
[Property] [Range( 25f, 1100f, 5f )] public float DuckSpeed { get; set; } = 50f;
|
||
[Property] [Range( 200f, 1500f, 20f )] public float JumpStrength { get; set; } = 350f;
|
||
[Property] [Range( 10f, 500f, 10f )] public float InteractDistance { get; set; } = 350f;
|
||
|
||
[Sync] public Angles NetworkedEyeAngles { get; set; }
|
||
public Angles EyeAngles { get; set; }
|
||
[Sync] private float IsDucking { get; set; } = 0f;
|
||
[Sync] public bool IsDancing { get; set; } = false;
|
||
[Sync] public int Health { get; set; } = 100;
|
||
private RagdollController RagdollController { get; set; }
|
||
|
||
[Property] public Vector3 OverrideGravity { get; set; } = Vector3.Zero;
|
||
|
||
private Vector3 _directionToAxis = Vector3.Up;
|
||
private Vector3 _up = Vector3.Up;
|
||
private Vector3 _forward = Vector3.Forward;
|
||
private Vector3 _right = Vector3.Right;
|
||
|
||
private Vector3 _wishDirection;
|
||
|
||
// Кэш для оптимизации
|
||
private bool _isOwner;
|
||
private float _lastBodyYaw;
|
||
private float _lastCameraYaw;
|
||
private bool _bodyRotationDirty = true;
|
||
|
||
protected override void OnStart()
|
||
{
|
||
InventoryStart();
|
||
|
||
RagdollController = Components.Get<RagdollController>();
|
||
// Renderer = Components.Get<SkinnedModelRenderer>( FindMode.EverythingInSelfAndDescendants );
|
||
|
||
var cameraComponent = Camera.GetComponent<CameraComponent>();
|
||
var listener = Components.Get<AudioListener>( true );
|
||
cameraComponent.Enabled = false;
|
||
InventoryUI.Enabled = false;
|
||
listener.Enabled = false;
|
||
|
||
if ( !Network.IsOwner ) return;
|
||
|
||
cameraComponent.Enabled = true;
|
||
listener.Enabled = true;
|
||
InventoryUI.Enabled = true;
|
||
|
||
if ( Renderer is null )
|
||
return;
|
||
|
||
Renderer.OnFootstepEvent += OnFootstepEvent;
|
||
}
|
||
|
||
protected override void OnUpdate()
|
||
{
|
||
_isOwner = Network.IsOwner;
|
||
|
||
UpdateCustomAnimations();
|
||
InventoryUpdate();
|
||
|
||
if ( _isOwner )
|
||
{
|
||
EyeAngles += Input.AnalogLook;
|
||
EyeAngles = EyeAngles.WithPitch( MathX.Clamp( EyeAngles.pitch, -89f, 89f ) );
|
||
NetworkedEyeAngles = EyeAngles;
|
||
|
||
UpdateBodyRotation();
|
||
|
||
RotateCamera();
|
||
InteractionsUpdate();
|
||
}
|
||
else
|
||
{
|
||
EyeAngles = NetworkedEyeAngles;
|
||
|
||
UpdateBodyRotation();
|
||
|
||
Camera.LocalRotation = EyeAngles.ToRotation();
|
||
|
||
var pivotOffset = CameraPivot.LocalRotation.Right * CamOffset.y +
|
||
CameraPivot.LocalRotation.Forward * CamOffset.x +
|
||
CameraPivot.LocalRotation.Up * CamOffset.z;
|
||
|
||
var rotatedOffset = pivotOffset * EyeAngles.ToRotation();
|
||
|
||
Camera.WorldPosition = CameraPivot.WorldPosition + rotatedOffset;
|
||
}
|
||
}
|
||
|
||
protected override void OnFixedUpdate()
|
||
{
|
||
UpdateMovement();
|
||
}
|
||
|
||
protected override void DrawGizmos()
|
||
{
|
||
DrawDebugGizmos();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Реализация интерфейса IUseContext
|
||
/// Возвращает список используемых предметов
|
||
/// </summary>
|
||
public IEnumerable<IUseable> GetUsables()
|
||
{
|
||
// Возвращаем пустой список, так как теперь оружие управляется через новую систему
|
||
return Enumerable.Empty<IUseable>();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Обновление поворота тела с учетом режима прицеливания
|
||
/// </summary>
|
||
private void UpdateBodyRotation()
|
||
{
|
||
var currentBodyYaw = Renderer.LocalRotation.Yaw();
|
||
var currentCameraYaw = EyeAngles.yaw;
|
||
|
||
// Проверяем, изменились ли углы
|
||
if ( Math.Abs( currentBodyYaw - _lastBodyYaw ) < 0.1f &&
|
||
Math.Abs( currentCameraYaw - _lastCameraYaw ) < 0.1f &&
|
||
!_bodyRotationDirty )
|
||
{
|
||
return; // Пропускаем обновление, если углы не изменились
|
||
}
|
||
|
||
_lastBodyYaw = currentBodyYaw;
|
||
_lastCameraYaw = currentCameraYaw;
|
||
_bodyRotationDirty = false;
|
||
|
||
var targetRotation = Rotation.LookAt( Rotation.FromYaw( EyeAngles.yaw ).Forward, -_directionToAxis );
|
||
var currentForward = Renderer.LocalRotation.Forward;
|
||
float angleDiff = currentForward.Angle( targetRotation.Forward );
|
||
|
||
// Проверяем, нужно ли поворачивать тело
|
||
bool shouldRotateBody = false;
|
||
|
||
|
||
if ( InAds )
|
||
{
|
||
if ( Controller.Velocity.Length > 10f )
|
||
{
|
||
// При беге - центрируем тело (угол > 15°)
|
||
if ( angleDiff > 15f )
|
||
{
|
||
shouldRotateBody = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// При стоянии - мертвая зона 85°
|
||
if ( angleDiff > 85f )
|
||
{
|
||
shouldRotateBody = true;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// В обычном режиме - поворот при движении и угле больше 15 градусов
|
||
if ( angleDiff > 15f && Controller.Velocity.Length > 10f )
|
||
{
|
||
shouldRotateBody = true;
|
||
}
|
||
}
|
||
|
||
if ( shouldRotateBody )
|
||
{
|
||
Renderer.LocalRotation = Rotation.Slerp( Renderer.LocalRotation, Rotation.FromYaw( EyeAngles.yaw ),
|
||
Time.Delta * 3f );
|
||
_bodyRotationDirty = true; // Помечаем, что тело повернулось
|
||
}
|
||
}
|
||
}
|