Compare commits
3 Commits
6c053954c8
...
2c4a71b343
Author | SHA1 | Date | |
---|---|---|---|
2c4a71b343 | |||
51306c57a9 | |||
e0545d6323 |
@ -0,0 +1,28 @@
|
||||
// © 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 "AbilitySystem/Abilities/OLSAbilityCost.h"
|
||||
|
||||
UOLSAbilityCost::UOLSAbilityCost()
|
||||
{
|
||||
}
|
||||
|
||||
bool UOLSAbilityCost::CheckCost(const UOLSGameplayAbility* ability,
|
||||
const FGameplayAbilitySpecHandle handle,
|
||||
const FGameplayAbilityActorInfo* actorInfo,
|
||||
FGameplayTagContainer* optionalRelevantTags) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void UOLSAbilityCost::ApplyCost(const UOLSGameplayAbility* ability,
|
||||
const FGameplayAbilitySpecHandle handle,
|
||||
const FGameplayAbilityActorInfo* actorInfo,
|
||||
const FGameplayAbilityActivationInfo activationInfo)
|
||||
{
|
||||
}
|
||||
|
||||
bool UOLSAbilityCost::ShouldOnlyApplyCostOnHit() const
|
||||
{
|
||||
return bShouldOnlyApplyCostOnHit;
|
||||
}
|
@ -8,10 +8,16 @@
|
||||
#include "OLSLog.h"
|
||||
#include "AbilitySystem/OLSBatchGameplayAbilityInterface.h"
|
||||
#include "AbilitySystem/OLSGlobaAbilitySubsystem.h"
|
||||
#include "AbilitySystem/Abilities/OLSGameplayAbility.h"
|
||||
#include "AnimInstances/OLSBaseLayerAnimInstance.h"
|
||||
#include "DataAssets/OLSAbilityTagRelationshipMappingDataAsset.h"
|
||||
#include "DataAssets/OLSGameDataAsset.h"
|
||||
#include "Systems/OLSAssetManager.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSAbilitySystemComponent);
|
||||
|
||||
UE_DEFINE_GAMEPLAY_TAG(TAG_Gameplay_AbilityInputBlocked, "Gameplay.AbilityInputBlocked");
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UOLSAbilitySystemComponent::UOLSAbilitySystemComponent()
|
||||
{
|
||||
@ -292,10 +298,142 @@ void UOLSAbilitySystemComponent::RemoveGameplayCueLocally(
|
||||
void UOLSAbilitySystemComponent::TryActivateAbilitiesOnSpawn()
|
||||
{
|
||||
ABILITYLIST_SCOPE_LOCK();
|
||||
|
||||
for (const FGameplayAbilitySpec& abilitySpec : GetActivatableAbilities())
|
||||
for (const FGameplayAbilitySpec& abilitySpec : ActivatableAbilities.Items)
|
||||
{
|
||||
// if (const UOLS)
|
||||
if (const UOLSGameplayAbility* abilityCDO = Cast<UOLSGameplayAbility>(abilitySpec.Ability))
|
||||
{
|
||||
// @TODO: Implement UOLSGameplayAbility.
|
||||
// abilityCDO->TryActivateAbilityOnSpawn(AbilityActorInfo.Get(), abilitySpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::AbilitySpecInputPressed(FGameplayAbilitySpec& spec)
|
||||
{
|
||||
Super::AbilitySpecInputPressed(spec);
|
||||
|
||||
// We don't support UGameplayAbility::bReplicateInputDirectly.
|
||||
// Use replicated events instead so that the WaitInputPress ability task works.
|
||||
if (spec.IsActive())
|
||||
{
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
const UGameplayAbility* instance = spec.GetPrimaryInstance();
|
||||
FPredictionKey originalPredictionKey = instance
|
||||
? instance->GetCurrentActivationInfo().GetActivationPredictionKey()
|
||||
: spec.ActivationInfo.GetActivationPredictionKey();
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
|
||||
// Invoke the InputPressed event. This is not replicated here. If someone is listening, they may replicate the InputPressed event to the server.
|
||||
InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, spec.Handle, originalPredictionKey);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::AbilitySpecInputReleased(FGameplayAbilitySpec& spec)
|
||||
{
|
||||
Super::AbilitySpecInputReleased(spec);
|
||||
|
||||
// We don't support UGameplayAbility::bReplicateInputDirectly.
|
||||
// Use replicated events instead so that the WaitInputRelease ability task works.
|
||||
if (spec.IsActive())
|
||||
{
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
const UGameplayAbility* instance = spec.GetPrimaryInstance();
|
||||
FPredictionKey originalPredictionKey = instance
|
||||
? instance->GetCurrentActivationInfo().GetActivationPredictionKey()
|
||||
: spec.ActivationInfo.GetActivationPredictionKey();
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
|
||||
// Invoke the InputReleased event. This is not replicated here. If someone is listening, they may replicate the InputReleased event to the server.
|
||||
InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputReleased, spec.Handle, originalPredictionKey);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::NotifyAbilityActivated(const FGameplayAbilitySpecHandle handle,
|
||||
UGameplayAbility* ability)
|
||||
{
|
||||
Super::NotifyAbilityActivated(handle, ability);
|
||||
|
||||
if (UOLSGameplayAbility* olsAbility = Cast<UOLSGameplayAbility>(ability))
|
||||
{
|
||||
// @TODO: Implement UOLSGameplayAbility.
|
||||
// AddAbilityToActivationGroup(olsAbility->GetActivationGroup(), ability);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::NotifyAbilityFailed(const FGameplayAbilitySpecHandle handle,
|
||||
UGameplayAbility* ability,
|
||||
const FGameplayTagContainer& failureReason)
|
||||
{
|
||||
Super::NotifyAbilityFailed(handle, ability, failureReason);
|
||||
|
||||
if (APawn* avatar = Cast<APawn>(GetAvatarActor()))
|
||||
{
|
||||
if (!avatar->IsLocallyControlled() && ability->IsSupportedForNetworking())
|
||||
{
|
||||
ClientNotifyAbilityFailed(ability, failureReason);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HandleAbilityFailed(ability, failureReason);
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::NotifyAbilityEnded(FGameplayAbilitySpecHandle handle, UGameplayAbility* Ability,
|
||||
bool wasCancelled)
|
||||
{
|
||||
Super::NotifyAbilityEnded(handle, Ability, wasCancelled);
|
||||
|
||||
if (UOLSGameplayAbility* olsAbility = Cast<UOLSGameplayAbility>(Ability))
|
||||
{
|
||||
// @TODO: Implement UOLSGameplayAbility.
|
||||
// RemoveAbilityFromActivationGroup(olsAbility->GetActivationGroup(), olsAbility);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& abilityTags,
|
||||
UGameplayAbility* requestingAbility,
|
||||
bool shouldEnableBlockTags,
|
||||
const FGameplayTagContainer& blockTags,
|
||||
bool shouldExecuteCancelTags,
|
||||
const FGameplayTagContainer& cancelTags)
|
||||
{
|
||||
FGameplayTagContainer modifiedBlockTags = blockTags;
|
||||
FGameplayTagContainer modifiedCancelTags = cancelTags;
|
||||
|
||||
if (TagRelationshipMapping)
|
||||
{
|
||||
// Use the mapping to expand the ability tags into block and cancel tag
|
||||
TagRelationshipMapping->GetAbilityTagsToBlockAndCancel(abilityTags, &modifiedBlockTags, &modifiedCancelTags);
|
||||
}
|
||||
|
||||
Super::ApplyAbilityBlockAndCancelTags(abilityTags, requestingAbility, shouldEnableBlockTags, modifiedBlockTags,
|
||||
shouldExecuteCancelTags, modifiedCancelTags);
|
||||
|
||||
//@TODO: Apply any special logic like blocking input or movement
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& abilityTags,
|
||||
UGameplayAbility* requestingAbility,
|
||||
bool canBeCanceled)
|
||||
{
|
||||
Super::HandleChangeAbilityCanBeCanceled(abilityTags, requestingAbility, canBeCanceled);
|
||||
|
||||
//@TODO: Apply any special logic like blocking input or movement
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::ClientNotifyAbilityFailed_Implementation(const UGameplayAbility* ability,
|
||||
const FGameplayTagContainer& failureReason)
|
||||
{
|
||||
HandleAbilityFailed(ability, failureReason);
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::HandleAbilityFailed(const UGameplayAbility* ability,
|
||||
const FGameplayTagContainer& failureReason)
|
||||
{
|
||||
if (const UOLSGameplayAbility* olsAbility = Cast<const UOLSGameplayAbility>(ability))
|
||||
{
|
||||
// @TODO: Implement UOLSGameplayAbility.
|
||||
// olsAbility->OnAbilityFailedToActivate(failureReason);
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,4 +465,271 @@ void UOLSAbilitySystemComponent::SetReplicatedMontageInfo(
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::CancelAbilitiesByFunc(TShouldCancelAbilityFunc shouldCancelFunc,
|
||||
bool shouldReplicateCancelAbility)
|
||||
{
|
||||
ABILITYLIST_SCOPE_LOCK();
|
||||
for (const FGameplayAbilitySpec& abilitySpec : ActivatableAbilities.Items)
|
||||
{
|
||||
if (!abilitySpec.IsActive())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UOLSGameplayAbility* abilityCDO = Cast<UOLSGameplayAbility>(abilitySpec.Ability);
|
||||
if (!abilityCDO)
|
||||
{
|
||||
OLS_LOG(LogOLSAbilitySystemComponent, Error,
|
||||
TEXT("CancelAbilitiesByFunc: Non-LyraGameplayAbility %s was Granted to ASC. Skipping."),
|
||||
GET_UOBJECT_NAME(abilitySpec.Ability));
|
||||
continue;
|
||||
}
|
||||
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
ensureMsgf(abilitySpec.Ability->GetInstancingPolicy() != EGameplayAbilityInstancingPolicy::NonInstanced, TEXT("CancelAbilitiesByFunc: All Abilities should be Instanced (NonInstanced is being deprecated due to usability issues)."));
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
|
||||
// Cancel all the spawned instances.
|
||||
TArray<UGameplayAbility*> Instances = abilitySpec.GetAbilityInstances();
|
||||
for (UGameplayAbility* AbilityInstance : Instances)
|
||||
{
|
||||
UOLSGameplayAbility* abilityInstance = CastChecked<UOLSGameplayAbility>(AbilityInstance);
|
||||
|
||||
if (shouldCancelFunc(abilityInstance, abilitySpec.Handle))
|
||||
{
|
||||
if (abilityInstance->CanBeCanceled())
|
||||
{
|
||||
abilityInstance->CancelAbility(abilitySpec.Handle, AbilityActorInfo.Get(), abilityInstance->GetCurrentActivationInfo(), shouldReplicateCancelAbility);
|
||||
}
|
||||
else
|
||||
{
|
||||
OLS_LOG(LogOLSAbilitySystemComponent, Error,
|
||||
TEXT("CancelAbilitiesByFunc: Can't cancel ability [%s] because CanBeCanceled is false."),
|
||||
GET_UOBJECT_NAME(abilityInstance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::CancelInputActivatedAbilities(bool shouldReplicateCancelAbility)
|
||||
{
|
||||
// @TODO: Implement UOLSGameplayAbility
|
||||
// auto shouldCancelFunc = [this](const UOLSGameplayAbility* ability, FGameplayAbilitySpecHandle handle)
|
||||
// {
|
||||
// const ELyraAbilityActivationPolicy ActivationPolicy = ability->GetActivationPolicy();
|
||||
// return ((ActivationPolicy == ELyraAbilityActivationPolicy::OnInputTriggered) || (ActivationPolicy == ELyraAbilityActivationPolicy::WhileInputActive));
|
||||
// };
|
||||
//
|
||||
// CancelAbilitiesByFunc(shouldCancelFunc, shouldReplicateCancelAbility);
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::AbilityInputTagPressed(const FGameplayTag& inputTag)
|
||||
{
|
||||
if (inputTag.IsValid())
|
||||
{
|
||||
for (const FGameplayAbilitySpec& abilitySpec : ActivatableAbilities.Items)
|
||||
{
|
||||
if (abilitySpec.Ability && (abilitySpec.GetDynamicSpecSourceTags().HasTagExact(inputTag)))
|
||||
{
|
||||
InputPressedSpecHandles.AddUnique(abilitySpec.Handle);
|
||||
InputHeldSpecHandles.AddUnique(abilitySpec.Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::AbilityInputTagReleased(const FGameplayTag& inputTag)
|
||||
{
|
||||
if (inputTag.IsValid())
|
||||
{
|
||||
for (const FGameplayAbilitySpec& abilitySpec : ActivatableAbilities.Items)
|
||||
{
|
||||
if (abilitySpec.Ability && (abilitySpec.GetDynamicSpecSourceTags().HasTagExact(inputTag)))
|
||||
{
|
||||
InputReleasedSpecHandles.AddUnique(abilitySpec.Handle);
|
||||
InputHeldSpecHandles.Remove(abilitySpec.Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::ProcessAbilityInput(float deltaTime, bool shouldGamePaused)
|
||||
{
|
||||
if (HasMatchingGameplayTag(TAG_Gameplay_AbilityInputBlocked))
|
||||
{
|
||||
ClearAbilityInput();
|
||||
return;
|
||||
}
|
||||
|
||||
static TArray<FGameplayAbilitySpecHandle> abilitiesToActivate;
|
||||
abilitiesToActivate.Reset();
|
||||
|
||||
//@TODO: See if we can use FScopedServerAbilityRPCBatcher ScopedRPCBatcher in some of these loops
|
||||
|
||||
//
|
||||
// Process all abilities that activate when the input is held.
|
||||
//
|
||||
for (const FGameplayAbilitySpecHandle& specHandle : InputHeldSpecHandles)
|
||||
{
|
||||
if (const FGameplayAbilitySpec* abilitySpec = FindAbilitySpecFromHandle(specHandle))
|
||||
{
|
||||
if (abilitySpec->Ability && !abilitySpec->IsActive())
|
||||
{
|
||||
const UOLSGameplayAbility* abilityCDO = Cast<UOLSGameplayAbility>(abilitySpec->Ability);
|
||||
|
||||
// @TODO: Implement UOLSGameplayAbility.
|
||||
// if (abilityCDO && abilityCDO->GetActivationPolicy() == ELyraAbilityActivationPolicy::WhileInputActive)
|
||||
// {
|
||||
// AbilitiesToActivate.AddUnique(AbilitySpec->Handle);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Process all abilities that had their input pressed this frame.
|
||||
//
|
||||
for (const FGameplayAbilitySpecHandle& specHandle : InputPressedSpecHandles)
|
||||
{
|
||||
if (FGameplayAbilitySpec* abilitySpec = FindAbilitySpecFromHandle(specHandle))
|
||||
{
|
||||
if (abilitySpec->Ability)
|
||||
{
|
||||
abilitySpec->InputPressed = true;
|
||||
|
||||
if (abilitySpec->IsActive())
|
||||
{
|
||||
// Ability is active so pass along the input event.
|
||||
AbilitySpecInputPressed(*abilitySpec);
|
||||
}
|
||||
else
|
||||
{
|
||||
const UOLSGameplayAbility* abilityCDO = Cast<UOLSGameplayAbility>(abilitySpec->Ability);
|
||||
|
||||
// @TODO: Implement UOLSGameplayAbility.
|
||||
// if (abilityCDO && abilityCDO->GetActivationPolicy() == ELyraAbilityActivationPolicy::OnInputTriggered)
|
||||
// {
|
||||
// abilitiesToActivate.AddUnique(abilitySpec->Handle);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Try to activate all the abilities that are from presses and holds.
|
||||
// We do it all at once so that held inputs don't activate the ability
|
||||
// and then also send a input event to the ability because of the press.
|
||||
//
|
||||
for (const FGameplayAbilitySpecHandle& abilitySpecHandle : abilitiesToActivate)
|
||||
{
|
||||
TryActivateAbility(abilitySpecHandle);
|
||||
}
|
||||
|
||||
//
|
||||
// Process all abilities that had their input released this frame.
|
||||
//
|
||||
for (const FGameplayAbilitySpecHandle& specHandle : InputReleasedSpecHandles)
|
||||
{
|
||||
if (FGameplayAbilitySpec* abilitySpec = FindAbilitySpecFromHandle(specHandle))
|
||||
{
|
||||
if (abilitySpec->Ability)
|
||||
{
|
||||
abilitySpec->InputPressed = false;
|
||||
|
||||
if (abilitySpec->IsActive())
|
||||
{
|
||||
// Ability is active so pass along the input event.
|
||||
AbilitySpecInputReleased(*abilitySpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Clear the cached ability handles.
|
||||
//
|
||||
InputPressedSpecHandles.Reset();
|
||||
InputReleasedSpecHandles.Reset();
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::ClearAbilityInput()
|
||||
{
|
||||
InputPressedSpecHandles.Reset();
|
||||
InputReleasedSpecHandles.Reset();
|
||||
InputHeldSpecHandles.Reset();
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::AddDynamicTagGameplayEffect(const FGameplayTag& tag)
|
||||
{
|
||||
const TSubclassOf<UGameplayEffect> dynamicTagGE = UOLSAssetManager::GetSubclass(UOLSGameDataAsset::Get().DynamicTagGameplayEffect);
|
||||
if (!dynamicTagGE)
|
||||
{
|
||||
OLS_LOG(LogOLSAbilitySystemComponent, Warning,
|
||||
TEXT("AddDynamicTagGameplayEffect: Unable to find DynamicTagGameplayEffect [%s]."),
|
||||
*UOLSGameDataAsset::Get().DynamicTagGameplayEffect.GetAssetName());
|
||||
return;
|
||||
}
|
||||
|
||||
const FGameplayEffectSpecHandle specHandle = MakeOutgoingSpec(dynamicTagGE, 1.0f, MakeEffectContext());
|
||||
FGameplayEffectSpec* spec = specHandle.Data.Get();
|
||||
|
||||
if (!spec)
|
||||
{
|
||||
OLS_LOG(LogOLSAbilitySystemComponent, Warning,
|
||||
TEXT("AddDynamicTagGameplayEffect: Unable to make outgoing spec for [%s]."),
|
||||
GET_UOBJECT_NAME(dynamicTagGE));
|
||||
return;
|
||||
}
|
||||
|
||||
spec->DynamicGrantedTags.AddTag(tag);
|
||||
|
||||
ApplyGameplayEffectSpecToSelf(*spec);
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::RemoveDynamicTagGameplayEffect(const FGameplayTag& Tag)
|
||||
{
|
||||
const TSubclassOf<UGameplayEffect> dynamicTagGE = UOLSAssetManager::GetSubclass(UOLSGameDataAsset::Get().DynamicTagGameplayEffect);
|
||||
if (!dynamicTagGE)
|
||||
{
|
||||
OLS_LOG(LogOLSAbilitySystemComponent, Warning,
|
||||
TEXT("RemoveDynamicTagGameplayEffect: Unable to find gameplay effect [%s]."),
|
||||
*UOLSGameDataAsset::Get().DynamicTagGameplayEffect.GetAssetName());
|
||||
return;
|
||||
}
|
||||
|
||||
FGameplayEffectQuery query = FGameplayEffectQuery::MakeQuery_MatchAnyOwningTags(FGameplayTagContainer(Tag));
|
||||
query.EffectDefinition = dynamicTagGE;
|
||||
|
||||
RemoveActiveEffects(query);
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::GetAbilityTargetData(const FGameplayAbilitySpecHandle abilityHandle,
|
||||
FGameplayAbilityActivationInfo activationInfo,
|
||||
FGameplayAbilityTargetDataHandle& outTargetDataHandle)
|
||||
{
|
||||
TSharedPtr<FAbilityReplicatedDataCache> replicatedData = AbilityTargetDataMap.Find(
|
||||
FGameplayAbilitySpecHandleAndPredictionKey(abilityHandle, activationInfo.GetActivationPredictionKey()));
|
||||
if (replicatedData.IsValid())
|
||||
{
|
||||
outTargetDataHandle = replicatedData->TargetData;
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::SetTagRelationshipMapping(UOLSAbilityTagRelationshipMappingDataAsset* newMapping)
|
||||
{
|
||||
TagRelationshipMapping = newMapping;
|
||||
}
|
||||
|
||||
void UOLSAbilitySystemComponent::GetAdditionalActivationTagRequirements(const FGameplayTagContainer& abilityTags,
|
||||
FGameplayTagContainer& outActivationRequired,
|
||||
FGameplayTagContainer& outActivationBlocked) const
|
||||
{
|
||||
if (TagRelationshipMapping)
|
||||
{
|
||||
TagRelationshipMapping->GetRequiredAndBlockedActivationTags(abilityTags, &outActivationRequired, &outActivationBlocked);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "Components/OLSHealthComponent.h"
|
||||
|
||||
#include "OLSLog.h"
|
||||
#include "AbilitySystem/OLSAbilitySystemComponent.h"
|
||||
#include "AbilitySystem/Attributes/OLSHealthAttributeSet.h"
|
||||
#include "DataAssets/OLSGameDataAsset.h"
|
||||
@ -13,6 +14,8 @@
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "Systems/OLSAssetManager.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSHealthComponent);
|
||||
|
||||
UOLSHealthComponent::UOLSHealthComponent(const FObjectInitializer& objectInitializer)
|
||||
: Super(objectInitializer)
|
||||
{
|
||||
@ -46,24 +49,29 @@ void UOLSHealthComponent::InitializeWithAbilitySystem(UOLSAbilitySystemComponent
|
||||
|
||||
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));
|
||||
OLS_LOG(LogOLSHealthComponent, Error,
|
||||
TEXT(
|
||||
"LyraHealthComponent: Health component for owner [%s] has already been initialized with an ability system."
|
||||
), GET_UOBJECT_NAME(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));
|
||||
OLS_LOG(LogOLSHealthComponent, Error,
|
||||
TEXT("Cannot initialize health component for owner [%s] with NULL ability system."),
|
||||
GET_UOBJECT_NAME(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));
|
||||
OLS_LOG(LogOLSHealthComponent, Error,
|
||||
TEXT(
|
||||
"LyraHealthComponent: Cannot initialize health component for owner [%s] with NULL health set on the ability system."
|
||||
), GET_UOBJECT_NAME(owner));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -72,7 +80,7 @@ void UOLSHealthComponent::InitializeWithAbilitySystem(UOLSAbilitySystemComponent
|
||||
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.
|
||||
// TEMP: Reset attributes to default values. Eventually this will be driven by a spreadsheet.
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UOLSHealthAttributeSet::GetHealthAttribute(), HealthSet->GetMaxHealth());
|
||||
|
||||
ClearGameplayTags();
|
||||
@ -182,8 +190,10 @@ void UOLSHealthComponent::DamageSelfDestruct(bool isFellOutOfWorld)
|
||||
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());
|
||||
OLS_LOG(LogOLSHealthComponent, Error,
|
||||
TEXT("DamageSelfDestruct failed for owner [%s]. Unable to find gameplay effect [%s]."),
|
||||
GET_UOBJECT_NAME(GetOwner()),
|
||||
*UOLSGameDataAsset::Get().DamageGameplayEffect_SetByCaller.GetAssetName());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -192,8 +202,9 @@ void UOLSHealthComponent::DamageSelfDestruct(bool isFellOutOfWorld)
|
||||
|
||||
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));
|
||||
OLS_LOG(LogOLSHealthComponent, Error,
|
||||
TEXT("DamageSelfDestruct failed for owner [%s]. Unable to make outgoing spec for [%s]."),
|
||||
GET_UOBJECT_NAME(GetOwner()), GET_UOBJECT_NAME(damageGE));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -356,11 +367,10 @@ void UOLSHealthComponent::OnRep_DeathState(EOLSDeathState 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()));
|
||||
OLS_LOG(LogOLSHealthComponent, Warning,
|
||||
TEXT("Predicted past server death state [%d] -> [%d] for owner [%s]."),
|
||||
(uint8)oldDeathState, (uint8)newDeathState, GET_UOBJECT_NAME(GetOwner()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -377,9 +387,8 @@ void UOLSHealthComponent::OnRep_DeathState(EOLSDeathState oldDeathState)
|
||||
}
|
||||
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()));
|
||||
OLS_LOG(LogOLSHealthComponent, Error, TEXT("Invalid death transition [%d] -> [%d] for owner [%s]."),
|
||||
(uint8)oldDeathState, (uint8)newDeathState, GET_UOBJECT_NAME(GetOwner()));
|
||||
}
|
||||
}
|
||||
else if (oldDeathState == EOLSDeathState::DeathStarted)
|
||||
@ -390,9 +399,8 @@ void UOLSHealthComponent::OnRep_DeathState(EOLSDeathState oldDeathState)
|
||||
}
|
||||
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()));
|
||||
OLS_LOG(LogOLSHealthComponent, Error, TEXT("Invalid death transition [%d] -> [%d] for owner [%s]."),
|
||||
(uint8)oldDeathState, (uint8)newDeathState, GET_UOBJECT_NAME(GetOwner()));
|
||||
}
|
||||
}
|
||||
|
||||
|
517
Source/ols/Private/Components/OLSHeroComponent.cpp
Normal file
517
Source/ols/Private/Components/OLSHeroComponent.cpp
Normal file
@ -0,0 +1,517 @@
|
||||
// © 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/OLSHeroComponent.h"
|
||||
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "OLSLog.h"
|
||||
#include "AbilitySystem/OLSAbilitySystemComponent.h"
|
||||
#include "Characters/OLSCharacter.h"
|
||||
#include "Components/GameFrameworkComponentManager.h"
|
||||
#include "Components/OLSPawnExtensionComponent.h"
|
||||
#include "DataAssets/OLSPawnDataAsset.h"
|
||||
#include "GameFeatures/OLSGameFeatureAction_AddInputContextMapping.h"
|
||||
#include "Player/OLSPlayerController.h"
|
||||
#include "Player/OLSPlayerState.h"
|
||||
|
||||
#if WITH_EDITOR
|
||||
#include "Misc/UObjectToken.h"
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSHeroComponent);
|
||||
|
||||
namespace OLSHero
|
||||
{
|
||||
static const float LookYawRate = 300.0f;
|
||||
static const float LookPitchRate = 165.0f;
|
||||
};
|
||||
|
||||
const FName UOLSHeroComponent::NAME_BindInputsNow("BindInputsNow");
|
||||
const FName UOLSHeroComponent::NAME_ActorFeatureName("Hero");
|
||||
|
||||
UOLSHeroComponent::UOLSHeroComponent(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
||||
{
|
||||
//@TODO: implement UOLSCameraMode.
|
||||
// AbilityCameraMode = nullptr;
|
||||
bIsReadyToBindInputs = false;
|
||||
}
|
||||
|
||||
FName UOLSHeroComponent::GetFeatureName() const
|
||||
{
|
||||
// Don't call Super since it does not fit in this.
|
||||
|
||||
return NAME_ActorFeatureName;
|
||||
}
|
||||
|
||||
bool UOLSHeroComponent::CanChangeInitState(UGameFrameworkComponentManager* manager,
|
||||
FGameplayTag currentState,
|
||||
FGameplayTag desiredState) const
|
||||
{
|
||||
// Don't call Super since it does not fit into this.
|
||||
|
||||
check(manager);
|
||||
|
||||
APawn* pawn = GetPawn<APawn>();
|
||||
|
||||
//@TODO: implement LyraGameplayTags::InitState_Spawned.
|
||||
//@TODO: implement LyraGameplayTags::InitState_DataAvailable.
|
||||
//@TODO: implement LyraGameplayTags::InitState_DataInitialized.
|
||||
//@TODO: implement LyraGameplayTags::InitState_GameplayReady.
|
||||
// if (!currentState.IsValid() && desiredState == LyraGameplayTags::InitState_Spawned)
|
||||
// {
|
||||
// // As long as we have a real pawn, let us transition
|
||||
// if (pawn)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// else if (currentState == LyraGameplayTags::InitState_Spawned && desiredState == LyraGameplayTags::InitState_DataAvailable)
|
||||
// {
|
||||
// // The player state is required.
|
||||
// if (!GetPlayerState<AOLSPlayerState>())
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// // If we're authority or autonomous, we need to wait for a controller with registered ownership of the player state.
|
||||
// if (pawn->GetLocalRole() != ROLE_SimulatedProxy)
|
||||
// {
|
||||
// AController* controller = GetController<AController>();
|
||||
//
|
||||
// const bool hasControllerPairedWithPS = (controller != nullptr) && \
|
||||
// (controller->PlayerState != nullptr) && \
|
||||
// (controller->PlayerState->GetOwner() == controller);
|
||||
//
|
||||
// if (!hasControllerPairedWithPS)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// const bool isLocallyControlled = pawn->IsLocallyControlled();
|
||||
// const bool isBot = pawn->IsBotControlled();
|
||||
//
|
||||
// if (isLocallyControlled && !isBot)
|
||||
// {
|
||||
// AOLSPlayerController* playerController = GetController<AOLSPlayerController>();
|
||||
//
|
||||
// // The input component and local player is required when locally controlled.
|
||||
// if (!pawn->InputComponent || !playerController || !playerController->GetLocalPlayer())
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
// else if (currentState == LyraGameplayTags::InitState_DataAvailable && desiredState == LyraGameplayTags::InitState_DataInitialized)
|
||||
// {
|
||||
// // Wait for player state and extension component
|
||||
// AOLSPlayerState* playerState = GetPlayerState<AOLSPlayerState>();
|
||||
//
|
||||
// return playerState && manager->HasFeatureReachedInitState(pawn, UOLSPawnExtensionComponent::NAME_ActorFeatureName, LyraGameplayTags::InitState_DataInitialized);
|
||||
// }
|
||||
// else if (currentState == LyraGameplayTags::InitState_DataInitialized && desiredState == LyraGameplayTags::InitState_GameplayReady)
|
||||
// {
|
||||
// // TODO add ability initialization checks?
|
||||
// return true;
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::HandleChangeInitState(UGameFrameworkComponentManager* manager,
|
||||
FGameplayTag currentState,
|
||||
FGameplayTag desiredState)
|
||||
{
|
||||
//@TODO: implement LyraGameplayTags::InitState_DataAvailable.
|
||||
//@TODO: implement LyraGameplayTags::InitState_DataInitialized.
|
||||
// if (currentState == LyraGameplayTags::InitState_DataAvailable && desiredState == LyraGameplayTags::InitState_DataInitialized)
|
||||
// {
|
||||
// APawn* pawn = GetPawn<APawn>();
|
||||
// AOLSPlayerState* playerState = GetPlayerState<AOLSPlayerState>();
|
||||
// if (!ensure(pawn && playerState))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// const UOLSPawnDataAsset* pawnData = nullptr;
|
||||
//
|
||||
// if (UOLSPawnExtensionComponent* PawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(pawn))
|
||||
// {
|
||||
// pawnData = PawnExtComp->GetPawnData<UOLSPawnDataAsset>();
|
||||
//
|
||||
// // The player state holds the persistent data for this player (state that persists across deaths and multiple pawns).
|
||||
// // The ability system component and attribute sets live on the player state.
|
||||
// PawnExtComp->InitializeAbilitySystem(playerState->GetOLSAbilitySystemComponent(), playerState);
|
||||
// }
|
||||
//
|
||||
// if (AOLSPlayerController* playerController = GetController<AOLSPlayerController>())
|
||||
// {
|
||||
// if (pawn->InputComponent)
|
||||
// {
|
||||
// InitializePlayerInput(pawn->InputComponent);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// //@TODO: implement UOLSCameraMode.
|
||||
// // Hook up the delegate for all pawns, in case we spectate later
|
||||
// // if (pawnData)
|
||||
// // {
|
||||
// // if (ULyraCameraComponent* CameraComponent = ULyraCameraComponent::FindCameraComponent(pawn))
|
||||
// // {
|
||||
// // CameraComponent->DetermineCameraModeDelegate.BindUObject(this, &ThisClass::DetermineCameraMode);
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::OnActorInitStateChanged(const FActorInitStateChangedParams& params)
|
||||
{
|
||||
// if (params.FeatureName == UOLSPawnExtensionComponent::NAME_ActorFeatureName)
|
||||
// {
|
||||
// //@TODO: implement LyraGameplayTags::InitState_DataAvailable.
|
||||
// if (params.FeatureState == LyraGameplayTags::InitState_DataInitialized)
|
||||
// {
|
||||
// // If the extension component says all other components are initialized, try to progress to next state
|
||||
// CheckDefaultInitialization();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::CheckDefaultInitialization()
|
||||
{
|
||||
//@TODO: implement LyraGameplayTags::InitState_Spawned.
|
||||
//@TODO: implement LyraGameplayTags::InitState_DataAvailable.
|
||||
//@TODO: implement LyraGameplayTags::InitState_DataInitialized.
|
||||
//@TODO: implement LyraGameplayTags::InitState_GameplayReady.
|
||||
// static const TArray<FGameplayTag> stateChain = {
|
||||
// LyraGameplayTags::InitState_Spawned, LyraGameplayTags::InitState_DataAvailable,
|
||||
// LyraGameplayTags::InitState_DataInitialized, LyraGameplayTags::InitState_GameplayReady
|
||||
// };
|
||||
//
|
||||
// // This will try to progress from spawned (which is only set in BeginPlay) through the data initialization stages until it gets to gameplay ready
|
||||
// ContinueInitStateChain(stateChain);
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::ClearAbilityCameraMode(const FGameplayAbilitySpecHandle& owningSpecHandle)
|
||||
{
|
||||
if (AbilityCameraModeOwningSpecHandle == owningSpecHandle)
|
||||
{
|
||||
//@TODO: implement UOLSCameraMode.
|
||||
// AbilityCameraMode = nullptr;
|
||||
AbilityCameraModeOwningSpecHandle = FGameplayAbilitySpecHandle();
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::AddAdditionalInputConfig(const UOLSInputConfigDataAsset* inputConfig)
|
||||
{
|
||||
TArray<uint32> bindHandles;
|
||||
|
||||
const APawn* pawn = GetPawn<APawn>();
|
||||
if (!pawn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const APlayerController* playerController = GetController<APlayerController>();
|
||||
check(playerController);
|
||||
|
||||
const ULocalPlayer* localPlayer = playerController->GetLocalPlayer();
|
||||
check(localPlayer);
|
||||
|
||||
UEnhancedInputLocalPlayerSubsystem* subsystem = localPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
|
||||
check(subsystem);
|
||||
|
||||
if (const UOLSPawnExtensionComponent* pawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(pawn))
|
||||
{
|
||||
//@TODO: Implement UOLSInputComponent.
|
||||
// UOLSInputComponent* inputComponent = pawn->FindComponentByClass<UOLSInputComponent>();
|
||||
// if (ensureMsgf(inputComponent,
|
||||
// TEXT(
|
||||
// "Unexpected Input Component class! The Gameplay Abilities will not be bound to their inputs. Change the input component to ULyraInputComponent or a subclass of it."
|
||||
// )))
|
||||
// {
|
||||
// inputComponent->BindAbilityActions(inputConfig, this, &ThisClass::Input_AbilityInputTagPressed,
|
||||
// &ThisClass::Input_AbilityInputTagReleased, /*out*/ bindHandles);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::RemoveAdditionalInputConfig(const UOLSInputConfigDataAsset* inputConfig)
|
||||
{
|
||||
//@TODO: Implement me!
|
||||
}
|
||||
|
||||
bool UOLSHeroComponent::IsReadyToBindInputs() const
|
||||
{
|
||||
return bIsReadyToBindInputs;
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::OnRegister()
|
||||
{
|
||||
Super::OnRegister();
|
||||
|
||||
if (!GetPawn<APawn>())
|
||||
{
|
||||
OLS_LOG(LogOLSHeroComponent, Error,
|
||||
TEXT(
|
||||
"This component has been added to a blueprint whose base class is not a Pawn. To use this component, it MUST be placed on a Pawn Blueprint."
|
||||
));
|
||||
|
||||
#if WITH_EDITOR
|
||||
if (GIsEditor)
|
||||
{
|
||||
static const FText message = NSLOCTEXT("OLSHeroComponent", "NotOnPawnError",
|
||||
"has been added to a blueprint whose base class is not a Pawn. To use this component, it MUST be placed on a Pawn Blueprint. This will cause a crash if you PIE!");
|
||||
static const FName heroMessageLogName = TEXT("OLSHeroComponent");
|
||||
|
||||
FMessageLog(heroMessageLogName).Error()
|
||||
->AddToken(FUObjectToken::Create(this, FText::FromString(GetNameSafe(this))))
|
||||
->AddToken(FTextToken::Create(message));
|
||||
|
||||
FMessageLog(heroMessageLogName).Open();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// Register with the init state system early, this will only work if this is a game world
|
||||
RegisterInitStateFeature();
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// Listen for when the pawn extension component changes init state
|
||||
BindOnActorInitStateChanged(UOLSPawnExtensionComponent::NAME_ActorFeatureName, FGameplayTag(), false);
|
||||
|
||||
// Notifies that we are done spawning, then try the rest of initialization
|
||||
//@TODO: implement LyraGameplayTags::InitState_Spawned.
|
||||
// ensure(TryToChangeInitState(LyraGameplayTags::InitState_Spawned));
|
||||
CheckDefaultInitialization();
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::EndPlay(const EEndPlayReason::Type endPlayReason)
|
||||
{
|
||||
UnregisterInitStateFeature();
|
||||
|
||||
Super::EndPlay(endPlayReason);
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::InitializePlayerInput(UInputComponent* playerInputComponent)
|
||||
{
|
||||
check(playerInputComponent);
|
||||
|
||||
const APawn* Pawn = GetPawn<APawn>();
|
||||
if (!Pawn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const APlayerController* playerController = GetController<APlayerController>();
|
||||
check(playerController);
|
||||
|
||||
//@TODO implement UOLSLocalPlayer.
|
||||
// const UOLSLocalPlayer* localPlayer = Cast<UOLSLocalPlayer>(playerController->GetLocalPlayer());
|
||||
// check(localPlayer);
|
||||
|
||||
// UEnhancedInputLocalPlayerSubsystem* subsystem = localPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
|
||||
// check(subsystem);
|
||||
//
|
||||
// subsystem->ClearAllMappings();
|
||||
|
||||
// if (const UOLSPawnExtensionComponent* pawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(Pawn))
|
||||
// {
|
||||
// if (const UOLSPawnDataAsset* pawnData = pawnExtComp->GetPawnData<UOLSPawnDataAsset>())
|
||||
// {
|
||||
// //@TODO: Implement UOLSInputConfig
|
||||
// if (const UOLSInputConfig* inputConfig = pawnData->InputConfig)
|
||||
// {
|
||||
// for (const FOLSInputMappingContextAndPriority& mapping : DefaultInputMappings)
|
||||
// {
|
||||
// if (UInputMappingContext* imc = mapping.InputMapping.Get())
|
||||
// {
|
||||
// if (mapping.bShouldRegisterWithSettings)
|
||||
// {
|
||||
// if (UEnhancedInputUserSettings* settings = subsystem->GetUserSettings())
|
||||
// {
|
||||
// settings->RegisterInputMappingContext(imc);
|
||||
// }
|
||||
//
|
||||
// FModifyContextOptions options = {};
|
||||
// options.bIgnoreAllPressedKeysUntilRelease = false;
|
||||
// // Actually add the config to the local player
|
||||
// subsystem->AddMappingContext(imc, mapping.Priority, options);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // The Lyra Input Component has some additional functions to map Gameplay Tags to an Input Action.
|
||||
// // If you want this functionality but still want to change your input component class, make it a subclass
|
||||
// // of the ULyraInputComponent or modify this component accordingly.
|
||||
// //@TODO: Implement UOLSInputComponent.
|
||||
// // UOLSInputComponent* inputComponent = Cast<UOLSInputComponent>(playerInputComponent);
|
||||
// if (ensureMsgf(inputComponent,
|
||||
// TEXT(
|
||||
// "Unexpected Input Component class! The Gameplay Abilities will not be bound to their inputs. Change the input component to ULyraInputComponent or a subclass of it."
|
||||
// )))
|
||||
// {
|
||||
// // Add the key mappings that may have been set by the player
|
||||
// inputComponent->AddInputMappings(inputConfig, subsystem);
|
||||
//
|
||||
// // This is where we actually bind and input action to a gameplay tag, which means that Gameplay Ability Blueprints will
|
||||
// // be triggered directly by these input actions Triggered events.
|
||||
// TArray<uint32> BindHandles;
|
||||
// inputComponent->BindAbilityActions(inputConfig, this, &ThisClass::Input_AbilityInputTagPressed, &ThisClass::Input_AbilityInputTagReleased, /*out*/ BindHandles);
|
||||
//
|
||||
// inputComponent->BindNativeAction(inputConfig, LyraGameplayTags::InputTag_Move, ETriggerEvent::Triggered, this, &ThisClass::Input_Move, /*bLogIfNotFound=*/ false);
|
||||
// inputComponent->BindNativeAction(inputConfig, LyraGameplayTags::InputTag_Look_Mouse, ETriggerEvent::Triggered, this, &ThisClass::Input_LookMouse, /*bLogIfNotFound=*/ false);
|
||||
// inputComponent->BindNativeAction(inputConfig, LyraGameplayTags::InputTag_Look_Stick, ETriggerEvent::Triggered, this, &ThisClass::Input_LookStick, /*bLogIfNotFound=*/ false);
|
||||
// inputComponent->BindNativeAction(inputConfig, LyraGameplayTags::InputTag_Crouch, ETriggerEvent::Triggered, this, &ThisClass::Input_Crouch, /*bLogIfNotFound=*/ false);
|
||||
// inputComponent->BindNativeAction(inputConfig, LyraGameplayTags::InputTag_AutoRun, ETriggerEvent::Triggered, this, &ThisClass::Input_AutoRun, /*bLogIfNotFound=*/ false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (ensure(!bIsReadyToBindInputs))
|
||||
{
|
||||
bIsReadyToBindInputs = true;
|
||||
}
|
||||
|
||||
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast<APlayerController*>(playerController), NAME_BindInputsNow);
|
||||
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast<APawn*>(Pawn), NAME_BindInputsNow);
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::Input_AbilityInputTagPressed(FGameplayTag inputTag)
|
||||
{
|
||||
if (const APawn* pawn = GetPawn<APawn>())
|
||||
{
|
||||
if (const UOLSPawnExtensionComponent* pawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(pawn))
|
||||
{
|
||||
if (UOLSAbilitySystemComponent* asc = pawnExtComp->GetOLSAbilitySystemComponent())
|
||||
{
|
||||
asc->AbilityInputTagPressed(inputTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::Input_AbilityInputTagReleased(FGameplayTag inputTag)
|
||||
{
|
||||
const APawn* pawn = GetPawn<APawn>();
|
||||
if (!pawn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (const UOLSPawnExtensionComponent* pawnExtComp = UOLSPawnExtensionComponent::FindPawnExtensionComponent(pawn))
|
||||
{
|
||||
if (UOLSAbilitySystemComponent* asc = pawnExtComp->GetOLSAbilitySystemComponent())
|
||||
{
|
||||
asc->AbilityInputTagReleased(inputTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::Input_Move(const FInputActionValue& inputActionValue)
|
||||
{
|
||||
APawn* pawn = GetPawn<APawn>();
|
||||
AController* controller = pawn ? pawn->GetController() : nullptr;
|
||||
|
||||
// If the player has attempted to move again then cancel auto running
|
||||
if (AOLSPlayerController* playerController = Cast<AOLSPlayerController>(controller))
|
||||
{
|
||||
//@TODO: Should we have this?
|
||||
// playerController->SetIsAutoRunning(false);
|
||||
}
|
||||
|
||||
if (controller)
|
||||
{
|
||||
const FVector2D value = inputActionValue.Get<FVector2D>();
|
||||
const FRotator movementRotation(0.0f, controller->GetControlRotation().Yaw, 0.0f);
|
||||
|
||||
if (value.X != 0.0f)
|
||||
{
|
||||
const FVector movementDirection = movementRotation.RotateVector(FVector::RightVector);
|
||||
pawn->AddMovementInput(movementDirection, value.X);
|
||||
}
|
||||
|
||||
if (value.Y != 0.0f)
|
||||
{
|
||||
const FVector movementDirection = movementRotation.RotateVector(FVector::ForwardVector);
|
||||
pawn->AddMovementInput(movementDirection, value.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::Input_LookMouse(const FInputActionValue& inputActionValue)
|
||||
{
|
||||
APawn* pawn = GetPawn<APawn>();
|
||||
|
||||
if (!pawn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const FVector2D value = inputActionValue.Get<FVector2D>();
|
||||
|
||||
if (value.X != 0.0f)
|
||||
{
|
||||
pawn->AddControllerYawInput(value.X);
|
||||
}
|
||||
|
||||
if (value.Y != 0.0f)
|
||||
{
|
||||
pawn->AddControllerPitchInput(value.Y);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::Input_LookStick(const FInputActionValue& inputActionValue)
|
||||
{
|
||||
APawn* pawn = GetPawn<APawn>();
|
||||
|
||||
if (!pawn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const FVector2D value = inputActionValue.Get<FVector2D>();
|
||||
|
||||
const UWorld* world = GetWorld();
|
||||
check(world);
|
||||
|
||||
if (value.X != 0.0f)
|
||||
{
|
||||
pawn->AddControllerYawInput(value.X * OLSHero::LookYawRate * world->GetDeltaSeconds());
|
||||
}
|
||||
|
||||
if (value.Y != 0.0f)
|
||||
{
|
||||
pawn->AddControllerPitchInput(value.Y * OLSHero::LookPitchRate * world->GetDeltaSeconds());
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::Input_Crouch(const FInputActionValue& inputActionValue)
|
||||
{
|
||||
if (AOLSCharacter* character = GetPawn<AOLSCharacter>())
|
||||
{
|
||||
character->ToggleCrouch();
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSHeroComponent::Input_AutoRun(const FInputActionValue& inputActionValue)
|
||||
{
|
||||
if (APawn* Pawp = GetPawn<APawn>())
|
||||
{
|
||||
if (AOLSPlayerController* controller = Cast<AOLSPlayerController>(Pawp->GetController()))
|
||||
{
|
||||
//@TODO: Should we have this?
|
||||
// Toggle auto running
|
||||
// controller->SetIsAutoRunning(!controller->GetIsAutoRunning());
|
||||
}
|
||||
}
|
||||
}
|
314
Source/ols/Private/Components/OLSPawnExtensionComponent.cpp
Normal file
314
Source/ols/Private/Components/OLSPawnExtensionComponent.cpp
Normal file
@ -0,0 +1,314 @@
|
||||
// © 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/OLSPawnExtensionComponent.h"
|
||||
|
||||
#include "OLSLog.h"
|
||||
#include "AbilitySystem/OLSAbilitySystemComponent.h"
|
||||
#include "Components/GameFrameworkComponentManager.h"
|
||||
#include "DataAssets/OLSPawnDataAsset.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSPawnExtensionComponent);
|
||||
|
||||
const FName UOLSPawnExtensionComponent::NAME_ActorFeatureName("PawnExtension");
|
||||
|
||||
UOLSPawnExtensionComponent::UOLSPawnExtensionComponent(const FObjectInitializer& objectInitializer)
|
||||
: Super(objectInitializer)
|
||||
{
|
||||
PrimaryComponentTick.bStartWithTickEnabled = false;
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
|
||||
SetIsReplicatedByDefault(true);
|
||||
|
||||
PawnData = nullptr;
|
||||
AbilitySystemComponent = nullptr;
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(UOLSPawnExtensionComponent, PawnData);
|
||||
}
|
||||
|
||||
FName UOLSPawnExtensionComponent::GetFeatureName() const
|
||||
{
|
||||
return NAME_ActorFeatureName;
|
||||
}
|
||||
|
||||
bool UOLSPawnExtensionComponent::CanChangeInitState(
|
||||
UGameFrameworkComponentManager* manager,
|
||||
FGameplayTag currentState,
|
||||
FGameplayTag desiredState) const
|
||||
{
|
||||
check(manager);
|
||||
|
||||
APawn* pawn = GetPawn<APawn>();
|
||||
|
||||
// @TODO: Implement LyraGameplayTags::InitState_Spawned.
|
||||
if (!currentState.IsValid() /* && desiredState == LyraGameplayTags::InitState_Spawned */)
|
||||
{
|
||||
// As long as we are on a valid pawn, we count as spawned
|
||||
if (pawn)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO: Implement LyraGameplayTags::InitState_Spawned,
|
||||
// @TODO: Implement LyraGameplayTags::InitState_DataAvailable,
|
||||
// @TODO: Implement LyraGameplayTags::InitState_DataInitialized,
|
||||
// @TODO: Implement LyraGameplayTags::InitState_GameplayReady
|
||||
// if (currentState == LyraGameplayTags::InitState_Spawned && desiredState == LyraGameplayTags::InitState_DataAvailable)
|
||||
// {
|
||||
// // Pawn data is required.
|
||||
// if (!PawnData)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// const bool bHasAuthority = pawn->HasAuthority();
|
||||
// const bool bIsLocallyControlled = pawn->IsLocallyControlled();
|
||||
//
|
||||
// if (bHasAuthority || bIsLocallyControlled)
|
||||
// {
|
||||
// // Check for being possessed by a controller.
|
||||
// if (!GetController<AController>())
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
// else if (currentState == LyraGameplayTags::InitState_DataAvailable && desiredState == LyraGameplayTags::InitState_DataInitialized)
|
||||
// {
|
||||
// // Transition to initialize if all features have their data available
|
||||
// return manager->HaveAllFeaturesReachedInitState(pawn, LyraGameplayTags::InitState_DataAvailable);
|
||||
// }
|
||||
// else if (currentState == LyraGameplayTags::InitState_DataInitialized && desiredState == LyraGameplayTags::InitState_GameplayReady)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::HandleChangeInitState(
|
||||
UGameFrameworkComponentManager* manager,
|
||||
FGameplayTag currentState,
|
||||
FGameplayTag desiredState)
|
||||
{
|
||||
// @TODO: Implement LyraGameplayTags::InitState_Spawned.
|
||||
// if (desiredState == LyraGameplayTags::InitState_DataInitialized)
|
||||
// {
|
||||
// // This is currently all handled by other components listening to this state change
|
||||
// }
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::OnActorInitStateChanged(const FActorInitStateChangedParams& Params)
|
||||
{
|
||||
// If another feature is now in DataAvailable, see if we should transition to DataInitialized
|
||||
if (Params.FeatureName != NAME_ActorFeatureName)
|
||||
{
|
||||
// @TODO: Implement LyraGameplayTags::InitState_DataAvailable.
|
||||
// if (Params.FeatureState == LyraGameplayTags::InitState_DataAvailable)
|
||||
// {
|
||||
// CheckDefaultInitialization();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::CheckDefaultInitialization()
|
||||
{
|
||||
// Before checking our progress, try progressing any other features we might depend on
|
||||
CheckDefaultInitializationForImplementers();
|
||||
|
||||
// @TODO: Implement LyraGameplayTags::InitState_Spawned,
|
||||
// @TODO: Implement LyraGameplayTags::InitState_DataAvailable,
|
||||
// @TODO: Implement LyraGameplayTags::InitState_DataInitialized,
|
||||
// @TODO: Implement LyraGameplayTags::InitState_GameplayReady
|
||||
// static const TArray<FGameplayTag> StateChain = {
|
||||
// LyraGameplayTags::InitState_Spawned, LyraGameplayTags::InitState_DataAvailable,
|
||||
// LyraGameplayTags::InitState_DataInitialized, LyraGameplayTags::InitState_GameplayReady
|
||||
// };
|
||||
|
||||
// This will try to progress from spawned (which is only set in BeginPlay) through the data initialization stages until it gets to gameplay ready
|
||||
// ContinueInitStateChain(StateChain);
|
||||
}
|
||||
|
||||
UOLSPawnExtensionComponent* UOLSPawnExtensionComponent::FindPawnExtensionComponent(const AActor* actor)
|
||||
{
|
||||
return (actor ? actor->FindComponentByClass<UOLSPawnExtensionComponent>() : nullptr);
|
||||
}
|
||||
|
||||
UOLSAbilitySystemComponent* UOLSPawnExtensionComponent::GetOLSAbilitySystemComponent() const
|
||||
{
|
||||
return AbilitySystemComponent;
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::SetPawnData(const UOLSPawnDataAsset* pawnData)
|
||||
{
|
||||
check(pawnData);
|
||||
|
||||
APawn* pawn = GetPawnChecked<APawn>();
|
||||
|
||||
if (pawn->GetLocalRole() != ROLE_Authority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (PawnData)
|
||||
{
|
||||
OLS_LOG(LogOLSPawnExtensionComponent, Error,
|
||||
TEXT("Trying to set PawnData [%s] on pawn [%s] that already has valid PawnData [%s]."),
|
||||
GET_UOBJECT_NAME(pawnData), GET_UOBJECT_NAME(pawn), GET_UOBJECT_NAME(PawnData));
|
||||
return;
|
||||
}
|
||||
|
||||
PawnData = pawnData;
|
||||
|
||||
pawn->ForceNetUpdate();
|
||||
|
||||
CheckDefaultInitialization();
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::InitializeAbilitySystem(UOLSAbilitySystemComponent* asc, AActor* ownerActor)
|
||||
{
|
||||
check(asc);
|
||||
check(ownerActor);
|
||||
|
||||
if (AbilitySystemComponent == asc)
|
||||
{
|
||||
// The ability system component hasn't changed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (AbilitySystemComponent)
|
||||
{
|
||||
// Clean up the old ability system component.
|
||||
UninitializeAbilitySystem();
|
||||
}
|
||||
|
||||
APawn* pawn = GetPawnChecked<APawn>();
|
||||
AActor* existingAvatar = asc->GetAvatarActor();
|
||||
|
||||
OLS_LOG(LogOLSPawnExtensionComponent, Verbose, TEXT("Setting up ASC [%s] on pawn [%s] owner [%s], existing [%s] "),
|
||||
GET_UOBJECT_NAME(asc), GET_UOBJECT_NAME(pawn), GET_UOBJECT_NAME(ownerActor),
|
||||
GET_UOBJECT_NAME(existingAvatar));
|
||||
|
||||
if ((existingAvatar != nullptr) && (existingAvatar != pawn))
|
||||
{
|
||||
OLS_LOG(LogOLSPawnExtensionComponent, Log, TEXT("Existing avatar (authority=%d)"), existingAvatar->HasAuthority() ? 1 : 0);
|
||||
|
||||
// There is already a pawn acting as the ASC's avatar, so we need to kick it out
|
||||
// This can happen on clients if they're lagged: their new pawn is spawned + possessed before the dead one is removed
|
||||
ensure(!existingAvatar->HasAuthority());
|
||||
|
||||
if (UOLSPawnExtensionComponent* OtherExtensionComponent = FindPawnExtensionComponent(existingAvatar))
|
||||
{
|
||||
OtherExtensionComponent->UninitializeAbilitySystem();
|
||||
}
|
||||
}
|
||||
|
||||
AbilitySystemComponent = asc;
|
||||
AbilitySystemComponent->InitAbilityActorInfo(ownerActor, pawn);
|
||||
|
||||
if (ensure(PawnData))
|
||||
{
|
||||
asc->SetTagRelationshipMapping(PawnData->TagRelationshipMapping);
|
||||
}
|
||||
|
||||
OnAbilitySystemInitialized.Broadcast();
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::UninitializeAbilitySystem()
|
||||
{
|
||||
if (!AbilitySystemComponent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Uninitialize the ASC if we're still the avatar actor (otherwise another pawn already did it when they became the avatar actor)
|
||||
if (AbilitySystemComponent->GetAvatarActor() == GetOwner())
|
||||
{
|
||||
FGameplayTagContainer abilityTypesToIgnore;
|
||||
|
||||
// @TOD:; Implement LyraGameplayTags::Ability_Behavior_SurvivesDeath;
|
||||
// abilityTypesToIgnore.AddTag(LyraGameplayTags::Ability_Behavior_SurvivesDeath);
|
||||
|
||||
AbilitySystemComponent->CancelAbilities(nullptr, &abilityTypesToIgnore);
|
||||
AbilitySystemComponent->ClearAbilityInput();
|
||||
AbilitySystemComponent->RemoveAllGameplayCues();
|
||||
|
||||
if (AbilitySystemComponent->GetOwnerActor() != nullptr)
|
||||
{
|
||||
AbilitySystemComponent->SetAvatarActor(nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the ASC doesn't have a valid owner, we need to clear *all* actor info, not just the avatar pairing
|
||||
AbilitySystemComponent->ClearActorInfo();
|
||||
}
|
||||
|
||||
OnAbilitySystemUninitialized.Broadcast();
|
||||
}
|
||||
|
||||
AbilitySystemComponent = nullptr;
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::HandleControllerChanged()
|
||||
{
|
||||
if (AbilitySystemComponent && (AbilitySystemComponent->GetAvatarActor() == GetPawnChecked<APawn>()))
|
||||
{
|
||||
ensure(AbilitySystemComponent->AbilityActorInfo->OwnerActor == AbilitySystemComponent->GetOwnerActor());
|
||||
if (AbilitySystemComponent->GetOwnerActor() == nullptr)
|
||||
{
|
||||
UninitializeAbilitySystem();
|
||||
}
|
||||
else
|
||||
{
|
||||
AbilitySystemComponent->RefreshAbilityActorInfo();
|
||||
}
|
||||
}
|
||||
|
||||
CheckDefaultInitialization();
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::HandlePlayerStateReplicated()
|
||||
{
|
||||
CheckDefaultInitialization();
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::SetupPlayerInputComponent()
|
||||
{
|
||||
CheckDefaultInitialization();
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::OnAbilitySystemInitialized_RegisterAndCall(
|
||||
FSimpleMulticastDelegate::FDelegate delegate)
|
||||
{
|
||||
if (!OnAbilitySystemInitialized.IsBoundToObject(delegate.GetUObject()))
|
||||
{
|
||||
OnAbilitySystemInitialized.Add(delegate);
|
||||
}
|
||||
|
||||
if (AbilitySystemComponent)
|
||||
{
|
||||
delegate.Execute();
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::OnAbilitySystemUninitialized_Register(FSimpleMulticastDelegate::FDelegate delegate)
|
||||
{
|
||||
if (!OnAbilitySystemUninitialized.IsBoundToObject(delegate.GetUObject()))
|
||||
{
|
||||
OnAbilitySystemUninitialized.Add(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSPawnExtensionComponent::OnRep_PawnData()
|
||||
{
|
||||
CheckDefaultInitialization();
|
||||
}
|
@ -3,6 +3,10 @@
|
||||
|
||||
#include "DataAssets/OLSInputConfigDataAsset.h"
|
||||
|
||||
#include "OLSLog.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSInputConfigDataAsset);
|
||||
|
||||
UOLSInputConfigDataAsset::UOLSInputConfigDataAsset(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
||||
{
|
||||
}
|
||||
@ -21,8 +25,9 @@ const UInputAction* UOLSInputConfigDataAsset::FindNativeInputActionForTag(
|
||||
|
||||
if (shouldLogNotFound)
|
||||
{
|
||||
//Todo: replace this with our custom log.
|
||||
//UE_LOG(LogLyra, Error, TEXT("Can't find NativeInputAction for InputTag [%s] on InputConfig [%s]."), *InputTag.ToString(), *GetNameSafe(this));
|
||||
OLS_LOG(LogOLSInputConfigDataAsset, Error,
|
||||
TEXT("Can't find NativeInputAction for InputTag [%s] on InputConfig [%s]."), GET_TAG_NAME(inputTag),
|
||||
GET_UOBJECT_NAME(this));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@ -32,19 +37,20 @@ const UInputAction* UOLSInputConfigDataAsset::FindAbilityInputActionForTag(
|
||||
const FGameplayTag& inputTag,
|
||||
bool shouldLogNotFound) const
|
||||
{
|
||||
for (const FOLSInputAction& action : AbilityInputActions)
|
||||
{
|
||||
if (action.InputAction && (action.InputTag == inputTag))
|
||||
{
|
||||
return action.InputAction;
|
||||
}
|
||||
}
|
||||
for (const FOLSInputAction& action : AbilityInputActions)
|
||||
{
|
||||
if (action.InputAction && (action.InputTag == inputTag))
|
||||
{
|
||||
return action.InputAction;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldLogNotFound)
|
||||
{
|
||||
//Todo: replace this with our custom log.
|
||||
//UE_LOG(LogLyra, Error, TEXT("Can't find AbilityInputAction for InputTag [%s] on InputConfig [%s]."), *InputTag.ToString(), *GetNameSafe(this));
|
||||
}
|
||||
if (shouldLogNotFound)
|
||||
{
|
||||
OLS_LOG(LogOLSInputConfigDataAsset, Error,
|
||||
TEXT("Can't find AbilityInputAction for InputTag [%s] on InputConfig [%s]."), GET_TAG_NAME(inputTag),
|
||||
GET_UOBJECT_NAME(this));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -0,0 +1,308 @@
|
||||
// © 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 "GameFeatures/OLSGameFeatureAction_AddInputContextMapping.h"
|
||||
|
||||
#include "UserSettings/EnhancedInputUserSettings.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "Systems/OLSAssetManager.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
|
||||
#if WITH_EDITOR
|
||||
#include "Misc/DataValidation.h"
|
||||
#endif
|
||||
|
||||
#include "InputMappingContext.h"
|
||||
#include "OLSLog.h"
|
||||
#include "Components/GameFrameworkComponentManager.h"
|
||||
#include "Components/OLSHeroComponent.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSGameFA_AddInputContextMapping);
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSGameFeatureAction_AddInputContextMapping)
|
||||
|
||||
#define LOCTEXT_NAMESPACE "GameFeatures"
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::OnGameFeatureRegistering()
|
||||
{
|
||||
Super::OnGameFeatureRegistering();
|
||||
|
||||
RegisterInputMappingContexts();
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::OnGameFeatureActivating(FGameFeatureActivatingContext& context)
|
||||
{
|
||||
FPerContextData& activeData = ContextData.FindOrAdd(context);
|
||||
if (!ensure(activeData.ExtensionRequestHandles.IsEmpty()) ||
|
||||
!ensure(activeData.ControllersAddedTo.IsEmpty()))
|
||||
{
|
||||
Reset(activeData);
|
||||
}
|
||||
|
||||
Super::OnGameFeatureActivating(context);
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& context)
|
||||
{
|
||||
Super::OnGameFeatureDeactivating(context);
|
||||
|
||||
FPerContextData* activeData = ContextData.Find(context);
|
||||
if (ensure(activeData))
|
||||
{
|
||||
Reset(*activeData);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::OnGameFeatureUnregistering()
|
||||
{
|
||||
Super::OnGameFeatureUnregistering();
|
||||
|
||||
UnregisterInputMappingContexts();
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
EDataValidationResult UOLSGameFeatureAction_AddInputContextMapping::IsDataValid(FDataValidationContext& context) const
|
||||
{
|
||||
// Don't call Super since it does not fit in this.
|
||||
|
||||
EDataValidationResult result = CombineDataValidationResults(Super::IsDataValid(context), EDataValidationResult::Valid);
|
||||
|
||||
int32 index = 0;
|
||||
|
||||
for (const FOLSInputMappingContextAndPriority& entry : InputMappings)
|
||||
{
|
||||
if (entry.InputMapping.IsNull())
|
||||
{
|
||||
result = EDataValidationResult::Invalid;
|
||||
context.AddError(FText::Format(LOCTEXT("NullInputMapping", "Null InputMapping at index {0}."), index));
|
||||
}
|
||||
++index;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::RegisterInputMappingContexts()
|
||||
{
|
||||
RegisterInputContextMappingsForGameInstanceHandle = FWorldDelegates::OnStartGameInstance.AddUObject(
|
||||
this, &UOLSGameFeatureAction_AddInputContextMapping::RegisterInputContextMappingsForGameInstance);
|
||||
|
||||
const TIndirectArray<FWorldContext>& worldContexts = GEngine->GetWorldContexts();
|
||||
for (TIndirectArray<FWorldContext>::TConstIterator worldContextIterator = worldContexts.CreateConstIterator();
|
||||
worldContextIterator; ++worldContextIterator)
|
||||
{
|
||||
RegisterInputContextMappingsForGameInstance(worldContextIterator->OwningGameInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::RegisterInputContextMappingsForGameInstance(
|
||||
UGameInstance* gameInstance)
|
||||
{
|
||||
if (gameInstance != nullptr && !gameInstance->OnLocalPlayerAddedEvent.IsBoundToObject(this))
|
||||
{
|
||||
gameInstance->OnLocalPlayerAddedEvent.AddUObject(
|
||||
this, &UOLSGameFeatureAction_AddInputContextMapping::RegisterInputMappingContextsForLocalPlayer);
|
||||
gameInstance->OnLocalPlayerRemovedEvent.AddUObject(
|
||||
this, &UOLSGameFeatureAction_AddInputContextMapping::UnregisterInputMappingContextsForLocalPlayer);
|
||||
|
||||
for (TArray<ULocalPlayer*>::TConstIterator localPlayerIterator = gameInstance->GetLocalPlayerIterator();
|
||||
localPlayerIterator; ++localPlayerIterator)
|
||||
{
|
||||
RegisterInputMappingContextsForLocalPlayer(*localPlayerIterator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::RegisterInputMappingContextsForLocalPlayer(ULocalPlayer* localPlayer)
|
||||
{
|
||||
if (ensure(localPlayer))
|
||||
{
|
||||
UOLSAssetManager& assetManager = UOLSAssetManager::Get();
|
||||
|
||||
if (UEnhancedInputLocalPlayerSubsystem* eiSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(localPlayer))
|
||||
{
|
||||
if (UEnhancedInputUserSettings* settings = eiSubsystem->GetUserSettings())
|
||||
{
|
||||
for (const FOLSInputMappingContextAndPriority& entry : InputMappings)
|
||||
{
|
||||
// Skip entries that don't want to be registered
|
||||
if (!entry.bShouldRegisterWithSettings)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Register this IMC with the settings!
|
||||
if (UInputMappingContext* imc = assetManager.GetAsset(entry.InputMapping))
|
||||
{
|
||||
settings->RegisterInputMappingContext(imc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::UnregisterInputMappingContexts()
|
||||
{
|
||||
FWorldDelegates::OnStartGameInstance.Remove(RegisterInputContextMappingsForGameInstanceHandle);
|
||||
RegisterInputContextMappingsForGameInstanceHandle.Reset();
|
||||
|
||||
const TIndirectArray<FWorldContext>& worldContexts = GEngine->GetWorldContexts();
|
||||
for (TIndirectArray<FWorldContext>::TConstIterator worldContextIterator = worldContexts.CreateConstIterator();
|
||||
worldContextIterator; ++worldContextIterator)
|
||||
{
|
||||
UnregisterInputContextMappingsForGameInstance(worldContextIterator->OwningGameInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::UnregisterInputContextMappingsForGameInstance(
|
||||
UGameInstance* gameInstance)
|
||||
{
|
||||
if (gameInstance)
|
||||
{
|
||||
gameInstance->OnLocalPlayerAddedEvent.RemoveAll(this);
|
||||
gameInstance->OnLocalPlayerRemovedEvent.RemoveAll(this);
|
||||
|
||||
for (TArray<ULocalPlayer*>::TConstIterator localPlayerIterator = gameInstance->GetLocalPlayerIterator();
|
||||
localPlayerIterator; ++localPlayerIterator)
|
||||
{
|
||||
UnregisterInputMappingContextsForLocalPlayer(*localPlayerIterator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::UnregisterInputMappingContextsForLocalPlayer(
|
||||
ULocalPlayer* localPlayer)
|
||||
{
|
||||
if (ensure(localPlayer))
|
||||
{
|
||||
if (UEnhancedInputLocalPlayerSubsystem* eiSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(localPlayer))
|
||||
{
|
||||
if (UEnhancedInputUserSettings* settings = eiSubsystem->GetUserSettings())
|
||||
{
|
||||
for (const FOLSInputMappingContextAndPriority& entry : InputMappings)
|
||||
{
|
||||
// Skip entries that don't want to be registered
|
||||
if (!entry.bShouldRegisterWithSettings)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Register this IMC with the settings!
|
||||
if (UInputMappingContext* imc = entry.InputMapping.Get())
|
||||
{
|
||||
settings->UnregisterInputMappingContext(imc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::AddToWorld(const FWorldContext& worldContext,
|
||||
const FGameFeatureStateChangeContext& changeContext)
|
||||
{
|
||||
UWorld* world = worldContext.World();
|
||||
UGameInstance* gameInstance = worldContext.OwningGameInstance;
|
||||
FPerContextData& activeData = ContextData.FindOrAdd(changeContext);
|
||||
|
||||
if (gameInstance && world && world->IsGameWorld())
|
||||
{
|
||||
if (UGameFrameworkComponentManager* componentManager = UGameInstance::GetSubsystem<
|
||||
UGameFrameworkComponentManager>(gameInstance))
|
||||
{
|
||||
UGameFrameworkComponentManager::FExtensionHandlerDelegate addAbilitiesDelegate =
|
||||
UGameFrameworkComponentManager::FExtensionHandlerDelegate::CreateUObject(
|
||||
this, &ThisClass::HandleControllerExtension, changeContext);
|
||||
TSharedPtr<FComponentRequestHandle> extensionRequestHandle =
|
||||
componentManager->AddExtensionHandler(APlayerController::StaticClass(), addAbilitiesDelegate);
|
||||
|
||||
activeData.ExtensionRequestHandles.Add(extensionRequestHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::Reset(FPerContextData& activeData)
|
||||
{
|
||||
activeData.ExtensionRequestHandles.Empty();
|
||||
|
||||
while (!activeData.ControllersAddedTo.IsEmpty())
|
||||
{
|
||||
TWeakObjectPtr<APlayerController> controllerPtr = activeData.ControllersAddedTo.Top();
|
||||
if (controllerPtr.IsValid())
|
||||
{
|
||||
RemoveInputMapping(controllerPtr.Get(), activeData);
|
||||
}
|
||||
else
|
||||
{
|
||||
activeData.ControllersAddedTo.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::HandleControllerExtension(AActor* actor,
|
||||
FName eventName,
|
||||
FGameFeatureStateChangeContext changeContext)
|
||||
{
|
||||
APlayerController* playerController = CastChecked<APlayerController>(actor);
|
||||
FPerContextData& activeData = ContextData.FindOrAdd(changeContext);
|
||||
|
||||
// TODO Why does this code mix and match controllers and local players? ControllersAddedTo is never modified
|
||||
if ((eventName == UGameFrameworkComponentManager::NAME_ExtensionRemoved) || (eventName == UGameFrameworkComponentManager::NAME_ReceiverRemoved))
|
||||
{
|
||||
RemoveInputMapping(playerController, activeData);
|
||||
}
|
||||
else if ((eventName == UGameFrameworkComponentManager::NAME_ExtensionAdded) || (eventName == UOLSHeroComponent::NAME_BindInputsNow))
|
||||
{
|
||||
AddInputMappingForPlayer(playerController->GetLocalPlayer(), activeData);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::AddInputMappingForPlayer(UPlayer* player,
|
||||
FPerContextData& activeData)
|
||||
{
|
||||
if (ULocalPlayer* localPlayer = Cast<ULocalPlayer>(player))
|
||||
{
|
||||
if (UEnhancedInputLocalPlayerSubsystem* inputSystem = localPlayer->GetSubsystem<
|
||||
UEnhancedInputLocalPlayerSubsystem>())
|
||||
{
|
||||
for (const FOLSInputMappingContextAndPriority& entry : InputMappings)
|
||||
{
|
||||
if (const UInputMappingContext* imc = entry.InputMapping.Get())
|
||||
{
|
||||
inputSystem->AddMappingContext(imc, entry.Priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OLS_LOG(LogOLSGameFA_AddInputContextMapping, Error,
|
||||
TEXT(
|
||||
"Failed to find `UEnhancedInputLocalPlayerSubsystem` for local player. Input mappings will not be added. Make sure you're set to use the EnhancedInput system via config file."
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_AddInputContextMapping::RemoveInputMapping(APlayerController* playerController,
|
||||
FPerContextData& activeData)
|
||||
{
|
||||
if (ULocalPlayer* localPlayer = playerController->GetLocalPlayer())
|
||||
{
|
||||
if (UEnhancedInputLocalPlayerSubsystem* inputSystem = localPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
|
||||
{
|
||||
for (const FOLSInputMappingContextAndPriority& entry : InputMappings)
|
||||
{
|
||||
if (const UInputMappingContext* imc = entry.InputMapping.Get())
|
||||
{
|
||||
inputSystem->RemoveMappingContext(imc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activeData.ControllersAddedTo.Remove(playerController);
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@ -0,0 +1,46 @@
|
||||
// © 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 "GameFeatures/OLSGameFeatureAction_WorldActionBase.h"
|
||||
|
||||
#include "GameFeaturesSubsystem.h"
|
||||
|
||||
void UOLSGameFeatureAction_WorldActionBase::OnGameFeatureActivating(FGameFeatureActivatingContext& context)
|
||||
{
|
||||
// Don't call Super since we don't need it.
|
||||
|
||||
GameInstanceStartHandles.FindOrAdd(context) = FWorldDelegates::OnStartGameInstance.AddUObject(this,
|
||||
&UOLSGameFeatureAction_WorldActionBase::HandleGameInstanceStart, FGameFeatureStateChangeContext(context));
|
||||
|
||||
// Add to any worlds with associated game instances that have already been initialized
|
||||
for (const FWorldContext& worldContext : GEngine->GetWorldContexts())
|
||||
{
|
||||
if (context.ShouldApplyToWorldContext(worldContext))
|
||||
{
|
||||
AddToWorld(worldContext, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_WorldActionBase::OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& context)
|
||||
{
|
||||
// Don't call Super since we don't need it.
|
||||
|
||||
FDelegateHandle* foundHandle = GameInstanceStartHandles.Find(context);
|
||||
if (ensure(foundHandle))
|
||||
{
|
||||
FWorldDelegates::OnStartGameInstance.Remove(*foundHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSGameFeatureAction_WorldActionBase::HandleGameInstanceStart(UGameInstance* gameInstance,
|
||||
FGameFeatureStateChangeContext changeContext)
|
||||
{
|
||||
if (FWorldContext* worldContext = gameInstance->GetWorldContext())
|
||||
{
|
||||
if (changeContext.ShouldApplyToWorldContext(*worldContext))
|
||||
{
|
||||
AddToWorld(*worldContext, changeContext);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,9 +3,12 @@
|
||||
|
||||
#include "ModularGameplayActors/OLSModularActor.h"
|
||||
|
||||
#include "OLSLog.h"
|
||||
#include "AbilitySystem/OLSAbilitySystemComponent.h"
|
||||
#include "Components/GameFrameworkComponentManager.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSModularActor);
|
||||
|
||||
AOLSModularActor::AOLSModularActor(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
||||
{
|
||||
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
@ -48,8 +51,8 @@ void AOLSModularActor::PostInitProperties()
|
||||
|
||||
if (AbilitySystemComponent)
|
||||
{
|
||||
//@Todo replace this log by our custom log.
|
||||
UE_LOG(LogTemp, Verbose, TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), *GetName(), ReplicationMode);
|
||||
OLS_LOG(LogOLSModularActor, Verbose, TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"),
|
||||
GET_UOBJECT_NAME(this), ReplicationMode);
|
||||
AbilitySystemComponent->SetReplicationMode(ReplicationMode);
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,13 @@
|
||||
|
||||
#include "ModularGameplayActors/OLSModularCharacter.h"
|
||||
|
||||
#include "OLSLog.h"
|
||||
#include "AbilitySystem/OLSAbilitySystemComponent.h"
|
||||
#include "Components/GameFrameworkComponentManager.h"
|
||||
|
||||
AOLSModularCharacter::AOLSModularCharacter(const FObjectInitializer& objectInitializer)
|
||||
DEFINE_LOG_CATEGORY(LogOLSModularCharacter);
|
||||
|
||||
AOLSModularCharacter::AOLSModularCharacter(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
||||
{
|
||||
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
@ -46,8 +49,9 @@ void AOLSModularCharacter::PostInitProperties()
|
||||
|
||||
if (AbilitySystemComponent)
|
||||
{
|
||||
//@Todo replace this log by our custom log.
|
||||
UE_LOG(LogTemp, Verbose, TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), *GetName(), ReplicationMode);
|
||||
OLS_LOG(LogOLSModularCharacter, Verbose,
|
||||
TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), GET_UOBJECT_NAME(this),
|
||||
ReplicationMode);
|
||||
AbilitySystemComponent->SetReplicationMode(ReplicationMode);
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,11 @@
|
||||
|
||||
#include "ModularGameplayActors/OLSModularDefaultPawn.h"
|
||||
|
||||
#include "OLSLog.h"
|
||||
#include "AbilitySystem/OLSAbilitySystemComponent.h"
|
||||
#include "Components/GameFrameworkComponentManager.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSModularDefaultPawn);
|
||||
|
||||
// Sets default values
|
||||
AOLSModularDefaultPawn::AOLSModularDefaultPawn(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
||||
@ -47,9 +49,9 @@ void AOLSModularDefaultPawn::PostInitProperties()
|
||||
Super::PostInitProperties();
|
||||
if (AbilitySystemComponent)
|
||||
{
|
||||
//@Todo replace this log by our custom log.
|
||||
UE_LOG(LogTemp, Verbose, TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), *GetName(),
|
||||
ReplicationMode);
|
||||
OLS_LOG(LogOLSModularDefaultPawn, Verbose,
|
||||
TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), GET_UOBJECT_NAME(this),
|
||||
ReplicationMode);
|
||||
AbilitySystemComponent->SetReplicationMode(ReplicationMode);
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,12 @@
|
||||
|
||||
#include "ModularGameplayActors/OLSModularPawn.h"
|
||||
|
||||
#include "OLSLog.h"
|
||||
#include "AbilitySystem/OLSAbilitySystemComponent.h"
|
||||
#include "Components/GameFrameworkComponentManager.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSModularPawn);
|
||||
|
||||
AOLSModularPawn::AOLSModularPawn(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
||||
{
|
||||
AbilitySystemComponent = CreateDefaultSubobject<UOLSAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
|
||||
@ -38,8 +41,9 @@ void AOLSModularPawn::PostInitProperties()
|
||||
|
||||
if (AbilitySystemComponent)
|
||||
{
|
||||
//@Todo replace this log by our custom log.
|
||||
UE_LOG(LogTemp, Verbose, TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), *GetName(), ReplicationMode);
|
||||
OLS_LOG(LogOLSModularPawn, Verbose,
|
||||
TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), GET_UOBJECT_NAME(this),
|
||||
ReplicationMode);
|
||||
AbilitySystemComponent->SetReplicationMode(ReplicationMode);
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,13 @@
|
||||
|
||||
#include "ModularGameplayActors/OLSModularPlayerState.h"
|
||||
|
||||
#include "OLSLog.h"
|
||||
#include "AbilitySystem/OLSAbilitySystemComponent.h"
|
||||
#include "Components/GameFrameworkComponentManager.h"
|
||||
#include "Components/PlayerStateComponent.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSModularPlayerState);
|
||||
|
||||
AOLSModularPlayerState::AOLSModularPlayerState(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
||||
{
|
||||
// Create ability system component, and set it to be explicitly replicated
|
||||
@ -41,8 +44,9 @@ void AOLSModularPlayerState::PostInitProperties()
|
||||
|
||||
if (AbilitySystemComponent)
|
||||
{
|
||||
//@Todo replace this log by our custom log.
|
||||
UE_LOG(LogTemp, Verbose, TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), *GetName(), ReplicationMode);
|
||||
OLS_LOG(LogOLSModularPlayerState, Verbose,
|
||||
TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), GET_UOBJECT_NAME(this),
|
||||
ReplicationMode);
|
||||
AbilitySystemComponent->SetReplicationMode(ReplicationMode);
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,12 @@
|
||||
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AbilitySystemGlobals.h"
|
||||
#include "OLSLog.h"
|
||||
#include "Components/GameFrameworkComponentManager.h"
|
||||
#include "GameFramework/PlayerState.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSModularPlayerStateCharacter);
|
||||
|
||||
AOLSModularPlayerStateCharacter::AOLSModularPlayerStateCharacter(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
||||
{
|
||||
SetMinNetUpdateFrequency(33.f);
|
||||
@ -40,8 +43,9 @@ void AOLSModularPlayerStateCharacter::PostInitProperties()
|
||||
|
||||
if (AbilitySystemComponent.IsValid())
|
||||
{
|
||||
//@Todo replace this log by our custom log.
|
||||
UE_LOG(LogTemp, Verbose, TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), *GetName(), ReplicationMode);
|
||||
OLS_LOG(LogOLSModularPlayerStateCharacter, Verbose,
|
||||
TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), GET_UOBJECT_NAME(this),
|
||||
ReplicationMode);
|
||||
AbilitySystemComponent->SetReplicationMode(ReplicationMode);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "Player/OLSPlayerState.h"
|
||||
|
||||
#include "OLSLog.h"
|
||||
#include "AbilitySystem/OLSAbilitySystemComponent.h"
|
||||
#include "AbilitySystem/Attributes/OLSCombatAttributeSet.h"
|
||||
#include "AbilitySystem/Attributes/OLSHealthAttributeSet.h"
|
||||
@ -15,6 +16,8 @@
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSPlayerState)
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogOLSPlayerState);
|
||||
|
||||
const FName AOLSPlayerState::NAME_OLSAbilityReady("OLSAbilitiesReady");
|
||||
|
||||
AOLSPlayerState::AOLSPlayerState(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
||||
@ -44,8 +47,9 @@ void AOLSPlayerState::SetPawnData(const UOLSPawnDataAsset* pawnData)
|
||||
|
||||
if (PawnData)
|
||||
{
|
||||
// @Todo: replace this by our custom log.
|
||||
// UE_LOG(LogLyra, Error, TEXT("Trying to set PawnData [%s] on player state [%s] that already has valid PawnData [%s]."), *GetNameSafe(InPawnData), *GetNameSafe(this), *GetNameSafe(PawnData));
|
||||
OLS_LOG(LogOLSPlayerState, Error,
|
||||
TEXT("Trying to set PawnData [%s] on player state [%s] that already has valid PawnData [%s]."),
|
||||
GET_UOBJECT_NAME(pawnData), GET_UOBJECT_NAME(this), GET_UOBJECT_NAME(PawnData));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -94,8 +98,7 @@ void AOLSPlayerState::OnExperienceLoaded(const UOLSExperienceDefinitionDataAsset
|
||||
}
|
||||
else
|
||||
{
|
||||
// @T ODO: Replace this with our custom.
|
||||
// UE_LOG(LogLyra, Error, TEXT("ALyraPlayerState::OnExperienceLoaded(): Unable to find PawnData to initialize player state [%s]!"), *GetNameSafe(this));
|
||||
OLS_LOG(LogOLSPlayerState, Error, TEXT("Unable to find PawnData to initialize player state [%s]!"), GET_UOBJECT_NAME(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
61
Source/ols/Public/AbilitySystem/Abilities/OLSAbilityCost.h
Normal file
61
Source/ols/Public/AbilitySystem/Abilities/OLSAbilityCost.h
Normal file
@ -0,0 +1,61 @@
|
||||
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trad`emark is strictly prohibited and may result in legal action.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayAbilitySpec.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Abilities/GameplayAbilityTypes.h"
|
||||
#include "OLSAbilityCost.generated.h"
|
||||
|
||||
|
||||
/**
|
||||
* UOLSAbilityCost
|
||||
*
|
||||
* Base class for costs that a LyraGameplayAbility has (e.g., ammo or charges)
|
||||
*/
|
||||
UCLASS(DefaultToInstanced, EditInlineNew, Abstract)
|
||||
class OLS_API UOLSAbilityCost : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UOLSAbilityCost();
|
||||
|
||||
/**
|
||||
* Checks if we can afford this cost.
|
||||
*
|
||||
* A failure reason tag can be added to OptionalRelevantTags (if non-null), which can be queried
|
||||
* elsewhere to determine how to provide user feedback (e.g., a clicking noise if a weapon is out of ammo)
|
||||
*
|
||||
* Ability and ActorInfo are guaranteed to be non-null on entry, but OptionalRelevantTags can be nullptr.
|
||||
*
|
||||
* @return true if we can pay for the ability, false otherwise.
|
||||
*/
|
||||
virtual bool CheckCost(const class UOLSGameplayAbility* ability,
|
||||
const FGameplayAbilitySpecHandle handle,
|
||||
const FGameplayAbilityActorInfo* actorInfo,
|
||||
FGameplayTagContainer* optionalRelevantTags) const;
|
||||
|
||||
/**
|
||||
* Applies the ability's cost to the target
|
||||
*
|
||||
* Notes:
|
||||
* - Your implementation don't need to check ShouldOnlyApplyCostOnHit(), the caller does that for you.
|
||||
* - Ability and ActorInfo are guaranteed to be non-null on entry.
|
||||
*/
|
||||
virtual void ApplyCost(const class UOLSGameplayAbility* ability,
|
||||
const FGameplayAbilitySpecHandle handle,
|
||||
const FGameplayAbilityActorInfo* actorInfo,
|
||||
const FGameplayAbilityActivationInfo activationInfo);
|
||||
|
||||
/** If true, this cost should only be applied if this ability hits successfully */
|
||||
bool ShouldOnlyApplyCostOnHit() const;
|
||||
|
||||
protected:
|
||||
|
||||
/** If true, this cost should only be applied if this ability hits successfully */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "OLS|Costs")
|
||||
uint8 bShouldOnlyApplyCostOnHit : 1 = false;
|
||||
};
|
@ -7,10 +7,106 @@
|
||||
#include "OLSGameplayAbility.generated.h"
|
||||
|
||||
/**
|
||||
* ELyraAbilityActivationPolicy
|
||||
*
|
||||
* Defines how an ability is meant to activate.
|
||||
*/
|
||||
UCLASS()
|
||||
UENUM(BlueprintType)
|
||||
enum class EOLSAbilityActivationPolicy : uint8
|
||||
{
|
||||
// Try to activate the ability when the input is triggered.
|
||||
OnInputTriggered,
|
||||
|
||||
// Continually try to activate the ability while the input is active.
|
||||
WhileInputActive,
|
||||
|
||||
// Try to activate the ability when an avatar is assigned.
|
||||
OnSpawn
|
||||
};
|
||||
|
||||
/**
|
||||
* ELyraAbilityActivationGroup
|
||||
*
|
||||
* Defines how an ability activates in relation to other abilities.
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EOLSAbilityActivationGroup : uint8
|
||||
{
|
||||
// Ability runs independently of all other abilities.
|
||||
Independent,
|
||||
|
||||
// Ability is canceled and replaced by other exclusive abilities.
|
||||
Exclusive_Replaceable,
|
||||
|
||||
// Ability blocks all other exclusive abilities from activating.
|
||||
Exclusive_Blocking,
|
||||
|
||||
MAX UMETA(Hidden)
|
||||
};
|
||||
|
||||
/** Failure reason that can be used to play an animation montage when a failure occurs */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FOLSAbilityMontageFailureMessage
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Player controller that failed to activate the ability, if the AbilitySystemComponent was player owned
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
TObjectPtr<APlayerController> PlayerController = nullptr;
|
||||
|
||||
// Avatar actor that failed to activate the ability
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
TObjectPtr<AActor> AvatarActor = nullptr;
|
||||
|
||||
// All the reasons why this ability has failed
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
FGameplayTagContainer FailureTags;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
TObjectPtr<UAnimMontage> FailureMontage = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* UOLSGameplayAbility
|
||||
*
|
||||
* The base gameplay ability class used by this project.
|
||||
*/
|
||||
UCLASS(Abstract, HideCategories = Input, Meta = (ShortTooltip = "The base gameplay ability class used by this project."))
|
||||
class OLS_API UOLSGameplayAbility : public UGameplayAbility
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// Defines how this ability is meant to activate.
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "OLS|Ability Activation")
|
||||
EOLSAbilityActivationPolicy ActivationPolicy;
|
||||
|
||||
// Defines the relationship between this ability activating and other abilities activating.
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "OLS|Ability Activation")
|
||||
EOLSAbilityActivationGroup ActivationGroup;
|
||||
|
||||
// Additional costs that must be paid to activate this ability
|
||||
UPROPERTY(EditDefaultsOnly, Instanced, Category = Costs)
|
||||
TArray<TObjectPtr<class UOLSAbilityCost>> AdditionalCosts;
|
||||
|
||||
// Map of failure tags to simple error messages
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Advanced")
|
||||
TMap<FGameplayTag, FText> FailureTagToUserFacingMessages;
|
||||
|
||||
// Map of failure tags to anim montages that should be played with them
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Advanced")
|
||||
TMap<FGameplayTag, TObjectPtr<UAnimMontage>> FailureTagToAnimMontage;
|
||||
|
||||
// If true, extra information should be logged when this ability is canceled. This is temporary, used for tracking a bug.
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Advanced")
|
||||
uint8 bShouldLogCancellation : 1 = false;
|
||||
|
||||
// Current camera mode set by the ability.
|
||||
// TSubclassOf<ULyraCameraMode> ActiveCameraMode;
|
||||
};
|
||||
|
@ -4,10 +4,13 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "NativeGameplayTags.h"
|
||||
#include "OLSAbilitySystemComponent.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSAbilitySystemComponent, Verbose, All);
|
||||
|
||||
OLS_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Gameplay_AbilityInputBlocked);
|
||||
|
||||
/**
|
||||
* CVAR to control the "Play Montage" flow.
|
||||
* Example: OLS.EnableDefaultPlayMontage true
|
||||
@ -44,7 +47,6 @@ public:
|
||||
float startTimeSeconds) override;
|
||||
// -- End Ability System Component implementation
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
@ -152,13 +154,79 @@ protected:
|
||||
*/
|
||||
void TryActivateAbilitiesOnSpawn();
|
||||
|
||||
virtual void AbilitySpecInputPressed(FGameplayAbilitySpec& spec) override;
|
||||
virtual void AbilitySpecInputReleased(FGameplayAbilitySpec& spec) override;
|
||||
|
||||
virtual void NotifyAbilityActivated(const FGameplayAbilitySpecHandle handle, UGameplayAbility* ability) override;
|
||||
virtual void NotifyAbilityFailed(const FGameplayAbilitySpecHandle handle, UGameplayAbility* ability, const FGameplayTagContainer& failureReason) override;
|
||||
virtual void NotifyAbilityEnded(FGameplayAbilitySpecHandle handle, UGameplayAbility* Ability, bool bWasCancelled) override;
|
||||
virtual void ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& abilityTags, UGameplayAbility* requestingAbility, bool shouldEnableBlockTags, const FGameplayTagContainer& blockTags, bool shouldExecuteCancelTags, const FGameplayTagContainer& cancelTags) override;
|
||||
virtual void HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& abilityTags, UGameplayAbility* requestingAbility, bool canBeCanceled) override;
|
||||
|
||||
/** Notify client that an ability failed to activate */
|
||||
UFUNCTION(Client, Unreliable)
|
||||
void ClientNotifyAbilityFailed(const UGameplayAbility* ability, const FGameplayTagContainer& failureReason);
|
||||
|
||||
void HandleAbilityFailed(const UGameplayAbility* ability, const FGameplayTagContainer& failureReason);
|
||||
|
||||
/**
|
||||
* Conveniently separates the code that sets the animation to replicate, so it can be further modified.
|
||||
*/
|
||||
virtual void SetReplicatedMontageInfo(FGameplayAbilityRepAnimMontage& mutableRepAnimMontageInfo, UAnimMontage* newMontageToPlay, const FName& startSectionName);
|
||||
|
||||
public:
|
||||
|
||||
typedef TFunctionRef<bool(const class UOLSGameplayAbility* ability, FGameplayAbilitySpecHandle handle)> TShouldCancelAbilityFunc;
|
||||
void CancelAbilitiesByFunc(TShouldCancelAbilityFunc shouldCancelFunc, bool shouldReplicateCancelAbility);
|
||||
|
||||
void CancelInputActivatedAbilities(bool shouldReplicateCancelAbility);
|
||||
|
||||
void AbilityInputTagPressed(const FGameplayTag& inputTag);
|
||||
void AbilityInputTagReleased(const FGameplayTag& inputTag);
|
||||
|
||||
void ProcessAbilityInput(float deltaTime, bool shouldGamePaused);
|
||||
void ClearAbilityInput();
|
||||
|
||||
// @TODO: Implement UOLSGameplayAbility.
|
||||
// bool IsActivationGroupBlocked(ELyraAbilityActivationGroup Group) const;
|
||||
// void AddAbilityToActivationGroup(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* LyraAbility);
|
||||
// void RemoveAbilityFromActivationGroup(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* LyraAbility);
|
||||
// void CancelActivationGroupAbilities(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* IgnoreLyraAbility, bool bReplicateCancelAbility);
|
||||
|
||||
// Uses a gameplay effect to add the specified dynamic granted tag.
|
||||
void AddDynamicTagGameplayEffect(const FGameplayTag& tag);
|
||||
|
||||
// Removes all active instances of the gameplay effect that was used to add the specified dynamic granted tag.
|
||||
void RemoveDynamicTagGameplayEffect(const FGameplayTag& Tag);
|
||||
|
||||
/** Gets the ability target data associated with the given ability handle and activation info */
|
||||
void GetAbilityTargetData(const FGameplayAbilitySpecHandle abilityHandle, FGameplayAbilityActivationInfo activationInfo, FGameplayAbilityTargetDataHandle& outTargetDataHandle);
|
||||
|
||||
/** Sets the current tag relationship mapping, if null it will clear it out */
|
||||
void SetTagRelationshipMapping(class UOLSAbilityTagRelationshipMappingDataAsset* newMapping);
|
||||
|
||||
/** Looks at ability tags and gathers additional required and blocking tags */
|
||||
void GetAdditionalActivationTagRequirements(const FGameplayTagContainer& abilityTags, FGameplayTagContainer& outActivationRequired, FGameplayTagContainer& outActivationBlocked) const;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "OLS Ability System", DisplayName = "Enable Ability Batch RPCs", meta = (AllowPrivateAccess = true))
|
||||
uint8 bShouldEnableBatchRPC : 1 = true;
|
||||
|
||||
protected:
|
||||
|
||||
// If set, this table is used to look up tag relationships for activate and cancel
|
||||
UPROPERTY()
|
||||
TObjectPtr<class UOLSAbilityTagRelationshipMappingDataAsset> TagRelationshipMapping = nullptr;
|
||||
|
||||
// Handles to abilities that had their input pressed this frame.
|
||||
TArray<FGameplayAbilitySpecHandle> InputPressedSpecHandles;
|
||||
|
||||
// Handles to abilities that had their input released this frame.
|
||||
TArray<FGameplayAbilitySpecHandle> InputReleasedSpecHandles;
|
||||
|
||||
// Handles to abilities that have their input held.
|
||||
TArray<FGameplayAbilitySpecHandle> InputHeldSpecHandles;
|
||||
};
|
||||
|
@ -11,6 +11,8 @@ DECLARE_MULTICAST_DELEGATE_OneParam(FOLSDeathEventNativeDelegate, class AActor*
|
||||
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 */)
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSHealthComponent, Verbose, All);
|
||||
|
||||
/**
|
||||
* EOLSDeathState
|
||||
*
|
||||
|
94
Source/ols/Public/Components/OLSHeroComponent.h
Normal file
94
Source/ols/Public/Components/OLSHeroComponent.h
Normal file
@ -0,0 +1,94 @@
|
||||
// © 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 "GameplayAbilitySpecHandle.h"
|
||||
#include "Components/GameFrameworkInitStateInterface.h"
|
||||
#include "Components/PawnComponent.h"
|
||||
#include "OLSHeroComponent.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSHeroComponent, Verbose, All);
|
||||
|
||||
namespace EEndPlayReason { enum Type : int; }
|
||||
|
||||
/**
|
||||
* Component that sets up input and camera handling for player controlled pawns (or bots that simulate players).
|
||||
* This depends on a PawnExtensionComponent to coordinate initialization.
|
||||
*/
|
||||
UCLASS(Blueprintable, Meta=(BlueprintSpawnableComponent))
|
||||
class OLS_API UOLSHeroComponent : public UPawnComponent, public IGameFrameworkInitStateInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UOLSHeroComponent(const FObjectInitializer& objectInitializer);
|
||||
|
||||
//~ Begin IGameFrameworkInitStateInterface interface
|
||||
virtual FName GetFeatureName() const override;
|
||||
virtual bool CanChangeInitState(UGameFrameworkComponentManager* manager, FGameplayTag currentState, FGameplayTag desiredState) const override;
|
||||
virtual void HandleChangeInitState(UGameFrameworkComponentManager* manager, FGameplayTag currentState, FGameplayTag desiredState) override;
|
||||
virtual void OnActorInitStateChanged(const FActorInitStateChangedParams& params) override;
|
||||
virtual void CheckDefaultInitialization() override;
|
||||
//~ End IGameFrameworkInitStateInterface interface
|
||||
|
||||
/** The name of the extension event sent via UGameFrameworkComponentManager when ability inputs are ready to bind */
|
||||
static const FName NAME_BindInputsNow;
|
||||
|
||||
/** The name of this component-implemented feature */
|
||||
static const FName NAME_ActorFeatureName;
|
||||
|
||||
/** Overrides the camera from an active gameplay ability */
|
||||
//@TODO: implement UOLSCameraMode.
|
||||
// void SetAbilityCameraMode(TSubclassOf<ULyraCameraMode> CameraMode, const FGameplayAbilitySpecHandle& OwningSpecHandle);
|
||||
|
||||
/** Clears the camera override if it is set */
|
||||
void ClearAbilityCameraMode(const FGameplayAbilitySpecHandle& owningSpecHandle);
|
||||
|
||||
/** Adds mode-specific input config */
|
||||
void AddAdditionalInputConfig(const class UOLSInputConfigDataAsset* inputConfig);
|
||||
|
||||
/** Removes a mode-specific input config if it has been added */
|
||||
void RemoveAdditionalInputConfig(const class UOLSInputConfigDataAsset* inputConfig);
|
||||
|
||||
/** True if this is controlled by a real player and has progressed far enough in initialization where additional input bindings can be added */
|
||||
bool IsReadyToBindInputs() const;
|
||||
|
||||
protected:
|
||||
|
||||
//~ Begin UActorComponent interface.
|
||||
virtual void OnRegister() override;
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type endPlayReason) override;
|
||||
//~ End UActorComponent interface.
|
||||
|
||||
virtual void InitializePlayerInput(UInputComponent* playerInputComponent);
|
||||
|
||||
void Input_AbilityInputTagPressed(FGameplayTag inputTag);
|
||||
void Input_AbilityInputTagReleased(FGameplayTag inputTag);
|
||||
|
||||
void Input_Move(const struct FInputActionValue& inputActionValue);
|
||||
void Input_LookMouse(const struct FInputActionValue& inputActionValue);
|
||||
void Input_LookStick(const struct FInputActionValue& inputActionValue);
|
||||
void Input_Crouch(const struct FInputActionValue& InputActionValue);
|
||||
void Input_AutoRun(const struct FInputActionValue& inputActionValue);
|
||||
|
||||
//@TODO: implement UOLSCameraMode.
|
||||
// TSubclassOf<ULyraCameraMode> DetermineCameraMode() const;
|
||||
|
||||
protected:
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
TArray<struct FOLSInputMappingContextAndPriority> DefaultInputMappings;
|
||||
|
||||
/** Camera mode set by an ability. */
|
||||
// UPROPERTY()
|
||||
// TSubclassOf<ULyraCameraMode> AbilityCameraMode;
|
||||
|
||||
/** Spec handle for the last ability to set a camera mode. */
|
||||
FGameplayAbilitySpecHandle AbilityCameraModeOwningSpecHandle;
|
||||
|
||||
/** True when player input bindings have been applied, will never be true for non - players */
|
||||
bool bIsReadyToBindInputs;
|
||||
};
|
103
Source/ols/Public/Components/OLSPawnExtensionComponent.h
Normal file
103
Source/ols/Public/Components/OLSPawnExtensionComponent.h
Normal file
@ -0,0 +1,103 @@
|
||||
// © 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/GameFrameworkInitStateInterface.h"
|
||||
#include "Components/PawnComponent.h"
|
||||
#include "OLSPawnExtensionComponent.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSPawnExtensionComponent, Verbose, All);
|
||||
|
||||
namespace EEndPlayReason { enum Type : int; }
|
||||
|
||||
/**
|
||||
* Component that adds functionality to all Pawn classes so it can be used for characters/vehicles/etc.
|
||||
* This coordinates the initialization of other components.
|
||||
*/
|
||||
UCLASS()
|
||||
class OLS_API UOLSPawnExtensionComponent : public UPawnComponent, public IGameFrameworkInitStateInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UOLSPawnExtensionComponent(const FObjectInitializer& objectInitializer);
|
||||
|
||||
/** The name of this overall feature, this one depends on the other named component features */
|
||||
static const FName NAME_ActorFeatureName;
|
||||
|
||||
//~ Begin UActorComponent interface
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
//~ End UActorComponent interface
|
||||
|
||||
//~ Begin IGameFrameworkInitStateInterface interface
|
||||
virtual FName GetFeatureName() const override;
|
||||
virtual bool CanChangeInitState(UGameFrameworkComponentManager* manager, FGameplayTag currentState, FGameplayTag desiredState) const override;
|
||||
virtual void HandleChangeInitState(UGameFrameworkComponentManager* manager, FGameplayTag currentState, FGameplayTag desiredState) override;
|
||||
virtual void OnActorInitStateChanged(const FActorInitStateChangedParams& Params) override;
|
||||
virtual void CheckDefaultInitialization() override;
|
||||
//~ End IGameFrameworkInitStateInterface interface
|
||||
|
||||
/** Returns the pawn extension component if one exists on the specified actor. */
|
||||
UFUNCTION(BlueprintPure, Category = "OLS|Pawn")
|
||||
static class UOLSPawnExtensionComponent* FindPawnExtensionComponent(const AActor* actor);
|
||||
|
||||
/** Gets the current ability system component, which may be owned by a different actor */
|
||||
UFUNCTION(BlueprintPure, Category = "OLS|Pawn")
|
||||
class UOLSAbilitySystemComponent* GetOLSAbilitySystemComponent() const ;
|
||||
|
||||
public:
|
||||
|
||||
/** Gets the pawn data, which is used to specify pawn properties in data */
|
||||
template <class T>
|
||||
const T* GetPawnData() const { return Cast<T>(PawnData); }
|
||||
|
||||
/** Sets the current pawn data */
|
||||
void SetPawnData(const class UOLSPawnDataAsset* pawnData);
|
||||
|
||||
/** Should be called by the owning pawn to become the avatar of the ability system. */
|
||||
void InitializeAbilitySystem(class UOLSAbilitySystemComponent* asc, AActor* ownerActor);
|
||||
|
||||
/** Should be called by the owning pawn to remove itself as the avatar of the ability system. */
|
||||
void UninitializeAbilitySystem();
|
||||
|
||||
/** Should be called by the owning pawn when the pawn's controller changes. */
|
||||
void HandleControllerChanged();
|
||||
|
||||
/** Should be called by the owning pawn when the player state has been replicated. */
|
||||
void HandlePlayerStateReplicated();
|
||||
|
||||
/** Should be called by the owning pawn when the input component is setup. */
|
||||
void SetupPlayerInputComponent();
|
||||
|
||||
/** Register with the OnAbilitySystemInitialized delegate and broadcast if our pawn has been registered with the ability system component */
|
||||
void OnAbilitySystemInitialized_RegisterAndCall(FSimpleMulticastDelegate::FDelegate delegate);
|
||||
|
||||
/** Register with the OnAbilitySystemUninitialized delegate fired when our pawn is removed as the ability system's avatar actor */
|
||||
void OnAbilitySystemUninitialized_Register(FSimpleMulticastDelegate::FDelegate delegate);
|
||||
|
||||
protected:
|
||||
|
||||
UFUNCTION()
|
||||
void OnRep_PawnData();
|
||||
|
||||
protected:
|
||||
|
||||
/** Delegate fired when our pawn becomes the ability system's avatar actor */
|
||||
FSimpleMulticastDelegate OnAbilitySystemInitialized;
|
||||
|
||||
/** Delegate fired when our pawn is removed as the ability system's avatar actor */
|
||||
FSimpleMulticastDelegate OnAbilitySystemUninitialized;
|
||||
|
||||
protected:
|
||||
|
||||
/** Pawn data used to create the pawn. Specified from a spawn function or on a placed instance. */
|
||||
UPROPERTY(EditInstanceOnly, ReplicatedUsing = OnRep_PawnData, Category = "Lyra|Pawn")
|
||||
TObjectPtr<const class UOLSPawnDataAsset> PawnData = nullptr;
|
||||
|
||||
/** Pointer to the ability system component that is cached for convenience. */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<class UOLSAbilitySystemComponent > AbilitySystemComponent = nullptr;
|
||||
};
|
@ -26,6 +26,8 @@ public:
|
||||
FGameplayTag InputTag = FGameplayTag::EmptyTag;
|
||||
};
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSInputConfigDataAsset, Verbose, All);
|
||||
|
||||
/**
|
||||
* UOLSInputConfigDataAsset
|
||||
*
|
||||
|
@ -0,0 +1,98 @@
|
||||
// © 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 "OLSGameFeatureAction_WorldActionBase.h"
|
||||
#include "OLSGameFeatureAction_AddInputContextMapping.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSGameFA_AddInputContextMapping, Verbose, All);
|
||||
|
||||
USTRUCT()
|
||||
struct FOLSInputMappingContextAndPriority
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="Input", meta=(AssetBundles="Client,Server"))
|
||||
TSoftObjectPtr<class UInputMappingContext> InputMapping;
|
||||
|
||||
// Higher priority input mappings will be prioritized over mappings with a lower priority.
|
||||
UPROPERTY(EditAnywhere, Category="Input")
|
||||
int32 Priority = 0;
|
||||
|
||||
/** If true, then this mapping context will be registered with the settings when this game feature action is registered. */
|
||||
UPROPERTY(EditAnywhere, Category="Input")
|
||||
uint8 bShouldRegisterWithSettings : 1 = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds InputMappingContext to local players' EnhancedInput system.
|
||||
* Expects that local players are set up to use the EnhancedInput system.
|
||||
*/
|
||||
UCLASS(MinimalAPI, meta = (DisplayName = "Add Input Mapping"))
|
||||
class UOLSGameFeatureAction_AddInputContextMapping : public UOLSGameFeatureAction_WorldActionBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
//~ Begin UGameFeatureAction interface
|
||||
virtual void OnGameFeatureRegistering() override;
|
||||
virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& context) override;
|
||||
virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& context) override;
|
||||
virtual void OnGameFeatureUnregistering() override;
|
||||
//~ End UGameFeatureAction interface
|
||||
|
||||
//~ Begin UObject interface
|
||||
#if WITH_EDITOR
|
||||
virtual EDataValidationResult IsDataValid(class FDataValidationContext& context) const override;
|
||||
#endif
|
||||
//~ End UObject interface
|
||||
|
||||
private:
|
||||
|
||||
/** Registers owned Input Mapping Contexts to the Input Registry Subsystem. Also binds onto the start of GameInstances and the adding/removal of Local Players. */
|
||||
void RegisterInputMappingContexts();
|
||||
|
||||
/** Registers owned Input Mapping Contexts to the Input Registry Subsystem for a specified GameInstance. This also gets called by a GameInstance Start. */
|
||||
void RegisterInputContextMappingsForGameInstance(class UGameInstance* gameInstance);
|
||||
|
||||
/** Registers owned Input Mapping Contexts to the Input Registry Subsystem for a specified Local Player. This also gets called when a Local Player is added. */
|
||||
void RegisterInputMappingContextsForLocalPlayer(class ULocalPlayer* localPlayer);
|
||||
|
||||
/** Unregisters owned Input Mapping Contexts from the Input Registry Subsystem. Also unbinds from the start of GameInstances and the adding/removal of Local Players. */
|
||||
void UnregisterInputMappingContexts();
|
||||
|
||||
/** Unregisters owned Input Mapping Contexts from the Input Registry Subsystem for a specified GameInstance. */
|
||||
void UnregisterInputContextMappingsForGameInstance(class UGameInstance* gameInstance);
|
||||
|
||||
/** Unregisters owned Input Mapping Contexts from the Input Registry Subsystem for a specified Local Player. This also gets called when a Local Player is removed. */
|
||||
void UnregisterInputMappingContextsForLocalPlayer(class ULocalPlayer* localPlayer);
|
||||
|
||||
//~UGameFeatureAction_WorldActionBase interface
|
||||
virtual void AddToWorld(const FWorldContext& worldContext, const struct FGameFeatureStateChangeContext& changeContext) override;
|
||||
//~End of UGameFeatureAction_WorldActionBase interface
|
||||
|
||||
struct FPerContextData
|
||||
{
|
||||
TArray<TSharedPtr<struct FComponentRequestHandle>> ExtensionRequestHandles;
|
||||
TArray<TWeakObjectPtr<class APlayerController>> ControllersAddedTo;
|
||||
};
|
||||
|
||||
void Reset(FPerContextData& activeData);
|
||||
void HandleControllerExtension(class AActor* actor, FName eventName, struct FGameFeatureStateChangeContext changeContext);
|
||||
void AddInputMappingForPlayer(class UPlayer* player, struct FPerContextData& activeData);
|
||||
void RemoveInputMapping(class APlayerController* playerController, FPerContextData& activeData);
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="Input")
|
||||
TArray<FOLSInputMappingContextAndPriority> InputMappings;
|
||||
|
||||
private:
|
||||
|
||||
TMap<struct FGameFeatureStateChangeContext, struct FPerContextData> ContextData;
|
||||
|
||||
/** Delegate for when the game instance is changed to register IMC's */
|
||||
FDelegateHandle RegisterInputContextMappingsForGameInstanceHandle;
|
||||
};
|
@ -0,0 +1,38 @@
|
||||
// © 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 "GameFeatureAction.h"
|
||||
#include "GameFeaturesSubsystem.h"
|
||||
|
||||
#include "OLSGameFeatureAction_WorldActionBase.generated.h"
|
||||
|
||||
/**
|
||||
* Base class for GameFeatureActions that wish to do something world specific.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class OLS_API UOLSGameFeatureAction_WorldActionBase : public UGameFeatureAction
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
//~ Begin UGameFeatureAction interface
|
||||
virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& context) override;
|
||||
virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& context) override;
|
||||
//~ End UGameFeatureAction interface
|
||||
|
||||
private:
|
||||
|
||||
void HandleGameInstanceStart(UGameInstance* gameInstance, FGameFeatureStateChangeContext changeContext);
|
||||
|
||||
/** Override with the action-specific logic */
|
||||
virtual void AddToWorld(const FWorldContext& worldContext,
|
||||
const FGameFeatureStateChangeContext& changeContext) PURE_VIRTUAL(
|
||||
UOLSGameFeatureAction_WorldActionBase::AddToWorld);
|
||||
|
||||
private:
|
||||
|
||||
TMap<FGameFeatureStateChangeContext, FDelegateHandle> GameInstanceStartHandles;
|
||||
};
|
@ -9,6 +9,8 @@
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "OLSModularActor.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSModularActor, Verbose, All);
|
||||
|
||||
/** Minimal class that supports extension by game feature plugins, direct child of AActor */
|
||||
UCLASS(Blueprintable)
|
||||
class OLS_API AOLSModularActor : public AActor, public IAbilitySystemInterface, public IGameplayTagAssetInterface
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "GameFramework/Character.h"
|
||||
#include "OLSModularCharacter.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSModularCharacter, Verbose, All);
|
||||
|
||||
/**
|
||||
* Minimal class that supports extension by game feature plugins
|
||||
*
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "GameFramework/DefaultPawn.h"
|
||||
#include "OLSModularDefaultPawn.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSModularDefaultPawn, Verbose, All);
|
||||
|
||||
/** Minimal class that supports extension by game feature plugins, direct child of ADefaultPawn */
|
||||
UCLASS(Blueprintable)
|
||||
class OLS_API AOLSModularDefaultPawn : public ADefaultPawn, public IAbilitySystemInterface, public IGameplayTagAssetInterface
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "OLSModularPawn.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSModularPawn, Verbose, All);
|
||||
|
||||
/** Minimal class that supports extension by game feature plugins, direct child of APawn */
|
||||
UCLASS(Blueprintable)
|
||||
class OLS_API AOLSModularPawn : public APawn, public IAbilitySystemInterface, public IGameplayTagAssetInterface
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "GameFramework/PlayerState.h"
|
||||
#include "OLSModularPlayerState.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSModularPlayerState, Verbose, All);
|
||||
|
||||
/** Minimal class that supports extension by game feature plugins */
|
||||
UCLASS(Blueprintable)
|
||||
class OLS_API AOLSModularPlayerState : public APlayerState, public IAbilitySystemInterface
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "GameFramework/Character.h"
|
||||
#include "OLSModularPlayerStateCharacter.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSModularPlayerStateCharacter, Verbose, All);
|
||||
|
||||
/**
|
||||
* Minimal class that supports extension by game feature plugins.
|
||||
*
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "Systems/OLSGameplayTagStack.h"
|
||||
#include "OLSPlayerState.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogOLSPlayerState, Verbose, All);
|
||||
|
||||
/**
|
||||
* AOLSPlayerState
|
||||
|
@ -27,7 +27,8 @@ public class ols : ModuleRules
|
||||
"InputCore",
|
||||
"AnimGraphRuntime",
|
||||
"GameplayMessageRuntime",
|
||||
"NetCore"
|
||||
"NetCore",
|
||||
"EnhancedInput",
|
||||
});
|
||||
|
||||
// Header files path
|
||||
@ -43,5 +44,8 @@ public class ols : ModuleRules
|
||||
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
|
||||
|
||||
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
|
||||
|
||||
|
||||
SetupGameplayDebuggerSupport(Target);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user