using Godot; namespace Movementtests.systems; public record DashComputation(bool HasHit, Vector3 Location, Vector3 CollisionPoint, Vector3 CollisionNormal); public record DashResolve(bool EndWithMantle, Vector3 DashLocation, Vector3 MantleLocation); public partial class DashSystem: Node3D { [Export(PropertyHint.Range, "0,0.2,0.01,or_greater")] public float DashSpeed { get; set; } = 0.05f; private Node3D _head; private ShapeCast3D _dashCast3D; private Camera3D _camera; private TweenQueueSystem _tweenQueueSystem; private MantleSystem _mantleSystem; private MeshInstance3D _dashTarget; private DashResolve _dashResolve; [Signal] public delegate void DashStartedEventHandler(); [Signal] public delegate void DashEndedEventHandler(); public void Init(Node3D head, Camera3D camera, TweenQueueSystem tweenQueueSystem) { _dashCast3D = GetNode("DashCast3D"); _head = head; _camera = camera; _tweenQueueSystem = tweenQueueSystem; _mantleSystem = GetNode("MantleSystem"); _mantleSystem.Init(this); _dashTarget = GetNode("DashTarget"); _dashTarget.SetVisible(false); } private DashComputation ComputeDashLocation() { if (!_dashCast3D.IsColliding()) { return new DashComputation(false, _dashCast3D.ToGlobal(_dashCast3D.TargetPosition), Vector3.Zero, Vector3.Zero); } var collisionPoint = _dashCast3D.GetCollisionPoint(0); var collisionNormal = _dashCast3D.GetCollisionNormal(0); var collisionShape = (SphereShape3D) _dashCast3D.GetShape(); var centerSphereLocation = collisionPoint + collisionNormal * collisionShape.Radius; return new DashComputation(true, centerSphereLocation, collisionPoint, collisionNormal); } public DashResolve PrepareDash() { _dashTarget.SetVisible(false); _dashCast3D.SetRotation(new Vector3( _camera.Rotation.X, _head.Rotation.Y, _camera.Rotation.Z)); var (hasHit, location, collisionPoint, collisionNormal) = ComputeDashLocation(); var shouldMantle = false; var mantleLocation = Vector3.Zero; if (hasHit && Mathf.Abs(collisionNormal.Y) < 0.01f) { var mantleResult = _mantleSystem.FindMantleLocationAtPoint(collisionPoint, collisionNormal); shouldMantle = mantleResult.IsSome(out mantleLocation); } var targetColor = shouldMantle ? new Color(0.2f, 0.2f, 1f) : new Color(1f, 1f, 1f); var targetMaterial = (StandardMaterial3D) _dashTarget.GetSurfaceOverrideMaterial(0); targetMaterial.SetAlbedo(targetColor); _dashTarget.SetVisible(true); _dashTarget.SetGlobalPosition(location); _dashResolve = new DashResolve(shouldMantle, location, mantleLocation); return _dashResolve; } public void CancelDash() { _dashTarget.SetVisible(false); } public void DashTweenEnded() { EmitSignal(SignalName.DashEnded); } public void Dash() { EmitSignal(SignalName.DashStarted); _dashTarget.SetVisible(false); var dashTweenInputs = new TweenQueueSystem.TweenInputs(_dashResolve.DashLocation, 0.1f); var dashTween = _tweenQueueSystem.TweenToLocation(dashTweenInputs); dashTween.Finished += DashTweenEnded; if (_dashResolve.EndWithMantle) { _tweenQueueSystem.QueueTween(_dashResolve.MantleLocation, 0.2f); } } }