using Godot; using System; using System.Collections.Generic; using System.Diagnostics; public partial class Tower : Sprite2D { public bool _hovered, _aiming; public int _launchSpeed = 1000, _arcIterations = 50; public Vector2 _aimOffset, _arcEnd; public List _arc = new(); public Marker2D _offset, _attackSpawn; public Area2D _area; public Commander _commander; public override void _Ready() { base._Ready(); _area = GetNode("Area"); _offset = GetNode("Offset"); _attackSpawn = GetNode("AttackSpawn"); } public override void _Process(double delta) { base._Process(delta); if (_commander?._actions > 0) { if (_aiming) { if (_arcEnd != GetGlobalMousePosition()) { DrawArc(_attackSpawn.GlobalPosition, GetGlobalMousePosition()); } if (Input.IsActionJustPressed("rightClick")) { _commander.UnloadAttack(); _aiming = false; } else if (Input.IsActionJustPressed("leftClick")) { _aimOffset = CalculateLaunchAngle(_attackSpawn.GlobalPosition, GetGlobalMousePosition()); _commander.ShootCurrentAttack(_aimOffset); ClearArc(); _aiming = false; } } else if (_hovered) { if (Input.IsActionJustPressed("leftClick")) { _aiming = true; _commander.LoadAttack(_attackSpawn.Position); } } } } public Vector2 CalculateLaunchAngle(Vector2 START, Vector2 END, int MAX_ITERATIONS = 20) { Vector2 offset = END - START; float baseAngle = offset.Angle(); float straightUpAngle = -Mathf.Pi / 2.0f; float lowAngle = 0.0f; float highAngle = 1.0f; float bestAngle = baseAngle; for (int i = 0; i < MAX_ITERATIONS; i++) { float averageAngle = (lowAngle + highAngle) / 2.0f; float testAngle = Mathf.LerpAngle(baseAngle, straightUpAngle, averageAngle); float error = EvaluateSimulationError(testAngle, START, END); if (Mathf.Abs(error) < 0.2f) { bestAngle = testAngle; break; } if (error > 0) { lowAngle = averageAngle; } else { highAngle = averageAngle; } bestAngle = testAngle; } return Vector2.FromAngle(bestAngle) * _launchSpeed; } public void ClearArc() { Path2D path = _commander._attack.GetNode("PredictedPath"); path.Curve.ClearPoints(); } public void DrawArc(Vector2 START, Vector2 END, int MAX_ITERATIONS = 20) { CalculateLaunchAngle(START, END); Path2D path = _commander._attack.GetNode("PredictedPath"); path.Curve.ClearPoints(); for (int i = 0; i < _arc.Count; i++) { path.Curve.AddPoint(_arc[i]); } } private float EvaluateSimulationError(float ANGLE, Vector2 START, Vector2 TARGET) { float speed = _launchSpeed, gravity = Globals._gravity, drag = Globals._drag, delta = 1.0f / 60.0f; Vector2 velocity = new Vector2(Mathf.Cos(ANGLE), Mathf.Sin(ANGLE)) * speed; Vector2 position = START; float directionSign = Mathf.Sign(TARGET.X - START.X); float maxFlightTime = 6.0f; int totalSteps = (int)(maxFlightTime / delta); _arc = [START]; for (int step = 0; step < totalSteps; step++) { velocity.Y += gravity * delta; velocity -= velocity * drag * delta; position += velocity * delta; _arc.Add(position); bool reachedTargetX = (directionSign >= 0) ? (position.X >= TARGET.X) : (position.X <= TARGET.X); if (reachedTargetX) { return position.Y - TARGET.Y; } } _arcEnd = _arc[^1]; return (position.Y > TARGET.Y) ? 99999f : -99999f; } public void StartTurn() { if (_commander != null) { _commander._actions = _commander._actionsMax; } } public void OnMouseEntered(){ _hovered = true; } public void OnMouseExited(){ _hovered = true; } }