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 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); } 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); } }