#include "Elimination/EliminationComponent.h" #include "GameFramework/Actor.h" #include "Kismet/GameplayStatics.h" #include "Player/MatchPlayerState.h" #include "Game/MatchGameState.h" UEliminationComponent::UEliminationComponent() { PrimaryComponentTick.bCanEverTick = false; SequentialElimInterval = 2.f; ElimsNeededForStreak = 5; LastElimTime = 0.f; SequentialElims = 0; Streak = 0; } void UEliminationComponent::BeginPlay() { Super::BeginPlay(); } AMatchPlayerState* UEliminationComponent::GetPlayerStateFromActor(AActor* Actor) { APawn* Pawn = Cast(Actor); if (IsValid(Pawn)) { return Pawn->GetPlayerState(); } return nullptr; } void UEliminationComponent::OnRoundReported(AActor* Attacker, AActor* Victim, bool bHit, bool bHeadShot, bool bLethal) { AMatchPlayerState* AttackerPS = GetPlayerStateFromActor(Attacker); if (!IsValid(AttackerPS)) return; ProcessHitOrMiss(bHit, AttackerPS); if (!bHit) return; // Early return if it was a miss AMatchPlayerState* VictimPS = GetPlayerStateFromActor(Victim); if (!IsValid(VictimPS)) return; if (bLethal) ProcessElimination(bHeadShot, AttackerPS, VictimPS); } void UEliminationComponent::ProcessHitOrMiss(bool bHit, AMatchPlayerState* AttackerPS) { if (bHit) { AttackerPS->AddHit(); } else { AttackerPS->AddMiss(); } } void UEliminationComponent::ProcessHeadshot(bool bHeadShot, ESpecialElimType& SpecialElimType, AMatchPlayerState* AttackerPS) { if (bHeadShot) { SpecialElimType |= ESpecialElimType::Headshot; AttackerPS->AddHeadShotElim(); } } void UEliminationComponent::ProcessSequentialEliminations(AMatchPlayerState* AttackerPS, ESpecialElimType& SpecialElimType) { float CurrentTime = GetWorld()->GetTimeSeconds(); if (CurrentTime - LastElimTime <= SequentialElimInterval) { SequentialElims++; } else { SequentialElims = 1; } LastElimTime = CurrentTime; if (SequentialElims > 1) { SpecialElimType |= ESpecialElimType::Sequential; AttackerPS->AddSequentialElim(SequentialElims); } } void UEliminationComponent::ProcessStreaks(AMatchPlayerState* AttackerPS, AMatchPlayerState* VictimPS, ESpecialElimType& SpecialElimType) { ++Streak; if (Streak >= ElimsNeededForStreak) { SpecialElimType |= ESpecialElimType::Streak; AttackerPS->SetOnStreak(true); AttackerPS->UpdateHighestStreak(Streak); } if (VictimPS->IsOnStreak()) { SpecialElimType |= ESpecialElimType::Showstopper; AttackerPS->AddShowStopperElim(); } VictimPS->SetOnStreak(false); if (AttackerPS->GetLastAttacker() == VictimPS) { SpecialElimType |= ESpecialElimType::Revenge; AttackerPS->AddRevengeElim(); AttackerPS->SetLastAttacker(nullptr); } VictimPS->SetLastAttacker(AttackerPS); } void UEliminationComponent::HandleFirstBlood(AMatchGameState* GameState, ESpecialElimType& SpecialElimType, AMatchPlayerState* AttackerPS) { if (!GameState->HasFirstBloodBeenHad()) { SpecialElimType |= ESpecialElimType::FirstBlood; AttackerPS->GotFirstBlood(); } } void UEliminationComponent::UpdateLeaderStatus(AMatchGameState* GameState, ESpecialElimType& SpecialElimType, AMatchPlayerState* AttackerPS, AMatchPlayerState* VictimPS) { AMatchPlayerState* LastLeader = GameState->GetLeader(); const bool bAttackerWasTiedForTheLead = GameState->IsTiedForTheLead(AttackerPS); GameState->UpdateLeader(); if (!bAttackerWasTiedForTheLead && GameState->IsTiedForTheLead(AttackerPS)) { SpecialElimType |= ESpecialElimType::TiedTheLeader; } if (IsValid(LastLeader) && LastLeader != GameState->GetLeader()) { // LastLeader has now lost the lead LastLeader->Client_LostTheLead(); if (VictimPS == LastLeader) { SpecialElimType |= ESpecialElimType::Dethrone; AttackerPS->AddDethroneElim(); } } if (AttackerPS != LastLeader && AttackerPS == GameState->GetLeader()) { SpecialElimType |= ESpecialElimType::GainedTheLead; } } bool UEliminationComponent::HasSpecialElimTypes(const ESpecialElimType& SpecialElimType) { return static_cast(SpecialElimType) != 0; } void UEliminationComponent::ProcessElimination(bool bHeadShot, AMatchPlayerState* AttackerPS, AMatchPlayerState* VictimPS) { AttackerPS->AddScoredElim(); VictimPS->AddDefeat(); ESpecialElimType SpecialElimType{}; ProcessHeadshot(bHeadShot, SpecialElimType, AttackerPS); ProcessSequentialEliminations(AttackerPS, SpecialElimType); ProcessStreaks(AttackerPS, VictimPS, SpecialElimType); AMatchGameState* GameState = Cast(UGameplayStatics::GetGameState(AttackerPS)); if (IsValid(GameState)) { HandleFirstBlood(GameState, SpecialElimType, AttackerPS); UpdateLeaderStatus(GameState, SpecialElimType, AttackerPS, VictimPS); } if (HasSpecialElimTypes(SpecialElimType)) { // inform the client of a special elim. AttackerPS->Client_SpecialElim(SpecialElimType, SequentialElims, Streak, AttackerPS->GetScoredElims()); } else { // no special elims; save some bandwidth. AttackerPS->Client_ScoredElim(AttackerPS->GetScoredElims()); } }