Files
OT6-Reconstruction/FortniteGame/Private/FortAbilitySystem.cpp
T
2025-11-15 23:04:22 +01:00

765 lines
21 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
// Fortnite Ability System - UE 4.12 Native Implementation (No GameplayAbilities Plugin)
// Source: D:\BuildFarm\buildmachine_++Fortnite+Release-Live\FortniteGame\Source\FortniteGame\Private\Abilities\FortAbilitySystemComponent.cpp
#include "FortAbilitySystem.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "TimerManager.h"
#include "Net/UnrealNetwork.h"
#include "FortWeapon.h"
#include "BuildingActor.h"
// ============================================================================
// UFortAbilitySystemComponent
// ============================================================================
UFortAbilitySystemComponent::UFortAbilitySystemComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, NextAbilityHandle(1)
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = true;
bHealthRegenDelayed = false;
bShieldRegenDelayed = false;
}
void UFortAbilitySystemComponent::InitializeComponent()
{
Super::InitializeComponent();
// Initialize default attributes
InitializeAttributes();
// Start regeneration timers
StartHealthRegeneration();
StartShieldRegeneration();
}
void UFortAbilitySystemComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// Additional ability system ticking logic
}
void UFortAbilitySystemComponent::InitializeAttributes()
{
// Initialize default attributes
Attributes.Add(FName("Health"), 100.0f);
Attributes.Add(FName("MaxHealth"), 100.0f);
Attributes.Add(FName("Shield"), 0.0f);
Attributes.Add(FName("MaxShield"), 100.0f);
Attributes.Add(FName("Energy"), 100.0f);
Attributes.Add(FName("MaxEnergy"), 100.0f);
}
FFortAbilityHandle UFortAbilitySystemComponent::GiveAbility(TSubclassOf<UFortGameplayAbility> AbilityClass)
{
if (!AbilityClass)
{
return FFortAbilityHandle();
}
// Create ability instance
UFortGameplayAbility* NewAbility = NewObject<UFortGameplayAbility>(this, AbilityClass);
if (NewAbility)
{
NewAbility->OwningAbilitySystemComponent = this;
int32 Handle = NextAbilityHandle++;
GrantedAbilities.Add(Handle, NewAbility);
UE_LOG(LogTemp, Log, TEXT("Granted ability: %s (Handle: %d)"), *NewAbility->GetName(), Handle);
return FFortAbilityHandle(Handle);
}
return FFortAbilityHandle();
}
void UFortAbilitySystemComponent::RemoveAbility(FFortAbilityHandle Handle)
{
if (GrantedAbilities.Contains(Handle.Handle))
{
UFortGameplayAbility* Ability = GrantedAbilities[Handle.Handle];
UE_LOG(LogTemp, Log, TEXT("Removed ability: %s (Handle: %d)"),
Ability ? *Ability->GetName() : TEXT("null"), Handle.Handle);
GrantedAbilities.Remove(Handle.Handle);
}
}
bool UFortAbilitySystemComponent::TryActivateAbility(FFortAbilityHandle Handle)
{
if (!GrantedAbilities.Contains(Handle.Handle))
{
return false;
}
UFortGameplayAbility* Ability = GrantedAbilities[Handle.Handle];
if (!Ability)
{
return false;
}
// Check if ability can activate
if (!Ability->CanActivateAbility(this))
{
return false;
}
// Check and apply cost
if (!Ability->CheckCost(this))
{
UE_LOG(LogTemp, Warning, TEXT("Cannot activate ability %s - insufficient resources"), *Ability->GetName());
return false;
}
Ability->ApplyCost(this);
// Activate ability
FFortAbilityActivationInfo ActivationInfo;
ActivationInfo.Instigator = GetOwner();
ActivationInfo.ActivationTime = GetWorld()->GetTimeSeconds();
Ability->ActivateAbility(this, ActivationInfo);
return true;
}
float UFortAbilitySystemComponent::GetAttributeValue(FName AttributeName) const
{
const float* Value = Attributes.Find(AttributeName);
return Value ? *Value : 0.0f;
}
void UFortAbilitySystemComponent::SetAttributeValue(FName AttributeName, float Value)
{
Attributes.Add(AttributeName, Value);
UE_LOG(LogTemp, Verbose, TEXT("Set attribute %s = %.1f"), *AttributeName.ToString(), Value);
}
void UFortAbilitySystemComponent::ModifyAttributeValue(FName AttributeName, float Delta)
{
float CurrentValue = GetAttributeValue(AttributeName);
SetAttributeValue(AttributeName, CurrentValue + Delta);
UE_LOG(LogTemp, Verbose, TEXT("Modified attribute %s by %.1f (now %.1f)"),
*AttributeName.ToString(), Delta, CurrentValue + Delta);
}
void UFortAbilitySystemComponent::OnTagUpdated(const FFortGameplayTag& Tag, bool bTagExists)
{
static const FName HealthRegenDelayTagName = FName(TEXT("Status.HealthRegenDelay"));
static const FName ShieldRegenDelayTagName = FName(TEXT("Status.ShieldRegenDelay"));
if (Tag.TagName == HealthRegenDelayTagName)
{
UE_LOG(LogTemp, Log, TEXT("UFortAbilitySystemComponent::OnTagUpdated is updating the HealthRegenDelay tag. bTagExists: %s"),
bTagExists ? TEXT("true") : TEXT("false"));
UpdateHealthRegenDelay(bTagExists);
}
else if (Tag.TagName == ShieldRegenDelayTagName)
{
UE_LOG(LogTemp, Log, TEXT("UFortAbilitySystemComponent::OnTagUpdated is updating the ShieldRegenDelay tag. bTagExists: %s"),
bTagExists ? TEXT("true") : TEXT("false"));
UpdateShieldRegenDelay(bTagExists);
}
}
void UFortAbilitySystemComponent::UpdateHealthRegenDelay(bool bShouldDelay)
{
bHealthRegenDelayed = bShouldDelay;
if (bShouldDelay)
{
// Stop health regeneration
if (AActor* Owner = GetOwner())
{
Owner->GetWorldTimerManager().ClearTimer(HealthRegenTimerHandle);
}
}
else
{
// Resume health regeneration
StartHealthRegeneration();
}
}
void UFortAbilitySystemComponent::UpdateShieldRegenDelay(bool bShouldDelay)
{
bShieldRegenDelayed = bShouldDelay;
if (bShouldDelay)
{
// Stop shield regeneration
if (AActor* Owner = GetOwner())
{
Owner->GetWorldTimerManager().ClearTimer(ShieldRegenTimerHandle);
}
}
else
{
// Resume shield regeneration
StartShieldRegeneration();
}
}
void UFortAbilitySystemComponent::StartHealthRegeneration()
{
if (AActor* Owner = GetOwner())
{
FTimerDelegate RegenDelegate;
RegenDelegate.BindLambda([this]()
{
if (!bHealthRegenDelayed)
{
float CurrentHealth = GetAttributeValue(FName("Health"));
float MaxHealth = GetAttributeValue(FName("MaxHealth"));
if (CurrentHealth < MaxHealth)
{
ModifyAttributeValue(FName("Health"), 1.0f); // Regen 1 HP per tick
}
}
});
Owner->GetWorldTimerManager().SetTimer(HealthRegenTimerHandle, RegenDelegate, 1.0f, true);
}
}
void UFortAbilitySystemComponent::StartShieldRegeneration()
{
if (AActor* Owner = GetOwner())
{
FTimerDelegate RegenDelegate;
RegenDelegate.BindLambda([this]()
{
if (!bShieldRegenDelayed)
{
float CurrentShield = GetAttributeValue(FName("Shield"));
float MaxShield = GetAttributeValue(FName("MaxShield"));
if (CurrentShield < MaxShield)
{
ModifyAttributeValue(FName("Shield"), 5.0f); // Regen 5 shield per tick
}
}
});
Owner->GetWorldTimerManager().SetTimer(ShieldRegenTimerHandle, RegenDelegate, 1.0f, true);
}
}
void UFortAbilitySystemComponent::AddFortAbilitySet(UFortAbilitySet* AbilitySet)
{
if (!AbilitySet)
{
return;
}
// Grant ability set
AbilitySet->GiveToAbilitySystemComponent(this);
// Track granted ability set
GrantedAbilitySets.AddUnique(AbilitySet);
UE_LOG(LogTemp, Log, TEXT("Added ability set: %s"), *AbilitySet->GetName());
}
void UFortAbilitySystemComponent::UnequipFortAbilitySet(UFortAbilitySet* AbilitySet)
{
if (!AbilitySet)
{
return;
}
// Remove ability set
AbilitySet->RemoveFromAbilitySystemComponent(this);
// Untrack ability set
GrantedAbilitySets.Remove(AbilitySet);
UE_LOG(LogTemp, Log, TEXT("Removed ability set: %s"), *AbilitySet->GetName());
}
// ============================================================================
// UFortGameplayAbility
// ============================================================================
UFortGameplayAbility::UFortGameplayAbility(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
AbilitySource = EFortAbilitySourceType::Default;
BaseDamage = 0.0f;
CooldownDuration = 0.0f;
EnergyCost = 0.0f;
OwningAbilitySystemComponent = nullptr;
}
bool UFortGameplayAbility::CanActivateAbility_Implementation(UFortAbilitySystemComponent* AbilitySystemComponent) const
{
// Base implementation - can always activate
return true;
}
void UFortGameplayAbility::ActivateAbility_Implementation(UFortAbilitySystemComponent* AbilitySystemComponent, const FFortAbilityActivationInfo& ActivationInfo)
{
// Base implementation - log activation
UE_LOG(LogTemp, Log, TEXT("Activated ability: %s"), *GetName());
}
void UFortGameplayAbility::EndAbility_Implementation(UFortAbilitySystemComponent* AbilitySystemComponent, bool bWasCancelled)
{
// Base implementation - log end
UE_LOG(LogTemp, Log, TEXT("Ended ability: %s (Cancelled: %s)"),
*GetName(), bWasCancelled ? TEXT("true") : TEXT("false"));
}
float UFortGameplayAbility::GetDamageStats() const
{
UE_LOG(LogTemp, Verbose, TEXT("UFortGameplayAbility::GetDamageStats"));
return BaseDamage;
}
bool UFortGameplayAbility::CheckCost(UFortAbilitySystemComponent* AbilitySystemComponent) const
{
if (!AbilitySystemComponent)
{
return false;
}
UE_LOG(LogTemp, Verbose, TEXT("UFortGameplayAbility::CheckCost for %s"), *GetName());
// Check energy cost via simple attribute lookup
if (EnergyCost > 0.0f)
{
float CurrentEnergy = AbilitySystemComponent->GetAttributeValue(FName("Energy"));
if (CurrentEnergy < EnergyCost)
{
UE_LOG(LogTemp, Warning, TEXT("Insufficient energy: need %.1f, have %.1f"),
EnergyCost, CurrentEnergy);
return false;
}
}
return true;
}
void UFortGameplayAbility::ApplyCost(UFortAbilitySystemComponent* AbilitySystemComponent)
{
if (!AbilitySystemComponent)
{
return;
}
UE_LOG(LogTemp, Verbose, TEXT("UFortGameplayAbility::ApplyCost from ability %s"), *GetName());
// Deduct energy cost directly
if (EnergyCost > 0.0f)
{
AbilitySystemComponent->ModifyAttributeValue(FName("Energy"), -EnergyCost);
UE_LOG(LogTemp, Verbose, TEXT("Applied energy cost: %.1f"), EnergyCost);
}
}
// ============================================================================
// UFortGameplayAbility_Action
// ============================================================================
UFortGameplayAbility_Action::UFortGameplayAbility_Action(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UFortGameplayAbility_Action::ActivateAbility_Implementation(UFortAbilitySystemComponent* AbilitySystemComponent, const FFortAbilityActivationInfo& ActivationInfo)
{
Super::ActivateAbility_Implementation(AbilitySystemComponent, ActivationInfo);
// Perform sphere trace in front of player to find interactables
if (!AbilitySystemComponent)
{
return;
}
AActor* Avatar = AbilitySystemComponent->GetOwner();
if (!Avatar)
{
return;
}
UWorld* World = Avatar->GetWorld();
if (!World)
{
return;
}
FVector StartLocation = Avatar->GetActorLocation();
FVector ForwardVector = Avatar->GetActorForwardVector();
FVector EndLocation = StartLocation + (ForwardVector * 300.0f); // 3 meter range
FHitResult HitResult;
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(Avatar);
// Sphere sweep for interactables
if (World->SweepSingleByChannel(
HitResult,
StartLocation,
EndLocation,
FQuat::Identity,
ECC_Visibility,
FCollisionShape::MakeSphere(100.0f), // 1 meter radius
QueryParams))
{
AActor* HitActor = HitResult.GetActor();
if (HitActor && HitActor->IsA(ABuildingActor::StaticClass()))
{
ABuildingActor* Building = Cast<ABuildingActor>(HitActor);
UE_LOG(LogTemp, Log, TEXT("Interacted with building: %s"), *Building->GetName());
// Building interaction logic would go here
// Examples: open door, activate trap, repair, etc.
}
else if (HitActor)
{
UE_LOG(LogTemp, Log, TEXT("Interacted with actor: %s"), *HitActor->GetName());
}
}
else
{
UE_LOG(LogTemp, Verbose, TEXT("Action ability found no interactable objects"));
}
EndAbility(AbilitySystemComponent, false);
}
// ============================================================================
// UFortGameplayAbility_Jump
// ============================================================================
UFortGameplayAbility_Jump::UFortGameplayAbility_Jump(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
JumpVelocity = 600.0f;
}
void UFortGameplayAbility_Jump::ActivateAbility_Implementation(UFortAbilitySystemComponent* AbilitySystemComponent, const FFortAbilityActivationInfo& ActivationInfo)
{
Super::ActivateAbility_Implementation(AbilitySystemComponent, ActivationInfo);
if (!AbilitySystemComponent)
{
return;
}
ACharacter* Character = Cast<ACharacter>(AbilitySystemComponent->GetOwner());
if (Character)
{
Character->Jump();
UE_LOG(LogTemp, Log, TEXT("Character jumped with velocity %.1f"), JumpVelocity);
}
EndAbility(AbilitySystemComponent, false);
}
// ============================================================================
// UFortGameplayAbility_Sprint
// ============================================================================
UFortGameplayAbility_Sprint::UFortGameplayAbility_Sprint(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
SprintSpeedMultiplier = 1.5f;
}
void UFortGameplayAbility_Sprint::ActivateAbility_Implementation(UFortAbilitySystemComponent* AbilitySystemComponent, const FFortAbilityActivationInfo& ActivationInfo)
{
Super::ActivateAbility_Implementation(AbilitySystemComponent, ActivationInfo);
if (!AbilitySystemComponent)
{
return;
}
ACharacter* Character = Cast<ACharacter>(AbilitySystemComponent->GetOwner());
if (Character)
{
UCharacterMovementComponent* Movement = Character->GetCharacterMovement();
if (Movement)
{
Movement->MaxWalkSpeed *= SprintSpeedMultiplier;
UE_LOG(LogTemp, Log, TEXT("Sprint activated - speed multiplier: %.1fx"), SprintSpeedMultiplier);
}
}
}
void UFortGameplayAbility_Sprint::EndAbility_Implementation(UFortAbilitySystemComponent* AbilitySystemComponent, bool bWasCancelled)
{
Super::EndAbility_Implementation(AbilitySystemComponent, bWasCancelled);
if (!AbilitySystemComponent)
{
return;
}
ACharacter* Character = Cast<ACharacter>(AbilitySystemComponent->GetOwner());
if (Character)
{
UCharacterMovementComponent* Movement = Character->GetCharacterMovement();
if (Movement)
{
Movement->MaxWalkSpeed /= SprintSpeedMultiplier;
UE_LOG(LogTemp, Log, TEXT("Sprint ended - speed restored"));
}
}
}
// ============================================================================
// UFortGameplayAbility_RangedWeapon
// ============================================================================
UFortGameplayAbility_RangedWeapon::UFortGameplayAbility_RangedWeapon(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
FireRate = 0.1f;
BulletsPerShot = 1;
}
void UFortGameplayAbility_RangedWeapon::ActivateAbility_Implementation(UFortAbilitySystemComponent* AbilitySystemComponent, const FFortAbilityActivationInfo& ActivationInfo)
{
Super::ActivateAbility_Implementation(AbilitySystemComponent, ActivationInfo);
FireWeapon();
EndAbility(AbilitySystemComponent, false);
}
void UFortGameplayAbility_RangedWeapon::FireWeapon()
{
// Fire weapon logic
UE_LOG(LogTemp, Log, TEXT("Fired weapon - bullets: %d, fire rate: %.2f"), BulletsPerShot, FireRate);
// this would:
// - Perform line trace for hit detection
// - Spawn projectile or apply instant damage
// - Play fire effects (muzzle flash, sound, recoil)
// - Consume ammo
}
// ============================================================================
// UFortGameplayAbility_Reload
// ============================================================================
UFortGameplayAbility_Reload::UFortGameplayAbility_Reload(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
ReloadDuration = 2.0f;
}
void UFortGameplayAbility_Reload::ActivateAbility_Implementation(UFortAbilitySystemComponent* AbilitySystemComponent, const FFortAbilityActivationInfo& ActivationInfo)
{
Super::ActivateAbility_Implementation(AbilitySystemComponent, ActivationInfo);
if (!AbilitySystemComponent)
{
return;
}
ACharacter* Character = Cast<ACharacter>(AbilitySystemComponent->GetOwner());
if (!Character)
{
EndAbility(AbilitySystemComponent, true);
return;
}
// Find equipped weapon (typically attached to character mesh)
TArray<AActor*> AttachedActors;
Character->GetAttachedActors(AttachedActors);
for (AActor* AttachedActor : AttachedActors)
{
AFortWeapon* Weapon = Cast<AFortWeapon>(AttachedActor);
if (Weapon && Weapon->WeaponFireMontage)
{
USkeletalMeshComponent* Mesh = Character->GetMesh();
if (Mesh && Mesh->AnimScriptInstance)
{
// Play reload montage
// In UE 4.12: Mesh->AnimScriptInstance->Montage_Play(ReloadMontage, 1.0f);
UE_LOG(LogTemp, Log, TEXT("Playing reload montage for weapon: %s (duration: %.1fs)"),
*Weapon->GetName(), ReloadDuration);
}
break;
}
}
// Set timer to end ability after reload duration
if (AActor* Owner = AbilitySystemComponent->GetOwner())
{
FTimerHandle ReloadTimerHandle;
FTimerDelegate ReloadDelegate;
ReloadDelegate.BindLambda([this, AbilitySystemComponent]()
{
EndAbility(AbilitySystemComponent, false);
});
Owner->GetWorldTimerManager().SetTimer(ReloadTimerHandle, ReloadDelegate, ReloadDuration, false);
}
}
// ============================================================================
// UFortAbilitySet
// ============================================================================
UFortAbilitySet::UFortAbilitySet(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UFortAbilitySet::GiveToAbilitySystemComponent(UFortAbilitySystemComponent* AbilitySystemComponent)
{
if (!AbilitySystemComponent)
{
return;
}
// Grant abilities
TArray<FFortAbilityHandle> GrantedHandles;
for (TSubclassOf<UFortGameplayAbility> AbilityClass : GameplayAbilities)
{
if (AbilityClass)
{
FFortAbilityHandle Handle = AbilitySystemComponent->GiveAbility(AbilityClass);
if (Handle.IsValid())
{
GrantedHandles.Add(Handle);
}
}
}
// Store handles for later removal
GrantedAbilityHandles.Add(AbilitySystemComponent, GrantedHandles);
// Initialize attributes
for (const auto& AttributePair : AttributeDefaults)
{
AbilitySystemComponent->SetAttributeValue(AttributePair.Key, AttributePair.Value);
}
UE_LOG(LogTemp, Log, TEXT("Granted ability set: %s (%d abilities, %d attributes)"),
*GetName(), GrantedHandles.Num(), AttributeDefaults.Num());
}
void UFortAbilitySet::RemoveFromAbilitySystemComponent(UFortAbilitySystemComponent* AbilitySystemComponent)
{
if (!AbilitySystemComponent)
{
return;
}
// Remove granted abilities
TArray<FFortAbilityHandle>* Handles = GrantedAbilityHandles.Find(AbilitySystemComponent);
if (Handles)
{
for (const FFortAbilityHandle& Handle : *Handles)
{
AbilitySystemComponent->RemoveAbility(Handle);
}
GrantedAbilityHandles.Remove(AbilitySystemComponent);
UE_LOG(LogTemp, Log, TEXT("Removed ability set: %s (%d abilities removed)"),
*GetName(), Handles->Num());
}
}
// ============================================================================
// UFortGameplayAbilityTooltip
// ============================================================================
UFortGameplayAbilityTooltip::UFortGameplayAbilityTooltip(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
FText UFortGameplayAbilityTooltip::GetCostAsText(const UFortGameplayAbility* Ability)
{
if (!Ability)
{
return FText::GetEmpty();
}
UE_LOG(LogTemp, Verbose, TEXT("UFortGameplayAbilityTooltip::GetCostAsText from ability %s"), *Ability->GetName());
if (Ability->EnergyCost > 0.0f)
{
return FText::FromString(FString::Printf(TEXT("%.0f Energy"), Ability->EnergyCost));
}
return FText::FromString(TEXT("No Cost"));
}
FText UFortGameplayAbilityTooltip::GetDamageAsText(const UFortGameplayAbility* Ability)
{
if (!Ability)
{
return FText::GetEmpty();
}
if (Ability->BaseDamage > 0.0f)
{
return FText::FromString(FString::Printf(TEXT("%.0f Damage"), Ability->BaseDamage));
}
return FText::GetEmpty();
}
FText UFortGameplayAbilityTooltip::GetCooldownAsText(const UFortGameplayAbility* Ability)
{
if (!Ability)
{
return FText::GetEmpty();
}
if (Ability->CooldownDuration > 0.0f)
{
return FText::FromString(FString::Printf(TEXT("%.1fs Cooldown"), Ability->CooldownDuration));
}
return FText::GetEmpty();
}
FText UFortGameplayAbilityTooltip::GenerateTooltip(const UFortGameplayAbility* Ability)
{
if (!Ability)
{
return FText::GetEmpty();
}
UE_LOG(LogTemp, Verbose, TEXT("Generating tooltip for ability: %s"), *Ability->GetName());
FString TooltipString = Ability->GetName();
FText Cost = GetCostAsText(Ability);
if (!Cost.IsEmpty())
{
TooltipString += FString::Printf(TEXT("\nCost: %s"), *Cost.ToString());
}
FText Damage = GetDamageAsText(Ability);
if (!Damage.IsEmpty())
{
TooltipString += FString::Printf(TEXT("\n%s"), *Damage.ToString());
}
FText Cooldown = GetCooldownAsText(Ability);
if (!Cooldown.IsEmpty())
{
TooltipString += FString::Printf(TEXT("\n%s"), *Cooldown.ToString());
}
return FText::FromString(TooltipString);
}