37. Effect Actor Improved

This commit is contained in:
Edward R. Gonzalez 2024-04-22 12:01:30 -04:00
parent eaed6cf337
commit cc1636b687
21 changed files with 202 additions and 97 deletions

Binary file not shown.

Binary file not shown.

View File

@ -130,7 +130,3 @@ ManualIPAddress=
+CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic") +CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic")
+CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle") +CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle")
+CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn") +CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn")
[CoreRedirects]
+ClassRedirects=(OldName="/Script/Gasa.UI_HostWidget",NewName="/Script/Gasa.HUDHostWidget")

Binary file not shown.

BIN
Project/Content/Core/Pickups/BP_HealthPotionDemo.uasset (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
Project/Content/Levels/StartupMap.umap (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "GasaAbilitySystemComponent.h" #include "GasaAbilitySystemComponent.h"
#include "AbilitySystemInterface.h" #include "AbilitySystemInterface.h"
#include "AbilitySystemGlobals.h"
namespace Gasa namespace Gasa
{ {
@ -13,4 +15,24 @@ namespace Gasa
} }
return nullptr; return nullptr;
} }
// From: UAbilitySystemGlobals::GetAbilitySystemComponentFromActor
inline
UGasaAbilitySystemComp* GetAbilitySystem(AActor* Actor, bool LookForComponent = false)
{
if (Actor == nullptr)
return nullptr;
const IAbilitySystemInterface* ASI = Cast<IAbilitySystemInterface>(Actor);
if (ASI)
return Cast<UGasaAbilitySystemComp>(ASI->GetAbilitySystemComponent());
if (LookForComponent)
{
// Fall back to a component search to better support BP-only actors
return Cast<UGasaAbilitySystemComp>(Actor->FindComponentByClass<UAbilitySystemComponent>());
}
return nullptr;
}
} }

View File

