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(); // Renderer = Components.Get( FindMode.EverythingInSelfAndDescendants ); var cameraComponent = Camera.GetComponent(); var listener = Components.Get( 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(); } /// /// Реализация интерфейса IUseContext /// Возвращает список используемых предметов /// public IEnumerable GetUsables() { // Возвращаем пустой список, так как теперь оружие управляется через новую систему return Enumerable.Empty(); } /// /// Обновление поворота тела с учетом режима прицеливания /// 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; // Помечаем, что тело повернулось } } }