Compare commits

...

3 Commits

Author SHA1 Message Date
2c4a71b343 Added GameFeatureActions, HeroComponent, and AbilityCost. 2025-01-17 15:14:51 -07:00
51306c57a9 Implemented OLSPawnExtensionComponent.
Added some functionalities for OLSAbilitySystemComponent
2025-01-16 16:04:14 -07:00
e0545d6323 Cleared all TODOs related to custom logs 2025-01-16 14:24:05 -07:00
32 changed files with 2300 additions and 65 deletions

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}
@ -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()));
}
}

View 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());
}
}
}

View 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();
}

View File

@ -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;
@ -42,8 +47,9 @@ const UInputAction* UOLSInputConfigDataAsset::FindAbilityInputActionForTag(
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));
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;

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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,8 +49,8 @@ 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(),
OLS_LOG(LogOLSModularDefaultPawn, Verbose,
TEXT("PostInitProperties for %s - Setting up ASC Replication Mode to: %d"), GET_UOBJECT_NAME(this),
ReplicationMode);
AbilitySystemComponent->SetReplicationMode(ReplicationMode);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}
}

View 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;
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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
*

View 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;
};

View 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;
};

View File

@ -26,6 +26,8 @@ public:
FGameplayTag InputTag = FGameplayTag::EmptyTag;
};
DECLARE_LOG_CATEGORY_EXTERN(LogOLSInputConfigDataAsset, Verbose, All);
/**
* UOLSInputConfigDataAsset
*

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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

View File

@ -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
*

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.
*

View File

@ -7,6 +7,7 @@
#include "Systems/OLSGameplayTagStack.h"
#include "OLSPlayerState.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogOLSPlayerState, Verbose, All);
/**
* AOLSPlayerState

View File

@ -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);
}
}