@ -10,29 +10,39 @@ UGasaAttributeSet::UGasaAttributeSet()
{ {
InitHealth( 50.f ); InitHealth( 50.f );
InitMaxHealth( 100.f ); InitMaxHealth( 100.f );
InitMana( 25.f ); InitMana( 50.f );
InitMaxMana( 50.f ); InitMaxMana( 50.f );
} }
#pragma region Rep Notifies
void UGasaAttributeSet::Client_OnRep_Health( FGameplayAttributeData& PrevHealth ) void UGasaAttributeSet::Client_OnRep_Health( FGameplayAttributeData& PrevHealth )
{ {
GAMEPLAYATTRIBUTE_REPNOTIFY( UGasaAttributeSet, Health, PrevHealth ) // From GAMEPLAYATTRIBUTE_REPNOTIFY
static FProperty* UGasaAttributeSetProperty = FindFieldChecked<FProperty>( StaticClass(), GET_MEMBER_NAME_CHECKED( UGasaAttributeSet, Health ) );
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication( FGameplayAttribute( UGasaAttributeSetProperty ), Health, PrevHealth );
} }
void UGasaAttributeSet::Client_OnRep_MaxHealth( FGameplayAttributeData& PrevMaxHealth ) void UGasaAttributeSet::Client_OnRep_MaxHealth( FGameplayAttributeData& PrevMaxHealth )
{ {
GAMEPLAYATTRIBUTE_REPNOTIFY( UGasaAttributeSet, MaxHealth, PrevMaxHealth ) // From GAMEPLAYATTRIBUTE_REPNOTIFY
static FProperty* UGasaAttributeSetProperty = FindFieldChecked<FProperty>( StaticClass(), GET_MEMBER_NAME_CHECKED( UGasaAttributeSet, MaxHealth ) );
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication( FGameplayAttribute( UGasaAttributeSetProperty ), MaxHealth, PrevMaxHealth );
} }
void UGasaAttributeSet::Client_OnRep_Mana( FGameplayAttributeData& PrevMana ) void UGasaAttributeSet::Client_OnRep_Mana( FGameplayAttributeData& PrevMana )
{ {
GAMEPLAYATTRIBUTE_REPNOTIFY( UGasaAttributeSet, Mana, PrevMana ) // From GAMEPLAYATTRIBUTE_REPNOTIFY
static FProperty* UGasaAttributeSetProperty = FindFieldChecked<FProperty>( StaticClass(), GET_MEMBER_NAME_CHECKED( UGasaAttributeSet, Mana ) );
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication( FGameplayAttribute( UGasaAttributeSetProperty ), Mana, PrevMana );
} }
void UGasaAttributeSet::Client_OnRep_MaxMana( FGameplayAttributeData& PrevMaxMana ) void UGasaAttributeSet::Client_OnRep_MaxMana( FGameplayAttributeData& PrevMaxMana )
{ {
GAMEPLAYATTRIBUTE_REPNOTIFY( UGasaAttributeSet, MaxMana, PrevMaxMana ) // From GAMEPLAYATTRIBUTE_REPNOTIFY
static FProperty* UGasaAttributeSetProperty = FindFieldChecked<FProperty>( StaticClass(), GET_MEMBER_NAME_CHECKED( UGasaAttributeSet, MaxMana ) );
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication( FGameplayAttribute( UGasaAttributeSetProperty ), MaxMana, PrevMaxMana );
} }
#pragma endregion Rep Notifies
void UGasaAttributeSet::GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps ) const void UGasaAttributeSet::GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps ) const
{ {
Super::GetLifetimeReplicatedProps( OutLifetimeProps ); Super::GetLifetimeReplicatedProps( OutLifetimeProps );

View File

@ -1,61 +1,23 @@
#include "GasaEffectActor.h" #include "GasaEffectActor.h"
#include "AbilitySystemInterface.h" #include "GasaAbilitySystemComponent_Inlines.h"
#include "GasaAttributeSet.h" using namespace Gasa;
#include "GasaAttributeSet_Inlines.h"
#include "Components/SphereComponent.h"
AGasaEffectActor::AGasaEffectActor() AGasaEffectActor::AGasaEffectActor()
{ {
PrimaryActorTick.bCanEverTick = false; PrimaryActorTick.bCanEverTick = false;
Mesh = CreateDefaultSubobject<UStaticMeshComponent>("Mesh"); RootComponent = CreateDefaultSubobject<USceneComponent>("Root");
Sphere = CreateDefaultSubobject<USphereComponent>("Sphere");
SetRootComponent(Mesh);
Sphere->SetupAttachment(Mesh);
} }
void AGasaEffectActor::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent void AGasaEffectActor::ApplyEffectToTarget(AActor* Target, TSubclassOf<UGameplayEffect> EffectClass)
, AActor* OtherActor
, UPrimitiveComponent* OtherComp
, int32 OtherBodyIndex
, bool bFromSweep
, FHitResult const& SweepResult)
{ {
// Demo of "restricted way" UGasaAbilitySystemComp* AS = GetAbilitySystem(Target, true);
if ( ! OtherActor->Implements<UAbilitySystemInterface>())
return; FGameplayEffectContextHandle
Context = AS->MakeEffectContext();
Context.AddSourceObject(Target);
IAbilitySystemInterface* ASI = Cast<IAbilitySystemInterface>(OtherActor); FGameplayEffectSpecHandle Spec = AS->MakeOutgoingSpec( EffectClass, 1.0f, Context );
if (ASI == nullptr) AS->ApplyGameplayEffectSpecToSelf( * Spec.Data );
return;
// TODO(Ed): Change this to use a gameplay effect instead
UAbilitySystemComponent* AbilitySystem = ASI->GetAbilitySystemComponent();
UGasaAttributeSet* MutAttributes = const_cast<UGasaAttributeSet*>(Gasa::GetAttributeSet(AbilitySystem));
MutAttributes->SetHealth( MutAttributes->GetHealth() + 25.f );
Destroy();
}
void AGasaEffectActor::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent
, AActor* OtherActor
, UPrimitiveComponent* OtherComp
, int32 OtherBodyIndex)
{
}
void AGasaEffectActor::BeginPlay()
{
Super::BeginPlay();
}
void AGasaEffectActor::PostInitializeComponents()
{
Super::PostInitializeComponents();
Sphere->OnComponentBeginOverlap.AddUniqueDynamic(this, &ThisClass::OnOverlapBegin);
Sphere->OnComponentEndOverlap.AddUniqueDynamic(this, &ThisClass::OnOverlapEnd);
} }

View File

@ -1,39 +1,19 @@
#pragma once #pragma once
#include "GasaCommon.h" #include "GasaCommon.h"
#include "GasaEffectActor.generated.h" #include "GasaEffectActor.generated.h"
UCLASS() UCLASS()
class GASA_API AGasaEffectActor : public AActor class GASA_API AGasaEffectActor : public AActor
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
UPROPERTY(VisibleAnywhere) UPROPERTY(EditAnywhere, Category = "Applied Effects")
TObjectPtr<UStaticMeshComponent> Mesh; TSoftClassPtr<UGameplayEffect> InstantEffectClass;
UPROPERTY(VisibleAnywhere)
TObjectPtr<USphereComponent> Sphere;
AGasaEffectActor(); AGasaEffectActor();
UFUNCTION() void ApplyEffectToTarget(AActor* Target, TSubclassOf<UGameplayEffect> EffectClass );
void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent
, AActor* OtherActor
, UPrimitiveComponent* OtherComp
, int32 OtherBodyIndex
, bool bFromSweep
, FHitResult const& SweepResult);
UFUNCTION()
void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent
, AActor* OtherActor
, UPrimitiveComponent* OtherComp
, int32 OtherBodyIndex);
#pragma region Actor
void BeginPlay() override;
void PostInitializeComponents() override;
#pragma endregion Actor
}; };

