#include "EnemyProjectile.h" #include "GameFramework/ProjectileMovementComponent.h" #include "Components/StaticMeshComponent.h" #include "SpaceshipPawn.h" // Include the player ship header #include "Kismet/GameplayStatics.h" #include "Engine/Engine.h" AEnemyProjectile::AEnemyProjectile() { PrimaryActorTick.bCanEverTick = true; // Create and setup the projectile mesh ProjectileMesh = CreateDefaultSubobject(TEXT("ProjectileMesh")); RootComponent = ProjectileMesh; ProjectileMesh->SetCollisionProfileName(TEXT("EnemyProjectile")); // Set up collision ProjectileMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); ProjectileMesh->SetCollisionObjectType(ECC_WorldDynamic); ProjectileMesh->SetCollisionResponseToAllChannels(ECR_Ignore); // Block GameTraceChannel1 (player ship) ProjectileMesh->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECR_Block); // Block world static for environment collisions ProjectileMesh->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block); // Block pawns for player ship collision ProjectileMesh->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block); // Enable overlap events ProjectileMesh->SetGenerateOverlapEvents(true); // Bind hit event ProjectileMesh->OnComponentHit.AddDynamic(this, &AEnemyProjectile::OnHit); // Bind overlap event for redundant collision detection ProjectileMesh->OnComponentBeginOverlap.AddDynamic(this, &AEnemyProjectile::OnOverlapBegin); // Create and setup projectile movement ProjectileMovement = CreateDefaultSubobject(TEXT("ProjectileMovement")); ProjectileMovement->UpdatedComponent = ProjectileMesh; ProjectileMovement->InitialSpeed = ProjectileSpeed; ProjectileMovement->MaxSpeed = ProjectileSpeed; ProjectileMovement->bRotationFollowsVelocity = true; ProjectileMovement->ProjectileGravityScale = 0.0f; // Important: Enable sweep to prevent tunneling at high speeds ProjectileMovement->bSweepCollision = true; // Set lifetime InitialLifeSpan = 3.0f; } void AEnemyProjectile::BeginPlay() { Super::BeginPlay(); // Debug message to verify projectile spawned if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Blue, TEXT("Enemy Projectile Spawned")); } } void AEnemyProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) { // Debug message to verify hit detection if (GEngine && OtherActor) { GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Orange, FString::Printf(TEXT("Enemy Projectile hit: %s"), *OtherActor->GetName())); } if (OtherActor && OtherActor != this) { // Check if we hit a player ship ASpaceshipPawn* Player = Cast(OtherActor); if (Player) { // Apply damage to player FDamageEvent DamageEvent; float ActualDamage = Player->TakeDamage(DamageAmount, DamageEvent, nullptr, this); // Debug message for damage if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, FString::Printf(TEXT("Dealt %.1f damage to player"), ActualDamage)); } // Spawn impact effect if set if (ImpactEffect) { UGameplayStatics::SpawnEmitterAtLocation( GetWorld(), ImpactEffect, Hit.Location, Hit.Normal.Rotation() ); } } } // Destroy the projectile Destroy(); } void AEnemyProjectile::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) { // This is a redundant collision detection method // Some fast-moving projectiles might miss hit events but catch overlap events // Debug message to verify overlap detection if (GEngine && OtherActor) { GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, FString::Printf(TEXT("Enemy Projectile overlap with: %s"), *OtherActor->GetName())); } if (OtherActor && OtherActor != this) { // Check if we overlap with a player ship ASpaceshipPawn* Player = Cast(OtherActor); if (Player) { // Apply damage to player FDamageEvent DamageEvent; float ActualDamage = Player->TakeDamage(DamageAmount, DamageEvent, nullptr, this); // Debug message for damage if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, FString::Printf(TEXT("Overlap - Dealt %.1f damage to player"), ActualDamage)); } // Spawn impact effect if set if (ImpactEffect) { UGameplayStatics::SpawnEmitterAtLocation( GetWorld(), ImpactEffect, GetActorLocation(), GetActorRotation() ); } // Destroy the projectile Destroy(); } } }