/*
* Neural Tanks is a game created by Eddie O'Hagan that leverages Neural Networking
* to use for AI. This was created in Unreal Engine 4 which is developed by Epic Games.
* (Copyright Epic Games, Inc. All Rights Reserved.)
*
* Smart Tanks was originally developed by Mat Buckland in 2002 as an example
* on how Neural Networking can be used to train a set of minesweepers
* to collect mines. I (Eddie O'Hagan) updated this project in 2024 to use more
* Object Oriented Programming and modern practices. To see the original tutorial;
* visit <see href="http://www.ai-junkie.com/ann/evolved/nnt1.html">Neural Networking Tutorial</see>
* and <see href="http://www.ai-junkie.com/ga/intro/gat1.html">Genetic Algorithm Tutorial</see>
*/
#pragma once
#include "CoreMinimal.h"
#include "TankPawn.h"
#include "../Items/TankItem.h"
#include <Camera/CameraComponent.h>
#include <Camera/CameraActor.h>
#include "PhysicsTankPawn.generated.h"

class UTrackPos;
class UTrackPoint;
class USoundCue;
class UCameraComponent;
class USplineComponent;
class UPhysicsConstraintComponent;
class UHierarchicalInstancedStaticMeshComponent;

/// <summary>
/// The Physics Tank is a Tank Pawn with physics components and is simulating physics.
/// It has tracks, turret, cannon, etc, which is all articulated, managed, and driven in
/// this class.
/// </summary>
UCLASS(config=Game)
class APhysicsTankPawn : public ATankPawn
{
	GENERATED_BODY()

protected:
	/// <summary>
	/// true to use simplified controls of moving and rotating the tank, 
	/// false to control the tank tracks individually (useful for AI).
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	bool myIsUsingSimpleControls;

	/// <summary>
	/// Time in seconds before the tank is drive-able.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	float myStartupTime;

	/// <summary>
	/// The current Y rotation angle for the turrets on top of the tank.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	float myTurretY;

	/// <summary>
	/// The current pitch (Y) angle for the cannon.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	float myCannonPitch;

	/// <summary>
	/// Effects the interpolation speed for left/right track forces. 
	/// The higher the number, the faster it interpolates.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	float myTankHandling;

	/// <summary>
	/// The static mesh component for the turret portion of this tank.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	UStaticMeshComponent* myTankTurret;

	/// <summary>
	/// The static mesh component for the body portion of this tank.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	UStaticMeshComponent* myTankBody;

	/// <summary>
	/// The static mesh component for the left turret's machine gun mount on this tank.
	/// </summary>	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	UStaticMeshComponent* myLeftTurretMachineGunMount;

	/// <summary>
	/// The static mesh component for the left turret's machine gun on this tank.
	/// </summary>	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	UStaticMeshComponent* myLeftTurretMachineGun;

	/// <summary>
	/// The static mesh component for the right turret's machine gun mount on this tank.
	/// </summary>	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	UStaticMeshComponent* myRightTurretMachineGunMount;

	/// <summary>
	/// The static mesh component for the right turret's machine gun on this tank.
	/// </summary>	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	UStaticMeshComponent* myRightTurretMachineGun;

	/// <summary>
	/// The static mesh used for aiming.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	UStaticMeshComponent* myTrajectoryComponent;

	/// <summary>
	/// The game camera used for this tank.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	UCameraComponent* myCamera;

	/// <summary>
	/// The top down camera for the level.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	ACameraActor* myLevelCamera;

	/// <summary>
	/// Physics constraint used to control movement of the turrets above the tank.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	UPhysicsConstraintComponent* myCenterTurretConstraintComponent;

	/// <summary>
	/// Physics constraint used to control movement of the main cannon.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	UPhysicsConstraintComponent* myCenterTurretCannonConstraintComponent;

	/// <summary>
	/// The spawned tank turret static mesh.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh")
	UStaticMesh* myTankTurretMesh;

	/// <summary>
	/// The spawned tank body static mesh.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh")
	UStaticMesh* myTankBodyMesh;

