<commit_before>
#include "FlareMeteorite.h"
#include "../Flare.h"
#include "../Game/FlareGame.h"
#include "../Data/FlareMeteoriteCatalog.h"
#include "Components/DestructibleComponent.h"
#include "../Player/FlarePlayerController.h"

#define LOCTEXT_NAMESPACE "FlareMeteorite"


/*----------------------------------------------------
	Constructor
----------------------------------------------------*/

AFlareMeteorite::AFlareMeteorite(const class FObjectInitializer& PCIP) : Super(PCIP)
{
	// Mesh
	Meteorite = PCIP.CreateDefaultSubobject<UDestructibleComponent>(this, TEXT("Meteorite"));
	Meteorite->bTraceComplexOnMove = true;
	Meteorite->SetLinearDamping(0);
	Meteorite->SetAngularDamping(0);
	Meteorite->SetSimulatePhysics(true);
	RootComponent = Meteorite;
	SetActorEnableCollision(true);

	// Physics
	Meteorite->SetMobility(EComponentMobility::Movable);
	Meteorite->SetCollisionProfileName("Destructible");
	Meteorite->GetBodyInstance()->SetUseAsyncScene(false);
	Meteorite->GetBodyInstance()->SetInstanceSimulatePhysics(true);
	Meteorite->SetNotifyRigidBodyCollision(true);

	// Settings
	Meteorite->PrimaryComponentTick.bCanEverTick = true;
	PrimaryActorTick.bCanEverTick = true;
	Paused = false;

	// Content
	static ConstructorHelpers::FObjectFinder<UParticleSystem> RockEffectTemplateObj(TEXT("/Game/Environment/Meteorites/Common/PS_Breakup_Rock.PS_Breakup_Rock"));
	static ConstructorHelpers::FObjectFinder<UParticleSystem> MetalEffectTemplateObj(TEXT("/Game/Environment/Meteorites/Common/PS_Breakup_Metal.PS_Breakup_Metal"));
	RockEffectTemplate = RockEffectTemplateObj.Object;
	MetalEffectTemplate = MetalEffectTemplateObj.Object;
}

void AFlareMeteorite::Load(FFlareMeteoriteSave* Data, UFlareSector* ParentSector)
{
	Parent = ParentSector;

	MeteoriteData = Data;
	SetupMeteoriteMesh();
	Meteorite->SetPhysicsLinearVelocity(Data->LinearVelocity);
	Meteorite->SetPhysicsAngularVelocity(Data->AngularVelocity);
	LifeTime = 0.f;
}

FFlareMeteoriteSave* AFlareMeteorite::Save()
{
	// Physical data
	MeteoriteData->Location = GetActorLocation();
	MeteoriteData->Rotation = GetActorRotation();
	if (!Paused)
	{
		MeteoriteData->LinearVelocity = Meteorite->GetPhysicsLinearVelocity();
		MeteoriteData->AngularVelocity = Meteorite->GetPhysicsAngularVelocity();
	}

	return MeteoriteData;
}

void AFlareMeteorite::BeginPlay()
{
	Super::BeginPlay();
}

