Added GameFeatureActions, HeroComponent, and AbilityCost.

This commit is contained in:
LongLy 2025-01-17 15:14:51 -07:00
parent 51306c57a9
commit 2c4a71b343
13 changed files with 1308 additions and 4 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

@ -695,7 +695,7 @@ void UOLSAbilitySystemComponent::RemoveDynamicTagGameplayEffect(const FGameplayT
{
OLS_LOG(LogOLSAbilitySystemComponent, Warning,
TEXT("RemoveDynamicTagGameplayEffect: Unable to find gameplay effect [%s]."),
UOLSGameDataAsset::Get().DynamicTagGameplayEffect.GetAssetName());
*UOLSGameDataAsset::Get().DynamicTagGameplayEffect.GetAssetName());
return;
}

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

@ -7,9 +7,12 @@
#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)
{
@ -22,6 +25,13 @@ UOLSPawnExtensionComponent::UOLSPawnExtensionComponent(const FObjectInitializer&
AbilitySystemComponent = nullptr;
}
void UOLSPawnExtensionComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UOLSPawnExtensionComponent, PawnData);
}
FName UOLSPawnExtensionComponent::GetFeatureName() const
{
return NAME_ActorFeatureName;

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

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

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

@ -28,6 +28,10 @@ public:
/** 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;

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

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