	/// <summary>
	/// The spawned tank track static mesh used for the track spline.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh")
	UStaticMesh* myTrackMesh;

	/// <summary>
	/// The spawned static mesh used for the tank wheels.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh")
	UStaticMesh* myTankWheelMesh;

	/// <summary>
	/// The spawned static mesh used for the master wheel on each track.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh")
	UStaticMesh* myTankMasterWheelMesh;

	/// <summary>
	/// The mesh to use on this tank's left turret machine gun mount.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh")
	UStaticMesh* myLeftTurretMachineGunMountMesh;

	/// <summary>
	/// The mesh to use on this tank's left turret machine gun.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh")
	UStaticMesh* myLeftTurretMachineGunMesh;

	/// <summary>
	/// The mesh to use on this tank's right turret machine gun mount.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh")
	UStaticMesh* myRightTurretMachineGunMountMesh;

	/// <summary>
	/// The mesh to use on this tank's right turret machine gun.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh")
	UStaticMesh* myRightTurretMachineGunMesh;

	/// <summary>
	/// The input axis value for moving the right track.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
	float myMoveRightTrackForwardInputValue;

	/// <summary>
	/// The input axis value for moving the left track.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
	float myMoveLeftTrackForwardInputValue;

	/// <summary>
	/// Calculated left/right turn value for rotating tank (simple controls).
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
	float myMoveLeftRightInputValue;

	/// <summary>
	/// true if the tank is currently shifting gears, false otherwise.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gearbox")
	bool myIsShiftingGears;

	/// <summary>
	/// The current gear number.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gearbox")
	int myCurrentGear;

	/// <summary>
	/// The total number of gears this tank has.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gearbox")
	int myTotalNumberOfGears;

	/// <summary>
	/// The amount of speed it takes before automatically shifting to second gear.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gearbox")
	float myGearOneSpeedThreshold;

	/// <summary>
	/// The amount of speed it takes before automatically shifting to third gear.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gearbox")
	float myGearTwoSpeedThreshold;

	/// <summary>
	/// The amount of speed it takes before automatically shifting to fourth gear.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gearbox")
	float myGearThreeSpeedThreshold;

	/// <summary>
	/// The amount of speed it takes before automatically shifting to fifth gear.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gearbox")
	float myGearFourSpeedThreshold;

	/// <summary>
	/// The amount of speed it takes before automatically shifting to sixth gear.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gearbox")
	float myGearFiveSpeedThreshold;

	/// <summary>
	/// The amount of speed it takes before automatically shifting to seventh gear.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gearbox")
	float myGearSixSpeedThreshold;

	/// <summary>
	/// The amount of speed it takes before automatically shifting to eighth gear.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Gearbox")
	float myGearSevenSpeedThreshold;

	/// <summary>
	/// The Z position offset used for aligning the wheels to the track spline dynamically.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myTrackZOffset;

	/// <summary>
	/// The tick frequency for updating the track speeds and positions.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myTrackUpdateFrequency;

	/// <summary>
	/// The length of an individual piece of track in the track spline.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myTrackLength;

	/// <summary>
	/// The calculated left track speed based on force and input. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myTrackSpeedLeft;

	/// <summary>
	/// The calculated left track speed based on force and input. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myTrackSpeedRight;

	/// <summary>
	/// The calculated left track speed based on the wheel multiplier, current gear, and track force.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myRawLeftSpeed;

	/// <summary>
	/// The calculated right track speed based on the wheel multiplier, current gear, and track force.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myRawRightSpeed;

	/// <summary>
	/// A multiplier used for effecting the raw left/right track speed.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myTrackSpeedMultiplier;

	/// <summary>
	/// The original length of the left track spline. This is used
	/// for dynamically adjusting the track length and while 
	/// the tank is in motion. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myOriginalLeftSplineLength;

	/// <summary>
	/// The original length of the right track spline. This is used
	/// for dynamically adjusting the track length and while 
	/// the tank is in motion. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myOriginalRightSplineLength; //aka Spline R Length

