diff --git a/maps/GYMs/metrics.tscn b/maps/GYMs/metrics.tscn index dccc971b..c641b8b4 100644 --- a/maps/GYMs/metrics.tscn +++ b/maps/GYMs/metrics.tscn @@ -30,6 +30,7 @@ glow_enabled = true [node name="Player" parent="." instance=ExtResource("1_1s2y7")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1.5) +TutorialDone = true [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_1bvp3") diff --git a/player_controller/PlayerController.tscn b/player_controller/PlayerController.tscn index 1a33f868..97a542da 100644 --- a/player_controller/PlayerController.tscn +++ b/player_controller/PlayerController.tscn @@ -84,6 +84,14 @@ PostDashSpeed = 30.0 WallHugGravityLesseningFactor = 15.0 WallHugDownwardMaxSpeed = 8.0 WallHugHorizontalDeceleration = 0.5 +WallRunAltitudeLossSpeed = 12.0 +WallRunSpeedThreshold = 5.0 + +[node name="WallRunSnapper" type="RayCast3D" parent="."] +unique_name_in_owner = true +transform = Transform3D(0.99999994, 0, 0, 0, 1, 0, 0, 0, 0.99999994, 0, 0, 0) +target_position = Vector3(0, 0, -5) +collision_mask = 2 [node name="InputController" type="Node3D" parent="."] script = ExtResource("16_v31n3") @@ -163,7 +171,6 @@ target_position = Vector3(0, 1, 0) [node name="TweenQueueSystem" parent="." instance=ExtResource("22_rpwev")] [node name="WallHugSystem" type="Node3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) script = ExtResource("27_n7qhm") [node name="back" type="RayCast3D" parent="WallHugSystem"] diff --git a/player_controller/Scripts/PlayerController.cs b/player_controller/Scripts/PlayerController.cs index 1381f04d..3d96a6bc 100644 --- a/player_controller/Scripts/PlayerController.cs +++ b/player_controller/Scripts/PlayerController.cs @@ -35,6 +35,7 @@ public partial class PlayerController : CharacterBody3D public Node3D DashIndicatorNode; public MeshInstance3D DashIndicatorMesh; public CylinderMesh DashIndicatorMeshCylinder; + public RayCast3D WallRunSnapper; private bool _movementEnabled = true; @@ -154,6 +155,16 @@ public partial class PlayerController : CharacterBody3D [Export(PropertyHint.Range, "0.1,10,0.1,or_greater")] public float WallHugHorizontalDeceleration { get; set; } = 5f; + // Wall run + [ExportGroup("Wall run")] + [Export(PropertyHint.Range, "1,20,0.1,or_greater")] + public float WallRunUpwardVelocity { get; set; } = 10f; + [Export(PropertyHint.Range, "1,20,0.1,or_greater")] + public float WallRunAltitudeLossSpeed { get; set; } = 10f; + + [Export(PropertyHint.Range, "1,20,0.1,or_greater")] + public float WallRunSpeedThreshold { get; set; } = 8f; + private float _targetSpeed; private float _gravity; @@ -246,6 +257,7 @@ public partial class PlayerController : CharacterBody3D DashSystem = GetNode("DashSystem"); StairsSystem = GetNode("StairsSystem"); WallHugSystem = GetNode("WallHugSystem"); + WallRunSnapper = GetNode("%WallRunSnapper"); RayCast3D stairsBelowRayCast3D = GetNode("StairsBelowRayCast3D"); RayCast3D stairsAheadRayCast3D = GetNode("StairsAheadRayCast3D"); _headCollisionDetectors = new RayCast3D[NUM_OF_HEAD_COLLISION_DETECTORS]; @@ -427,8 +439,6 @@ public partial class PlayerController : CharacterBody3D _playerState.SendEvent("start_falling"); } - private float _wallRunSpeedThreshold = 8f; - public void HandleAirborne(float delta) { MoveInAir(delta); @@ -444,19 +454,21 @@ public partial class PlayerController : CharacterBody3D // Should we start a wall run var wallNormal = WallHugSystem.WallHugNormal.UnwrapOr(Vector3.Zero); + var isIndeedWall = wallNormal.Y < 0.1; var hvel = new Vector3(Velocity.X, 0, Velocity.Z); var hvelProjected = hvel.Slide(_wallHugStartNormal); - var haveEnoughSpeed = hvelProjected.Length() > _wallRunSpeedThreshold; - var isHeadPlanting = Velocity.AngleTo(wallNormal) < Math.PI / 4; - var isGoingDownwards = Velocity.AngleTo(Vector3.Down) < Math.PI / 4; - if (haveEnoughSpeed && !isHeadPlanting && !isGoingDownwards) + var haveEnoughSpeed = hvelProjected.Length() > WallRunSpeedThreshold; + var isCoplanarEnough = Velocity.AngleTo(wallNormal) > Math.PI/4 && Velocity.AngleTo(wallNormal) < 3*Math.PI/4; + var isGoingDownwards = Velocity.AngleTo(Vector3.Down) < Math.PI/4; + if (haveEnoughSpeed && isCoplanarEnough && !isGoingDownwards && isIndeedWall && !_coyoteEnabled.Active) { + SetVerticalVelocity(WallRunUpwardVelocity); _playerState.SendEvent("wall_run"); return; } // If all else fail and we go down, we hug - if (Velocity.Y < 0) + if (Velocity.Y < 0 && !_coyoteEnabled.Active) _playerState.SendEvent("wall_hug"); } @@ -476,14 +488,11 @@ public partial class PlayerController : CharacterBody3D _wallHugStartNormal = newWallNormal; } - private float _timeSinceWallStarted; - public void OnWallStarted() { _wallHugStartNormal = WallHugSystem.WallHugNormal.UnwrapOr(Vector3.Up); _wallHugStartLocation = WallHugSystem.WallHugLocation.UnwrapOr(Vector3.Zero) + _wallHugStartNormal * _playerRadius; _wallHugStartProjectedVelocity = Velocity.Slide(_wallHugStartNormal); - _timeSinceWallStarted = 0; } public void OnWallStopped() @@ -505,23 +514,26 @@ public partial class PlayerController : CharacterBody3D WallHang(delta); } - private float _wallRunUpwardVelocity = 10f; - public void HandleWallRunning(float delta) { - _timeSinceWallStarted += delta; - + // Find horizontal velocity projected on the current wall var hvel = new Vector3(Velocity.X, 0, Velocity.Z); var hvelProjected = hvel.Slide(_wallHugStartNormal); - Velocity = hvelProjected + hvelProjected.Length()*Vector3.Up*0.3f/(1+_timeSinceWallStarted); - Velocity *= 0.99f; - - // if (CanMantle()) - // { - // MantleToLocation(_plannedMantleLocation.Unwrap()); - // } - if (!WallHugSystem.IsWallHugging() || Velocity.Length() < _wallRunSpeedThreshold) + // Reorient horizontal velocity so we keep it coplanar with the wall without losing speed + var finalHVel = hvelProjected.Normalized() * hvel.Length(); + + // Adapt vertical speed + var verticalSpeed = Velocity.Y - WallRunAltitudeLossSpeed * delta; + Velocity = finalHVel + Vector3.Up*verticalSpeed; + Velocity *= 0.995f; + + if (WallRunSnapper.IsColliding()) + { + GD.Print((WallRunSnapper.GetCollisionPoint() - WallRunSnapper.GlobalPosition).Length()); + } + + if (!WallHugSystem.IsWallHugging() || Velocity.Length() < WallRunSpeedThreshold) { _playerState.SendEvent("start_falling"); } @@ -549,7 +561,10 @@ public partial class PlayerController : CharacterBody3D { if (CanMantle()) { - MantleToLocation(_plannedMantleLocation.Unwrap()); + var location = _plannedMantleLocation.UnwrapOr(Vector3.Zero); + if (location == Vector3.Zero) + return; // For some reason CanMantle can return an invalid location so fuck off I guess + MantleToLocation(location); return; } @@ -558,6 +573,7 @@ public partial class PlayerController : CharacterBody3D _playerState.SendEvent("megajump"); return; } + _playerState.SendEvent("jump"); } @@ -582,6 +598,7 @@ public partial class PlayerController : CharacterBody3D public void OnDoubleJumpStarted() { _canDash = true; + _canDashAirborne = true; OnJumpStarted(DoubleJumpStartVelocity); } public void OnMegaJumpStarted() @@ -943,6 +960,9 @@ public partial class PlayerController : CharacterBody3D public void PlaceWeaponForTutorial() { + if (TutorialDone) + return; + RemoveChild(WeaponRoot); GetTree().GetRoot().CallDeferred(Node.MethodName.AddChild, WeaponRoot); WeaponRoot.CallDeferred(Node3D.MethodName.SetGlobalPosition, TutorialWeaponTarget.GlobalPosition); @@ -1158,7 +1178,7 @@ public partial class PlayerController : CharacterBody3D Bobbing.CameraBobbingParams cameraBobbingParams = new Bobbing.CameraBobbingParams { Delta = delta, - IsOnFloorCustom = isOnFloorCustom(), + IsOnFloorCustom = isOnFloorCustom() || _onWallRunning.Active, Velocity = Velocity, SettingsMultiplier = _headBobbingMultiplier };