using Godot; namespace Movementtests.systems; public partial class DashSystem: Node3D { public record DashLocation(bool HasHit, Vector3 TargetLocation, Vector3 CollisionPoint, Vector3 CollisionNormal); [Export(PropertyHint.Range, "0,0.2,0.01,or_greater")] public float DashSpeed { get; set; } = 0.1f; [Export(PropertyHint.Range, "0,1000,1,or_greater")] public float PostDashSpeed { get; set; } = 0f; public bool HasHit { get; set; } public Vector3 TargetLocation { get; set; } public Vector3 CollisionPoint { get; set; } public Vector3 CollisionNormal { get; set; } public Vector3 PlannedLocation { get; set; } public bool ShouldMantle { get; set; } public Vector3 PlannedMantleLocation { get; set; } private Node3D _head; private ShapeCast3D _dashCast3D; private ShapeCast3D _playerCast3D; private Camera3D _camera; private TweenQueueSystem _tweenQueueSystem; private Vector3 _dashDirection = Vector3.Zero; private MantleSystem _mantleSystem; private MeshInstance3D _dashTarget; private CpuParticles3D _dashIndicator; private AnimationPlayer _dashIndicatorAnim; [Export] public PackedScene DashIndicatorScene { get; set; } [Signal] public delegate void DashStartedEventHandler(); [Signal] public delegate void DashEndedEventHandler(); [Signal] public delegate void DashProgressEventHandler(float progress); private Vector3 _globalDashPosition = Vector3.Zero; private float _playerHeight; private float _playerRadius; public float DashCastRadius { get; set; } public void Init(Node3D head, Camera3D camera, TweenQueueSystem tweenQueueSystem) { _dashCast3D = GetNode("DashCast3D"); var dashShape = _dashCast3D.GetShape() as SphereShape3D; DashCastRadius = dashShape!.Radius; _playerCast3D = GetNode("PlayerShapeCast3D"); var playerShape = _playerCast3D.GetShape() as CapsuleShape3D; _playerHeight = playerShape!.Height; _playerRadius = playerShape!.Radius; _head = head; _camera = camera; _tweenQueueSystem = tweenQueueSystem; _mantleSystem = GetNode("MantleSystem"); _mantleSystem.Init(this); _dashTarget = GetNode("DashTarget"); _dashTarget.SetVisible(false); _dashIndicator = GetNode("DashIndicator"); _dashIndicatorAnim = GetNode("DashIndicator/AnimationPlayer"); } private Vector3 RecurseThroughCollisions(Vector3 previousCollisionPoint, Vector3 previousNormal, int recursionDepth) { if (recursionDepth == 0) return previousCollisionPoint; var startPoint = previousCollisionPoint + previousNormal*_playerHeight; _playerCast3D.SetGlobalPosition(startPoint); _playerCast3D.SetTargetPosition(-previousNormal*_playerRadius); var hasHit = _playerCast3D.IsColliding(); if (!hasHit) return previousCollisionPoint; return RecurseThroughCollisions( _playerCast3D.GetCollisionPoint(0), _playerCast3D.GetCollisionNormal(0), recursionDepth - 1); } private DashLocation ComputeDashLocation() { var targetLocation = _dashCast3D.ToGlobal(_dashCast3D.TargetPosition); var hasHit = _dashCast3D.IsColliding(); if (!hasHit) { return new DashLocation(false, targetLocation, Vector3.Zero, Vector3.Zero); } var collisionPoint = _dashCast3D.GetCollisionPoint(0); var collisionNormal = _dashCast3D.GetCollisionNormal(0); var fraction = _dashCast3D.GetClosestCollisionSafeFraction(); var globalSweepPath = targetLocation - _dashCast3D.GlobalPosition; var locationAlongPath = _dashCast3D.GlobalPosition + globalSweepPath * fraction; return new DashLocation(true, locationAlongPath, collisionPoint, collisionNormal); // // Pushes the point down when dashing to under a platform so head doesn't clip // var maxPushDownDistance = 0.9f; // var correctionProportion = (float) Mathf.Remap(CollisionNormal.Y, -0.5, -1, 0, 1); // var proportion = (float)Mathf.Remap(_dashCast3D.GlobalRotation.X, 0, 1.57, 0, 1); // var finalLocation = locationAlongPath // + CollisionNormal // * maxPushDownDistance // * Mathf.Clamp(proportion, 0, 1) // * Mathf.Clamp(correctionProportion, 0, 1); // // return new DashLocation(true, finalLocation); } public void PrepareDash() { _dashCast3D.SetRotation(new Vector3( _camera.Rotation.X, _head.Rotation.Y, _camera.Rotation.Z)); (HasHit, PlannedLocation, CollisionPoint, CollisionNormal) = ComputeDashLocation(); ShouldMantle = false; // var mantleLocation = Vector3.Zero; // if (HasHit && Mathf.Abs(CollisionNormal.Y) < 0.5f) // { // var mantleResult = _mantleSystem.FindMantleLocationAtPoint(PlannedLocation, CollisionNormal); // ShouldMantle = mantleResult.IsSome(out mantleLocation); // } // PlannedMantleLocation = mantleLocation; // Setup dash target var targetColor = HasHit ? new Color(1f, 0.2f, 0.2f) : new Color(1f, 1f, 1f); targetColor = ShouldMantle ? new Color(0.2f, 0.2f, 1f) : targetColor; var targetMaterial = (StandardMaterial3D) _dashTarget.GetSurfaceOverrideMaterial(0); targetMaterial.SetAlbedo(targetColor); _dashTarget.SetVisible(true); _dashTarget.SetGlobalPosition(PlannedLocation); } public void StopPreparingDash() { _dashTarget.SetVisible(false); } public void StartPreparingDash() { _dashTarget.SetVisible(true); } }