	/// <summary>
	/// The calculated offset value (between -1.0 and 1.0) for dynamically
	/// adjusting the left track spline length. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myLeftTrackOffset;

	/// <summary>
	/// The calculated offset value (between -1.0 and 1.0) for dynamically
	/// adjusting the right track spline length. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	float myRightTrackOffset;

	/// <summary>
	/// The spawned left track spline.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	USplineComponent* myLeftSpline;

	/// <summary>
	/// The spawned right track spline.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	USplineComponent* myRightSpline;

	/// <summary>
	/// A single piece of static mesh track in the left track spline.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	UHierarchicalInstancedStaticMeshComponent* myLeftTrackHISM; //aka Track_L

	/// <summary>
	/// A single piece of static mesh track in the right track spline.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	UHierarchicalInstancedStaticMeshComponent* myRightTrackHISM; //aka Track_R

	/// <summary>
	/// The positions of all of the track points in the left spline.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	TArray<UTrackPos*> myTrackDistancesLeft;

	/// <summary>
	/// The positions of all of the track points in the right spline.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	TArray<UTrackPos*> myTrackDistancesRight;

	/// <summary>
	/// The arrow track points for defining one of the tracks 
	/// (used for constructing myLeftTrackPoints and myRightTrackPoints).
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	TArray<UArrowComponent*> myTrackPoints;

	/// <summary>
	/// The track points (including assigned wheel, arrow component, and spline point index).
	/// of the left tank track.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	TArray<UTrackPoint*> myLeftTrackPoints; //aka Track Points L

	/// <summary>
	/// The track points (including assigned wheel, arrow component, and spline point index).
	/// of the right tank track.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Track")
	TArray<UTrackPoint*> myRightTrackPoints; //aka Track Points R

	/// <summary>
	/// The suspension Z offset for adjusting the suspension of the tank 
	/// while in and out of motion.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Suspension")
	float mySuspensionZOffset;

	/// <summary>
	/// The strength of the tank's suspension.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Suspension")
	float mySuspensionStrength;

	/// <summary>
	/// The dampening of the tank while in and out of motion.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Suspension")
	float mySuspensionDamping;

	/// <summary>
	/// The left track's suspension physics constraints.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Suspension")
	TArray<UPhysicsConstraintComponent*> myLeftSuspensionPhysicsConstraints;

	/// <summary>
	/// The right track's suspension physics constraints.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Suspension")
	TArray<UPhysicsConstraintComponent*> myRightSuspensionPhysicsConstraints;

	/// <summary>
	/// The calculated left track force.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wheel")
	float myForwardLeftForce;

	/// <summary>
	/// The calculated right track force.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wheel")
	float myForwardRightForce;
	
	/// <summary>
	/// A value for adjusting the rotation speed of the wheels.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wheel")
	float myWheelRotationMultiplier;

	/// <summary>
	/// The relative Y distance from the center for the wheel.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wheel")
	float myWheelDistanceFromCenter;

	/// <summary>
	/// The collection of static mesh wheels for the left track.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wheel")
	TArray<UStaticMeshComponent*> myLeftWheels;

	/// <summary>
	/// The collection of static mesh wheels for the right track.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wheel")
	TArray<UStaticMeshComponent*> myRightWheels;

	/// <summary>
	/// The left wheel's physics constraints.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wheel")
	TArray<UPhysicsConstraintComponent*> myLeftWheelPhysicsConstraints;

	/// <summary>
	/// The right wheel's physics constraints.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wheel")
	TArray<UPhysicsConstraintComponent*> myRightWheelPhysicsConstraints;

	/// <summary>
	/// The left track RPM used for determining the corresponding torque value.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Engine")
	float myLeftRPM;

	/// <summary>
	/// The right track RPM used for determining the corresponding torque value.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Engine")
	float myRightRPM;

	/// <summary>
	/// Modifier used for increasing/decreasing the rate that the RPM rises. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Engine")
	float myRPMRate;

	/// <summary>
	/// The rate at which rpm decrease is applied.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Engine")
	float myBreakingRate;

