Implemented OLSHealthComponent and OLSGameDataAsset.
This commit is contained in:
parent
1c148494ff
commit
57be5728b7
402
Source/ols/Private/Components/OLSHealthComponent.cpp
Normal file
402
Source/ols/Private/Components/OLSHealthComponent.cpp
Normal file
@ -0,0 +1,402 @@
|
||||
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
|
||||
|
||||
|
||||
#include "Components/OLSHealthComponent.h"
|
||||
|
||||
#include "AbilitySystem/OLSAbilitySystemComponent.h"
|
||||
#include "AbilitySystem/Attributes/OLSHealthAttributeSet.h"
|
||||
#include "DataAssets/OLSGameDataAsset.h"
|
||||
#include "GameFramework/GameplayMessageSubsystem.h"
|
||||
#include "GameFramework/PlayerState.h"
|
||||
#include "Messages/OLSVerbMessage.h"
|
||||
#include "Messages/OLSVerbMessageHelpers.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "Systems/OLSAssetManager.h"
|
||||
|
||||
UOLSHealthComponent::UOLSHealthComponent(const FObjectInitializer& objectInitializer)
|
||||
: Super(objectInitializer)
|
||||
{
|
||||
PrimaryComponentTick.bStartWithTickEnabled = false;
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
|
||||
SetIsReplicatedByDefault(true);
|
||||
|
||||
AbilitySystemComponent = nullptr;
|
||||
HealthSet = nullptr;
|
||||
DeathState = EOLSDeathState::NotDead;
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(ThisClass, DeathState);
|
||||
}
|
||||
|
||||
UOLSHealthComponent* UOLSHealthComponent::FindHealthComponent(const AActor* actor)
|
||||
{
|
||||
return (actor ? actor->FindComponentByClass<UOLSHealthComponent>() : nullptr);
|
||||
}
|
||||
|
||||
|
||||
void UOLSHealthComponent::InitializeWithAbilitySystem(UOLSAbilitySystemComponent* asc)
|
||||
{
|
||||
AActor* owner = GetOwner();
|
||||
check(owner);
|
||||
|
||||
if (AbilitySystemComponent)
|
||||
{
|
||||
// @TODO replace this by our custom log.
|
||||
// UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: Health component for owner [%s] has already been initialized with an ability system."), *GetNameSafe(Owner));
|
||||
return;
|
||||
}
|
||||
|
||||
AbilitySystemComponent = asc;
|
||||
if (!AbilitySystemComponent)
|
||||
{
|
||||
// @TODO replace this by our custom log.
|
||||
// UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: Cannot initialize health component for owner [%s] with NULL ability system."), *GetNameSafe(Owner));
|
||||
return;
|
||||
}
|
||||
|
||||
HealthSet = AbilitySystemComponent->GetSet<UOLSHealthAttributeSet>();
|
||||
if (!HealthSet)
|
||||
{
|
||||
// @TODO replace this by our custom log.
|
||||
// UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: Cannot initialize health component for owner [%s] with NULL health set on the ability system."), *GetNameSafe(Owner));
|
||||
return;
|
||||
}
|
||||
|
||||
// Register to listen for attribute changes.
|
||||
HealthSet->OnHealthChanged.AddUObject(this, &ThisClass::HandleHealthChanged);
|
||||
HealthSet->OnMaxHealthChanged.AddUObject(this, &ThisClass::HandleMaxHealthChanged);
|
||||
HealthSet->OnOutOfHealth.AddUObject(this, &ThisClass::HandleOutOfHealth);
|
||||
|
||||
// TEMP: Reset attributes to default values. Eventually this will be driven by a spread sheet.
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UOLSHealthAttributeSet::GetHealthAttribute(), HealthSet->GetMaxHealth());
|
||||
|
||||
ClearGameplayTags();
|
||||
|
||||
Broadcast_OnHealthChanged(this, HealthSet->GetHealth(), HealthSet->GetHealth(), nullptr);
|
||||
Broadcast_OnMaxHealthChanged(this, HealthSet->GetHealth(), HealthSet->GetMaxHealth(), nullptr);
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::UninitializeFromAbilitySystem()
|
||||
{
|
||||
ClearGameplayTags();
|
||||
|
||||
if (HealthSet)
|
||||
{
|
||||
HealthSet->OnHealthChanged.RemoveAll(this);
|
||||
HealthSet->OnMaxHealthChanged.RemoveAll(this);
|
||||
HealthSet->OnOutOfHealth.RemoveAll(this);
|
||||
}
|
||||
|
||||
HealthSet = nullptr;
|
||||
AbilitySystemComponent = nullptr;
|
||||
}
|
||||
|
||||
float UOLSHealthComponent::GetHealth() const
|
||||
{
|
||||
return (HealthSet ? HealthSet->GetHealth() : 0.0f);
|
||||
}
|
||||
|
||||
float UOLSHealthComponent::GetMaxHealth() const
|
||||
{
|
||||
return (HealthSet ? HealthSet->GetMaxHealth() : 0.0f);
|
||||
}
|
||||
|
||||
float UOLSHealthComponent::GetHealthNormalized() const
|
||||
{
|
||||
if (HealthSet)
|
||||
{
|
||||
const float health = HealthSet->GetHealth();
|
||||
const float maxHealth = HealthSet->GetMaxHealth();
|
||||
|
||||
return ((maxHealth > 0.0f) ? (health / maxHealth) : 0.0f);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
EOLSDeathState UOLSHealthComponent::GetDeathState() const
|
||||
{
|
||||
return DeathState;
|
||||
}
|
||||
|
||||
bool UOLSHealthComponent::IsDeadOrDying() const
|
||||
{
|
||||
return (DeathState > EOLSDeathState::NotDead);
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::StartDeath()
|
||||
{
|
||||
if (DeathState != EOLSDeathState::NotDead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DeathState = EOLSDeathState::DeathStarted;
|
||||
|
||||
if (AbilitySystemComponent)
|
||||
{
|
||||
// @TODO: Add LyraGameplayTags::Status_Death_Dying.
|
||||
// AbilitySystemComponent->SetLooseGameplayTagCount(LyraGameplayTags::Status_Death_Dying, 1);
|
||||
}
|
||||
|
||||
AActor* owner = GetOwner();
|
||||
check(owner);
|
||||
|
||||
Broadcast_OnDeathStarted(owner);
|
||||
|
||||
owner->ForceNetUpdate();
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::FinishDeath()
|
||||
{
|
||||
if (DeathState != EOLSDeathState::DeathStarted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DeathState = EOLSDeathState::DeathFinished;
|
||||
|
||||
if (AbilitySystemComponent)
|
||||
{
|
||||
// @TODO: Add LyraGameplayTags::Status_Death_Dead.
|
||||
// AbilitySystemComponent->SetLooseGameplayTagCount(LyraGameplayTags::Status_Death_Dead, 1);
|
||||
}
|
||||
|
||||
AActor* owner = GetOwner();
|
||||
check(owner);
|
||||
|
||||
Broadcast_OnDeathFinished(owner);
|
||||
|
||||
owner->ForceNetUpdate();
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::DamageSelfDestruct(bool isFellOutOfWorld)
|
||||
{
|
||||
if ((DeathState == EOLSDeathState::NotDead) && AbilitySystemComponent)
|
||||
{
|
||||
const TSubclassOf<UGameplayEffect> damageGE = UOLSAssetManager::GetSubclass(UOLSGameDataAsset::Get().DamageGameplayEffect_SetByCaller);
|
||||
if (!damageGE)
|
||||
{
|
||||
// @TODO: replace this with our custom log.
|
||||
// UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: DamageSelfDestruct failed for owner [%s]. Unable to find gameplay effect [%s]."), *GetNameSafe(GetOwner()), *ULyraGameData::Get().DamageGameplayEffect_SetByCaller.GetAssetName());
|
||||
return;
|
||||
}
|
||||
|
||||
FGameplayEffectSpecHandle SpecHandle = AbilitySystemComponent->MakeOutgoingSpec(damageGE, 1.0f, AbilitySystemComponent->MakeEffectContext());
|
||||
FGameplayEffectSpec* Spec = SpecHandle.Data.Get();
|
||||
|
||||
if (!Spec)
|
||||
{
|
||||
// @TODO: replace this with our custom log.
|
||||
// UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: DamageSelfDestruct failed for owner [%s]. Unable to make outgoing spec for [%s]."), *GetNameSafe(GetOwner()), *GetNameSafe(DamageGE));
|
||||
return;
|
||||
}
|
||||
|
||||
Spec->AddDynamicAssetTag(TAG_Gameplay_DamageSelfDestruct);
|
||||
|
||||
if (isFellOutOfWorld)
|
||||
{
|
||||
Spec->AddDynamicAssetTag(TAG_Gameplay_FellOutOfWorld);
|
||||
}
|
||||
|
||||
const float DamageAmount = GetMaxHealth();
|
||||
|
||||
|
||||
// @TODO: Add LyraGameplayTags::SetByCaller_Damage.
|
||||
// Spec->SetSetByCallerMagnitude(LyraGameplayTags::SetByCaller_Damage, DamageAmount);
|
||||
AbilitySystemComponent->ApplyGameplayEffectSpecToSelf(*Spec);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::Broadcast_OnHealthChanged(UOLSHealthComponent* healthComponent, float oldValue,
|
||||
float newValue, AActor* instigator) const
|
||||
{
|
||||
if (OnHealthChangedDynamicDelegate.IsBound())
|
||||
{
|
||||
OnHealthChangedDynamicDelegate.Broadcast(healthComponent, oldValue, newValue, instigator);
|
||||
}
|
||||
|
||||
if (OnHealthChangedNativeDelegate.IsBound())
|
||||
{
|
||||
OnHealthChangedNativeDelegate.Broadcast(healthComponent, oldValue, newValue, instigator);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::Broadcast_OnMaxHealthChanged(UOLSHealthComponent* healthComponent, float oldValue,
|
||||
float newValue, AActor* instigator) const
|
||||
{
|
||||
if (OnMaxHealthChangedDynamicDelegate.IsBound())
|
||||
{
|
||||
OnMaxHealthChangedDynamicDelegate.Broadcast(healthComponent, oldValue, newValue, instigator);
|
||||
}
|
||||
|
||||
if (OnMaxHealthChangedNativeDelegate.IsBound())
|
||||
{
|
||||
OnMaxHealthChangedNativeDelegate.Broadcast(healthComponent, oldValue, newValue, instigator);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::Broadcast_OnDeathStarted(AActor* owningActor) const
|
||||
{
|
||||
if (OnDeathStartedDynamicDelegate.IsBound())
|
||||
{
|
||||
OnDeathStartedDynamicDelegate.Broadcast(owningActor);
|
||||
}
|
||||
|
||||
if (OnDeathStartNativeDelegate.IsBound())
|
||||
{
|
||||
OnDeathStartNativeDelegate.Broadcast(owningActor);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::Broadcast_OnDeathFinished(AActor* owningActor) const
|
||||
{
|
||||
if (OnDeathFinishedDynamicDelegate.IsBound())
|
||||
{
|
||||
OnDeathFinishedDynamicDelegate.Broadcast(owningActor);
|
||||
}
|
||||
|
||||
if (OnDeathFinishedNativeDelegate.IsBound())
|
||||
{
|
||||
OnDeathFinishedNativeDelegate.Broadcast(owningActor);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::OnUnregister()
|
||||
{
|
||||
UninitializeFromAbilitySystem();
|
||||
|
||||
Super::OnUnregister();
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::ClearGameplayTags()
|
||||
{
|
||||
if (HealthSet)
|
||||
{
|
||||
HealthSet->OnHealthChanged.RemoveAll(this);
|
||||
HealthSet->OnMaxHealthChanged.RemoveAll(this);
|
||||
HealthSet->OnOutOfHealth.RemoveAll(this);
|
||||
}
|
||||
|
||||
HealthSet = nullptr;
|
||||
AbilitySystemComponent = nullptr;
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::HandleHealthChanged(AActor* damageInstigator, AActor* damageCauser,
|
||||
const FGameplayEffectSpec* damageEffectSpec, float damageMagnitude,
|
||||
float oldValue, float newValue)
|
||||
{
|
||||
Broadcast_OnHealthChanged(this, oldValue, newValue, damageInstigator);
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::HandleMaxHealthChanged(AActor* damageInstigator, AActor* damageCauser,
|
||||
const FGameplayEffectSpec* damageEffectSpec, float damageMagnitude,
|
||||
float oldValue, float newValue)
|
||||
{
|
||||
Broadcast_OnMaxHealthChanged(this, oldValue, newValue, damageInstigator);
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::HandleOutOfHealth(AActor* damageInstigator, AActor* damageCauser,
|
||||
const FGameplayEffectSpec* damageEffectSpec, float damageMagnitude,
|
||||
float oldValue, float newValue)
|
||||
{
|
||||
#if WITH_SERVER_CODE
|
||||
if (AbilitySystemComponent && damageEffectSpec)
|
||||
{
|
||||
// Send the "GameplayEvent.Death" gameplay event through the owner's ability system. This can be used to trigger a death gameplay ability.
|
||||
{
|
||||
FGameplayEventData payload;
|
||||
// @TODO: Add LyraGameplayTags::GameplayEvent_Death.
|
||||
// payload.EventTag = LyraGameplayTags::GameplayEvent_Death;
|
||||
payload.Instigator = damageInstigator;
|
||||
payload.Target = AbilitySystemComponent->GetAvatarActor();
|
||||
payload.OptionalObject = damageEffectSpec->Def;
|
||||
payload.ContextHandle = damageEffectSpec->GetEffectContext();
|
||||
payload.InstigatorTags = *damageEffectSpec->CapturedSourceTags.GetAggregatedTags();
|
||||
payload.TargetTags = *damageEffectSpec->CapturedTargetTags.GetAggregatedTags();
|
||||
payload.EventMagnitude = damageMagnitude;
|
||||
|
||||
FScopedPredictionWindow newScopedWindow(AbilitySystemComponent, true);
|
||||
AbilitySystemComponent->HandleGameplayEvent(payload.EventTag, &payload);
|
||||
}
|
||||
|
||||
// Send a standardized verb message that other systems can observe
|
||||
{
|
||||
FOLSVerbMessage message;
|
||||
// @TODO: Add LyraGameplayTags::TAG_Lyra_Elimination_Message.
|
||||
// message.Verb = TAG_Lyra_Elimination_Message;
|
||||
message.Instigator = damageInstigator;
|
||||
message.InstigatorTags = *damageEffectSpec->CapturedSourceTags.GetAggregatedTags();
|
||||
message.Target = UOLSVerbMessageHelpers::GetPlayerStateFromObject(AbilitySystemComponent->GetAvatarActor());
|
||||
message.TargetTags = *damageEffectSpec->CapturedTargetTags.GetAggregatedTags();
|
||||
//@TODO: Fill out context tags, and any non-ability-system source/instigator tags
|
||||
//@TODO: Determine if it's an opposing team kill, self-own, team kill, etc...
|
||||
|
||||
UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(GetWorld());
|
||||
MessageSystem.BroadcastMessage(message.Verb, message);
|
||||
}
|
||||
|
||||
//@TODO: assist messages (could compute from damage dealt elsewhere)?
|
||||
}
|
||||
|
||||
#endif // #if WITH_SERVER_CODE
|
||||
}
|
||||
|
||||
void UOLSHealthComponent::OnRep_DeathState(EOLSDeathState oldDeathState)
|
||||
{
|
||||
const EOLSDeathState newDeathState = DeathState;
|
||||
|
||||
// Revert the death state for now since we rely on StartDeath and FinishDeath to change it.
|
||||
DeathState = oldDeathState;
|
||||
|
||||
if (oldDeathState > newDeathState)
|
||||
{
|
||||
// The server is trying to set us back but we've already predicted past the server state.
|
||||
// @TODO replace this with our custom log.
|
||||
// UE_LOG(LogLyra, Warning,
|
||||
// TEXT("LyraHealthComponent: Predicted past server death state [%d] -> [%d] for owner [%s]."),
|
||||
// (uint8)OldDeathState, (uint8)newDeathStateameSafe(GetOwner()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldDeathState == EOLSDeathState::NotDead)
|
||||
{
|
||||
if (newDeathState == EOLSDeathState::DeathStarted)
|
||||
{
|
||||
StartDeath();
|
||||
}
|
||||
else if (newDeathState == EOLSDeathState::DeathFinished)
|
||||
{
|
||||
StartDeath();
|
||||
FinishDeath();
|
||||
}
|
||||
else
|
||||
{
|
||||
// @TODO replace this with our custom log.
|
||||
// UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: Invalid death transition [%d] -> [%d] for owner [%s]."),
|
||||
// (uint8)OldDeathState, (uint8)newDeathStateameSafe(GetOwner()));
|
||||
}
|
||||
}
|
||||
else if (oldDeathState == EOLSDeathState::DeathStarted)
|
||||
{
|
||||
if (newDeathState == EOLSDeathState::DeathFinished)
|
||||
{
|
||||
FinishDeath();
|
||||
}
|
||||
else
|
||||
{
|
||||
// @TODO replace this with our custom log.
|
||||
// UE_LOG(LogLyra, Error, TEXT("LyraHealthComponent: Invalid death transition [%d] -> [%d] for owner [%s]."),
|
||||
// (uint8)OldDeathState, (uint8)newDeathStateameSafe(GetOwner()));
|
||||
}
|
||||
}
|
||||
|
||||
ensureMsgf((DeathState == newDeathState),
|
||||
TEXT("OLSHealthComponent: Death transition failed [%d] -> [%d] for owner [%s]."),
|
||||
static_cast<uint8>(oldDeathState), static_cast<uint8>(newDeathState), *GetNameSafe(GetOwner()));
|
||||
}
|
15
Source/ols/Private/DataAssets/OLSGameDataAsset.cpp
Normal file
15
Source/ols/Private/DataAssets/OLSGameDataAsset.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
|
||||
|
||||
|
||||
#include "DataAssets/OLSGameDataAsset.h"
|
||||
|
||||
#include "Systems/OLSAssetManager.h"
|
||||
|
||||
UOLSGameDataAsset::UOLSGameDataAsset()
|
||||
{
|
||||
}
|
||||
|
||||
const UOLSGameDataAsset& UOLSGameDataAsset::Get()
|
||||
{
|
||||
return UOLSAssetManager::Get().GetGameData();
|
||||
}
|
@ -7,6 +7,9 @@
|
||||
#include "GameFramework/PlayerState.h"
|
||||
#include "Messages/OLSVerbMessage.h"
|
||||
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSVerbMessageHelpers)
|
||||
|
||||
APlayerState* UOLSVerbMessageHelpers::GetPlayerStateFromObject(UObject* object)
|
||||
{
|
||||
if (APlayerController* playerController = Cast<APlayerController>(object))
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
|
||||
#include "Systems/OLSAssetManager.h"
|
||||
|
||||
#include "DataAssets/OLSGameDataAsset.h"
|
||||
#include "Misc/App.h"
|
||||
#include "Stats/StatsMisc.h"
|
||||
#include "Misc/ScopedSlowTask.h"
|
||||
@ -49,62 +51,15 @@ UOLSAssetManager& UOLSAssetManager::Get()
|
||||
return *NewObject<UOLSAssetManager>();
|
||||
}
|
||||
|
||||
template <typename AssetType>
|
||||
AssetType* UOLSAssetManager::GetAsset(const TSoftObjectPtr<AssetType>& assetPointer, bool shouldKeepInMemory)
|
||||
{
|
||||
AssetType* loadedAsset = nullptr;
|
||||
|
||||
const FSoftObjectPath& assetPath = assetPointer.ToSoftObjectPath();
|
||||
|
||||
if (assetPath.IsValid())
|
||||
{
|
||||
loadedAsset = assetPointer.Get();
|
||||
if (!loadedAsset)
|
||||
{
|
||||
loadedAsset = Cast<AssetType>(SynchronousLoadAsset(assetPath));
|
||||
ensureAlwaysMsgf(loadedAsset, TEXT("Failed to load asset [%s]"), *assetPointer.ToString());
|
||||
}
|
||||
|
||||
if (loadedAsset && shouldKeepInMemory)
|
||||
{
|
||||
// Added to loaded asset list.
|
||||
Get().AddLoadedAsset(Cast<UObject>(loadedAsset));
|
||||
}
|
||||
}
|
||||
|
||||
return loadedAsset;
|
||||
}
|
||||
|
||||
template <typename AssetType>
|
||||
TSubclassOf<AssetType> UOLSAssetManager::GetSubclass(const TSoftClassPtr<AssetType>& assetPointer, bool shouldKeepInMemory)
|
||||
{
|
||||
TSubclassOf<AssetType> loadedSubclass = nullptr;
|
||||
|
||||
const FSoftObjectPath& assetPath = assetPointer.ToSoftObjectPath();
|
||||
|
||||
if (assetPath.IsValid())
|
||||
{
|
||||
loadedSubclass = assetPointer.Get();
|
||||
if (!loadedSubclass)
|
||||
{
|
||||
loadedSubclass = Cast<UClass>(SynchronousLoadAsset(assetPath));
|
||||
ensureAlwaysMsgf(loadedSubclass, TEXT("Failed to load asset class [%s]"), *assetPointer.ToString());
|
||||
}
|
||||
|
||||
if (loadedSubclass && shouldKeepInMemory)
|
||||
{
|
||||
// Added to loaded asset list.
|
||||
Get().AddLoadedAsset(Cast<UObject>(loadedSubclass));
|
||||
}
|
||||
}
|
||||
|
||||
return loadedSubclass;
|
||||
}
|
||||
|
||||
void UOLSAssetManager::DumpLoadedAssets()
|
||||
{
|
||||
}
|
||||
|
||||
const UOLSGameDataAsset& UOLSAssetManager::GetGameData()
|
||||
{
|
||||
return GetOrLoadTypedGameData<UOLSGameDataAsset>(OLSGameDataPath);
|
||||
}
|
||||
|
||||
const UOLSPawnDataAsset* UOLSAssetManager::GetDefaultPawnData() const
|
||||
{
|
||||
return GetAsset(DefaultPawnData);
|
||||
|
138
Source/ols/Public/Components/OLSHealthComponent.h
Normal file
138
Source/ols/Public/Components/OLSHealthComponent.h
Normal file
@ -0,0 +1,138 @@
|
||||
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/GameFrameworkComponent.h"
|
||||
#include "OLSHealthComponent.generated.h"
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOLSDeathEventDynamicDelegate, class AActor*, owningActor);
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FOLSDeathEventNativeDelegate, class AActor* /* owningActor */)
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FOLSAttributeChangedDynamicDelegate, class UOLSHealthComponent*, healthComponent, float, oldValue, float, newValue, class AActor*, instigator);
|
||||
DECLARE_MULTICAST_DELEGATE_FourParams(FOLSAttributeChangedNativeDelegate, class UOLSHealthComponent* /* healthComponent */, float /* oldValue */, float /* newValue */, class AActor* /* instigator */)
|
||||
|
||||
/**
|
||||
* EOLSDeathState
|
||||
*
|
||||
* Defines current state of death.
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EOLSDeathState : uint8
|
||||
{
|
||||
NotDead = 0,
|
||||
DeathStarted,
|
||||
DeathFinished
|
||||
};
|
||||
|
||||
/**
|
||||
* UOLSHealthComponent
|
||||
*
|
||||
* An actor component used to handle anything related to health.
|
||||
*/
|
||||
UCLASS(Blueprintable, Meta=(BlueprintSpawnableComponent))
|
||||
class OLS_API UOLSHealthComponent : public UGameFrameworkComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UOLSHealthComponent(const FObjectInitializer& objectInitializer);
|
||||
|
||||
//~ Begin UActorComponent interface.
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
//~ End UActorComponent interface.
|
||||
|
||||
// Returns the health component if one exists on the specified actor.
|
||||
UFUNCTION(BlueprintPure, Category = "OLS|Health")
|
||||
static class UOLSHealthComponent* FindHealthComponent(const AActor* actor);
|
||||
|
||||
// Initialize the component using an ability system component.
|
||||
UFUNCTION(BlueprintCallable, Category = "OLS|Health")
|
||||
void InitializeWithAbilitySystem(class UOLSAbilitySystemComponent* asc);
|
||||
|
||||
// Uninitialize the component, clearing any references to the ability system.
|
||||
UFUNCTION(BlueprintCallable, Category = "OLS|Health")
|
||||
void UninitializeFromAbilitySystem();
|
||||
|
||||
// Returns the current health value.
|
||||
UFUNCTION(BlueprintCallable, Category = "OLS|Health")
|
||||
float GetHealth() const;
|
||||
|
||||
// Returns the current maximum health value.
|
||||
UFUNCTION(BlueprintCallable, Category = "OLS|Health")
|
||||
float GetMaxHealth() const;
|
||||
|
||||
// Returns the current health in the range [0.0, 1.0].
|
||||
UFUNCTION(BlueprintCallable, Category = "OLS|Health")
|
||||
float GetHealthNormalized() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "OLS|Health")
|
||||
EOLSDeathState GetDeathState() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure = false, Category = "OLS|Health", Meta = (ExpandBoolAsExecs = "ReturnValue"))
|
||||
bool IsDeadOrDying() const;
|
||||
|
||||
// Begins the death sequence for the owner.
|
||||
virtual void StartDeath();
|
||||
|
||||
// Ends the death sequence for the owner.
|
||||
virtual void FinishDeath();
|
||||
|
||||
// Applies enough damage to kill the owner.
|
||||
virtual void DamageSelfDestruct(bool isFellOutOfWorld = false);
|
||||
|
||||
public:
|
||||
|
||||
// Delegate fired when the health value has changed. This is called on the client but the instigator may not be valid
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOLSAttributeChangedDynamicDelegate OnHealthChangedDynamicDelegate;
|
||||
FOLSAttributeChangedNativeDelegate OnHealthChangedNativeDelegate;
|
||||
|
||||
// Delegate fired when the max health value has changed. This is called on the client but the instigator may not be valid
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOLSAttributeChangedDynamicDelegate OnMaxHealthChangedDynamicDelegate;
|
||||
FOLSAttributeChangedNativeDelegate OnMaxHealthChangedNativeDelegate;
|
||||
|
||||
// Delegate fired when the death sequence has started.
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOLSDeathEventDynamicDelegate OnDeathStartedDynamicDelegate;
|
||||
FOLSDeathEventNativeDelegate OnDeathStartNativeDelegate;
|
||||
|
||||
// Delegate fired when the death sequence has finished.
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOLSDeathEventDynamicDelegate OnDeathFinishedDynamicDelegate;
|
||||
FOLSDeathEventNativeDelegate OnDeathFinishedNativeDelegate;
|
||||
|
||||
void Broadcast_OnHealthChanged(UOLSHealthComponent* healthComponent, float oldValue, float newValue, AActor* instigator) const;
|
||||
void Broadcast_OnMaxHealthChanged(UOLSHealthComponent* healthComponent, float oldValue, float newValue, AActor* instigator) const;
|
||||
void Broadcast_OnDeathStarted(class AActor* owningActor) const;
|
||||
void Broadcast_OnDeathFinished(class AActor* owningActor) const;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void OnUnregister() override;
|
||||
|
||||
void ClearGameplayTags();
|
||||
|
||||
virtual void HandleHealthChanged(AActor* damageInstigator, AActor* damageCauser, const struct FGameplayEffectSpec* damageEffectSpec, float damageMagnitude, float oldValue, float newValue);
|
||||
virtual void HandleMaxHealthChanged(AActor* damageInstigator, AActor* damageCauser, const struct FGameplayEffectSpec* damageEffectSpec, float damageMagnitude, float dldValue, float newValue);
|
||||
virtual void HandleOutOfHealth(AActor* damageInstigator, AActor* damageCauser, const struct FGameplayEffectSpec* damageEffectSpec, float damageMagnitude, float oldValue, float newValue);
|
||||
|
||||
UFUNCTION()
|
||||
virtual void OnRep_DeathState(EOLSDeathState oldDeathState = EOLSDeathState::NotDead);
|
||||
|
||||
protected:
|
||||
|
||||
// Ability system used by this component.
|
||||
UPROPERTY()
|
||||
TObjectPtr<class UOLSAbilitySystemComponent> AbilitySystemComponent = nullptr;
|
||||
|
||||
// Health set used by this component.
|
||||
UPROPERTY()
|
||||
TObjectPtr<const class UOLSHealthAttributeSet> HealthSet = nullptr;
|
||||
|
||||
// Replicated state used to handle dying.
|
||||
UPROPERTY(ReplicatedUsing = OnRep_DeathState)
|
||||
EOLSDeathState DeathState = EOLSDeathState::NotDead;
|
||||
};
|
39
Source/ols/Public/DataAssets/OLSGameDataAsset.h
Normal file
39
Source/ols/Public/DataAssets/OLSGameDataAsset.h
Normal file
@ -0,0 +1,39 @@
|
||||
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "OLSPrimaryDataAsset.h"
|
||||
#include "OLSGameDataAsset.generated.h"
|
||||
|
||||
/**
|
||||
* UOLSGameDataAsset
|
||||
*
|
||||
* Non-mutable data asset that contains global game data.
|
||||
*/
|
||||
UCLASS(BlueprintType, Const, Meta = (DisplayName = "OLS Game Data", ShortTooltip = "Data asset containing global game data."))
|
||||
class OLS_API UOLSGameDataAsset : public UOLSPrimaryDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UOLSGameDataAsset();
|
||||
|
||||
// Returns the loaded game data.
|
||||
static const UOLSGameDataAsset& Get();
|
||||
|
||||
public:
|
||||
|
||||
// Gameplay effect used to apply damage. Uses SetByCaller for the damage magnitude.
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Default Gameplay Effects", meta = (DisplayName = "Damage Gameplay Effect (SetByCaller)"))
|
||||
TSoftClassPtr<class UGameplayEffect> DamageGameplayEffect_SetByCaller;
|
||||
|
||||
// Gameplay effect used to apply healing. Uses SetByCaller for the healing magnitude.
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Default Gameplay Effects", meta = (DisplayName = "Heal Gameplay Effect (SetByCaller)"))
|
||||
TSoftClassPtr<class UGameplayEffect> HealGameplayEffect_SetByCaller;
|
||||
|
||||
// Gameplay effect used to add and remove dynamic tags.
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Default Gameplay Effects")
|
||||
TSoftClassPtr<class UGameplayEffect> DynamicTagGameplayEffect;
|
||||
};
|
@ -16,15 +16,15 @@ class OLS_API UOLSVerbMessageHelpers : public UBlueprintFunctionLibrary
|
||||
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Lyra")
|
||||
UFUNCTION(BlueprintCallable, Category = "OLS")
|
||||
static class APlayerState* GetPlayerStateFromObject(UObject* object);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Lyra")
|
||||
UFUNCTION(BlueprintCallable, Category = "OLS")
|
||||
static class APlayerController* GetPlayerControllerFromObject(UObject* object);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Lyra")
|
||||
UFUNCTION(BlueprintCallable, Category = "OLS")
|
||||
static struct FGameplayCueParameters VerbMessageToCueParameters(const struct FOLSVerbMessage& message);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Lyra")
|
||||
UFUNCTION(BlueprintCallable, Category = "OLS")
|
||||
static struct FOLSVerbMessage CueParametersToVerbMessage(const FGameplayCueParameters& params);
|
||||
};
|
||||
|
@ -33,17 +33,62 @@ public:
|
||||
|
||||
// Returns the asset referenced by a TSoftObjectPtr. This will synchronously load the asset if it's not already loaded.
|
||||
template<typename AssetType>
|
||||
static AssetType* GetAsset(const TSoftObjectPtr<AssetType>& assetPointer, bool shouldKeepInMemory = true);
|
||||
static AssetType* GetAsset(const TSoftObjectPtr<AssetType>& assetPointer, bool shouldKeepInMemory = true)
|
||||
{
|
||||
AssetType* loadedAsset = nullptr;
|
||||
|
||||
const FSoftObjectPath& assetPath = assetPointer.ToSoftObjectPath();
|
||||
|
||||
if (assetPath.IsValid())
|
||||
{
|
||||
loadedAsset = assetPointer.Get();
|
||||
if (!loadedAsset)
|
||||
{
|
||||
loadedAsset = Cast<AssetType>(SynchronousLoadAsset(assetPath));
|
||||
ensureAlwaysMsgf(loadedAsset, TEXT("Failed to load asset [%s]"), *assetPointer.ToString());
|
||||
}
|
||||
|
||||
if (loadedAsset && shouldKeepInMemory)
|
||||
{
|
||||
// Added to loaded asset list.
|
||||
Get().AddLoadedAsset(Cast<UObject>(loadedAsset));
|
||||
}
|
||||
}
|
||||
|
||||
return loadedAsset;
|
||||
}
|
||||
|
||||
// Returns the subclass referenced by a TSoftClassPtr. This will synchronously load the asset if it's not already loaded.
|
||||
template<typename AssetType>
|
||||
static TSubclassOf<AssetType> GetSubclass(const TSoftClassPtr<AssetType>& assetPointer, bool shouldKeepInMemory = true);
|
||||
|
||||
static TSubclassOf<AssetType> GetSubclass(const TSoftClassPtr<AssetType>& assetPointer, bool shouldKeepInMemory = true)
|
||||
{
|
||||
TSubclassOf<AssetType> loadedSubclass = nullptr;
|
||||
|
||||
const FSoftObjectPath& assetPath = assetPointer.ToSoftObjectPath();
|
||||
|
||||
if (assetPath.IsValid())
|
||||
{
|
||||
loadedSubclass = assetPointer.Get();
|
||||
if (!loadedSubclass)
|
||||
{
|
||||
loadedSubclass = Cast<UClass>(SynchronousLoadAsset(assetPath));
|
||||
ensureAlwaysMsgf(loadedSubclass, TEXT("Failed to load asset class [%s]"), *assetPointer.ToString());
|
||||
}
|
||||
|
||||
if (loadedSubclass && shouldKeepInMemory)
|
||||
{
|
||||
// Added to loaded asset list.
|
||||
Get().AddLoadedAsset(Cast<UObject>(loadedSubclass));
|
||||
}
|
||||
}
|
||||
|
||||
return loadedSubclass;
|
||||
}
|
||||
|
||||
// Logs all assets currently loaded and tracked by the asset manager.
|
||||
static void DumpLoadedAssets();
|
||||
|
||||
// @Todo implement this function.
|
||||
// const class UOLSPawnPrimaryDataAsset& GetGameData();
|
||||
const class UOLSGameDataAsset& GetGameData();
|
||||
const class UOLSPawnDataAsset* GetDefaultPawnData() const;
|
||||
|
||||
protected:
|
||||
@ -82,10 +127,9 @@ private:
|
||||
|
||||
protected:
|
||||
|
||||
// @Todo implement this.
|
||||
// Global game data asset to use.
|
||||
// UPROPERTY(Config)
|
||||
// TSoftObjectPtr<ULyraGameData> LyraGameDataPath;
|
||||
UPROPERTY(Config)
|
||||
TSoftObjectPtr<class UOLSGameDataAsset> OLSGameDataPath;
|
||||
|
||||
// Loaded version of the game data
|
||||
UPROPERTY(Transient)
|
||||
|
Loading…
Reference in New Issue
Block a user