/*
* 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>
*/
#include "TankProjectile.h"
#include <DestructibleActor.h>
#include <DestructibleComponent.h>
#include <Kismet/GameplayStatics.h>
#include "../Pawns/AITankPawn.h"

/// <summary>
/// The default constructor for the tank projectile.
/// </summary>
ATankProjectile::ATankProjectile() : Super()
{
	myImpuseToApply = -10000.0f;
	myDamageToApply = 25.0f;

	//Use a sphere as a simple collision representation
	myCollisionComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("CollisionComponent"));
	myCollisionComponent->InitBoxExtent(FVector(50.0f, 5.0f, 5.0f));
	myCollisionComponent->BodyInstance.SetCollisionProfileName("Projectile");
	myCollisionComponent->OnComponentHit.AddDynamic(this, &ATankProjectile::OnHit);		// set up a notification for when this component hits something blocking

	//Players can't walk on it
	myCollisionComponent->SetWalkableSlopeOverride(FWalkableSlopeOverride(WalkableSlope_Unwalkable, 0.0f));
	myCollisionComponent->CanCharacterStepUpOn = ECB_No;

	//Set as root component
	RootComponent = myCollisionComponent;

	//Use a ProjectileMovementComponent to govern this projectile's movement
	myProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileComponent"));
	myProjectileMovementComponent->UpdatedComponent = myCollisionComponent;
	myProjectileMovementComponent->InitialSpeed = 6000.0f;
	myProjectileMovementComponent->MaxSpeed = 6000.0f;
	myProjectileMovementComponent->bRotationFollowsVelocity = true;
	myProjectileMovementComponent->bShouldBounce = false;
	myProjectileMovementComponent->SetComponentTickEnabled(true);

	//Die after 30 seconds.
	InitialLifeSpan = 30.0f;
}

/// <summary>
/// Called when the scene/level first starts. Finds the static mesh component
/// from the blueprint.
/// </summary>
void ATankProjectile::BeginPlay()
{
	Super::BeginPlay();

	myStaticMeshComponent = FindComponentByClass<UStaticMeshComponent>(); 
}

/// <summary>
/// Called when this projectile collides with something. Depending on what it collides with, 
/// there are different responses.
/// 
/// Here is the original comment from the parent function: Event called when a component hits 
/// (or is hit by) something solid. This could happen due to things like Character movement, 
/// using Set Location with 'sweep' enabled, or physics simulation. For events when objects 
/// overlap(e.g.walking into a trigger) see the 'Overlap' event.
///
/// @note For collisions during physics simulation to generate hit events, 'Simulation Generates Hit Events' must be enabled for this component.
/// @note When receiving a hit from another object's movement, the directions of 'Hit.Normal' and 'Hit.ImpactNormal'
/// will be adjusted to indicate force from the other object against this object.
/// @note NormalImpulse will be filled in for physics - simulating bodies, but will be zero for swept - component blocking collisions.
/// </summary>
/// <param name="hitComp">The specific component that was hit on this actor.</param>
/// <param name="otherActor">The actor this projectile collided with.</param>
/// <param name="otherComp">The specific component in otherActor that was hit.</param>
/// <param name="normalImpulse">NormalImpulse will be filled in for physics-simulating bodies, but will be zero for swept-component blocking collisions.</param>
/// <param name="hitResult">Structure containing information about one hit of a trace, such as point of impact and surface normal at that point.</param>
void ATankProjectile::OnHit(UPrimitiveComponent* hitComp, AActor* otherActor, UPrimitiveComponent* otherComp, FVector normalImpulse, const FHitResult& hitResult)
{
	//Ensure otherActor is available and that we are NOT colliding with ourselves or the owner. 
	if ((otherActor != NULL) && (otherActor != this) && (otherActor != this->GetOwner()))
	{
		ATankPawn* collidedTank = Cast<ATankPawn>(otherActor);

		if (collidedTank != NULL && collidedTank->GetIsTankDestroyed() == false)
		{
			ATankPawn* shootingTank = Cast<ATankPawn>(GetOwner());
			
			collidedTank->OnTankHit(shootingTank, myDamageToApply);

			shootingTank->OnSuccessfullyHitTank(collidedTank);

			ExplodeAndDestroy();
		}
		else
		{
			//Damage any destructible we hit.
			ADestructibleActor* destructibleActor = Cast<ADestructibleActor>(otherActor);
			if (destructibleActor != NULL)
			{
				destructibleActor->GetDestructibleComponent()->ApplyDamage(10000.0f, hitResult.Location, FVector::OneVector, 10000.0f);

				ExplodeAndDestroy();
			}
		}
	}
}

/// <summary>
/// Plays exploding sound effect, starts explosion particles, and destroys/removes the projectile.
/// </summary>
void ATankProjectile::ExplodeAndDestroy()
{
	//Play ballistics sound effect.
	UGameplayStatics::PlaySoundAtLocation(this, myTankExplosionAudio, GetActorLocation());

	FTransform emitterTransform = GetActorTransform();
	emitterTransform.SetScale3D(FVector(4, 4, 4));

	//Spawn smoke particle effects at the fire(spawn) location.
	UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), myTankHitParticles, GetActorTransform());

	Destroy();
}

/// <summary>
/// Returns the ammount of impulse generated by shooting the projectile.
/// </summary>
/// <returns>The ammount of impulse generated by shooting the projectile.</returns>
float ATankProjectile::GetImpulseToApply() const
{
	return myImpuseToApply;
}

/// <summary>
/// Returns a pointer to this projectile's Projectile Movement Component.
/// </summary>
/// <returns>A pointer to this projectile's Projectile Movement Component.</returns>
UProjectileMovementComponent* ATankProjectile::GetProjectileMovementComponent() const
{
	return myProjectileMovementComponent;
}