	/// <summary>
	/// The speed that determines if we need to break first or start reversing.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Engine")
	float myBreakingThreshold;

	/// <summary>
	/// The curve that correlates rpm to torque.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Engine")
	UCurveFloat* myTorqueCurve;

	/// <summary>
	/// The audio sound effect to play when the tank engine starts.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio")
	UAudioComponent* myEngineStartAudio; 

	/// <summary>
	/// The audio sound effect to play while the tank engine is running.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio")
	UAudioComponent* myEngineRunningAudio;

	/// <summary>
	/// The audio sound effect to play for the tracks moving.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio")
	UAudioComponent* myTracksRunningAudio;

	/// <summary>
	/// The particle system to use for the engine exhaust heat effect.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Particles")
	UParticleSystemComponent* myHeatParticles;

	/// <summary>
	/// The physics constraint component on the tow hitch (the hook on the back of the tank).
	/// This is used for attaching a static mesh (such as the tank nuts) to the hitch/hook, but
	/// also have physics. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Internal")
	UPhysicsConstraintComponent* myTankBodyHitchPhysicsConstraintComponent;
	
	/// <summary>
	/// The static mesh component to attach to the physics constraint
	/// hitch/hook on the back of the tank.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Internal")
	UStaticMeshComponent* myTankBodyHitchStaticMeshComponent;

	/// <summary>
	/// Adds the provided static mesh component trackHISMC to the provided spline. HISMC stands for
	/// Hierarchical Static Mesh Component.
	/// </summary>
	/// <param name="spline">The complete track spline.</param>
	/// <param name="trackHISMC">The static mesh piece of track.</param>
	/// <param name="isRightTrack">true if the track is the right side spline, false otherwise.</param>
	void AddHismToTrack(USplineComponent* spline, UHierarchicalInstancedStaticMeshComponent* trackHISMC, const bool isRightTrack);

	/// <summary>
	/// Adjusts the position of the spline points to match the wheel positions.
	/// </summary>
	/// <param name="trackPoint">The specific spline point in the provided spline.</param>
	/// <param name="spline">The track spline.</param>
	void AdjustSplinePointPerWheel(UTrackPoint* trackPoint, USplineComponent* spline);

	/// <summary>
	/// Adjusts the left and right suspension physics constraint.
	/// </summary>
	void AdjustSuspensions();

	/// <summary>
	/// Adjusts the left and right track points to the wheels, also keeps the track lengths constant.
	/// </summary>
	void AdjustTrackSuspension();

	/// <summary>
	/// Turns off all sound effects related to this tank.
	/// </summary>
	void DeActivateSoundsAndEffects();

	/// <summary>
	/// Finds this tank's blueprint components and initializes them.
	/// </summary>
	void FindComponents();

	/// <summary>
	/// Calculates and updates the left and right track speed based 
	/// on the input and other variables/multipliers.
	/// </summary>
	void CalculateTrackSpeed();

	/// <summary>
	/// Calculates how long (in seconds) the projectile is in the air.
	/// </summary>
	/// <param name="startingLocation">The world/level X,Y,Z location of where the projectile started moving.</param>
	/// <param name="initialVelocity">The initial/starting velocity of the projectile.</param>
	/// <param name="gravityAcceleration">The acceleration of gravity (9.81 m/s^2 (-981.0 in UE)).</param>
	/// <returns>The amount of time in seconds the projectile is in the air.</returns>
	float CalculateProjectileHangTime(const FVector& startingLocation, const FVector& initialVelocity, const float& gravityAcceleration);

	/// <summary>
	/// Uses the classic Projectile Motion formula to determine the current projectile world location 
	/// based on the provided values.
	/// </summary>
	/// <param name="startingLocation">The world/level X,Y,Z location of where the projectile started moving.</param>
	/// <param name="initialVelocity">The initial/starting velocity of the projectile.</param>
	/// <param name="gravityAcceleration">The acceleration of gravity (9.81 m/s^2 (-981.0 in UE)).</param>
	/// <param name="time">The current time (in seconds) elapsed.</param>
	/// <returns>The current projectile world location based on the provided values.</returns>
	FVector CalculateProjectileMotion(const FVector& startingLocation, const FVector& initialVelocity, const float& gravityAcceleration, const float& time);