View File

@ -0,0 +1,62 @@
#include "GasaEffectActorDemo.h"
#include "AbilitySystemInterface.h"
#include "GasaAttributeSet.h"
#include "GasaAttributeSet_Inlines.h"
#include "Components/SphereComponent.h"
AGasaEffectActorDemo::AGasaEffectActorDemo()
{
PrimaryActorTick.bCanEverTick = false;
Mesh = CreateDefaultSubobject<UStaticMeshComponent>("Mesh");
Sphere = CreateDefaultSubobject<USphereComponent>("Sphere");
SetRootComponent(Mesh);
Sphere->SetupAttachment(Mesh);
}
void AGasaEffectActorDemo::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent
, AActor* OtherActor
, UPrimitiveComponent* OtherComp
, int32 OtherBodyIndex
, bool bFromSweep
, FHitResult const& SweepResult)
{
// Demo of "restricted way"
if ( ! OtherActor->Implements<UAbilitySystemInterface>())
return;
IAbilitySystemInterface* ASI = Cast<IAbilitySystemInterface>(OtherActor);
if (ASI == nullptr)
return;
// TODO(Ed): Change this to use a gameplay effect instead
UAbilitySystemComponent* AbilitySystem = ASI->GetAbilitySystemComponent();
UGasaAttributeSet* MutAttributes = const_cast<UGasaAttributeSet*>(Gasa::GetAttributeSet(AbilitySystem));
MutAttributes->SetHealth( MutAttributes->GetHealth() + 25.f );
MutAttributes->SetMana( MutAttributes->GetMana() - 25.f );
Destroy();
}
void AGasaEffectActorDemo::OnOverlapEnd(UPrimitiveComponent* OverlappedComponent
, AActor* OtherActor
, UPrimitiveComponent* OtherComp
, int32 OtherBodyIndex)
{
}
void AGasaEffectActorDemo::BeginPlay()
{
Super::BeginPlay();
}
void AGasaEffectActorDemo::PostInitializeComponents()
{
Super::PostInitializeComponents();
Sphere->OnComponentBeginOverlap.AddUniqueDynamic(this, &ThisClass::OnOverlapBegin);
Sphere->OnComponentEndOverlap.AddUniqueDynamic(this, &ThisClass::OnOverlapEnd);
}

View File

@ -0,0 +1,40 @@
#pragma once
#include "GasaCommon.h"
#include "GasaEffectActorDemo.generated.h"
// Old demonstration code used before part 37.
UCLASS()
class GASA_API AGasaEffectActorDemo : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(VisibleAnywhere)
TObjectPtr<UStaticMeshComponent> Mesh;
UPROPERTY(VisibleAnywhere)
TObjectPtr<USphereComponent> Sphere;
AGasaEffectActorDemo();
UFUNCTION()
void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent
, AActor* OtherActor
, UPrimitiveComponent* OtherComp
, int32 OtherBodyIndex
, bool bFromSweep
, FHitResult const& SweepResult);
UFUNCTION()
void OnOverlapEnd(UPrimitiveComponent* OverlappedComponent
, AActor* OtherActor
, UPrimitiveComponent* OtherComp
, int32 OtherBodyIndex);
#pragma region Actor
void BeginPlay() override;
void PostInitializeComponents() override;
#pragma endregion Actor
};

View File

@ -22,6 +22,7 @@ class UAbilitySystemComponent;
class UAbilitySystemInterface; class UAbilitySystemInterface;
class UAttributeSet; class UAttributeSet;
class UCameraComponent; class UCameraComponent;
class UGameplayEffect;
class UInputAction; class UInputAction;
class UInputMappingContext; class UInputMappingContext;
class USphereComponent; class USphereComponent;

View File

