mirror of
https://github.com/Ed94/Cog.git
synced 2026-07-28 10:20:03 +00:00
Cog Sample: Adding more gameplay
Cog Sample: Make Area a component instead of an actor Cog Sample: Add Basic actor for Projectiles, Areas, etc... Cog Sample: Start to add Projectile Cog Sample: Start to add SpawnPrediction
This commit is contained in:
@@ -19,6 +19,7 @@ public class CogSample : ModuleRules
|
||||
"GameplayTags",
|
||||
"InputCore",
|
||||
"NetCore",
|
||||
"Niagara",
|
||||
});
|
||||
|
||||
if (Target.Configuration != UnrealTargetConfiguration.Shipping && Target.Type != TargetRules.TargetType.Server)
|
||||
|
||||
@@ -29,7 +29,7 @@ void UCogSampleAbilityTask_PlayMontageAndWaitForEvent::OnMontageBlendingOut(UAni
|
||||
// Reset AnimRootMotionTranslationScale
|
||||
ACharacter* Character = Cast<ACharacter>(GetAvatarActor());
|
||||
if (Character && (Character->GetLocalRole() == ROLE_Authority ||
|
||||
(Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted)))
|
||||
(Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::Predicted)))
|
||||
{
|
||||
Character->SetAnimRootMotionTranslationScale(1.f);
|
||||
}
|
||||
@@ -158,7 +158,7 @@ void UCogSampleAbilityTask_PlayMontageAndWaitForEvent::Activate()
|
||||
|
||||
ACharacter* Character = Cast<ACharacter>(GetAvatarActor());
|
||||
if (Character && (Character->GetLocalRole() == ROLE_Authority ||
|
||||
(Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::LocalPredicted)))
|
||||
(Character->GetLocalRole() == ROLE_AutonomousProxy && Ability->GetNetExecutionPolicy() == EGameplayAbilityNetExecutionPolicy::Predicted)))
|
||||
{
|
||||
Character->SetAnimRootMotionTranslationScale(AnimRootMotionTranslationScale);
|
||||
}
|
||||
|
||||
@@ -1,434 +0,0 @@
|
||||
#include "CogSampleArea.h"
|
||||
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AbilitySystemGlobals.h"
|
||||
#include "CogCommon.h"
|
||||
#include "CogSampleDamageableInterface.h"
|
||||
#include "CogSampleFunctionLibrary_Team.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/GameStateBase.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
ACogSampleArea::ACogSampleArea(const class FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
bReplicates = true;
|
||||
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
SetActorTickEnabled(true);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(ACogSampleArea, HalfExtent);
|
||||
DOREPLIFETIME(ACogSampleArea, Level);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::BeginPlay()
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogArea, ELogVerbosity::Verbose, GetInstigator(), TEXT("Area:%s"), *GetName());
|
||||
|
||||
IsAtStart = true;
|
||||
IsAtEnd = false;
|
||||
|
||||
if (HasAuthority())
|
||||
{
|
||||
if (TickRate > 0.0f && IsInstant == false)
|
||||
{
|
||||
GetWorld()->GetTimerManager().SetTimer(TickTimerHandle, this, &ACogSampleArea::OnTickEffect, TickRate, true, InitialTickDelay);
|
||||
}
|
||||
|
||||
RegisterAllEffects();
|
||||
|
||||
OnActorBeginOverlap.AddDynamic(this, &ACogSampleArea::OnActorEntered);
|
||||
OnActorEndOverlap.AddDynamic(this, &ACogSampleArea::OnActorExited);
|
||||
}
|
||||
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogArea, ELogVerbosity::Verbose, GetInstigator(), TEXT("Area:%s"), *GetName());
|
||||
|
||||
IsAtEnd = true;
|
||||
|
||||
if (CanPerformDetection() && IsInstant == false)
|
||||
{
|
||||
int32 EventTypeFilter = (int32)ECogSampleAreaEventType::OnEnd;
|
||||
|
||||
if (ApplyTickEffectOnExit)
|
||||
{
|
||||
EventTypeFilter |= (int32)ECogSampleAreaEventType::OnTick;
|
||||
}
|
||||
|
||||
ApplyEffectsOnActors(InsideActors, EventTypeFilter);
|
||||
|
||||
for (AActor* Actor : InsideActors)
|
||||
{
|
||||
AffectExitingActor(Actor);
|
||||
}
|
||||
}
|
||||
|
||||
InsideActors.Empty();
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::Tick(float DeltaSeconds)
|
||||
{
|
||||
Super::Tick(DeltaSeconds);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// We cannot execute OnStart event inside the OnActorEntered callback because
|
||||
// we need to know all the actors that are inside to execute the OnStart event.
|
||||
// This is because we must iterate over all actors for each effects we need
|
||||
// to apply. See ApplyEffectsOnActors as to why.
|
||||
// So instead we execute it on Tick
|
||||
//------------------------------------------------------------------------------
|
||||
if (IsAtStart)
|
||||
{
|
||||
int32 EventTypeFilter = (int32)ECogSampleAreaEventType::OnStart;
|
||||
|
||||
if (ApplyTickEffectOnEnter)
|
||||
{
|
||||
EventTypeFilter |= (int32)ECogSampleAreaEventType::OnTick;
|
||||
}
|
||||
|
||||
ApplyEffectsOnActors(InsideActors, EventTypeFilter);
|
||||
|
||||
IsAtStart = false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
AActor* ACogSampleArea::GetInstigatorActor() const
|
||||
{
|
||||
AActor* AreaInstigator = GetInstigator();
|
||||
if (AreaInstigator != nullptr)
|
||||
{
|
||||
return AreaInstigator;
|
||||
}
|
||||
|
||||
if (ParentActor != nullptr)
|
||||
{
|
||||
return ParentActor;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// The game state is the default Instigator
|
||||
//-------------------------------------------------
|
||||
UWorld* World = GetWorld();
|
||||
if (World != nullptr)
|
||||
{
|
||||
return World->GetGameState();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::RegisterAllEffects()
|
||||
{
|
||||
AActor* AreaInstigator = GetInstigatorActor();
|
||||
if (AreaInstigator == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* AbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(AreaInstigator);
|
||||
if (AbilitySystem == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FGameplayEffectContextHandle EffectContextHandle = AbilitySystem->MakeEffectContext();
|
||||
|
||||
for (const FCogSampleAreaEffectConfig& EffectConfig : Effects)
|
||||
{
|
||||
for (TSubclassOf<UGameplayEffect> EffectClass : EffectConfig.Effects)
|
||||
{
|
||||
if (EffectsMap.Contains(EffectClass))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FGameplayEffectSpecHandle* BakedEffectSpecHandle = BakedEffects.FindByPredicate([EffectClass](const FGameplayEffectSpecHandle& Handle)
|
||||
{
|
||||
return Handle.Data->Def.GetClass() == EffectClass;
|
||||
});
|
||||
|
||||
if (BakedEffectSpecHandle != nullptr)
|
||||
{
|
||||
EffectsMap.Add(EffectClass, *BakedEffectSpecHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
FGameplayEffectSpecHandle EffectSpecHandle = AbilitySystem->MakeOutgoingSpec(EffectClass, Level, EffectContextHandle);
|
||||
EffectsMap.Add(EffectClass, EffectSpecHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// We iterate over effects and then over actors to make sure effects are applied in order on all actors.
|
||||
// One case where this is useful is an area that applies first a corruption effect and damage effect.
|
||||
// The corruption must be given to everyone in the area before any damage is applied, because the corruption
|
||||
// propagate the damage to other corrupted actors.
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::ApplyEffectsOnActors(const TArray<AActor*>& HitActors, int32 EventTypeFilter)
|
||||
{
|
||||
for (const FCogSampleAreaEffectConfig& EffectConfig : Effects)
|
||||
{
|
||||
if (((int32)EffectConfig.Event & (int32)EventTypeFilter) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (TSubclassOf<UGameplayEffect> EffectClass : EffectConfig.Effects)
|
||||
{
|
||||
FGameplayEffectSpecHandle* Handle = EffectsMap.Find(EffectClass);
|
||||
if (Handle == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FGameplayEffectSpec* Spec = Handle->Data.Get();
|
||||
if (Spec == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (AActor* HitActor : HitActors)
|
||||
{
|
||||
if (UCogSampleFunctionLibrary_Team::MatchAllegianceBetweenTeamAndActor(Team, HitActor, EffectConfig.Allegiance) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsAliveOfAffectDead(HitActor) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(HitActor);
|
||||
if (TargetAbilitySystem != nullptr)
|
||||
{
|
||||
TArray<FActiveGameplayEffectHandle>* TargetInsideEffects = nullptr;
|
||||
if ((EventTypeFilter & (int32)ECogSampleAreaEventType::OnInside) != 0)
|
||||
{
|
||||
TargetInsideEffects = &InsideEffects.FindOrAdd(HitActor);
|
||||
}
|
||||
|
||||
const FVector HitNormal = (HitActor->GetActorLocation() - GetActorLocation()) .GetSafeNormal(0.1f, FVector::UpVector);
|
||||
const FHitResult HitResult(HitActor, nullptr, HitActor->GetActorLocation(), HitNormal);
|
||||
Spec->GetContext().AddHitResult(HitResult, true);
|
||||
|
||||
FActiveGameplayEffectHandle ActiveEffectHandle = TargetAbilitySystem->ApplyGameplayEffectSpecToSelf(*Spec);
|
||||
|
||||
if (EffectConfig.Event == ECogSampleAreaEventType::OnInside && TargetInsideEffects != nullptr)
|
||||
{
|
||||
TargetInsideEffects->Add(ActiveEffectHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool ACogSampleArea::MakeAreaOutgoingEffectSpecs(TSubclassOf<UGameplayEffect> EffectClass, FGameplayEffectSpecHandle& EffectSpecHandle) const
|
||||
{
|
||||
AActor* AreaInstigator = GetInstigatorActor();
|
||||
if (AreaInstigator == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* AbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(AreaInstigator);
|
||||
if (AbilitySystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
EffectSpecHandle = AbilitySystem->MakeOutgoingSpec(EffectClass, Level, AbilitySystem->MakeEffectContext());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool ACogSampleArea::CanPerformDetection() const
|
||||
{
|
||||
if (OnlyDetectOnAuthority == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasAuthority())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::OnTickEffect_Implementation()
|
||||
{
|
||||
ApplyEffectsOnActors(InsideActors, (int32)ECogSampleAreaEventType::OnTick);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::OnActorEntered_Implementation(AActor* OverlappedActor, AActor* EnteringActor)
|
||||
{
|
||||
check(EnteringActor);
|
||||
if (CanBeAffected(EnteringActor) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AffectEnteringActor(EnteringActor);
|
||||
InsideActors.Add(EnteringActor);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::OnActorExited_Implementation(AActor* OverlappedActor, AActor* ExitingActor)
|
||||
{
|
||||
if (IsInstant)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int32 Index = InsideActors.Find(ExitingActor);
|
||||
if (Index == INDEX_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AffectExitingActor(ExitingActor);
|
||||
|
||||
InsideActors.RemoveAt(Index);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool ACogSampleArea::CanBeAffected_Implementation(AActor* OtherActor) const
|
||||
{
|
||||
check(OtherActor);
|
||||
|
||||
if (IsAlreadyAffected(OtherActor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool ACogSampleArea::IsAliveOfAffectDead(AActor* OtherActor) const
|
||||
{
|
||||
check(OtherActor);
|
||||
|
||||
if (AffectDead)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const ICogSampleDamageableInterface* Damageable = Cast<ICogSampleDamageableInterface>(OtherActor);
|
||||
if (Damageable == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Damageable->IsDead() == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool ACogSampleArea::IsAlreadyAffected(AActor* OtherActor) const
|
||||
{
|
||||
check(OtherActor);
|
||||
|
||||
if (InsideActors.Contains(OtherActor))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::AffectEnteringActor_Implementation(AActor* EnteringActor)
|
||||
{
|
||||
check(EnteringActor);
|
||||
|
||||
if (IsInstant == false)
|
||||
{
|
||||
TArray<AActor*> Actors;
|
||||
Actors.Add(EnteringActor);
|
||||
ApplyEffectsOnActors(Actors, (int32)ECogSampleAreaEventType::OnEnter | (int32)ECogSampleAreaEventType::OnInside);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::AffectExitingActor_Implementation(AActor* ExitingActor)
|
||||
{
|
||||
if (IsInstant || ExitingActor == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<AActor*> Actors;
|
||||
Actors.Add(ExitingActor);
|
||||
ApplyEffectsOnActors(Actors, (int32)ECogSampleAreaEventType::OnExit);
|
||||
|
||||
RemoveInsideEffects(ExitingActor);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::RemoveInsideEffects_Implementation(AActor* HitActor)
|
||||
{
|
||||
UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(HitActor);
|
||||
if (TargetAbilitySystem == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<FActiveGameplayEffectHandle>* TargetInsideEffects = InsideEffects.Find(HitActor);
|
||||
if (TargetInsideEffects == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (FActiveGameplayEffectHandle Handle : *TargetInsideEffects)
|
||||
{
|
||||
TargetAbilitySystem->RemoveActiveGameplayEffect(Handle, 1);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::ApplyInstantEffects_Implementation(AActor* HitActor)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::ApplyInsideEffects_Implementation(AActor* HitActor)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleArea::ApplyTickEffects_Implementation(AActor* HitActor)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,472 @@
|
||||
#include "CogSampleAreaComponent.h"
|
||||
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AbilitySystemGlobals.h"
|
||||
#include "CogCommon.h"
|
||||
#include "CogSampleDamageableInterface.h"
|
||||
#include "CogSampleFunctionLibrary_Gameplay.h"
|
||||
#include "CogSampleFunctionLibrary_Team.h"
|
||||
#include "CogSampleLogCategories.h"
|
||||
#include "Components/BoxComponent.h"
|
||||
#include "Components/SphereComponent.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/GameStateBase.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "NiagaraComponent.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
#if ENABLE_COG
|
||||
#include "CogDebugDraw.h"
|
||||
#include "CogDebugLog.h"
|
||||
#endif //ENABLE_COG
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
UCogSampleAreaComponent::UCogSampleAreaComponent(const class FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
SetIsReplicatedByDefault(true);
|
||||
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
PrimaryComponentTick.bStartWithTickEnabled = true;
|
||||
|
||||
bWantsInitializeComponent = true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::InitializeComponent()
|
||||
{
|
||||
Super::InitializeComponent();
|
||||
|
||||
RefreshOtherComponentsValues();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
//void UCogSampleAreaComponent::PreInitializeComponents()
|
||||
//{
|
||||
// Super::PreInitializeComponents();
|
||||
//
|
||||
// RefreshOtherComponentsValues();
|
||||
//}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
FDoRepLifetimeParams Params;
|
||||
Params.bIsPushBased = true;
|
||||
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(UCogSampleAreaComponent, HalfExtent, Params);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::BeginPlay()
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogArea, ELogVerbosity::Verbose, GetOwner()->GetInstigator(), TEXT("Area:%s"), *GetName());
|
||||
|
||||
IsAtStart = true;
|
||||
IsAtEnd = false;
|
||||
|
||||
if (GetOwner()->HasAuthority())
|
||||
{
|
||||
if (TickRate > 0.0f && DurationType != ECogSampleAreaDurationType::Instant)
|
||||
{
|
||||
GetWorld()->GetTimerManager().SetTimer(TickTimerHandle, this, &UCogSampleAreaComponent::OnTickEffect, TickRate, true, InitialTickDelay);
|
||||
}
|
||||
|
||||
if (DurationType == ECogSampleAreaDurationType::HasDuration)
|
||||
{
|
||||
GetWorld()->GetTimerManager().SetTimer(DurationTimerHandle, this, &UCogSampleAreaComponent::OnDurationElapsed, Duration, false);
|
||||
}
|
||||
|
||||
RegisterAllEffects();
|
||||
|
||||
GetOwner()->OnActorBeginOverlap.AddDynamic(this, &UCogSampleAreaComponent::OnActorEntered);
|
||||
GetOwner()->OnActorEndOverlap.AddDynamic(this, &UCogSampleAreaComponent::OnActorExited);
|
||||
|
||||
|
||||
}
|
||||
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogArea, ELogVerbosity::Verbose, GetOwner()->GetInstigator(), TEXT("Area:%s"), *GetName());
|
||||
|
||||
IsAtEnd = true;
|
||||
|
||||
if (CanPerformDetection() && DurationType != ECogSampleAreaDurationType::Instant)
|
||||
{
|
||||
int32 EventTypeFilter = (int32)ECogSampleAreaEventType::OnEnd;
|
||||
|
||||
if (ApplyTickEffectOnExit)
|
||||
{
|
||||
EventTypeFilter |= (int32)ECogSampleAreaEventType::OnTick;
|
||||
}
|
||||
|
||||
ApplyEffectsOnActors(InsideActors, EventTypeFilter);
|
||||
|
||||
for (AActor* Actor : InsideActors)
|
||||
{
|
||||
AffectExitingActor(Actor);
|
||||
}
|
||||
}
|
||||
|
||||
InsideActors.Empty();
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::OnDurationElapsed_Implementation()
|
||||
{
|
||||
GetOwner()->Destroy();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// We cannot execute OnStart event inside the OnActorEntered callback because
|
||||
// we need to know all the actors that are inside to execute the OnStart event.
|
||||
// This is because we must iterate over all actors for each effects we need
|
||||
// to apply. See ApplyEffectsOnActors as to why.
|
||||
// So instead we execute it on Tick
|
||||
//------------------------------------------------------------------------------
|
||||
if (IsAtStart)
|
||||
{
|
||||
int32 EventTypeFilter = (int32)ECogSampleAreaEventType::OnStart;
|
||||
|
||||
if (ApplyTickEffectOnEnter)
|
||||
{
|
||||
EventTypeFilter |= (int32)ECogSampleAreaEventType::OnTick;
|
||||
}
|
||||
|
||||
ApplyEffectsOnActors(InsideActors, EventTypeFilter);
|
||||
|
||||
IsAtStart = false;
|
||||
}
|
||||
|
||||
#if ENABLE_COG
|
||||
|
||||
const AActor* AreaInstigator = UCogSampleFunctionLibrary_Gameplay::GetInstigator(GetOwner());
|
||||
if (FCogDebugLog::IsLogCategoryActive(LogCogArea) && FCogDebugSettings::IsDebugActiveForObject(AreaInstigator))
|
||||
{
|
||||
TArray<USceneComponent*, TInlineAllocator<8>> Components;
|
||||
GetOwner()->GetComponents<USceneComponent>(Components);
|
||||
for (USceneComponent* SceneComponent : Components)
|
||||
{
|
||||
if (USphereComponent* SphereComponent = Cast<USphereComponent>(SceneComponent))
|
||||
{
|
||||
FCogDebugDraw::Sphere(LogCogArea, this, SphereComponent->GetComponentLocation(), SphereComponent->GetScaledSphereRadius(), FColor::Yellow, false, 0);
|
||||
}
|
||||
else if (UBoxComponent* BoxComponent = Cast<UBoxComponent>(SceneComponent))
|
||||
{
|
||||
FCogDebugDraw::Box(LogCogArea, this, BoxComponent->GetComponentLocation(), BoxComponent->GetScaledBoxExtent(), BoxComponent->GetComponentQuat(), FColor::Yellow, false, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //ENABLE_COG
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::RegisterAllEffects()
|
||||
{
|
||||
TArray<TSubclassOf<UGameplayEffect>> AllEffects;
|
||||
|
||||
for (const FCogSampleAreaEffectConfig& EffectConfig : Effects)
|
||||
{
|
||||
for (TSubclassOf<UGameplayEffect> EffectClass : EffectConfig.Effects)
|
||||
{
|
||||
AllEffects.Add(EffectClass);
|
||||
}
|
||||
}
|
||||
|
||||
UCogSampleFunctionLibrary_Gameplay::MakeOutgoingSpecs(GetOwner(), AllEffects, BakedEffects, EffectsMap);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// We iterate over effects and then over actors to make sure effects are applied in order on all actors.
|
||||
// One case where this is useful is an area that applies first a corruption effect and damage effect.
|
||||
// The corruption must be given to everyone in the area before any damage is applied, because the corruption
|
||||
// propagate the damage to other corrupted actors.
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::ApplyEffectsOnActors(const TArray<AActor*>& HitActors, int32 EventTypeFilter)
|
||||
{
|
||||
for (const FCogSampleAreaEffectConfig& EffectConfig : Effects)
|
||||
{
|
||||
if (((int32)EffectConfig.Event & (int32)EventTypeFilter) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (TSubclassOf<UGameplayEffect> EffectClass : EffectConfig.Effects)
|
||||
{
|
||||
FGameplayEffectSpecHandle* Handle = EffectsMap.Find(EffectClass);
|
||||
if (Handle == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FGameplayEffectSpec* Spec = Handle->Data.Get();
|
||||
if (Spec == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (AActor* HitActor : HitActors)
|
||||
{
|
||||
if (UCogSampleFunctionLibrary_Team::MatchAllegiance(GetOwner(), HitActor, EffectConfig.Allegiance) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (UCogSampleFunctionLibrary_Gameplay::IsDead(HitActor) && EffectConfig.AffectDead == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(HitActor);
|
||||
if (TargetAbilitySystem == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TArray<FActiveGameplayEffectHandle>* TargetInsideEffects = nullptr;
|
||||
if ((EventTypeFilter & (int32)ECogSampleAreaEventType::OnInside) != 0)
|
||||
{
|
||||
TargetInsideEffects = &InsideEffects.FindOrAdd(HitActor);
|
||||
}
|
||||
|
||||
const FVector HitNormal = (HitActor->GetActorLocation() - GetOwner()->GetActorLocation()) .GetSafeNormal(0.1f, FVector::UpVector);
|
||||
const FHitResult HitResult(HitActor, nullptr, HitActor->GetActorLocation(), HitNormal);
|
||||
Spec->GetContext().AddHitResult(HitResult, true);
|
||||
|
||||
FActiveGameplayEffectHandle ActiveEffectHandle = TargetAbilitySystem->ApplyGameplayEffectSpecToSelf(*Spec);
|
||||
|
||||
if (EffectConfig.Event == ECogSampleAreaEventType::OnInside && TargetInsideEffects != nullptr)
|
||||
{
|
||||
TargetInsideEffects->Add(ActiveEffectHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool UCogSampleAreaComponent::MakeAreaOutgoingEffectSpecs(TSubclassOf<UGameplayEffect> EffectClass, FGameplayEffectSpecHandle& EffectSpecHandle) const
|
||||
{
|
||||
AActor* AreaInstigator = UCogSampleFunctionLibrary_Gameplay::GetInstigator(GetOwner());
|
||||
if (AreaInstigator == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* AbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(AreaInstigator);
|
||||
if (AbilitySystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32 Level = UCogSampleFunctionLibrary_Gameplay::GetProgressionLevel(GetOwner());
|
||||
EffectSpecHandle = AbilitySystem->MakeOutgoingSpec(EffectClass, Level, AbilitySystem->MakeEffectContext());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool UCogSampleAreaComponent::CanPerformDetection() const
|
||||
{
|
||||
if (OnlyDetectOnAuthority == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GetOwner()->HasAuthority())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::OnTickEffect_Implementation()
|
||||
{
|
||||
ApplyEffectsOnActors(InsideActors, (int32)ECogSampleAreaEventType::OnTick);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::OnActorEntered_Implementation(AActor* OverlappedActor, AActor* EnteringActor)
|
||||
{
|
||||
check(EnteringActor);
|
||||
if (CanBeAffected(EnteringActor) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AffectEnteringActor(EnteringActor);
|
||||
InsideActors.Add(EnteringActor);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::OnActorExited_Implementation(AActor* OverlappedActor, AActor* ExitingActor)
|
||||
{
|
||||
if (DurationType == ECogSampleAreaDurationType::Instant)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int32 Index = InsideActors.Find(ExitingActor);
|
||||
if (Index == INDEX_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AffectExitingActor(ExitingActor);
|
||||
|
||||
InsideActors.RemoveAt(Index);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool UCogSampleAreaComponent::CanBeAffected_Implementation(AActor* OtherActor) const
|
||||
{
|
||||
check(OtherActor);
|
||||
|
||||
if (IsAlreadyAffected(OtherActor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool UCogSampleAreaComponent::IsAlreadyAffected(AActor* OtherActor) const
|
||||
{
|
||||
check(OtherActor);
|
||||
|
||||
if (InsideActors.Contains(OtherActor))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::AffectEnteringActor_Implementation(AActor* EnteringActor)
|
||||
{
|
||||
check(EnteringActor);
|
||||
|
||||
if (DurationType == ECogSampleAreaDurationType::Instant)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<AActor*> Actors;
|
||||
Actors.Add(EnteringActor);
|
||||
ApplyEffectsOnActors(Actors, (int32)ECogSampleAreaEventType::OnEnter | (int32)ECogSampleAreaEventType::OnInside);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::AffectExitingActor_Implementation(AActor* ExitingActor)
|
||||
{
|
||||
if (DurationType == ECogSampleAreaDurationType::Instant || ExitingActor == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<AActor*> Actors;
|
||||
Actors.Add(ExitingActor);
|
||||
ApplyEffectsOnActors(Actors, (int32)ECogSampleAreaEventType::OnExit);
|
||||
|
||||
RemoveInsideEffects(ExitingActor);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::RemoveInsideEffects_Implementation(AActor* HitActor)
|
||||
{
|
||||
UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(HitActor);
|
||||
if (TargetAbilitySystem == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<FActiveGameplayEffectHandle>* TargetInsideEffects = InsideEffects.Find(HitActor);
|
||||
if (TargetInsideEffects == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (FActiveGameplayEffectHandle Handle : *TargetInsideEffects)
|
||||
{
|
||||
TargetAbilitySystem->RemoveActiveGameplayEffect(Handle, 1);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::ApplyInstantEffects_Implementation(AActor* HitActor)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::ApplyInsideEffects_Implementation(AActor* HitActor)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::ApplyTickEffects_Implementation(AActor* HitActor)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::SetHalfExtent(const FVector& Value)
|
||||
{
|
||||
COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(UCogSampleAreaComponent, HalfExtent, Value, this);
|
||||
RefreshOtherComponentsValues();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::OnRep_HalfExtent()
|
||||
{
|
||||
RefreshOtherComponentsValues();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleAreaComponent::RefreshOtherComponentsValues()
|
||||
{
|
||||
TArray<USceneComponent*, TInlineAllocator<8>> Components;
|
||||
GetOwner()->GetComponents<USceneComponent>(Components);
|
||||
|
||||
for (USceneComponent* SceneComponent : Components)
|
||||
{
|
||||
if (USphereComponent* SphereComponent = Cast<USphereComponent>(SceneComponent))
|
||||
{
|
||||
if (SphereComponent->GetUnscaledSphereRadius() != HalfExtent.X)
|
||||
{
|
||||
SphereComponent->SetSphereRadius(HalfExtent.X);
|
||||
}
|
||||
}
|
||||
else if (UBoxComponent* BoxComponent = Cast<UBoxComponent>(SceneComponent))
|
||||
{
|
||||
if (BoxComponent->GetUnscaledBoxExtent() != HalfExtent)
|
||||
{
|
||||
BoxComponent->SetBoxExtent(HalfExtent);
|
||||
}
|
||||
}
|
||||
else if (UNiagaraComponent* NiagaraComponent = Cast<UNiagaraComponent>(SceneComponent))
|
||||
{
|
||||
NiagaraComponent->SetNiagaraVariableVec3("HalfExtent", HalfExtent);
|
||||
NiagaraComponent->SetNiagaraVariableFloat("TickRate", TickRate);
|
||||
NiagaraComponent->SetNiagaraVariableFloat("Duration", Duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "GameplayEffect.h"
|
||||
|
||||
#include "CogSampleArea.generated.h"
|
||||
#include "CogSampleAreaComponent.generated.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// ECogSampleAreaEventType
|
||||
@@ -27,7 +25,7 @@ ENUM_CLASS_FLAGS(ECogSampleAreaEventType);
|
||||
// FCogSampleAreaEffectConfig
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
USTRUCT(BlueprintType)
|
||||
struct COGSAMPLE_API FCogSampleAreaEffectConfig
|
||||
struct FCogSampleAreaEffectConfig
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
@@ -37,72 +35,88 @@ struct COGSAMPLE_API FCogSampleAreaEffectConfig
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Bitmask, BitmaskEnum = "/Script/CogSample.ECogSampleAllegianceFilter"))
|
||||
int32 Allegiance = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool AffectDead = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<TSubclassOf<UGameplayEffect>> Effects;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// ECogSampleAreaDurationType
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
UENUM()
|
||||
enum class ECogSampleAreaDurationType : uint8
|
||||
{
|
||||
Instant,
|
||||
Infinite,
|
||||
HasDuration
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// ACogSampleArea
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
UCLASS()
|
||||
class COGSAMPLE_API ACogSampleArea : public AActor
|
||||
UCLASS(BlueprintType, meta = (BlueprintSpawnableComponent))
|
||||
class UCogSampleAreaComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
|
||||
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
virtual void InitializeComponent() override;
|
||||
|
||||
//virtual void PreInitializeComponents() override;
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true))
|
||||
TObjectPtr<AActor> ParentActor = nullptr;
|
||||
FVector GetHalfExtent() const { return HalfExtent; }
|
||||
|
||||
UPROPERTY(Replicated, EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true))
|
||||
FVector HalfExtent = FVector(100.f, 100.f, 100.f);
|
||||
void SetHalfExtent(const FVector& Value);
|
||||
|
||||
UPROPERTY(Replicated, EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true))
|
||||
int32 Level = 0;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area")
|
||||
ECogSampleAreaDurationType DurationType;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true))
|
||||
int32 Team = 0;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area", meta = (EditConditionHides, EditCondition = "DurationType == ECogSampleAreaDurationType::HasDuration"))
|
||||
float Duration = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true))
|
||||
bool IsInstant = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true))
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area", meta = (EditConditionHides, EditCondition = "DurationType != ECogSampleAreaDurationType::Instant"))
|
||||
float TickRate = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General", meta = (ExposeOnSpawn = true))
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area", meta = (EditConditionHides, EditCondition = "DurationType != ECogSampleAreaDurationType::Instant"))
|
||||
float InitialTickDelay = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General")
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area", meta = (EditConditionHides, EditCondition = "DurationType != ECogSampleAreaDurationType::Instant"))
|
||||
bool ApplyTickEffectOnEnter = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General")
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area", meta = (EditConditionHides, EditCondition = "DurationType != ECogSampleAreaDurationType::Instant"))
|
||||
bool ApplyTickEffectOnExit = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General")
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area")
|
||||
bool OnlyDetectOnAuthority = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|General")
|
||||
bool AffectDead = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area|Effects", meta = (ExposeOnSpawn = true))
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Area")
|
||||
TArray<FCogSampleAreaEffectConfig> Effects;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadWrite, Category = "Internal", meta = (ExposeOnSpawn = true))
|
||||
UPROPERTY(Transient, BlueprintReadWrite, Category = "Internal")
|
||||
TArray<FGameplayEffectSpecHandle> BakedEffects;
|
||||
|
||||
protected:
|
||||
|
||||
UFUNCTION(Category = "Area")
|
||||
void OnRep_HalfExtent();
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Area")
|
||||
void OnTickEffect();
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Area")
|
||||
void OnDurationElapsed();
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Area")
|
||||
void OnActorEntered(AActor* OverlappedActor, AActor* EnteringActor);
|
||||
|
||||
@@ -133,21 +147,20 @@ protected:
|
||||
UFUNCTION(BlueprintPure, Category = "Area")
|
||||
bool CanPerformDetection() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Area")
|
||||
bool IsAliveOfAffectDead(AActor* OtherActor) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Area")
|
||||
bool IsAlreadyAffected(AActor* OtherActor) const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Area")
|
||||
AActor* GetInstigatorActor() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure=false, Category = "Area")
|
||||
bool MakeAreaOutgoingEffectSpecs(TSubclassOf<UGameplayEffect> EffectClass, FGameplayEffectSpecHandle& EffectSpecHandle) const;
|
||||
|
||||
void RegisterAllEffects();
|
||||
UPROPERTY(ReplicatedUsing = OnRep_HalfExtent, EditAnywhere, BlueprintReadWrite, Category = "Area")
|
||||
FVector HalfExtent = FVector(100.f, 100.f, 100.f);
|
||||
|
||||
void ApplyEffectsOnActors(const TArray<AActor*>& HitActors, int32 EventTypeFilter);
|
||||
virtual void RegisterAllEffects();
|
||||
|
||||
virtual void ApplyEffectsOnActors(const TArray<AActor*>& HitActors, int32 EventTypeFilter);
|
||||
|
||||
virtual void RefreshOtherComponentsValues();
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Transient)
|
||||
bool IsAtStart = true;
|
||||
@@ -162,4 +175,6 @@ protected:
|
||||
TMap<AActor*, TArray<FActiveGameplayEffectHandle>> InsideEffects;
|
||||
|
||||
FTimerHandle TickTimerHandle;
|
||||
|
||||
FTimerHandle DurationTimerHandle;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "CogSampleBasicActor.h"
|
||||
|
||||
#include "Net/Core/PushModel/PushModel.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
ACogSampleBasicActor::ACogSampleBasicActor(const FObjectInitializer& ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleBasicActor::GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
FDoRepLifetimeParams Params;
|
||||
Params.bIsPushBased = true;
|
||||
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleBasicActor, Team, Params);
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleBasicActor, ProgressionLevel, Params);
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleBasicActor, Creator, Params);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleBasicActor::SetTeam(int32 Value)
|
||||
{
|
||||
COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ACogSampleBasicActor, Team, Value, this);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleBasicActor::SetProgressionLevel(int32 Value)
|
||||
{
|
||||
COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ACogSampleBasicActor, ProgressionLevel, Value, this);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleBasicActor::SetCreator(AActor* Value)
|
||||
{
|
||||
COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ACogSampleBasicActor, Creator, Value, this);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "CogSampleProgressionLevelInterface.h"
|
||||
#include "CogSampleTeamInterface.h"
|
||||
#include "CogSampleSpawnableInterface.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "CogSampleBasicActor.generated.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
UCLASS(config=Game)
|
||||
class ACogSampleBasicActor : public AActor
|
||||
, public ICogSampleProgressionLevelInterface
|
||||
, public ICogSampleSpawnableInterface
|
||||
, public ICogSampleTeamInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
ACogSampleBasicActor(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
virtual int32 GetTeam() const override { return Team; }
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual void SetTeam(int32 Value) override;
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
virtual int32 GetProgressionLevel() const override { return ProgressionLevel; }
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual void SetProgressionLevel(int32 Value) override;
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
virtual AActor* GetCreator() const override { return Creator.Get(); }
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual void SetCreator(AActor* Value) override;
|
||||
|
||||
protected:
|
||||
|
||||
UPROPERTY(Replicated, EditAnywhere, BlueprintReadOnly, Category = Team, meta = (AllowPrivateAccess, ExposeOnSpawn))
|
||||
int32 Team = 0;
|
||||
|
||||
UPROPERTY(Replicated, EditAnywhere, BlueprintReadOnly, Category = Team, meta = (AllowPrivateAccess, ExposeOnSpawn))
|
||||
int32 ProgressionLevel = 0;
|
||||
|
||||
UPROPERTY(Replicated, EditAnywhere, BlueprintReadOnly, Category = Team, meta = (AllowPrivateAccess, ExposeOnSpawn))
|
||||
TWeakObjectPtr<AActor> Creator = nullptr;
|
||||
};
|
||||
|
||||
@@ -77,13 +77,14 @@ void ACogSampleCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty >
|
||||
|
||||
FDoRepLifetimeParams Params;
|
||||
Params.bIsPushBased = true;
|
||||
Params.Condition = COND_OwnerOnly;
|
||||
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, ActiveAbilityHandles, Params);
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, Team, Params);
|
||||
|
||||
Params.Condition = COND_None;
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, Team, Params);
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, ProgressionLevel, Params);
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, Scale, Params);
|
||||
|
||||
Params.Condition = COND_OwnerOnly;
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ACogSampleCharacter, ActiveAbilityHandles, Params);
|
||||
}
|
||||
|
||||
|
||||
@@ -709,10 +710,15 @@ void ACogSampleCharacter::OnRep_Scale()
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleCharacter::SetTeamID(int32 Value)
|
||||
void ACogSampleCharacter::SetTeam(int32 Value)
|
||||
{
|
||||
Team = Value;
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ACogSampleCharacter, Team, this);
|
||||
COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ACogSampleCharacter, Team, Value, this);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void ACogSampleCharacter::SetProgressionLevel(int32 Value)
|
||||
{
|
||||
COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ACogSampleCharacter, ProgressionLevel, Value, this);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AbilitySystemInterface.h"
|
||||
#include "ActiveGameplayEffectHandle.h"
|
||||
#include "AttributeSet.h"
|
||||
#include "CogCommonAllegianceActorInterface.h"
|
||||
#include "CogCommonDebugFilteredActorInterface.h"
|
||||
#include "CogSampleDamageEvent.h"
|
||||
#include "CogSampleDefines.h"
|
||||
#include "CogSampleDamageableInterface.h"
|
||||
#include "CogSampleDefines.h"
|
||||
#include "CogSampleProgressionLevelInterface.h"
|
||||
#include "CogSampleTargetableInterface.h"
|
||||
#include "CogSampleTeamInterface.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameplayAbilitySpecHandle.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
@@ -82,6 +82,7 @@ class ACogSampleCharacter : public ACharacter
|
||||
, public ICogCommonDebugFilteredActorInterface
|
||||
, public ICogCommonAllegianceActorInterface
|
||||
, public ICogSampleTeamInterface
|
||||
, public ICogSampleProgressionLevelInterface
|
||||
, public ICogSampleTargetableInterface
|
||||
, public ICogSampleDamageableInterface
|
||||
{
|
||||
@@ -151,10 +152,16 @@ public:
|
||||
virtual int32 GetTeam() const override { return Team; }
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void SetTeamID(int32 Value);
|
||||
virtual void SetTeam(int32 Value) override;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Team, Replicated, meta = (AllowPrivateAccess = "true"))
|
||||
int32 Team = 0;
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// Level
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
virtual int32 GetProgressionLevel() const override { return ProgressionLevel; }
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual void SetProgressionLevel(int32 Value) override;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// Camera
|
||||
@@ -163,10 +170,10 @@ public:
|
||||
|
||||
UCameraComponent* GetFollowCamera() const { return FollowCamera; }
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess))
|
||||
USpringArmComponent* CameraBoom;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess))
|
||||
UCameraComponent* FollowCamera;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@@ -179,26 +186,26 @@ public:
|
||||
FVector GetMoveInputInWorldSpace() const { return MoveInputInWorldSpace; }
|
||||
|
||||
/** MappingContext */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess))
|
||||
UInputMappingContext* DefaultMappingContext;
|
||||
|
||||
/** MappingContext */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess))
|
||||
UInputMappingContext* GhostMappingContext;
|
||||
|
||||
/** Move Input Action */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess))
|
||||
UInputAction* MoveAction;
|
||||
|
||||
/** Move Input Action */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess))
|
||||
UInputAction* MoveZAction;
|
||||
|
||||
/** Look Input Action */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess))
|
||||
UInputAction* LookAction;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess))
|
||||
TArray<UInputAction*> ItemActions;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@@ -211,7 +218,7 @@ public:
|
||||
|
||||
void OnRevived(AActor* InInstigator, AActor* InCauser, const FGameplayEffectSpec& InEffectSpec, float InMagnitude);
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = Ability, meta = (AllowPrivateAccess = "true"))
|
||||
UPROPERTY(BlueprintReadOnly, Category = Ability, meta = (AllowPrivateAccess))
|
||||
UCogSampleAbilitySystemComponent* AbilitySystem = nullptr;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Ability)
|
||||
@@ -259,11 +266,16 @@ public:
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure=false)
|
||||
bool GetMontage(FName MontageName, UAnimMontage*& Montage, bool bPrintWarning) const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
friend class ACogSamplePlayerController;
|
||||
|
||||
void RefreshServerAnimTickOption();
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = Team, Replicated, meta = (AllowPrivateAccess))
|
||||
int32 Team = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = Level, Replicated, meta = (AllowPrivateAccess))
|
||||
int32 ProgressionLevel = 0;
|
||||
|
||||
UPROPERTY()
|
||||
AController* InitialController = nullptr;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
};
|
||||
|
||||
UCLASS(meta = (ScriptName = "CogSampleFunctionLibrary_Damage"))
|
||||
class COGSAMPLE_API UCogSampleFunctionLibrary_Damage : public UBlueprintFunctionLibrary
|
||||
class UCogSampleFunctionLibrary_Damage : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
#include "Abilities/GameplayAbility.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AbilitySystemGlobals.h"
|
||||
#include "CogSampleDamageableInterface.h"
|
||||
#include "CogSampleGameplayEffectContext.h"
|
||||
#include "CogSampleProgressionLevelInterface.h"
|
||||
#include "CogSampleSpawnableInterface.h"
|
||||
#include "CogSampleTargetableInterface.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/GameStateBase.h"
|
||||
#include "GameplayCueNotifyTypes.h"
|
||||
#include "GameplayEffectTypes.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
@@ -317,7 +321,7 @@ bool UCogSampleFunctionLibrary_Gameplay::IsActorMatchingTags(const AActor* Actor
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
AActor* UCogSampleFunctionLibrary_Gameplay::GetActorInstigator(AActor* Actor)
|
||||
AActor* UCogSampleFunctionLibrary_Gameplay::GetInstigator(const AActor* Actor)
|
||||
{
|
||||
if (Actor == nullptr)
|
||||
{
|
||||
@@ -340,4 +344,149 @@ AActor* UCogSampleFunctionLibrary_Gameplay::GetActorInstigator(AActor* Actor)
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
int32 UCogSampleFunctionLibrary_Gameplay::GetProgressionLevel(const AActor* Actor)
|
||||
{
|
||||
if (Actor == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ICogSampleProgressionLevelInterface* LevelActor = Cast<ICogSampleProgressionLevelInterface>(Actor);
|
||||
if (LevelActor == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int32 Value = LevelActor->GetProgressionLevel();
|
||||
return Value;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleFunctionLibrary_Gameplay::SetProgressionLevel(AActor* Actor, int32 Value)
|
||||
{
|
||||
if (Actor == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ICogSampleProgressionLevelInterface* LevelActor = Cast<ICogSampleProgressionLevelInterface>(Actor);
|
||||
if (LevelActor == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LevelActor->SetProgressionLevel(Value);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
AActor* UCogSampleFunctionLibrary_Gameplay::GetCreator(const AActor* Actor)
|
||||
{
|
||||
if (Actor == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ICogSampleSpawnableInterface* Spawnable = Cast<ICogSampleSpawnableInterface>(Actor);
|
||||
if (Spawnable == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
AActor* Value = Spawnable->GetCreator();
|
||||
return Value;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleFunctionLibrary_Gameplay::SetCreator(AActor* Actor, AActor* Value)
|
||||
{
|
||||
if (Actor == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ICogSampleSpawnableInterface* Spawnable = Cast<ICogSampleSpawnableInterface>(Actor);
|
||||
if (Spawnable == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Spawnable->SetCreator(Value);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool UCogSampleFunctionLibrary_Gameplay::IsAlive(const AActor* Actor)
|
||||
{
|
||||
if (Actor == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const ICogSampleDamageableInterface* Damageable = Cast<ICogSampleDamageableInterface>(Actor);
|
||||
if (Damageable == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Damageable->IsDead() == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool UCogSampleFunctionLibrary_Gameplay::IsDead(const AActor* Actor)
|
||||
{
|
||||
return IsAlive(Actor) == false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleFunctionLibrary_Gameplay::MakeOutgoingSpecs(
|
||||
const AActor* Actor,
|
||||
const TArray<TSubclassOf<UGameplayEffect>>& Effects,
|
||||
const TArray<FGameplayEffectSpecHandle>& BakedEffects,
|
||||
TMap<TSubclassOf<UGameplayEffect>, FGameplayEffectSpecHandle>& Results)
|
||||
{
|
||||
const AActor* Instigator = UCogSampleFunctionLibrary_Gameplay::GetInstigator(Actor);
|
||||
if (Instigator == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const UAbilitySystemComponent* AbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Instigator);
|
||||
if (AbilitySystem == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 Level = UCogSampleFunctionLibrary_Gameplay::GetProgressionLevel(Actor);
|
||||
|
||||
FGameplayEffectContextHandle EffectContextHandle = AbilitySystem->MakeEffectContext();
|
||||
|
||||
for (const TSubclassOf<UGameplayEffect>& EffectClass : Effects)
|
||||
{
|
||||
if (Results.Contains(EffectClass))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const FGameplayEffectSpecHandle* BakedEffectSpecHandle = BakedEffects.FindByPredicate([EffectClass](const FGameplayEffectSpecHandle& Handle)
|
||||
{
|
||||
return Handle.Data->Def.GetClass() == EffectClass;
|
||||
});
|
||||
|
||||
if (BakedEffectSpecHandle != nullptr)
|
||||
{
|
||||
Results.Add(EffectClass, *BakedEffectSpecHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
FGameplayEffectSpecHandle EffectSpecHandle = AbilitySystem->MakeOutgoingSpec(EffectClass, Level, EffectContextHandle);
|
||||
Results.Add(EffectClass, EffectSpecHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,18 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "CogSampleDefines.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "CogSampleFunctionLibrary_Gameplay.generated.h"
|
||||
|
||||
class UAbilitySystemComponent;
|
||||
class UGameplayAbility;
|
||||
class UGameplayEffect;
|
||||
struct FGameplayAbilitySpecHandle;
|
||||
struct FGameplayAttribute;
|
||||
struct FGameplayAttributeData;
|
||||
struct FGameplayCueNotify_SpawnResult;
|
||||
struct FGameplayCueParameters;
|
||||
struct FGameplayEffectSpecHandle;
|
||||
struct FGameplayTagContainer;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -65,7 +68,25 @@ public:
|
||||
static void FindCapsulePointDistance(const FVector2D& CapsulePoint1, const FVector2D& CapsulePoint2, const float CapsuleRadius, const FVector2D& Point, FVector2D& Projection, float& Time, float& Distance);
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
static AActor* GetActorInstigator(AActor* Actor);
|
||||
static AActor* GetInstigator(const AActor* Actor);
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
static int32 GetProgressionLevel(const AActor* Actor);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
static void SetProgressionLevel(AActor* Actor, int32 Value);
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
static AActor* GetCreator(const AActor* Actor);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
static void SetCreator(AActor* Actor, AActor* Value);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
static bool IsAlive(const AActor* Actor);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
static bool IsDead(const AActor* Actor);
|
||||
|
||||
static void AdjustAttributeForMaxChange(UAbilitySystemComponent* AbilityComponent, FGameplayAttributeData& AffectedAttribute, float OldValue, float NewMaxValue, const FGameplayAttribute& AffectedAttributeProperty);
|
||||
|
||||
@@ -85,5 +106,5 @@ public:
|
||||
|
||||
static bool IsActorMatchingTags(const AActor* Actor, const FGameplayTagContainer& RequiredTags, const FGameplayTagContainer& IgnoredTags);
|
||||
|
||||
static bool MatchCooldownTag(const FGameplayTagContainer& TagContainer);
|
||||
static void MakeOutgoingSpecs(const AActor* Actor, const TArray<TSubclassOf<UGameplayEffect>>& Effects, const TArray<FGameplayEffectSpecHandle>& BakedEffects, TMap<TSubclassOf<UGameplayEffect>, FGameplayEffectSpecHandle>& Result);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "CogSampleTeamInterface.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
int32 UCogSampleFunctionLibrary_Team::GetTeamSafe(const AActor* Actor)
|
||||
int32 UCogSampleFunctionLibrary_Team::GetTeam(const AActor* Actor)
|
||||
{
|
||||
if (Actor == nullptr)
|
||||
{
|
||||
@@ -20,6 +20,24 @@ int32 UCogSampleFunctionLibrary_Team::GetTeamSafe(const AActor* Actor)
|
||||
return Team;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleFunctionLibrary_Team::SetTeam(AActor* Actor, int32 Value)
|
||||
{
|
||||
if (Actor == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ICogSampleTeamInterface* TeamActor = Cast<ICogSampleTeamInterface>(Actor);
|
||||
if (TeamActor == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TeamActor->SetTeam(Value);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
ECogSampleAllegiance UCogSampleFunctionLibrary_Team::GetTeamsAllegiance(int32 Team1, int32 Team2)
|
||||
{
|
||||
@@ -39,8 +57,8 @@ ECogSampleAllegiance UCogSampleFunctionLibrary_Team::GetTeamsAllegiance(int32 Te
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
ECogSampleAllegiance UCogSampleFunctionLibrary_Team::GetActorsAllegiance(const AActor* Actor1, const AActor* Actor2)
|
||||
{
|
||||
const int32 Team1 = GetTeamSafe(Actor1);
|
||||
const int32 Team2 = GetTeamSafe(Actor2);
|
||||
const int32 Team1 = GetTeam(Actor1);
|
||||
const int32 Team2 = GetTeam(Actor2);
|
||||
const ECogSampleAllegiance Allegiance = GetTeamsAllegiance(Team1, Team2);
|
||||
return Allegiance;
|
||||
}
|
||||
@@ -64,7 +82,7 @@ bool UCogSampleFunctionLibrary_Team::MatchAllegianceFromTeams(int32 Team1, int32
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool UCogSampleFunctionLibrary_Team::MatchAllegianceBetweenTeamAndActor(int32 Team, const AActor* Actor, int32 AllegianceFilter)
|
||||
{
|
||||
const int32 ActorTeam = GetTeamSafe(Actor);
|
||||
const int32 ActorTeam = GetTeam(Actor);
|
||||
const ECogSampleAllegiance Allegiance = GetTeamsAllegiance(Team, ActorTeam);
|
||||
const bool Result = MatchAllegianceFilter(Allegiance, AllegianceFilter);
|
||||
return Result;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "CogSampleFunctionLibrary_Team.generated.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -32,7 +33,10 @@ class UCogSampleFunctionLibrary_Team : public UBlueprintFunctionLibrary
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
static int32 GetTeamSafe(const AActor* Actor);
|
||||
static int32 GetTeam(const AActor* Actor);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
static void SetTeam(AActor* Actor, int32 Value);
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
static ECogSampleAllegiance GetTeamsAllegiance(int32 Team1, int32 Team2);
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
#include "CogSampleFunctionLibrary_Tag.h"
|
||||
#include "CogSampleGameplayEffectContext.h"
|
||||
#include "CogSampleLogCategories.h"
|
||||
|
||||
|
||||
|
||||
#include "CogSamplePlayerController.h"
|
||||
#include "CogSampleSpawnPredictionComponent.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
UCogSampleGameplayAbility::UCogSampleGameplayAbility()
|
||||
@@ -235,4 +234,49 @@ void UCogSampleGameplayAbility::GetCooldownInfos(float& TimeRemaining, float& Co
|
||||
//-------------------------------------------------------------------------------------
|
||||
FGameplayAbilitySpecHandle Handle;
|
||||
GetCooldownTimeRemainingAndDuration(Handle, CurrentActorInfo, TimeRemaining, CooldownDuration);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleGameplayAbility::SetupSpawnPrediction(AActor* Actor, int32 InstanceIndex)
|
||||
{
|
||||
if (Actor == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UCogSampleSpawnPredictionComponent* SpawnPrediction = Actor->FindComponentByClass<UCogSampleSpawnPredictionComponent>();
|
||||
if (SpawnPrediction == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupSpawnPredictionComponent(SpawnPrediction, InstanceIndex);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleGameplayAbility::SetupSpawnPredictionComponent(UCogSampleSpawnPredictionComponent* SpawnPrediction, int32 InstanceIndex)
|
||||
{
|
||||
if (SpawnPrediction == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SpawnPrediction->SetCreator(CurrentActorInfo->AvatarActor.Get());
|
||||
|
||||
FCogSampleSpawnPredictionKey PredictedActorKey = FCogSampleSpawnPredictionKey::MakeFromAbility(*this, InstanceIndex);
|
||||
SpawnPrediction->SetSpawnPredictionKey(PredictedActorKey);
|
||||
|
||||
if (GetCurrentActorInfo()->IsNetAuthority())
|
||||
{
|
||||
SpawnPrediction->SetRole(ECogSampleSpawnPredictionRole::Server);
|
||||
}
|
||||
else
|
||||
{
|
||||
SpawnPrediction->SetRole(ECogSampleSpawnPredictionRole::Predicted);
|
||||
|
||||
if (ACogSamplePlayerController* PlayerController = Cast<ACogSamplePlayerController>(GetCurrentActorInfo()->PlayerController))
|
||||
{
|
||||
PlayerController->SpawnPredictions.Add(SpawnPrediction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "Abilities/GameplayAbility.h"
|
||||
#include "CogSampleGameplayAbility.generated.h"
|
||||
|
||||
class UCogSampleSpawnPredictionComponent;
|
||||
|
||||
UCLASS()
|
||||
class UCogSampleGameplayAbility : public UGameplayAbility
|
||||
{
|
||||
@@ -17,7 +19,9 @@ public:
|
||||
// UGameplayAbility overrides
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
virtual void PreActivate(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, FOnGameplayAbilityEnded::FDelegate* OnGameplayAbilityEndedDelegate, const FGameplayEventData* TriggerEventData) override;
|
||||
|
||||
virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
|
||||
|
||||
virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
@@ -59,6 +63,16 @@ public:
|
||||
UFUNCTION(BlueprintPure)
|
||||
int32 GetIntValueAtAbilityLevel(const FScalableFloat& ScalableFloat) const;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// Predicated Actors
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void SetupSpawnPrediction(AActor* PredictedSpawn, int32 InstanceIndex);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void SetupSpawnPredictionComponent(UCogSampleSpawnPredictionComponent* SpawnPrediction, int32 InstanceIndex);
|
||||
|
||||
private:
|
||||
|
||||
bool IsCostGameplayEffectIsZero(const UGameplayEffect* GameplayEffect, float Level, const FGameplayEffectContextHandle& EffectContext) const;
|
||||
|
||||
@@ -18,6 +18,8 @@ DEFINE_LOG_CATEGORY(LogCogControlRotation);
|
||||
DEFINE_LOG_CATEGORY(LogCogInput);
|
||||
DEFINE_LOG_CATEGORY(LogCogPosition);
|
||||
DEFINE_LOG_CATEGORY(LogCogPossession);
|
||||
DEFINE_LOG_CATEGORY(LogCogPredictedActor);
|
||||
DEFINE_LOG_CATEGORY(LogCogProjectile);
|
||||
DEFINE_LOG_CATEGORY(LogCogRotation);
|
||||
DEFINE_LOG_CATEGORY(LogCogSkeleton);
|
||||
DEFINE_LOG_CATEGORY(LogCogTargetAcquisition);
|
||||
@@ -39,7 +41,9 @@ namespace CogSampleLog
|
||||
FCogDebugLog::AddLogCategory(LogCogControlRotation, "Control Rotation", "Debug Draw of the Character Control Rotation");
|
||||
FCogDebugLog::AddLogCategory(LogCogInput, "Input", "Log about the input actions");
|
||||
FCogDebugLog::AddLogCategory(LogCogPosition, "Position", "Debug draw of a character position");
|
||||
FCogDebugLog::AddLogCategory(LogCogPossession, "Possession", "Log about the possession of player controller over a Character");
|
||||
FCogDebugLog::AddLogCategory(LogCogPossession, "Possession", "Log about the possession of a PlayerController over a Character");
|
||||
FCogDebugLog::AddLogCategory(LogCogPredictedActor, "Predicted Actor", "Log and debug draw about the prediction of actors.");
|
||||
FCogDebugLog::AddLogCategory(LogCogProjectile, "Projectile", "Log and debug draw about the projectiles.");
|
||||
FCogDebugLog::AddLogCategory(LogCogRotation, "Rotation", "Debug Draw a Character Rotation");
|
||||
FCogDebugLog::AddLogCategory(LogCogSkeleton, "Skeleton", "Debug Draw a Character Skeleton");
|
||||
FCogDebugLog::AddLogCategory(LogCogTargetAcquisition, "Target Acquisition", "Debug Draw the target acquisition debug draw");
|
||||
|
||||
@@ -12,6 +12,8 @@ DECLARE_LOG_CATEGORY_EXTERN(LogCogControlRotation, Warning, All);
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogCogInput, Warning, All);
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogCogPosition, Warning, All);
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogCogPossession, Warning, All);
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogCogPredictedActor, Warning, All);
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogCogProjectile, Warning, All);
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogCogRotation, Warning, All);
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogCogSkeleton, Warning, All);
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogCogTargetAcquisition, Warning, All);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "CogSampleCharacter.h"
|
||||
#include "CogSampleLogCategories.h"
|
||||
#include "CogSampleTargetAcquisition.h"
|
||||
#include "GameFramework/PlayerState.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
#if ENABLE_COG
|
||||
@@ -171,7 +172,7 @@ void ACogSamplePlayerController::TickTargeting(float DeltaSeconds)
|
||||
#if ENABLE_COG
|
||||
if (Target != nullptr && PossessedCharacter != nullptr)
|
||||
{
|
||||
FCogDebugDraw::Segment(LogCogTargetAcquisition, PossessedCharacter.Get(), PossessedCharacter->GetActorLocation(), Target->GetActorLocation(), FColor::White, false);
|
||||
FCogDebugDraw::Segment(LogCogTargetAcquisition, this, PossessedCharacter->GetActorLocation(), Target->GetActorLocation(), FColor::White, false);
|
||||
}
|
||||
#endif //ENABLE_COG
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "CogSamplePlayerController.generated.h"
|
||||
|
||||
class UCogSampleTargetAcquisition;
|
||||
class ACogSampleCharacter;
|
||||
class UCogSampleSpawnPredictionComponent;
|
||||
class UCogSampleTargetAcquisition;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FCogSampleTargetChangedEventDelegate, ACogSamplePlayerController*, Controller, AActor*, NewTarget, AActor*, OldTarget);
|
||||
@@ -53,10 +54,15 @@ public:
|
||||
|
||||
AActor* GetTarget() const { return Target.Get(); }
|
||||
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FCogSampleTargetChangedEventDelegate OnTargetChanged;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// Spawn Predictions
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
UPROPERTY()
|
||||
TArray<UCogSampleSpawnPredictionComponent*> SpawnPredictions;
|
||||
|
||||
private:
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "CogSampleProgressionLevelInterface.generated.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
UINTERFACE(MinimalAPI, Blueprintable)
|
||||
class UCogSampleProgressionLevelInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
class ICogSampleProgressionLevelInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
virtual int32 GetProgressionLevel() const { return 0; }
|
||||
|
||||
virtual void SetProgressionLevel(int32) { }
|
||||
};
|
||||
@@ -0,0 +1,428 @@
|
||||
#include "CogSampleProjectileComponent.h"
|
||||
|
||||
#include "AbilitySystemGlobals.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "CogCommon.h"
|
||||
#include "CogSampleFunctionLibrary_Gameplay.h"
|
||||
#include "CogSampleFunctionLibrary_Team.h"
|
||||
#include "CogSampleLogCategories.h"
|
||||
|
||||
#if ENABLE_COG
|
||||
#include "CogDebugLog.h"
|
||||
#include "CogDebugDraw.h"
|
||||
#endif //ENABLE_COG
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleProjectileComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
Creator = UCogSampleFunctionLibrary_Gameplay::GetCreator(GetOwner());
|
||||
SpawnPrediction = GetOwner()->FindComponentByClass<UCogSampleSpawnPredictionComponent>();
|
||||
|
||||
if (GetOwner()->HasAuthority())
|
||||
{
|
||||
RegisterAllEffects();
|
||||
}
|
||||
|
||||
Collision = Cast<USphereComponent>(CollisionReference.GetComponent(GetOwner()));
|
||||
if (Collision != nullptr)
|
||||
{
|
||||
Collision->OnComponentBeginOverlap.AddDynamic(this, &UCogSampleProjectileComponent::OnCollisionOverlapBegin);
|
||||
}
|
||||
|
||||
AssistanceOverlap = Cast<USphereComponent>(OverlapReference.GetComponent(GetOwner()));
|
||||
if (AssistanceOverlap != nullptr)
|
||||
{
|
||||
AssistanceOverlap->OnComponentBeginOverlap.AddDynamic(this, &UCogSampleProjectileComponent::OnAssistanceOverlapBegin);
|
||||
}
|
||||
|
||||
#if ENABLE_COG
|
||||
if (FCogDebugLog::IsLogCategoryActive(LogCogProjectile))
|
||||
{
|
||||
LastDebugLocation = GetOwner()->GetActorLocation();
|
||||
}
|
||||
#endif //ENABLE_COG
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleProjectileComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
//if (CosmeticComponent != nullptr)
|
||||
//{
|
||||
// CosmeticComponent->AddLocalRotation(CurrentCosmeticAngularVelocity * DeltaTime);
|
||||
// CurrentCosmeticAngularVelocity *= FMath::Clamp(1.0f - CosmeticAngularDrag * DeltaTime, 0.0f, 1.0f);
|
||||
//}
|
||||
|
||||
//PreviousVelocity = Velocity;
|
||||
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
//TravelingTime += DeltaTime;
|
||||
|
||||
#if ENABLE_COG
|
||||
|
||||
if (FCogDebugLog::IsLogCategoryActive(LogCogProjectile))
|
||||
{
|
||||
const float CollisionRadius = Collision != nullptr ? Collision->GetScaledSphereRadius() : 0.0f;
|
||||
const float AssistanceRadius = AssistanceOverlap != nullptr ? AssistanceOverlap->GetScaledSphereRadius() : 0.0f;
|
||||
const float DebugRadius = FMath::Max(CollisionRadius, AssistanceRadius);
|
||||
const FColor Color = SpawnPrediction != nullptr ? SpawnPrediction->GetRoleColor() : FColor(128, 128, 128, 255);
|
||||
const bool Show = SpawnPrediction == nullptr || SpawnPrediction->GetRole() != ECogSampleSpawnPredictionRole::Replicated;
|
||||
|
||||
if (Show && UpdatedComponent != nullptr)
|
||||
{
|
||||
const FVector Location = UpdatedComponent->GetComponentLocation();
|
||||
const FVector Delta = Location - LastDebugLocation;
|
||||
|
||||
if (LogCogProjectile.GetVerbosity() == ELogVerbosity::VeryVerbose)
|
||||
{
|
||||
FCogDebugDraw::Sphere(LogCogProjectile, GetOwner(), Location, DebugRadius, Color, true, 0);
|
||||
FCogDebugDraw::Axis(LogCogProjectile, GetOwner(), Location, UpdatedComponent->GetComponentRotation(), 50.0f, true, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
FCogDebugDraw::Point(LogCogProjectile, GetOwner(), Location, 5.0f, Color, true, 0);
|
||||
}
|
||||
|
||||
if (Delta.IsNearlyZero() == false)
|
||||
{
|
||||
FCogDebugDraw::Segment(LogCogProjectile, GetOwner(), LastDebugLocation, Location, Color, true, 0);
|
||||
}
|
||||
|
||||
LastDebugLocation = Location;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //ENABLE_COG
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
//void UCogSampleProjectileComponent::CatchupReplicatedActor(float CatchupDuration)
|
||||
//{
|
||||
// COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Projectile:%s - Role:%s - CatchupDuration:%0.2f"), *GetName(), *GetRoleName(), CatchupDuration);
|
||||
//
|
||||
// const FVector OldPosition = GetOwner()->GetActorLocation();
|
||||
// const float OldSpeed = Velocity.Length();
|
||||
//
|
||||
// TickComponent(CatchupDuration, LEVELTICK_All, nullptr);
|
||||
//
|
||||
// COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator, TEXT("Distance:%0.2f - OldSpeed:%0.2f - NewSpeed:%0.2f"),
|
||||
// (GetOwner()->GetActorLocation() - OldPosition).Length(),
|
||||
// OldSpeed,
|
||||
// Velocity.Length());
|
||||
//
|
||||
// if (SpawnPrediction != nullptr)
|
||||
// {
|
||||
// if (UCogSampleSpawnPrediction* SpawnPrediction2 = SpawnPrediction->GetSpawnPrediction())
|
||||
// {
|
||||
// SpawnPrediction2->ProjectileMovement->Velocity = ProjectileMovement->Velocity;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleProjectileComponent::RegisterAllEffects()
|
||||
{
|
||||
TArray<TSubclassOf<UGameplayEffect>> AllEffects;
|
||||
|
||||
for (const FCogSampleProjectileEffectConfig& EffectConfig : Effects)
|
||||
{
|
||||
for (TSubclassOf<UGameplayEffect> EffectClass : EffectConfig.Effects)
|
||||
{
|
||||
AllEffects.Add(EffectClass);
|
||||
}
|
||||
}
|
||||
|
||||
UCogSampleFunctionLibrary_Gameplay::MakeOutgoingSpecs(GetOwner(), AllEffects, BakedEffects, EffectsMap);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool UCogSampleProjectileComponent::ShouldProcessOverlap(AActor* OtherActor, UPrimitiveComponent* OtherComp, bool RequireValidActor)
|
||||
{
|
||||
if (RequireValidActor && OtherActor == nullptr)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:InvalidActor"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetOwner()->GetLocalRole() != ROLE_Authority)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:NotAuthority"));
|
||||
return false;
|
||||
}
|
||||
|
||||
//if (IsStopped)
|
||||
//{
|
||||
// //----------------------------------------------------------------------------------------
|
||||
// // We can receive overlap events the same frame Stop is called. It shouldn't happen after.
|
||||
// //----------------------------------------------------------------------------------------
|
||||
// COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:Stopped"));
|
||||
// return false;
|
||||
//}
|
||||
|
||||
if (OtherComp == nullptr)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Warning, Creator.Get(), TEXT("Skipped:InvalidCollider"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OtherActor == GetOwner())
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:HittingSelf"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CanHitCreator == false && OtherActor == Creator)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:HittingCreator"));
|
||||
return false;
|
||||
}
|
||||
|
||||
////-----------------------------------------------------------------------------------------
|
||||
//// Don't overlap if OtherActor is the simulated proxy replicated one time from server to
|
||||
//// client for synch-up
|
||||
////-----------------------------------------------------------------------------------------
|
||||
//if (OtherActor->IsA(AGPCoreProjectile::StaticClass()) && OtherActor->GetLocalRole() == ROLE_SimulatedProxy)
|
||||
//{
|
||||
// COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:SimulatedProxyProjectile"));
|
||||
// return false;
|
||||
//}
|
||||
|
||||
if (IsAlreadyProcessingAnOverlap)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:AlreadyProcessingAnOverlap"));
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
// Ignore multiple hits on the same actor. This can happen if this the has multiple
|
||||
// collisions, such as the character hit volumes (head, arm, chest, ...)
|
||||
//-----------------------------------------------------------------------------------------
|
||||
if (HitActors.Contains(OtherActor))
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped:AlreadyHit"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleProjectileComponent::OnCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool IsFromSweep, const FHitResult& SweepHit)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Projectile:%s - Role:%s - Other:%s - Comp:%s"),
|
||||
*GetName(),
|
||||
*GetRoleName(),
|
||||
OtherActor != nullptr ? *OtherActor->GetName() : TEXT("NULL"),
|
||||
OtherComp != nullptr ? *OtherComp->GetName() : TEXT("NULL"));
|
||||
|
||||
if (ShouldProcessOverlap(OtherActor, OtherComp, false) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TGuardValue<bool> OverlapGuard(IsAlreadyProcessingAnOverlap, true);
|
||||
FHitResult PreciseHit;
|
||||
|
||||
if (IsFromSweep)
|
||||
{
|
||||
//-----------------------------------------------------------------------------
|
||||
// When the projectile moves, it moves with sweep activated.
|
||||
//-----------------------------------------------------------------------------
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Method:IsFromSweep"));
|
||||
PreciseHit = SweepHit;
|
||||
}
|
||||
else if (Collision != nullptr)
|
||||
{
|
||||
//-----------------------------------------------------------------------------
|
||||
// Trace to find the accurate collision location. Use the largest collider
|
||||
// which should be the assistance sphere, to make sure we find a result.
|
||||
// If we were using the collision sphere after an assistance sphere overlap,
|
||||
// we could miss the sweep.
|
||||
//-----------------------------------------------------------------------------
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Method:SweepComponent"));
|
||||
|
||||
USphereComponent* TestComp = (AssistanceOverlap != nullptr && AssistanceOverlap->GetUnscaledSphereRadius() > Collision->GetUnscaledSphereRadius()) ? AssistanceOverlap : Collision;
|
||||
OtherComp->SweepComponent(PreciseHit, GetOwner()->GetActorLocation() - Velocity * 10.f, GetOwner()->GetActorLocation() + Velocity, FQuat::Identity, TestComp->GetCollisionShape(), TestComp->bTraceComplexOnMove);
|
||||
|
||||
// SweepComponent specifically does not return us the Physical Material of the hit surface, so we look it up manually
|
||||
if (PreciseHit.GetComponent() != nullptr)
|
||||
{
|
||||
PreciseHit.PhysMaterial = PreciseHit.GetComponent()->GetBodyInstance()->GetSimplePhysicalMaterial();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//-----------------------------------------------------------------------------
|
||||
// Fallback that uses a raycast if we have no CollisionComp to find a more
|
||||
// accurate collision location. It should never happen.
|
||||
//-----------------------------------------------------------------------------
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Method:LineTrace"));
|
||||
static const FName TraceTag(TEXT("UCogSampleProjectileComponent::OnCollisionOverlapBegin"));
|
||||
FCollisionQueryParams QueryParams(TraceTag, false, GetOwner());
|
||||
QueryParams.bReturnPhysicalMaterial = true;
|
||||
OtherComp->LineTraceComponent(PreciseHit, GetOwner()->GetActorLocation() - Velocity * 10.f, GetOwner()->GetActorLocation() + Velocity, QueryParams);
|
||||
}
|
||||
|
||||
TryHit(PreciseHit);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleProjectileComponent::OnAssistanceOverlapBegin(UPrimitiveComponent* overlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool IsFromSweep, const FHitResult& Hit)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Projectile:%s - Role:%s - Other:%s - Comp:%s"),
|
||||
*GetName(),
|
||||
*GetRoleName(),
|
||||
OtherActor != nullptr ? *OtherActor->GetName() : TEXT("NULL"),
|
||||
OtherComp != nullptr ? *OtherComp->GetName() : TEXT("NULL"));
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Call ShouldProcessOverlap with a requirement of a valid actor because the
|
||||
// assistance overlap is made to overlap only against actors, not static objects which
|
||||
// would be null actor
|
||||
//-------------------------------------------------------------------------------------
|
||||
if (ShouldProcessOverlap(OtherActor, OtherComp, true) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Since PawnOverlapSphere doesn't hit blocking objects, it is possible it is touching
|
||||
// a target through a wall. Make sure that the hit is valid before proceeding.
|
||||
//
|
||||
// TODO: This is an approximation that does not always work if the assistance sphere
|
||||
// is big. The raycast can pass even if there is a collision in the trajectory of the
|
||||
// projectile, because we don't trace along the projectiles trajectory, but against
|
||||
// both actors positions.
|
||||
//
|
||||
// This test is skipped if the projectile collision has been disabled. When the
|
||||
// collision is disabled it means we want the projectile to pass through walls.
|
||||
//-------------------------------------------------------------------------------------
|
||||
bool IsValidOverlap = true;
|
||||
if (Collision->GetCollisionEnabled())
|
||||
{
|
||||
static const FName TraceTag(TEXT("UCogSampleProjectileComponent::OnAssistanceOverlapBegin"));
|
||||
FCollisionQueryParams QueryParams(TraceTag, true, GetOwner());
|
||||
QueryParams.AddIgnoredActor(OtherActor);
|
||||
const FVector OtherLocation = IsFromSweep ? (FVector)Hit.Location : OtherActor->GetActorLocation();
|
||||
if (GetWorld()->LineTraceTestByProfile(OtherLocation, GetOwner()->GetActorLocation(), Collision->GetCollisionProfileName(), QueryParams) == false)
|
||||
{
|
||||
IsValidOverlap = true;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Call OnCollisionOverlapBegin since its doing the sweep test.
|
||||
//-------------------------------------------------------------------------------------
|
||||
if (IsValidOverlap)
|
||||
{
|
||||
OnCollisionOverlapBegin(Collision, OtherActor, OtherComp, OtherBodyIndex, IsFromSweep, Hit);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleProjectileComponent::TryHit(const FHitResult& HitResult)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Projectile:%s - Role:%s - Other:%s - Comp:%s - Bone:%s"),
|
||||
*GetName(),
|
||||
*GetRoleName(),
|
||||
HitResult.GetActor() != nullptr ? *HitResult.GetActor()->GetName() : TEXT("NULL"),
|
||||
HitResult.GetComponent() != nullptr ? *HitResult.GetComponent()->GetName() : TEXT("NULL"),
|
||||
*HitResult.BoneName.ToString());
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
// User defined callback for gameplay logic
|
||||
//-----------------------------------------------------------------------------------------
|
||||
if (ShouldHit(HitResult) == false)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogProjectile, ELogVerbosity::Verbose, Creator.Get(), TEXT("Skipped by ShouldHit callback"));
|
||||
return;
|
||||
}
|
||||
|
||||
HitActors.Add(HitResult.GetActor());
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
// User defined callback to decide what should happen to projectile (Stop, Attach, ...)
|
||||
//-----------------------------------------------------------------------------------------
|
||||
FCogSampleHitConsequence HitConsequence;
|
||||
Hit(HitResult, HitConsequence);
|
||||
|
||||
if (HitConsequence.Stop)
|
||||
{
|
||||
//InternalStop(HitResult, HitConsequence);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleProjectileComponent::ClearHitActors()
|
||||
{
|
||||
HitActors.Empty();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool UCogSampleProjectileComponent::ShouldHit_Implementation(const FHitResult& Hit)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleProjectileComponent::Hit_Implementation(const FHitResult& HitResult, FCogSampleHitConsequence& hitConsequence)
|
||||
{
|
||||
AActor* HitActor = HitResult.GetActor();
|
||||
|
||||
#if ENABLE_COG
|
||||
FCogDebugDraw::Arrow(LogCogProjectile, GetOwner(), HitResult.Location, HitResult.Location + HitResult.Normal * 50.0f, FColor::Red, true, 0);
|
||||
FCogDebugDraw::Box(LogCogProjectile, GetOwner(), HitResult.Location, FVector(0.0f, 5.0f, 5.0f), FRotationMatrix::MakeFromX(HitResult.Normal).ToQuat(), FColor::Red, true, 0);
|
||||
#endif //ENABLE_COG
|
||||
|
||||
for (const FCogSampleProjectileEffectConfig& EffectConfig : Effects)
|
||||
{
|
||||
for (TSubclassOf<UGameplayEffect> EffectClass : EffectConfig.Effects)
|
||||
{
|
||||
if (UCogSampleFunctionLibrary_Team::MatchAllegiance(GetOwner(), HitActor, EffectConfig.Allegiance) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FGameplayEffectSpecHandle* Handle = EffectsMap.Find(EffectClass);
|
||||
if (Handle == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FGameplayEffectSpec* Spec = Handle->Data.Get();
|
||||
if (Spec == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (UCogSampleFunctionLibrary_Gameplay::IsDead(HitActor) && EffectConfig.AffectDead == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* TargetAbilitySystem = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(HitActor);
|
||||
if (TargetAbilitySystem == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Spec->GetContext().AddHitResult(HitResult, true);
|
||||
TargetAbilitySystem->ApplyGameplayEffectSpecToSelf(*Spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FString UCogSampleProjectileComponent::GetRoleName() const
|
||||
{
|
||||
if (SpawnPrediction == nullptr)
|
||||
{
|
||||
return TEXT("No Prediction");
|
||||
}
|
||||
|
||||
return SpawnPrediction->GetRoleName();
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/SphereComponent.h"
|
||||
#include "GameFramework/ProjectileMovementComponent.h"
|
||||
#include "CogSampleSpawnPredictionComponent.h"
|
||||
#include "GameplayEffect.h"
|
||||
#include "CogSampleProjectileComponent.generated.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// FCogSampleProjectileEffectConfig
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCogSampleProjectileEffectConfig
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Bitmask, BitmaskEnum = "/Script/CogSample.ECogSampleAllegianceFilter"))
|
||||
int32 Allegiance = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool AffectDead = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<TSubclassOf<UGameplayEffect>> Effects;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// FCogSampleHitConsequence
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCogSampleHitConsequence
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
bool Stop = false;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
bool AttachToComponent = false;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
bool HideOnStop = false;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// UCogSampleProjectileComponent
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
UCLASS(BlueprintType, meta = (BlueprintSpawnableComponent))
|
||||
class UCogSampleProjectileComponent : public UProjectileMovementComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void ClearHitActors();
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile")
|
||||
TArray<FCogSampleProjectileEffectConfig> Effects;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadWrite, Category = "Internal")
|
||||
TArray<FGameplayEffectSpecHandle> BakedEffects;
|
||||
|
||||
protected:
|
||||
|
||||
virtual FString GetRoleName() const;
|
||||
|
||||
//virtual void CatchupReplicatedActor(float CatchupDuration);
|
||||
|
||||
virtual bool ShouldProcessOverlap(AActor* OtherActor, UPrimitiveComponent* OtherComp, bool RequireValidActor);
|
||||
|
||||
virtual void TryHit(const FHitResult& Hit);
|
||||
|
||||
UFUNCTION()
|
||||
virtual void OnCollisionOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool IsFromSweep, const FHitResult& SweepHit);
|
||||
|
||||
UFUNCTION()
|
||||
virtual void OnAssistanceOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool IsFromSweep, const FHitResult& SweepHit);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
|
||||
bool ShouldHit(const FHitResult& hit);
|
||||
|
||||
/** Blueprint hook called when projectile hits something */
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
|
||||
void Hit(const FHitResult& Hit, FCogSampleHitConsequence& HitConsequence);
|
||||
|
||||
void RegisterAllEffects();
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
FComponentReference CollisionReference;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
FComponentReference OverlapReference;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile")
|
||||
bool CanHitCreator = false;
|
||||
|
||||
/** Sphere collision component */
|
||||
UPROPERTY()
|
||||
TObjectPtr<USphereComponent> Collision = nullptr;
|
||||
|
||||
/** Overlap collision component */
|
||||
UPROPERTY()
|
||||
TObjectPtr<USphereComponent> AssistanceOverlap = nullptr;
|
||||
|
||||
UPROPERTY()
|
||||
TWeakObjectPtr<AActor> Creator = nullptr;
|
||||
|
||||
UPROPERTY()
|
||||
TWeakObjectPtr<UCogSampleSpawnPredictionComponent> SpawnPrediction = nullptr;
|
||||
|
||||
/** Re-entrancy guard */
|
||||
bool IsAlreadyProcessingAnOverlap = false;
|
||||
|
||||
/** The actors that were hit with this projectile. Used to prevent touching the same actor multiple times */
|
||||
TSet<AActor*> HitActors;
|
||||
|
||||
TMap<TSubclassOf<UGameplayEffect>, FGameplayEffectSpecHandle> EffectsMap;
|
||||
|
||||
#if ENABLE_COG
|
||||
FVector LastDebugLocation = FVector::ZeroVector;
|
||||
#endif //ENABLE_COG
|
||||
};
|
||||
@@ -0,0 +1,334 @@
|
||||
#include "CogSampleSpawnPredictionComponent.h"
|
||||
|
||||
#include "Abilities/GameplayAbility.h"
|
||||
#include "Abilities/GameplayAbilityTypes.h"
|
||||
#include "CogSampleLogCategories.h"
|
||||
#include "CogSampleFunctionLibrary_Gameplay.h"
|
||||
#include "CogSamplePlayerController.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
#if ENABLE_COG
|
||||
#include "CogDebugDraw.h"
|
||||
#include "CogDebugLog.h"
|
||||
#endif //ENABLE_COG
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// FCogSampleSpawnPredictionKey
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FString FCogSampleSpawnPredictionKey::ToString() const
|
||||
{
|
||||
return FString::Printf(TEXT("[Creator=%s Ability=%s PredictionKey=%s Index=%d]"), *Creator.ToString(), *Ability.ToString(), *PredictionKey.ToString(), InstanceIndex);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool FCogSampleSpawnPredictionKey::operator==(const FCogSampleSpawnPredictionKey& other) const
|
||||
{
|
||||
return Creator == other.Creator
|
||||
&& Ability == other.Ability
|
||||
&& PredictionKey == other.PredictionKey
|
||||
&& InstanceIndex == other.InstanceIndex;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
bool FCogSampleSpawnPredictionKey::operator!=(const FCogSampleSpawnPredictionKey& other) const
|
||||
{
|
||||
return ((*this == other) == false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FCogSampleSpawnPredictionKey FCogSampleSpawnPredictionKey::MakeFromAbility(const UGameplayAbility& InAbility, int32 InInstanceIndex)
|
||||
{
|
||||
FGameplayAbilitySpec* Spec = InAbility.GetCurrentAbilitySpec();
|
||||
check(Spec);
|
||||
|
||||
FCogSampleSpawnPredictionKey Key;
|
||||
Key.Creator = InAbility.GetAvatarActorFromActorInfo() != nullptr ? InAbility.GetAvatarActorFromActorInfo()->GetFName() : FName();
|
||||
Key.Ability = InAbility.GetFName();
|
||||
Key.PredictionKey = Spec->ActivationInfo.GetActivationPredictionKey();
|
||||
Key.InstanceIndex = InInstanceIndex;
|
||||
Key.GameTime = InAbility.GetWorld()->GetTimeSeconds();
|
||||
return Key;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// UCogSampleSpawnPredictionComponent
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
UCogSampleSpawnPredictionComponent::UCogSampleSpawnPredictionComponent(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
SetIsReplicatedByDefault(true);
|
||||
|
||||
PredictedSpawn = nullptr;
|
||||
ReplicatedActor = nullptr;
|
||||
|
||||
#if ENABLE_COG
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
PrimaryComponentTick.bStartWithTickEnabled = true;
|
||||
#endif //ENABLE_COG
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleSpawnPredictionComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// TODO: Push Model
|
||||
//-------------------------------------------------------------------------
|
||||
DOREPLIFETIME_CONDITION(UCogSampleSpawnPredictionComponent, PredictedActorKey, COND_OwnerOnly);
|
||||
DOREPLIFETIME(UCogSampleSpawnPredictionComponent, Creator);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleSpawnPredictionComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
APawn* PawnCreator = Cast<APawn>(Creator);
|
||||
PlayerController = PawnCreator ? Cast<ACogSamplePlayerController>(PawnCreator->Controller) : nullptr;
|
||||
//IsReplicatedActor = GetOwner()->HasAuthority() == false && PlayerController != nullptr;
|
||||
|
||||
if (PlayerController != nullptr)
|
||||
{
|
||||
if (GetOwner()->HasAuthority() == false)
|
||||
{
|
||||
Role = ECogSampleSpawnPredictionRole::Replicated;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Role = ECogSampleSpawnPredictionRole::Remote;
|
||||
}
|
||||
|
||||
if (PlayerController != nullptr)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Verbose, UCogSampleFunctionLibrary_Gameplay::GetInstigator(GetOwner()), TEXT("Actor:%s - Role:%s"), *GetName(), *GetRoleName());
|
||||
ReconcileReplicatedWithPredicted();
|
||||
}
|
||||
|
||||
#if ENABLE_COG
|
||||
if (FCogDebugLog::IsLogCategoryActive(LogCogPredictedActor))
|
||||
{
|
||||
LastDebugLocation = GetOwner()->GetActorLocation();
|
||||
}
|
||||
#endif //ENABLE_COG
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleSpawnPredictionComponent::ReconcileReplicatedWithPredicted()
|
||||
{
|
||||
//-------------------------------------------------------------------------
|
||||
// If we are a replicated actor on a local client, find the corresponding
|
||||
// predicted actor and sync them together.
|
||||
//-------------------------------------------------------------------------
|
||||
if (Role != ECogSampleSpawnPredictionRole::Replicated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CleanInvalidPredictedActors();
|
||||
|
||||
int32 PredictedActorIndex = FindSpawnPredictionIndex();
|
||||
if (PredictedActorIndex == INDEX_NONE)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Warning, Creator.Get(), TEXT("Failed to find predicted actor. Key:%s"), *PredictedActorKey.ToString());
|
||||
|
||||
#if ENABLE_COG
|
||||
for (const UCogSampleSpawnPredictionComponent* Candidate : PlayerController->SpawnPredictions)
|
||||
{
|
||||
if (Candidate != nullptr)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Warning, Creator.Get(), TEXT(" Candidate:%s"), *Candidate->PredictedActorKey.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Warning, Creator.Get(), TEXT(" Candidate:NULL"));
|
||||
}
|
||||
}
|
||||
#endif //ENABLE_COG
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UCogSampleSpawnPredictionComponent* SpawnPrediction = PlayerController->SpawnPredictions[PredictedActorIndex];
|
||||
PlayerController->SpawnPredictions.RemoveAt(PredictedActorIndex, 1);
|
||||
SyncReplicatedWithPredicted(SpawnPrediction);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleSpawnPredictionComponent::OnRep_PredictedActorKey()
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Verbose, Creator.Get(), TEXT("Actor:%s - Role:%s - PredictedActorKey:%s"), *GetName(), *GetRoleName(), *PredictedActorKey.ToString());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleSpawnPredictionComponent::CleanInvalidPredictedActors()
|
||||
{
|
||||
if (PlayerController == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<UCogSampleSpawnPredictionComponent*>& SpawnPredictions = PlayerController->SpawnPredictions;
|
||||
for (int32 i = 0; i < SpawnPredictions.Num(); )
|
||||
{
|
||||
UCogSampleSpawnPredictionComponent* Candidate = SpawnPredictions[i];
|
||||
if (Candidate == nullptr || Candidate->GetOwner()->IsPendingKillPending())
|
||||
{
|
||||
SpawnPredictions.RemoveAt(i, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
int32 UCogSampleSpawnPredictionComponent::FindSpawnPredictionIndex()
|
||||
{
|
||||
if (PlayerController == nullptr)
|
||||
{
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
int32 Index = PlayerController->SpawnPredictions.IndexOfByPredicate([this](const UCogSampleSpawnPredictionComponent* Candidate)
|
||||
{
|
||||
if (Candidate == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Candidate->GetClass() != GetClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Candidate->PredictedActorKey != PredictedActorKey)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return Index;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleSpawnPredictionComponent::SyncReplicatedWithPredicted(UCogSampleSpawnPredictionComponent* InPredictedActor)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Verbose, Creator.Get(), TEXT("Actor:%s - Role:%s - PredictedActor:%s"), *GetName(), *GetRoleName(), InPredictedActor != nullptr ? *InPredictedActor->GetName() : TEXT("NULL"));
|
||||
|
||||
PredictedSpawn = InPredictedActor;
|
||||
|
||||
if (PredictedSpawn != nullptr)
|
||||
{
|
||||
PredictedSpawn->ReplicatedActor = this;
|
||||
|
||||
const float PredictedLifeSpawn = PredictedSpawn->GetOwner()->GetLifeSpan();
|
||||
GetOwner()->SetLifeSpan(PredictedLifeSpawn);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Destroying the replicated actor doesn't currently work because the server will keep it alive on all clients
|
||||
// no matter what. We should investigate a way to make it work, but currently we hide it.
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
if (DestroyReplicatedActor)
|
||||
{
|
||||
SetVisibility(false);
|
||||
GetOwner()->Destroy();
|
||||
}
|
||||
else if (HideReplicatedActor)
|
||||
{
|
||||
SetVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleSpawnPredictionComponent::SetVisibility(bool Value)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Verbose, Creator.Get(), TEXT("Actor:%s - Role:%s - Visibility:%d"), *GetName(), *GetRoleName(), Value);
|
||||
|
||||
GetOwner()->SetActorHiddenInGame(!Value);
|
||||
|
||||
TArray<USceneComponent*> OtherComponents;
|
||||
GetOwner()->GetComponents<USceneComponent>(OtherComponents);
|
||||
|
||||
for (int32 i = 0; i < OtherComponents.Num(); i++)
|
||||
{
|
||||
OtherComponents[i]->SetVisibility(Value);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
void UCogSampleSpawnPredictionComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
|
||||
{
|
||||
COG_LOG_OBJECT(LogCogPredictedActor, ELogVerbosity::Verbose, Creator.Get(), TEXT("Actor:%s - Role:%s"), *GetName(), *GetRoleName());
|
||||
|
||||
if (ReplicatedActor != nullptr)
|
||||
{
|
||||
ReplicatedActor->GetOwner()->Destroy();
|
||||
}
|
||||
|
||||
Super::OnComponentDestroyed(bDestroyingHierarchy);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FString UCogSampleSpawnPredictionComponent::GetRoleName() const
|
||||
{
|
||||
switch (Role)
|
||||
{
|
||||
case ECogSampleSpawnPredictionRole::Server: return "Server";
|
||||
case ECogSampleSpawnPredictionRole::Predicted: return "Predicted";
|
||||
case ECogSampleSpawnPredictionRole::Replicated: return "Replicated";
|
||||
case ECogSampleSpawnPredictionRole::Remote: return "Remote";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
FColor UCogSampleSpawnPredictionComponent::GetRoleColor() const
|
||||
{
|
||||
switch (Role)
|
||||
{
|
||||
case ECogSampleSpawnPredictionRole::Server: return FColor::FColor(255, 0, 0, 255);
|
||||
case ECogSampleSpawnPredictionRole::Predicted: return FColor::FColor(255, 255, 0, 255);
|
||||
case ECogSampleSpawnPredictionRole::Replicated: return FColor::FColor(128, 128, 0, 255);
|
||||
case ECogSampleSpawnPredictionRole::Remote: return FColor::FColor(255, 0, 255, 255);
|
||||
}
|
||||
|
||||
return FColor(128, 128, 128, 255);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
#if ENABLE_COG
|
||||
|
||||
void UCogSampleSpawnPredictionComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
const FVector Location = GetOwner()->GetActorLocation();
|
||||
|
||||
if (FCogDebugLog::IsLogCategoryActive(LogCogPredictedActor))
|
||||
{
|
||||
const FColor Color = GetRoleColor();
|
||||
const FVector Delta = Location - LastDebugLocation;
|
||||
|
||||
FCogDebugDraw::Axis(LogCogPredictedActor, this, Location, GetOwner()->GetActorRotation(), 50.0f, true, 0);
|
||||
FCogDebugDraw::Point(LogCogPredictedActor, this, Location, 8.0f, Color, true, 0);
|
||||
|
||||
if (Delta.IsNearlyZero() == false)
|
||||
{
|
||||
FCogDebugDraw::Segment(LogCogPredictedActor, this, LastDebugLocation, Location, Color, true, 0);
|
||||
}
|
||||
}
|
||||
|
||||
LastDebugLocation = Location;
|
||||
}
|
||||
|
||||
#endif //ENABLE_COG
|
||||
@@ -0,0 +1,146 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "CogCommon.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "GameplayPrediction.h"
|
||||
#include "CogSampleSpawnPredictionComponent.generated.h"
|
||||
|
||||
class UGameplayAbility;
|
||||
class ACogSamplePlayerController;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// FCogSamplePredictedActorKey
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
USTRUCT()
|
||||
struct FCogSampleSpawnPredictionKey
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
FName Creator;
|
||||
|
||||
UPROPERTY()
|
||||
FName Ability;
|
||||
|
||||
UPROPERTY()
|
||||
FPredictionKey PredictionKey;
|
||||
|
||||
UPROPERTY()
|
||||
int32 InstanceIndex = 0;
|
||||
|
||||
UPROPERTY()
|
||||
float GameTime = 0;
|
||||
|
||||
FString ToString() const;
|
||||
|
||||
bool operator==(const FCogSampleSpawnPredictionKey& other) const;
|
||||
|
||||
bool operator!=(const FCogSampleSpawnPredictionKey& other) const;
|
||||
|
||||
static FCogSampleSpawnPredictionKey MakeFromAbility(const UGameplayAbility& InAbility, int32 InInstanceIndex);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
UENUM()
|
||||
enum class ECogSampleSpawnPredictionRole : uint8
|
||||
{
|
||||
Server,
|
||||
Predicted,
|
||||
Replicated,
|
||||
Remote,
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
// UCogSamplePredictedActorComponent
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
UCLASS(BlueprintType, meta = (BlueprintSpawnableComponent))
|
||||
class UCogSampleSpawnPredictionComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UCogSampleSpawnPredictionComponent(const class FObjectInitializer& ObjectInitializer);
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override;
|
||||
|
||||
#if ENABLE_COG
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
#endif //ENABLE_COG
|
||||
|
||||
virtual TWeakObjectPtr<AActor> GetCreator() const { return Creator; }
|
||||
|
||||
virtual void SetCreator(TWeakObjectPtr<AActor> Value) { Creator = Value; }
|
||||
|
||||
virtual const FCogSampleSpawnPredictionKey& GetSpawnPredictionKey() const { return PredictedActorKey; }
|
||||
|
||||
virtual void SetSpawnPredictionKey(const FCogSampleSpawnPredictionKey& Value) { PredictedActorKey = Value; }
|
||||
|
||||
virtual void SetSpawnTransform(FTransform Transform) { SpawnTransform = Transform; }
|
||||
|
||||
virtual FTransform GetSpawnTransform() { return SpawnTransform; }
|
||||
|
||||
virtual ECogSampleSpawnPredictionRole GetRole() const { return Role; }
|
||||
|
||||
virtual void SetRole(ECogSampleSpawnPredictionRole Value) { Role = Value; }
|
||||
|
||||
UCogSampleSpawnPredictionComponent* GetPredicted() const { return PredictedSpawn; }
|
||||
|
||||
UCogSampleSpawnPredictionComponent* GetReplicated() const { return ReplicatedActor; }
|
||||
|
||||
virtual void ReconcileReplicatedWithPredicted();
|
||||
|
||||
FString GetRoleName() const;
|
||||
|
||||
FColor GetRoleColor() const;
|
||||
|
||||
protected:
|
||||
|
||||
virtual int32 FindSpawnPredictionIndex();
|
||||
|
||||
virtual void SyncReplicatedWithPredicted(UCogSampleSpawnPredictionComponent* PredictedSpawn);
|
||||
|
||||
void CleanInvalidPredictedActors();
|
||||
|
||||
void SetVisibility(bool Value);
|
||||
|
||||
UFUNCTION()
|
||||
void OnRep_PredictedActorKey();
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
TWeakObjectPtr<AActor> Creator = nullptr;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadonly)
|
||||
bool DestroyReplicatedActor = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadonly)
|
||||
bool HideReplicatedActor = true;
|
||||
|
||||
UPROPERTY(BlueprintReadonly)
|
||||
UCogSampleSpawnPredictionComponent* PredictedSpawn = nullptr;
|
||||
|
||||
UPROPERTY(BlueprintReadonly)
|
||||
UCogSampleSpawnPredictionComponent* ReplicatedActor = nullptr;
|
||||
|
||||
UPROPERTY(BlueprintReadonly)
|
||||
ECogSampleSpawnPredictionRole Role = ECogSampleSpawnPredictionRole::Replicated;
|
||||
|
||||
UPROPERTY(BlueprintReadonly)
|
||||
TWeakObjectPtr<ACogSamplePlayerController> PlayerController = nullptr;
|
||||
|
||||
UPROPERTY(ReplicatedUsing = OnRep_PredictedActorKey)
|
||||
FCogSampleSpawnPredictionKey PredictedActorKey;
|
||||
|
||||
FTransform SpawnTransform;
|
||||
|
||||
#if ENABLE_COG
|
||||
FVector LastDebugLocation = FVector::ZeroVector;
|
||||
#endif //ENABLE_COG
|
||||
};
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "CogSampleSpawnableInterface.generated.h"
|
||||
|
||||
UINTERFACE(MinimalAPI, Blueprintable)
|
||||
class UCogSampleSpawnableInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
class ICogSampleSpawnableInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
virtual AActor* GetCreator() const { return nullptr; }
|
||||
|
||||
virtual void SetCreator(AActor* Value) { }
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "CogSampleTeamInterface.generated.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------------
|
||||
UINTERFACE(MinimalAPI, Blueprintable)
|
||||
class UCogSampleTeamInterface : public UInterface
|
||||
{
|
||||
@@ -17,4 +16,7 @@ class ICogSampleTeamInterface
|
||||
public:
|
||||
|
||||
virtual int32 GetTeam() const { return 0; }
|
||||
|
||||
virtual void SetTeam(int32) { }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user