	/// <summary>
	/// Sets the linear dampening for the tank body when in and out of motion.
	/// </summary>
	void HandleLinearDamping();

	/// <summary>
	/// Constantly keeps the left and right track lengths constant.
	/// </summary>
	/// <param name="isRightTrack">true if this is for the right track, false otherwise.</param>
	void KeepTrackLengthConstant(const bool isRightTrack);

	/// <summary>
	/// Adjusts the volume and intensity of the sound effects based on RPM and speed.
	/// </summary>
	void ModulateSounds();

	/// <summary>
	/// Calculates the left and right master wheel speed based on the force, rotation multiplier,
	/// and the current gear.
	/// </summary>
	void CalculateWheelSpeed();

	/// <summary>
	/// Sets the wheel speed indirectly by setting the angular velocity of the attached 
	/// wheel physics constraints.
	/// </summary>
	/// <param name="overrideWheelSpeed">Allows for overriding the wheel speed.</param>
	void SetWheelSpeed();

	/// <summary>
	/// Increments the current gear (clamps the value between 0 and total number of gears + 1).
	/// </summary>
	void ShiftGearUp();

	/// <summary>
	/// Decrements the current gear (clamps the value between 0 and total number of gears + 1).
	/// </summary>
	void ShiftGearDown();

	/// <summary>
	/// Stars the sound effects for the engine revving up.
	/// </summary>
	void StartEngineSounds();

	/// <summary>
	/// Enables control of this tank and kicks off 3 septate tick functions 
	/// running at different frequencies. These are used update the 
	/// track, speed, and handling of the tank. Also starts the sound effects
	/// for the tank.
	/// </summary>
	void StartTank();

	/// <summary>
	/// Stops control of the tank (and physically stops the tank), stops the 
	/// engine sound effects, and disables heat particles.
	/// </summary>
	void StopTank();

	/// <summary>
	/// Basically automatic transmission. This function gets called constantly to determine
	/// if/when to shift up or shift down gears based on the current speed of the tank. 
	/// </summary>
	void TryShiftGears();

	/// <summary>
	/// Updates and moves the track parts in the left/right track spline depending on the current track speed
	/// and the current spline length (or the adjusted spline length).
	/// </summary>
	/// <param name="tracks">The track points that make up the track spline.</param>
	/// <param name="trackSpeed">The current speed of the track.</param>
	void UpdateTrackInstances(TArray<UTrackPos*> tracks, const float trackSpeed);

	/// <summary>
	/// Tick/Update function for updating wheel speed, adjusting suspension, and adjusts the orientations
	/// of the turrets, and tank cannon.
	/// </summary>
	UFUNCTION()
	void HandlingTickFunc();

	/// <summary>
	/// Tick/Update function that calculates the current track speed.
	/// </summary>
	UFUNCTION()
	void TrackMovementTickFunc();

	/// <summary>
	/// Tick/Update function that calculates the overall velocity magnitude 
	/// (the speed) of this tank.
	/// </summary>
	UFUNCTION()
	void SpeedTickFunc();

public:
	/// <summary>
	/// Default constructor.
	/// </summary>
	APhysicsTankPawn();

	/// <summary>
	/// Called when this tank is first spawned in the world.
	/// </summary>
	virtual void BeginPlay() override;

	/// <summary>
	/// Called when this blueprint is first opened or initialized. This is equivalent to a 
	/// constructor but for Unreal Engine. 
	/// </summary>
	/// <param name="transform">The transform the actor was constructed at.</param>
	virtual void OnConstruction(const FTransform& transform) override;

	/// <summary>
	/// Called once per frame to update the left/right track force based on player input.
	/// </summary>
	/// <param name="deltaTime">Time in milliseconds between frames.</param>
	virtual void Tick(float deltaTime) override;

	/// <summary>
	/// Starts the tank (and allows control of it) after myStartupTime seconds.
	/// </summary>
	UFUNCTION(BlueprintCallable)
	void Initialize();