void AFlareMeteorite::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);

	if(!Target)
	{
		Target = Parent->FindSpacecraft(MeteoriteData->TargetStation);
	}

	float DeltaveSq = (MeteoriteData->LinearVelocity - Meteorite->GetPhysicsLinearVelocity()).SizeSquared();

	float const SafeThresold = 5.f;

	if(LifeTime < SafeThresold)
	{
		LifeTime += DeltaSeconds;
		if(DeltaveSq > 100)
		{
			Meteorite->SetPhysicsLinearVelocity(MeteoriteData->LinearVelocity);
		}
	}

	/*FLOGV("Meteorite %s vel=%s", *GetName(), *Meteorite->GetPhysicsLinearVelocity().ToString());
	FLOGV(" - IsPhysicsCollisionEnabled %d", Meteorite->IsPhysicsCollisionEnabled());
	FLOGV(" - IsPhysicsStateCreated %d", Meteorite->IsPhysicsStateCreated());
	FLOGV(" - IsAnySimulatingPhysics %d", Meteorite->IsAnySimulatingPhysics());
	FLOGV(" - IsAnyRigidBodyAwake %d", Meteorite->IsAnyRigidBodyAwake());
	FLOGV(" - IsCollisionEnabled %d", Meteorite->IsCollisionEnabled());
	FLOGV(" - IsSimulatingPhysics %d", Meteorite->IsSimulatingPhysics());*/

	/*float CollisionSize = Asteroid->GetCollisionShape().GetExtent().Size();
	if (SpawnLocation.Size() <= 0.1)
	{
		SpawnLocation = GetActorLocation();
		DrawDebugSphere(GetWorld(), SpawnLocation, CollisionSize / 2, 16, FColor::Red, true);
	}
	else
	{
		DrawDebugSphere(GetWorld(), GetActorLocation(), CollisionSize / 2, 16, FColor::Blue, false);
		DrawDebugLine(GetWorld(), GetActorLocation(), SpawnLocation, FColor::Green, false);
	}*/

	if (LifeTime > SafeThresold)
	{
		if (!IsBroken()  && !MeteoriteData->HasMissed && Target)
		{
			FVector CurrentVelocity =  Meteorite->GetPhysicsLinearVelocity();
			FVector CurrentDirection = CurrentVelocity.GetUnsafeNormal();
			float Velocity = MeteoriteData->LinearVelocity.Size();

			FVector TargetDirection = (Target->GetActorLocation() + MeteoriteData->TargetOffset - GetActorLocation()).GetUnsafeNormal();
			FVector TargetVelocity = TargetDirection * Velocity;
			float Dot = FVector::DotProduct(CurrentDirection, TargetDirection);

			//UKismetSystemLibrary::DrawDebugLine(GetWorld(), GetActorLocation(), GetActorLocation() + TargetDirection * 10000,FColor::Green, 1000.f);
			//UKismetSystemLibrary::DrawDebugLine(GetWorld(), GetActorLocation(), GetActorLocation() + CurrentDirection * 10000,FColor::Red, 1000.f);

			if (Dot > 0)
			{
				// Don't fix if miss
				FVector DeltaVelocity = TargetVelocity - CurrentVelocity;				
				FVector DeltaVelocityClamped = DeltaVelocity.GetClampedToMaxSize(50.f * DeltaSeconds);
				FVector CorrectedVelocity = CurrentVelocity + DeltaVelocityClamped;
				FVector CorrectedDirection = CorrectedVelocity.GetUnsafeNormal();

				//UKismetSystemLibrary::DrawDebugLine(GetWorld(), GetActorLocation(), GetActorLocation() + CorrectedDirection * 10000 ,FColor::Blue, 1000.f);
				Meteorite->SetPhysicsLinearVelocity(CorrectedDirection * Velocity);
			}
			else if (Dot < -0.5f)
			{
				MeteoriteData->HasMissed = true;

				Parent->GetGame()->GetPC()->Notify(LOCTEXT("MeteoriteMiss", "Meteorite miss"),
										FText::Format(LOCTEXT("MeteoriteMissFormat", "A meteorite missed {0}. It's not dangerous anymore."), UFlareGameTools::DisplaySpacecraftName(Target->GetParent())),
										FName("meteorite-miss"),
										EFlareNotification::NT_Military,
										false);
				
				Parent->GetGame()->GetQuestManager()->OnEvent(FFlareBundle().PutTag("meteorite-miss-station").PutName("sector", Parent->GetSimulatedSector()->GetIdentifier()));
			}
		}
	}
}


