using Godot; using Movementtests.interfaces; namespace Movementtests.systems; [GlobalClass, Icon("res://assets/ui/IconGodotNode/node_3D/icon_wind.png")] public partial class DashSystem: Node3D { public record DashLocation(bool HasHit, Vector3 TargetLocation, Vector3 CollisionPoint, Vector3 CollisionNormal, GodotObject HitObject = null); [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 bool CanDashThroughTarget { get; set; } public Vector3 TargetLocation { get; set; } public Vector3 CollisionPoint { get; set; } public Vector3 CollisionNormal { get; set; } public GodotObject? CollidedObject { get; set; } public Vector3 PlannedLocation { get; set; } public bool ShouldMantle { get; set; } public Vector3 PlannedMantleLocation { get; set; } public MantleSystem MantleSystem { get; set; } = null!; internal HeadSystem Head = null!; public ShapeCast3D DashCast3D = null!; internal Camera3D Camera = null!; internal Vector3 DashDirection = Vector3.Zero; internal ShapeCast3D DashCastDrop = null!; internal MeshInstance3D DashDropIndicator = null!; internal MeshInstance3D DashDropLocationIndicator = null!; internal MeshInstance3D DashTarget = null!; internal CpuParticles3D DashIndicator = null!; internal AnimationPlayer DashIndicatorAnim = null!; [Export] public PackedScene DashIndicatorScene { get; set; } = null!; [Signal] public delegate void DashStartedEventHandler(); [Signal] public delegate void DashEndedEventHandler(); [Signal] public delegate void DashProgressEventHandler(float progress); private Vector3 _globalDashPosition = Vector3.Zero; public float DashCastRadius { get; set; } public void Init(HeadSystem head, Camera3D camera) { DashCast3D = GetNode("DashCast3D"); var dashShape = DashCast3D.GetShape() as SphereShape3D; DashCastRadius = dashShape!.Radius; DashCastDrop = GetNode("DashCastDrop"); DashDropIndicator = GetNode("DashDropIndicator"); DashDropIndicator.Visible = false; DashDropLocationIndicator = GetNode("DashDropLocationIndicator"); DashDropLocationIndicator.Visible = false; Head = head; Camera = camera; MantleSystem = GetNode("MantleSystem"); MantleSystem.Init(); DashTarget = GetNode("DashTarget"); DashTarget.SetVisible(false); DashIndicator = GetNode("DashIndicator"); DashIndicatorAnim = GetNode("DashIndicator/AnimationPlayer"); } internal 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 collidedObject = DashCast3D.GetCollider(0); var fraction = DashCast3D.GetClosestCollisionSafeFraction(); var globalSweepPath = targetLocation - DashCast3D.GlobalPosition; var locationAlongPath = DashCast3D.GlobalPosition + globalSweepPath * fraction; return new DashLocation(true, locationAlongPath, collisionPoint, collisionNormal, collidedObject); } public void PrepareDash() { DashCast3D.SetRotation(Head.GetGlobalLookRotation()); (HasHit, PlannedLocation, CollisionPoint, CollisionNormal, CollidedObject) = ComputeDashLocation(); CanDashThroughTarget = false; if (CollidedObject is ITargetable) { DashTarget.SetVisible(false); CanDashThroughTarget = true; return; } MantleSystem.SetGlobalPosition(PlannedLocation); MantleSystem.SetRotation(new Vector3( MantleSystem.Rotation.X, Head.Rotation.Y, MantleSystem.Rotation.Z)); MantleSystem.ProcessMantle(false); ShouldMantle = MantleSystem.IsMantlePossible; // 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); var targetLocation = ShouldMantle ? MantleSystem.FirstMantleProfilePoint : PlannedLocation; DashTarget.SetGlobalPosition(targetLocation); } public void StopPreparingDash() { CanDashThroughTarget = false; DashTarget.SetVisible(false); DashDropIndicator.SetVisible(false); DashDropLocationIndicator.SetVisible(false); } }