	/// <summary>
	/// Adjusts the axis input value based on the RPM torque curve and the current gear. This handles
	/// movement for the tank based on the current provided axisInputValue and trackRPM.
	/// </summary>
	/// <param name="axisInputValue">The input axis value typically between 0.0 and 1.0.</param>
	/// <param name="trackRPM">The calculated RPM for the track.</param>
	UFUNCTION(BlueprintCallable)
	virtual void MoveTrackForward(float& axisInputValue, float& trackRPM);

	/// <summary>
	/// Destroys this tank by stopping input, deactivates the sounds and effects.
	/// </summary>
	UFUNCTION(BlueprintCallable)
	void DestroyTank();

	/// <summary>
	/// Displays a trajectory crosshair for the player. By default, this draws the
	/// final landing location of the projectile. If you change the drawLines variable
	/// to true, you will see the full projectile path.
	/// </summary>
	/// <param name="initialVelocity">The initial/starting velocity of the projectile.</param>
	UFUNCTION(BlueprintCallable)
	void DrawTrajectory(const FVector& initialVelocity);

	/// <summary>
	/// Attaches the provided item to the hook/hitch on the back of the tank.
	/// </summary>
	/// <param name="itemData">The tank item to attach to the hook/hitch.</param>
	/// <returns>true on success, false otherwise.</returns>
	UFUNCTION()
	bool AttachItemToTankBodyHitch(UTankItemData* itemData);

	/// <summary>
	/// Attaches the provided item to the top plates of the Tank's turret (the chest).
	/// </summary>
	/// <param name="itemData">The item to attach to the tank turret's forward plates.</param>
	/// <returns>true on success, false otherwise.</returns>
	UFUNCTION()
	bool AttachItemToTankTurretChest(UTankItemData* itemData);

	/// <summary>
	/// Moves the tank forward with the provided axis input value.
	/// </summary>
	/// <param name="value">The axis input value.</param>
	UFUNCTION()
	void MoveForward(float value);

	/// <summary>
	/// Moves the left track forward with the provided axis input value.
	/// </summary>
	/// <param name="value">The axis input value.</param>
	UFUNCTION()
	void MoveLeftTrackForward(float value);

	/// <summary>
	/// Moves the right track forward with the provided axis input value.
	/// </summary>
	/// <param name="value">The axis input value.</param>
	UFUNCTION()
	void MoveRightTrackForward(float value);

	/// <summary>
	/// Turns the tank left/right depending on the provided axis value.
	/// </summary>
	/// <param name="value">The axis input value.</param>
	UFUNCTION()
	void MoveLeftRight(float value);

	/// <summary>
	/// Returns true if this tank is started and able to be moved/controlled.
	/// </summary>
	/// <returns>true if this tank is started and able to be moved/controlled, false otherwise.</returns>
	bool GetHasStarted();

	/// <summary>
	/// Returns the current speed of this tank.
	/// </summary>
	/// <returns>The current speed of this tank.<returns>
	FString GetSpeedDisplayString();

	/// <summary>
	/// Returns the current gear the tank is in.
	/// </summary>
	/// <returns>The current gear the tank is in.</returns>
	FString GetGearDisplayString();

	/// <summary>
	/// Updates/Changes the corresponding tank mesh based on the provided tank item. 
	/// </summary>
	/// <param name="theItemData">The tank item to set to the tank.</param>
	/// <returns>true on success, false otherwise.</returns>
	bool SetStaticMeshForEquipmentType(UTankItemData* tankItemData);

	/// <summary>
	/// Updates/Changes the specified static mesh based on the provided component name.
	/// </summary>
	/// <param name="theNamedComponent">The name of the component on the tank blueprint.</param>
	/// <param name="theStaticMesh">The static mesh to attach to the corresponding component.</param>
	/// <returns>true on success, false otherwise.</returns>
	bool SetStaticMeshForNamedComponent(const FString theNamedComponent, UStaticMesh* theStaticMesh);
};