void AFlareMeteorite::OnCollision(class AActor* Other, FVector HitLocation)
{
	AFlareAsteroid* Asteroid = Cast<AFlareAsteroid>(Other);
	AFlareSpacecraft* Spacecraft = Cast<AFlareSpacecraft>(Other);

	if(Asteroid)
	{
		ApplyDamage(MeteoriteData->BrokenDamage, 1.f , HitLocation, EFlareDamage::DAM_Collision, NULL, FString());
	}
	else if (Spacecraft && (Spacecraft->IsStation() || Spacecraft->GetSize() == EFlarePartSize::L))
	{
		if (!IsBroken())
		{
			FVector DeltaVelocity = Spacecraft->GetLinearVelocity() - Meteorite->GetPhysicsLinearVelocity();
			float Energy = MeteoriteData->BrokenDamage * DeltaVelocity.SizeSquared() * (Spacecraft->IsStation() ? 0.00001 : 0.00000001);

			Spacecraft->GetDamageSystem()->ApplyDamage(Energy, MeteoriteData->BrokenDamage, HitLocation, EFlareDamage::DAM_Collision, NULL, FString());

			// Spawn FX
			UParticleSystemComponent* PSC = UGameplayStatics::SpawnEmitterAtLocation(
				Spacecraft,
				MeteoriteData->IsMetal ? MetalEffectTemplate : RockEffectTemplate,
				GetActorLocation(),
				FRotator::ZeroRotator,<commit_msg>#979 Nerf meteorite damage to stations<commit_after>
#include "FlareMeteorite.h"
#include "../Flare.h"
#include "../Game/FlareGame.h"
#include "../Data/FlareMeteoriteCatalog.h"
#include "Components/DestructibleComponent.h"
#include "../Player/FlarePlayerController.h"

#define LOCTEXT_NAMESPACE "FlareMeteorite"


/*----------------------------------------------------
	Constructor
----------------------------------------------------*/

AFlareMeteorite::AFlareMeteorite(const class FObjectInitializer& PCIP) : Super(PCIP)
{
	// Mesh
	Meteorite = PCIP.CreateDefaultSubobject<UDestructibleComponent>(this, TEXT("Meteorite"));
	Meteorite->bTraceComplexOnMove = true;
	Meteorite->SetLinearDamping(0);
	Meteorite->SetAngularDamping(0);
	Meteorite->SetSimulatePhysics(true);
	RootComponent = Meteorite;
	SetActorEnableCollision(true);

	// Physics
	Meteorite->SetMobility(EComponentMobility::Movable);
	Meteorite->SetCollisionProfileName("Destructible");
	Meteorite->GetBodyInstance()->SetUseAsyncScene(false);
	Meteorite->GetBodyInstance()->SetInstanceSimulatePhysics(true);
	Meteorite->SetNotifyRigidBodyCollision(true);

	// Settings
	Meteorite->PrimaryComponentTick.bCanEverTick = true;
	PrimaryActorTick.bCanEverTick = true;
	Paused = false;

	// Content
	static ConstructorHelpers::FObjectFinder<UParticleSystem> RockEffectTemplateObj(TEXT("/Game/Environment/Meteorites/Common/PS_Breakup_Rock.PS_Breakup_Rock"));
	static ConstructorHelpers::FObjectFinder<UParticleSystem> MetalEffectTemplateObj(TEXT("/Game/Environment/Meteorites/Common/PS_Breakup_Metal.PS_Breakup_Metal"));
	RockEffectTemplate = RockEffectTemplateObj.Object;
	MetalEffectTemplate = MetalEffectTemplateObj.Object;
}

void AFlareMeteorite::Load(FFlareMeteoriteSave* Data, UFlareSector* ParentSector)
{
	Parent = ParentSector;

	MeteoriteData = Data;
	SetupMeteoriteMesh();
	Meteorite->SetPhysicsLinearVelocity(Data->LinearVelocity);
	Meteorite->SetPhysicsAngularVelocity(Data->AngularVelocity);
	LifeTime = 0.f;
}

FFlareMeteoriteSave* AFlareMeteorite::Save()
{
	// Physical data
	MeteoriteData->Location = GetActorLocation();
	MeteoriteData->Rotation = GetActorRotation();
	if (!Paused)
	{
		MeteoriteData->LinearVelocity = Meteorite->GetPhysicsLinearVelocity();
		MeteoriteData->AngularVelocity = Meteorite->GetPhysicsAngularVelocity();
	}

	return MeteoriteData;
}

void AFlareMeteorite::BeginPlay()
{
	Super::BeginPlay();
}

void AFlareMeteorite::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);

	if(!Target)
	{
		Target = Parent->FindSpacecraft(MeteoriteData->TargetStation);
	}

	float DeltaveSq = (MeteoriteData->LinearVelocity - Meteorite->GetPhysicsLinearVelocity()).SizeSquared();

	float const SafeThresold = 5.f;

	if(LifeTime < SafeThresold)
	{
		LifeTime += DeltaSeconds;
		if(DeltaveSq > 100)
		{
			Meteorite->SetPhysicsLinearVelocity(MeteoriteData->LinearVelocity);
		}
	}

	/*FLOGV("Meteorite %s vel=%s", *GetName(), *Meteorite->GetPhysicsLinearVelocity().ToString());
	FLOGV(" - IsPhysicsCollisionEnabled %d", Meteorite->IsPhysicsCollisionEnabled());
	FLOGV(" - IsPhysicsStateCreated %d", Meteorite->IsPhysicsStateCreated());
	FLOGV(" - IsAnySimulatingPhysics %d", Meteorite->IsAnySimulatingPhysics());
	FLOGV(" - IsAnyRigidBodyAwake %d", Meteorite->IsAnyRigidBodyAwake());
	FLOGV(" - IsCollisionEnabled %d", Meteorite->IsCollisionEnabled());
	FLOGV(" - IsSimulatingPhysics %d", Meteorite->IsSimulatingPhysics());*/

	/*float CollisionSize = Asteroid->GetCollisionShape().GetExtent().Size();
	if (SpawnLocation.Size() <= 0.1)
	{
		SpawnLocation = GetActorLocation();
		DrawDebugSphere(GetWorld(), SpawnLocation, CollisionSize / 2, 16, FColor::Red, true);
	}
	else
	{
		DrawDebugSphere(GetWorld(), GetActorLocation(), CollisionSize / 2, 16, FColor::Blue, false);
		DrawDebugLine(GetWorld(), GetActorLocation(), SpawnLocation, FColor::Green, false);
	}*/

	if (LifeTime > SafeThresold)
	{
		if (!IsBroken()  && !MeteoriteData->HasMissed && Target)
		{
			FVector CurrentVelocity =  Meteorite->GetPhysicsLinearVelocity();
			FVector CurrentDirection = CurrentVelocity.GetUnsafeNormal();
			float Velocity = MeteoriteData->LinearVelocity.Size();

			FVector TargetDirection = (Target->GetActorLocation() + MeteoriteData->TargetOffset - GetActorLocation()).GetUnsafeNormal();
			FVector TargetVelocity = TargetDirection * Velocity;
			float Dot = FVector::DotProduct(CurrentDirection, TargetDirection);

			//UKismetSystemLibrary::DrawDebugLine(GetWorld(), GetActorLocation(), GetActorLocation() + TargetDirection * 10000,FColor::Green, 1000.f);
			//UKismetSystemLibrary::DrawDebugLine(GetWorld(), GetActorLocation(), GetActorLocation() + CurrentDirection * 10000,FColor::Red, 1000.f);

			if (Dot > 0)
			{
				// Don't fix if miss
				FVector DeltaVelocity = TargetVelocity - CurrentVelocity;				
				FVector DeltaVelocityClamped = DeltaVelocity.GetClampedToMaxSize(50.f * DeltaSeconds);
				FVector CorrectedVelocity = CurrentVelocity + DeltaVelocityClamped;
				FVector CorrectedDirection = CorrectedVelocity.GetUnsafeNormal();

				//UKismetSystemLibrary::DrawDebugLine(GetWorld(), GetActorLocation(), GetActorLocation() + CorrectedDirection * 10000 ,FColor::Blue, 1000.f);
				Meteorite->SetPhysicsLinearVelocity(CorrectedDirection * Velocity);
			}
			else if (Dot < -0.5f)
			{
				MeteoriteData->HasMissed = true;

				Parent->GetGame()->GetPC()->Notify(LOCTEXT("MeteoriteMiss", "Meteorite miss"),
										FText::Format(LOCTEXT("MeteoriteMissFormat", "A meteorite missed {0}. It's not dangerous anymore."), UFlareGameTools::DisplaySpacecraftName(Target->GetParent())),
										FName("meteorite-miss"),
										EFlareNotification::NT_Military,
										false);
				
				Parent->GetGame()->GetQuestManager()->OnEvent(FFlareBundle().PutTag("meteorite-miss-station").PutName("sector", Parent->GetSimulatedSector()->GetIdentifier()));
			}
		}
	}
}


void AFlareMeteorite::OnCollision(class AActor* Other, FVector HitLocation)
{
	AFlareAsteroid* Asteroid = Cast<AFlareAsteroid>(Other);
	AFlareSpacecraft* Spacecraft = Cast<AFlareSpacecraft>(Other);

	if(Asteroid)
	{
		ApplyDamage(MeteoriteData->BrokenDamage, 1.f , HitLocation, EFlareDamage::DAM_Collision, NULL, FString());
	}
	else if (Spacecraft && (Spacecraft->IsStation() || Spacecraft->GetSize() == EFlarePartSize::L))
	{
		if (!IsBroken())
		{
			FVector DeltaVelocity = Spacecraft->GetLinearVelocity() - Meteorite->GetPhysicsLinearVelocity();

			float EnergyBaseRatio = 0.00001f;
			float Energy = EnergyBaseRatio * MeteoriteData->BrokenDamage * DeltaVelocity.SizeSquared() * (Spacecraft->IsStation() ? 0.2f : 0.0001f);

			Spacecraft->GetDamageSystem()->ApplyDamage(Energy, MeteoriteData->BrokenDamage, HitLocation, EFlareDamage::DAM_Collision, NULL, FString());

			// Spawn FX
			UParticleSystemComponent* PSC = UGameplayStatics::SpawnEmitterAtLocation(
				Spacecraft,
				MeteoriteData->IsMetal ? MetalEffectTemplate : RockEffectTemplate,
				GetActorLocation(),
				FRotator::ZeroRotator,
{'fraction_non_alphanumeric': 0.14696739680474227, 'fraction_numerical': 0.00932552591628714, 'mean_word_length': 5.731873479318735, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 145, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1448601', 'n_tokens_mistral': 5088, 'n_tokens_neox': 4476, 'n_words': 650}