Files
2026-03-03 01:23:02 +08:00

110 lines
3.5 KiB
C++

// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "CombatFlow/GCS_AttackResult.h"
#include "GameplayEffect.h"
#include "GCS_LogChannels.h"
#include "GCS_CombatSystemComponent.h"
#include "CombatFlow/GCS_CombatFlow.h"
#include "Utility/GCS_CombatFunctionLibrary.h"
void FGCS_AttackResult::PostReplicatedAdd(const struct FGCS_AttackResultContainer& InArray)
{
}
FGCS_AttackResultContainer::FGCS_AttackResultContainer(): CombatFlow(nullptr), CombatSystemComponent(nullptr), MaxSize(5)
{
}
FGCS_AttackResultContainer::FGCS_AttackResultContainer(UGCS_CombatFlow* InCombatFlow, int32 InMaxSize): CombatFlow(InCombatFlow), CombatSystemComponent(nullptr), MaxSize(InMaxSize)
{
}
FGCS_AttackResultContainer::FGCS_AttackResultContainer(UGCS_CombatSystemComponent* InCombatSystemComponent, int32 InMaxSize): CombatFlow(nullptr), CombatSystemComponent(InCombatSystemComponent),
MaxSize(InMaxSize)
{
}
void FGCS_AttackResultContainer::AddEntry(FGCS_AttackResult& NewEntry)
{
if (Results.Num() >= 5)
{
Results.RemoveAtSwap(0);
MarkArrayDirty();
}
Results.Add(NewEntry);
check(CombatSystemComponent != nullptr && CombatSystemComponent->GetOwner());
if (CombatSystemComponent->GetCombatFlow())
{
CombatSystemComponent->GetCombatFlow()->HandleAttackResult(NewEntry);
NewEntry.bConsumed = true;
}
else
{
UE_LOG(LogGCS, Error, TEXT("Missing Combat Flow on %s's combat system component."), *CombatSystemComponent->GetOwner()->GetName());
}
MarkItemDirty(NewEntry);
}
void FGCS_AttackResultContainer::PostReplicatedAdd(const TArrayView<int32> AddedIndices, int32 FinalSize)
{
for (int32 Index : AddedIndices)
{
FGCS_AttackResult& Entry = Results[Index];
FGCS_ContextPayload_Combat* CombatPayload = UGCS_CombatFunctionLibrary::EffectContextGetMutableCombatPayload(Results[Index].EffectContextHandle);
if (CombatPayload != nullptr && CombatPayload->PredictionKey.IsLocalClientKey())
{
checkf(!CombatPayload->bIsPredictingContext, TEXT("PredictingContext never should hit this!"))
// PredictionKey will only be valid on the client that predicted it. So if this has a valid PredictionKey, we can assume we already predicted it and shouldn't handle attack results.
if (HasPredictedResultWithPredictedKey(CombatPayload->PredictionKey))
{
GCS_LOG(Verbose, "Found already predicted attack result!")
Entry.bWasPredicated = true;
}
}
Entry.bWasReplicated = true;
CombatSystemComponent->GetCombatFlow()->HandleAttackResult(Entry);
Entry.bConsumed = true;
}
}
void FGCS_AttackResultContainer::PostReplicatedChange(const TArrayView<int32>& ChangedIndices, int32 FinalSize)
{
for (int32 Index : ChangedIndices)
{
if (!Results[Index].bConsumed)
{
CombatSystemComponent->GetCombatFlow()->HandleAttackResult(Results[Index]);
Results[Index].bConsumed = true;
}
}
}
bool FGCS_AttackResultContainer::HasPredictedResultWithPredictedKey(FPredictionKey PredictionKey) const
{
for (const FGCS_AttackResult& Result : Results)
{
if (Result.bConsumed)
{
continue;
}
if (const FGCS_ContextPayload_Combat* CombatPayload = UGCS_CombatFunctionLibrary::EffectContextGetMutableCombatPayload(Result.EffectContextHandle))
{
if (CombatPayload->PredictionKey.IsValidKey() && CombatPayload->PredictionKey == PredictionKey && CombatPayload->PredictionKey.WasReceived() == false)
{
return true;
}
}
}
return false;
}