using Godot; using GodotStateCharts; using Movementtests.systems; using Movementtests.player_controller.Scripts; public partial class PlayerController : CharacterBody3D { // User API to important child nodes. public HeadSystem HeadSystem; public Bobbing Bobbing; public FieldOfView FieldOfView; public Stamina Stamina; public StairsSystem StairsSystem; public MantleSystem MantleSystem; public DashSystem DashSystem; public CapsuleCollider CapsuleCollider; public Gravity Gravity; public HealthSystem HealthSystem; public MoveSystem MoveSystem; public TweenQueueSystem TweenQueueSystem; public StateChart PlayerState; private bool _movementEnabled = true; private bool _shouldMantle; private Vector3 _dashLocation = Vector3.Zero; private Vector3 _mantleLocation = Vector3.Zero; private float _lastFrameWasOnFloor = -Mathf.Inf; private const int NUM_OF_HEAD_COLLISION_DETECTORS = 4; private RayCast3D[] _headCollisionDetectors; private Vector3 _inputMove = Vector3.Zero; private float _inputRotateY; private float _inputRotateFloorplane; private bool _isAiming; private bool _dashCanceled; public override void _Ready() { _headCollisionDetectors = new RayCast3D[NUM_OF_HEAD_COLLISION_DETECTORS]; for (int i = 0; i < NUM_OF_HEAD_COLLISION_DETECTORS; i++) { _headCollisionDetectors[i] = GetNode( "HeadCollisionDetectors/HeadCollisionDetector" + i); } HeadSystem = GetNode("HeadSystem"); HeadSystem.Init(); // Get the state chart node and wrap it in a StateChart object like so, GetNode doesn't work PlayerState = StateChart.Of(GetNode("%StateChart")); // Get the poisoned state node and wrap it in a State object, so we can easily // interact with it from C#. // _poisonedStateChartState = StateChartState.Of(GetNode("%Poisoned")); // Getting dependencies of the components (In godot we manage this from upwards to downwards not vice versa) Camera3D camera = GetNode("HeadSystem/CameraSmooth/Camera3D"); Node3D cameraSmooth = GetNode("HeadSystem/CameraSmooth"); RayCast3D stairsBelowRayCast3D = GetNode("StairsBelowRayCast3D"); RayCast3D stairsAheadRayCast3D = GetNode("StairsAheadRayCast3D"); // Getting universal setting from GODOT editor to be in sync float gravitySetting = (float)ProjectSettings.GetSetting("physics/3d/default_gravity"); ColorRect vignetteRect = GetNode( "HeadSystem/CameraSmooth/Camera3D/CLVignette(Layer_1)/HealthVignetteRect"); ColorRect distortionRect = GetNode( "HeadSystem/CameraSmooth/Camera3D/CLDistortion(Layer_2)/HealthDistortionRect"); ColorRect blurRect = GetNode("HeadSystem/CameraSmooth/Camera3D/CLBlur(Layer_2)/BlurRect"); // Node3D mapNode = GetTree().Root.FindChild("Map", true, false) as Node3D; // Getting components Bobbing = GetNode("Bobbing"); Bobbing.Init(camera); FieldOfView = GetNode("FieldOfView"); FieldOfView.Init(camera); CapsuleCollider = GetNode("CapsuleCollider"); Gravity = GetNode("Gravity"); Gravity.Init(gravitySetting); MantleSystem = GetNode("MantleSystem"); MantleSystem.Init(HeadSystem); TweenQueueSystem = GetNode("TweenQueueSystem"); TweenQueueSystem.Init(this); MoveSystem = GetNode("MoveSystem"); var moveSystemParams = new MoveSystem.MoveSystemParameters(this, Gravity, MantleSystem, TweenQueueSystem, HeadSystem, CapsuleCollider); MoveSystem.Init(moveSystemParams); Stamina = GetNode("Stamina"); Stamina.SetSpeeds(MoveSystem.WalkSpeed, MoveSystem.SprintSpeed); StairsSystem = GetNode("StairsSystem"); StairsSystem.Init(stairsBelowRayCast3D, stairsAheadRayCast3D, cameraSmooth); DashSystem = GetNode("DashSystem"); DashSystem.Init(HeadSystem, camera, TweenQueueSystem); DashSystem.DashEnded += OnDashEnded; HealthSystem = GetNode("HealthSystem"); HealthSystem.HealthSystemInitParams healthSystemParams = new HealthSystem.HealthSystemInitParams() { Gravity = Gravity, Parent = this, Camera = camera, Head = HeadSystem, VignetteRect = vignetteRect, DistortionRect = distortionRect, BlurRect = blurRect, }; HealthSystem.Init(healthSystemParams); } public void OnInputMove(Vector3 value) { _inputMove = value; } public void OnInputRotateY(float value) { _inputRotateY = value; } public void OnInputRotateFloorplane(float value) { _inputRotateFloorplane = value; } public void OnInputAimPressed() { PlayerState.SendEvent("aim_pressed"); if (_dashCanceled) return; DashSystem.PrepareDash(); } public void OnInputAimReleased() { PlayerState.SendEvent("aim_released"); if (!_dashCanceled) DashSystem.Dash(); _dashCanceled = false; } public void OnInputAimCanceled() { PlayerState.SendEvent("aim_canceled"); _dashCanceled = true; DashSystem.CancelDash(); } public void OnInputHitPressed() { PlayerState.SendEvent("hit_pressed"); } public void OnInputJumpPressed() { bool doesCapsuleHaveCrouchingHeight = CapsuleCollider.IsCrouchingHeight(); bool isPlayerDead = HealthSystem.IsDead(); if (!doesCapsuleHaveCrouchingHeight && !isPlayerDead) MoveSystem.Jump(IsOnFloor()); } public void OnDashEnded() { PlayerState.SendEvent("dash_ended"); } public override void _PhysicsProcess(double delta) { TweenQueueSystem.ProcessTweens(); var isPlayerDead = HealthSystem.IsDead(); var isHeadTouchingCeiling = IsHeadTouchingCeiling(); var moveAroundParams = new MoveSystem.MoveAroundParameters( delta, _inputMove, isOnFloorCustom(), isPlayerDead, isHeadTouchingCeiling); MoveSystem.MoveAround(moveAroundParams); Vector2 inputLookDir = new Vector2(_inputRotateY, _inputRotateFloorplane); HeadSystem.LookAround(inputLookDir); Bobbing.CameraBobbingParams cameraBobbingParams = new Bobbing.CameraBobbingParams { Delta = (float)delta, IsOnFloorCustom = isOnFloorCustom(), Velocity = Velocity }; Bobbing.PerformCameraBobbing(cameraBobbingParams); FieldOfView.FovParameters fovParams = new FieldOfView.FovParameters { IsCrouchingHeight = CapsuleCollider.IsCrouchingHeight(), Delta = (float)delta, SprintSpeed = MoveSystem.SprintSpeed, Velocity = Velocity }; FieldOfView.PerformFovAdjustment(fovParams); StairsSystem.UpStairsCheckParams upStairsCheckParams = new StairsSystem.UpStairsCheckParams { IsOnFloorCustom = isOnFloorCustom(), IsCapsuleHeightLessThanNormal = CapsuleCollider.IsCapsuleHeightLessThanNormal(), CurrentSpeedGreaterThanWalkSpeed = MoveSystem._currentSpeed > MoveSystem.WalkSpeed, IsCrouchingHeight = CapsuleCollider.IsCrouchingHeight(), Delta = (float)delta, FloorMaxAngle = FloorMaxAngle, GlobalPositionFromDriver = GlobalPosition, Velocity = Velocity, GlobalTransformFromDriver = GlobalTransform, Rid = GetRid() }; // TODO: SnapUpStairsCheck influences the ability of player to crouch because of `stepHeightY <= 0.01` part // Ideally, it should not. SnapUpStairsCheck and SnapDownStairsCheck should be called, when player is actually // on the stairs StairsSystem.UpStairsCheckResult upStairsCheckResult = StairsSystem.SnapUpStairsCheck(upStairsCheckParams); if (upStairsCheckResult.UpdateRequired) { upStairsCheckResult.Update(this); } else { MoveAndSlide(); StairsSystem.DownStairsCheckParams downStairsCheckParams = new StairsSystem.DownStairsCheckParams { IsOnFloor = IsOnFloor(), // TODO: replace on IsOnFloor Custom IsCrouchingHeight = CapsuleCollider.IsCrouchingHeight(), LastFrameWasOnFloor = _lastFrameWasOnFloor, CapsuleDefaultHeight = CapsuleCollider.GetDefaultHeight(), CurrentCapsuleHeight = CapsuleCollider.GetCurrentHeight(), FloorMaxAngle = FloorMaxAngle, VelocityY = Velocity.Y, GlobalTransformFromDriver = GlobalTransform, Rid = GetRid() }; StairsSystem.DownStairsCheckResult downStairsCheckResult = StairsSystem.SnapDownStairsCheck( downStairsCheckParams); if (downStairsCheckResult.UpdateIsRequired) { downStairsCheckResult.Update(this); } } StairsSystem.SlideCameraParams slideCameraParams = new StairsSystem.SlideCameraParams { IsCapsuleHeightLessThanNormal = CapsuleCollider.IsCapsuleHeightLessThanNormal(), CurrentSpeedGreaterThanWalkSpeed = MoveSystem._currentSpeed > MoveSystem.WalkSpeed, BetweenCrouchingAndNormalHeight = CapsuleCollider.IsBetweenCrouchingAndNormalHeight(), Delta = (float)delta }; StairsSystem.SlideCameraSmoothBackToOrigin(slideCameraParams); } private bool IsHeadTouchingCeiling() { for (int i = 0; i < NUM_OF_HEAD_COLLISION_DETECTORS; i++) { if (_headCollisionDetectors[i].IsColliding()) { return true; } } return false; } private bool isOnFloorCustom() { return IsOnFloor() || StairsSystem.WasSnappedToStairsLastFrame(); } }