@ -29,6 +29,8 @@ void UHostWidgetController::MaxManaChanged( FOnAttributeChangeData const& Attrib
void UHostWidgetController::BroadcastInitialValues() void UHostWidgetController::BroadcastInitialValues()
{ {
// This function is managed by: GenGasa/GenGasa_HostWidgetController.cpp
UGasaAttributeSet* GasaAttribs = Cast<UGasaAttributeSet>( Data.Attributes ); UGasaAttributeSet* GasaAttribs = Cast<UGasaAttributeSet>( Data.Attributes );
if ( GasaAttribs ) if ( GasaAttribs )
{ {
@ -37,10 +39,13 @@ void UHostWidgetController::BroadcastInitialValues()
Event_OnManaChanged.Broadcast( GasaAttribs->GetMana() ); Event_OnManaChanged.Broadcast( GasaAttribs->GetMana() );
Event_OnMaxManaChanged.Broadcast( GasaAttribs->GetMaxMana() ); Event_OnMaxManaChanged.Broadcast( GasaAttribs->GetMaxMana() );
} }
BindCallbacksToDependencies();
} }
void UHostWidgetController::BindCallbacksToDependencies() void UHostWidgetController::BindCallbacksToDependencies()
{ {
// This function is managed by: GenGasa/GenGasa_HostWidgetController.cpp
UGasaAbilitySystemComp* AbilitySystem = Cast<UGasaAbilitySystemComp>( Data.AbilitySystem ); UGasaAbilitySystemComp* AbilitySystem = Cast<UGasaAbilitySystemComp>( Data.AbilitySystem );
UGasaAttributeSet* GasaAttribs = Cast<UGasaAttributeSet>( Data.Attributes ); UGasaAttributeSet* GasaAttribs = Cast<UGasaAttributeSet>( Data.Attributes );

View File

@ -301,4 +301,4 @@ void gen_UHostWidgetController()
source.write(); source.write();
format_file(path_gasa_ui "HostWidgetController.cpp"); format_file(path_gasa_ui "HostWidgetController.cpp");
} }
} }

View File

@ -307,19 +307,37 @@ void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties
void impl_attribute_fields( CodeBody body, StrC class_name, Array<StringCached> properties ) void impl_attribute_fields( CodeBody body, StrC class_name, Array<StringCached> properties )
{ {
body.append(def_pragma( txt("region Rep Notifies")));
for ( String property : properties ) for ( String property : properties )
{ {
body.append(fmt_newline); body.append(fmt_newline);
CodeFn field_impl = parse_function( token_fmt( "class_name", class_name, "property", (StrC)property, CodeFn field_impl = parse_function( token_fmt(
"class_name", class_name, "property", (StrC)property, "from_notice", txt("\n// From GAMEPLAYATTRIBUTE_REPNOTIFY\n"),
stringize( stringize(
void <class_name>::Client_OnRep_<property>(FGameplayAttributeData& Prev<property>) void <class_name>::Client_OnRep_<property>(FGameplayAttributeData& Prev<property>)
{ {
GAMEPLAYATTRIBUTE_REPNOTIFY(<class_name>, <property>, Prev<property>) <from_notice>
static FProperty* <class_name>Property = FindFieldChecked<FProperty>( StaticClass(), GET_MEMBER_NAME_CHECKED(<class_name>, <property>));
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication(FGameplayAttribute(<class_name>Property), <property>, Prev<property>);
} }
))); )));
body.append( field_impl ); body.append( field_impl );
} }
body.append( def_pragma( txt("endregion Rep Notifies")));
}
inline
Code gen_GAMEPLAYATTRIBUTE_REPNOTIFY(StrC class_name, StrC property_name, StrC old_value)
{
Code rep_notify = code_fmt(
"class_name", class_name
, "property_name", property_name
, "old_value", old_value,
stringize(
static FProperty* <class_name>Property = FindFieldChecked<FProperty>(<class_name>::StaticClass(), GET_MEMBER_NAME_CHECKED(<class_name>, <property_name>));
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication(FGameplayAttribute(<class_name>Property), <property_name>, <old_value>);
));
} }
#pragma pop_macro("FORCEINLINE") #pragma pop_macro("FORCEINLINE")

View File

@ -8635,18 +8635,18 @@ namespace parser
result->Type = Function_Body; result->Type = Function_Body;
// TODO : Support actual parsing of function body // TODO : Support actual parsing of function body
Token start = currtok; Token start = currtok_noskip;
s32 level = 0; s32 level = 0;
while ( left && ( currtok.Type != TokType::BraceCurly_Close || level > 0 ) ) while ( left && ( currtok_noskip.Type != TokType::BraceCurly_Close || level > 0 ) )
{ {
if ( currtok.Type == TokType::BraceCurly_Open ) if ( currtok_noskip.Type == TokType::BraceCurly_Open )
level++; level++;
else if ( currtok.Type == TokType::BraceCurly_Close && level > 0 ) else if ( currtok_noskip.Type == TokType::BraceCurly_Close && level > 0 )
level--; level--;
eat( currtok.Type ); eat( currtok_noskip.Type );
} }
Token previous = prevtok; Token previous = prevtok;