197 lines
5.5 KiB
C#
197 lines
5.5 KiB
C#
using Godot;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
|
|
public partial class Tower : HoverableNode
|
|
{
|
|
public bool _aiming;
|
|
public int _launchSpeed = 1000, _arcIterations = 50;
|
|
public Vector2 _aimOffset, _arcEnd;
|
|
public List<Vector2> _arc = new();
|
|
public Marker2D _offset, _ballSpawn;
|
|
public Area2D _area;
|
|
public Commander _commander;
|
|
public override void _Ready()
|
|
{
|
|
base._Ready();
|
|
|
|
_offset = GetNode<Marker2D>("Offset");
|
|
_ballSpawn = GetNode<Marker2D>("BallSpawn");
|
|
// Click += HandleClick;
|
|
}
|
|
public override void _Process(double delta)
|
|
{
|
|
base._Process(delta);
|
|
if (_commander?._actions > 0)
|
|
{
|
|
if (_aiming)
|
|
{
|
|
if (_arcEnd != GetGlobalMousePosition())
|
|
{
|
|
DrawArc(_ballSpawn.GlobalPosition, GetGlobalMousePosition());
|
|
}
|
|
if (Input.IsActionJustPressed("rightClick"))
|
|
{
|
|
_commander.UnloadBall();
|
|
_aiming = false;
|
|
}
|
|
else if (Input.IsActionJustPressed("leftClick"))
|
|
{
|
|
|
|
_aimOffset = ToLaunchVector(CalculateLaunchAngle(_ballSpawn.GlobalPosition, GetGlobalMousePosition()));
|
|
|
|
_commander.ShootCurrentBall(_aimOffset);
|
|
|
|
ClearArc();
|
|
_aiming = false;
|
|
}
|
|
}
|
|
else if (_hovered)
|
|
{
|
|
if (Input.IsActionJustPressed("leftClick"))
|
|
{
|
|
_aiming = true;
|
|
_commander.LoadBall(_ballSpawn.Position);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public float 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;
|
|
}
|
|
if (ToLaunchVector(bestAngle).Y < 0)
|
|
{
|
|
if (END.X < START.X)
|
|
{
|
|
bestAngle = (float)Math.PI;
|
|
}
|
|
else
|
|
{
|
|
bestAngle = 0;
|
|
}
|
|
}
|
|
return bestAngle;
|
|
}
|
|
|
|
public void ClearArc()
|
|
{
|
|
Path2D path = _commander._ball.GetNode<Path2D>("PredictedPath");
|
|
path.Curve.ClearPoints();
|
|
}
|
|
|
|
public void DrawArc(Vector2 START, Vector2 END, int MAX_ITERATIONS = 20)
|
|
{
|
|
float arcAngle = CalculateLaunchAngle(START, END);
|
|
|
|
float speed = _launchSpeed, gravity = Globals._gravity, drag = Globals._drag, delta = 1.0f / 60.0f;
|
|
Vector2 velocity = new Vector2(Mathf.Cos(arcAngle), Mathf.Sin(arcAngle)) * speed;
|
|
Vector2 position = START;
|
|
|
|
float directionSign = Mathf.Sign(END.X - START.X);
|
|
float maxFlightTime = 6.0f;
|
|
int totalSteps = (int)(maxFlightTime / delta);
|
|
List<Vector2> 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 >= END.X) : (position.X <= END.X);
|
|
|
|
if (reachedTargetX)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
Path2D path = _commander._ball.GetNode<Path2D>("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);
|
|
|
|
for (int step = 0; step < totalSteps; step++)
|
|
{
|
|
velocity.Y += gravity * delta;
|
|
velocity -= velocity * drag * delta;
|
|
position += velocity * delta;
|
|
|
|
bool reachedTargetX = (directionSign >= 0) ? (position.X >= TARGET.X) : (position.X <= TARGET.X);
|
|
|
|
if (reachedTargetX)
|
|
{
|
|
return position.Y - TARGET.Y;
|
|
}
|
|
}
|
|
|
|
return (position.Y > TARGET.Y) ? 99999f : -99999f;
|
|
}
|
|
|
|
public void StartTurn()
|
|
{
|
|
if (_commander != null)
|
|
{
|
|
_commander._actions = _commander._actionsMax;
|
|
}
|
|
}
|
|
|
|
public Vector2 ToLaunchVector(float ANGLE)
|
|
{
|
|
return Vector2.FromAngle(ANGLE) * _launchSpeed;
|
|
}
|
|
|
|
}
|