using System.Collections.Generic; using Game.Features.Players.Contracts; using Game.Features.Players.Domain; using Game.Features.Players.Domain.Movement; using Game.Godot; using Game.Godot.Extensions; using Game.Godot.Mappings; using Godot; namespace Game.Features.Players.Godot; public partial class PlayerController : Node3D, IMovementModeSwitcher { [Export] public CharacterBody3D Body; [Export] public CollisionShape3D Collision; [Export] public Camera3D Camera; [Export] public float MoveSpeed = 4.0f; [Export] public float JumpVelocity = 4.5f; [Export] public float NoclipFlySpeed = 8.0f; [Export] public float MouseSensitivity = 0.003f; [Export] public float MaxLookAngleDegrees = 89.0f; private Dictionary _movementStrategies; private IMovementStrategy _currentMovementStrategy; private PlayerLook _look; public MovementMode ActiveMovementMode { get; private set; } = MovementMode.Ground; public void SetMovementMode(MovementMode movementMode) { ActiveMovementMode = movementMode; _currentMovementStrategy = _movementStrategies[movementMode]; Collision.Disabled = movementMode == MovementMode.Noclip; } public override void _Ready() { this.AssertRequiredExports(); _look = new PlayerLook(MouseSensitivity, MaxLookAngleDegrees); float gravity = (float)ProjectSettings.GetSetting(ProjectSetting.Gravity.SettingPath()); _movementStrategies = new Dictionary { [MovementMode.Ground] = new GroundMovement(MoveSpeed, JumpVelocity, gravity), [MovementMode.Noclip] = new NoclipMovement(NoclipFlySpeed) }; _currentMovementStrategy = _movementStrategies[ActiveMovementMode]; } public override void _UnhandledInput(InputEvent @event) { if (@event is not InputEventMouseMotion mouseMotion || !MouseCapture.IsCaptured) return; Body.RotateY(_look.CalculateYawDelta(mouseMotion.Relative.X)); _look.UpdatePitch(mouseMotion.Relative.Y); Camera.Rotation = new Vector3(_look.Pitch, 0f, 0f); } public override void _PhysicsProcess(double delta) { Vector2 inputDirection = Input.GetVector( InputAction.MoveLeft.ActionName(), InputAction.MoveRight.ActionName(), InputAction.MoveForward.ActionName(), InputAction.MoveBack.ActionName() ); MovementInput input = new( Right: Body.Transform.Basis.X, Back: Body.Transform.Basis.Z, Direction: inputDirection, IsOnFloor: Body.IsOnFloor(), JumpPressed: Input.IsActionJustPressed(InputAction.Jump.ActionName()), AscendPressed: Input.IsActionPressed(InputAction.Jump.ActionName()), DescendPressed: Input.IsActionPressed(InputAction.MoveDown.ActionName()), Delta: (float)delta ); Body.Velocity = _currentMovementStrategy.CalculateVelocity(Body.Velocity, input); Body.MoveAndSlide(); } }