2022-05-23 18:41:30 +00:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
|
|
#include "LyraWeaponSpawner.h"
|
2022-09-13 07:18:28 +00:00
|
|
|
|
|
|
|
#include "AbilitySystemBlueprintLibrary.h"
|
2022-05-23 18:41:30 +00:00
|
|
|
#include "Components/CapsuleComponent.h"
|
2022-09-13 07:18:28 +00:00
|
|
|
#include "Components/PrimitiveComponent.h"
|
|
|
|
#include "Components/StaticMeshComponent.h"
|
|
|
|
#include "Containers/Array.h"
|
|
|
|
#include "Containers/UnrealString.h"
|
|
|
|
#include "Delegates/Delegate.h"
|
|
|
|
#include "Engine/EngineBaseTypes.h"
|
|
|
|
#include "Engine/World.h"
|
2022-05-23 18:41:30 +00:00
|
|
|
#include "Equipment/LyraPickupDefinition.h"
|
2022-09-13 07:18:28 +00:00
|
|
|
#include "GameFramework/Pawn.h"
|
|
|
|
#include "GameplayTagContainer.h"
|
2022-05-23 18:41:30 +00:00
|
|
|
#include "Inventory/InventoryFragment_SetStats.h"
|
2022-09-13 07:18:28 +00:00
|
|
|
#include "Inventory/LyraInventoryItemDefinition.h"
|
|
|
|
#include "Kismet/GameplayStatics.h"
|
|
|
|
#include "Logging/LogCategory.h"
|
|
|
|
#include "Logging/LogMacros.h"
|
2022-05-23 18:41:30 +00:00
|
|
|
#include "LyraLogChannels.h"
|
2022-09-13 07:18:28 +00:00
|
|
|
#include "Math/Rotator.h"
|
|
|
|
#include "Misc/AssertionMacros.h"
|
|
|
|
#include "Net/UnrealNetwork.h"
|
|
|
|
#include "NiagaraFunctionLibrary.h"
|
|
|
|
#include "Templates/Casts.h"
|
|
|
|
#include "Templates/ChooseClass.h"
|
|
|
|
#include "TimerManager.h"
|
|
|
|
#include "Trace/Detail/Channel.h"
|
|
|
|
#include "UObject/ObjectPtr.h"
|
|
|
|
#include "UObject/UObjectBaseUtility.h"
|
|
|
|
|
|
|
|
class FLifetimeProperty;
|
|
|
|
class UNiagaraSystem;
|
|
|
|
class USoundBase;
|
|
|
|
struct FHitResult;
|
2022-05-23 18:41:30 +00:00
|
|
|
|
|
|
|
// Sets default values
|
|
|
|
ALyraWeaponSpawner::ALyraWeaponSpawner()
|
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
RootComponent = CollisionVolume = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CollisionVolume"));
|
|
|
|
CollisionVolume->InitCapsuleSize(80.f, 80.f);
|
|
|
|
CollisionVolume->OnComponentBeginOverlap.AddDynamic(this, &ALyraWeaponSpawner::OnOverlapBegin);
|
|
|
|
|
|
|
|
PadMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PadMesh"));
|
|
|
|
PadMesh->SetupAttachment(RootComponent);
|
|
|
|
|
|
|
|
WeaponMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("WeaponMesh"));
|
|
|
|
WeaponMesh->SetupAttachment(RootComponent);
|
|
|
|
|
|
|
|
WeaponMeshRotationSpeed = 40.0f;
|
|
|
|
CoolDownTime = 30.0f;
|
|
|
|
CheckExistingOverlapDelay = 0.25f;
|
|
|
|
bIsWeaponAvailable = true;
|
|
|
|
bReplicates = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called when the game starts or when spawned
|
|
|
|
void ALyraWeaponSpawner::BeginPlay()
|
|
|
|
{
|
|
|
|
Super::BeginPlay();
|
|
|
|
|
|
|
|
if (WeaponDefinition && WeaponDefinition->InventoryItemDefinition)
|
|
|
|
{
|
|
|
|
CoolDownTime = WeaponDefinition->SpawnCoolDownSeconds;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UE_LOG(LogLyra, Error, TEXT("'%s' does not have a valid weapon definition! Make sure to set this data on the instance!"), *GetNameSafe(this));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|
|
|
{
|
|
|
|
if (UWorld* World = GetWorld())
|
|
|
|
{
|
|
|
|
World->GetTimerManager().ClearTimer(CoolDownTimerHandle);
|
|
|
|
World->GetTimerManager().ClearTimer(CheckOverlapsDelayTimerHandle);
|
|
|
|
}
|
|
|
|
|
|
|
|
Super::EndPlay(EndPlayReason);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called every frame
|
|
|
|
void ALyraWeaponSpawner::Tick(float DeltaTime)
|
|
|
|
{
|
|
|
|
Super::Tick(DeltaTime);
|
|
|
|
|
|
|
|
//Update the CoolDownPercentage property to drive respawn time indicators
|
|
|
|
UWorld* World = GetWorld();
|
|
|
|
if (World->GetTimerManager().IsTimerActive(CoolDownTimerHandle))
|
|
|
|
{
|
|
|
|
CoolDownPercentage = 1.0f - World->GetTimerManager().GetTimerRemaining(CoolDownTimerHandle)/CoolDownTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
WeaponMesh->AddRelativeRotation(FRotator(0.0f, World->GetDeltaSeconds() * WeaponMeshRotationSpeed, 0.0f));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::OnConstruction(const FTransform& Transform)
|
|
|
|
{
|
|
|
|
if (WeaponDefinition != nullptr && WeaponDefinition->DisplayMesh != nullptr)
|
|
|
|
{
|
|
|
|
WeaponMesh->SetStaticMesh(WeaponDefinition->DisplayMesh);
|
|
|
|
WeaponMesh->SetRelativeLocation(WeaponDefinition->WeaponMeshOffset);
|
|
|
|
WeaponMesh->SetRelativeScale3D(WeaponDefinition->WeaponMeshScale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult)
|
|
|
|
{
|
|
|
|
APawn* OverlappingPawn = Cast<APawn>(OtherActor);
|
|
|
|
if (GetLocalRole() == ROLE_Authority && bIsWeaponAvailable && OverlappingPawn != nullptr)
|
|
|
|
{
|
|
|
|
AttemptPickUpWeapon(OverlappingPawn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::CheckForExistingOverlaps()
|
|
|
|
{
|
|
|
|
TArray<AActor*> OverlappingActors;
|
|
|
|
GetOverlappingActors(OverlappingActors, APawn::StaticClass());
|
|
|
|
|
|
|
|
for (AActor* OverlappingActor : OverlappingActors)
|
|
|
|
{
|
|
|
|
AttemptPickUpWeapon(Cast<APawn>(OverlappingActor));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::AttemptPickUpWeapon_Implementation(APawn* Pawn)
|
|
|
|
{
|
|
|
|
if (GetLocalRole() == ROLE_Authority && bIsWeaponAvailable && UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Pawn))
|
|
|
|
{
|
|
|
|
TSubclassOf<ULyraInventoryItemDefinition> WeaponItemDefinition = WeaponDefinition ? WeaponDefinition->InventoryItemDefinition : nullptr;
|
|
|
|
if (WeaponItemDefinition != nullptr)
|
|
|
|
{
|
|
|
|
//Attempt to grant the weapon
|
|
|
|
if (GiveWeapon(WeaponItemDefinition, Pawn))
|
|
|
|
{
|
|
|
|
//Weapon picked up by pawn
|
|
|
|
bIsWeaponAvailable = false;
|
|
|
|
SetWeaponPickupVisibility(false);
|
|
|
|
PlayPickupEffects();
|
|
|
|
StartCoolDown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::StartCoolDown()
|
|
|
|
{
|
|
|
|
if (UWorld* World = GetWorld())
|
|
|
|
{
|
|
|
|
World->GetTimerManager().SetTimer(CoolDownTimerHandle, this, &ALyraWeaponSpawner::OnCoolDownTimerComplete, CoolDownTime);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::ResetCoolDown()
|
|
|
|
{
|
|
|
|
UWorld* World = GetWorld();
|
|
|
|
|
|
|
|
if (World)
|
|
|
|
{
|
|
|
|
World->GetTimerManager().ClearTimer(CoolDownTimerHandle);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GetLocalRole() == ROLE_Authority)
|
|
|
|
{
|
|
|
|
bIsWeaponAvailable = true;
|
|
|
|
PlayRespawnEffects();
|
|
|
|
SetWeaponPickupVisibility(true);
|
|
|
|
|
|
|
|
if (World)
|
|
|
|
{
|
|
|
|
World->GetTimerManager().SetTimer(CheckOverlapsDelayTimerHandle, this, &ALyraWeaponSpawner::CheckForExistingOverlaps, CheckExistingOverlapDelay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CoolDownPercentage = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::OnCoolDownTimerComplete()
|
|
|
|
{
|
|
|
|
ResetCoolDown();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::SetWeaponPickupVisibility(bool bShouldBeVisible)
|
|
|
|
{
|
|
|
|
WeaponMesh->SetVisibility(bShouldBeVisible, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::PlayPickupEffects_Implementation()
|
|
|
|
{
|
|
|
|
if (WeaponDefinition != nullptr)
|
|
|
|
{
|
|
|
|
USoundBase* PickupSound = WeaponDefinition->PickedUpSound;
|
|
|
|
if (PickupSound != nullptr)
|
|
|
|
{
|
|
|
|
UGameplayStatics::PlaySoundAtLocation(this, PickupSound, GetActorLocation());
|
|
|
|
}
|
|
|
|
|
|
|
|
UNiagaraSystem* PickupEffect = WeaponDefinition->PickedUpEffect;
|
|
|
|
if (PickupEffect != nullptr)
|
|
|
|
{
|
|
|
|
UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, PickupEffect, WeaponMesh->GetComponentLocation());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::PlayRespawnEffects_Implementation()
|
|
|
|
{
|
|
|
|
if (WeaponDefinition != nullptr)
|
|
|
|
{
|
|
|
|
USoundBase* RespawnSound = WeaponDefinition->RespawnedSound;
|
|
|
|
if (RespawnSound != nullptr)
|
|
|
|
{
|
|
|
|
UGameplayStatics::PlaySoundAtLocation(this, RespawnSound, GetActorLocation());
|
|
|
|
}
|
|
|
|
|
|
|
|
UNiagaraSystem* RespawnEffect = WeaponDefinition->RespawnedEffect;
|
|
|
|
if (RespawnEffect != nullptr)
|
|
|
|
{
|
|
|
|
UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, RespawnEffect, WeaponMesh->GetComponentLocation());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::OnRep_WeaponAvailability()
|
|
|
|
{
|
|
|
|
if (bIsWeaponAvailable)
|
|
|
|
{
|
|
|
|
PlayRespawnEffects();
|
|
|
|
SetWeaponPickupVisibility(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SetWeaponPickupVisibility(false);
|
|
|
|
StartCoolDown();
|
|
|
|
PlayPickupEffects();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ALyraWeaponSpawner::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
|
|
|
{
|
|
|
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
|
|
|
|
|
|
|
DOREPLIFETIME(ALyraWeaponSpawner, bIsWeaponAvailable);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 ALyraWeaponSpawner::GetDefaultStatFromItemDef(const TSubclassOf<ULyraInventoryItemDefinition> WeaponItemClass, FGameplayTag StatTag)
|
|
|
|
{
|
|
|
|
if (WeaponItemClass != nullptr)
|
|
|
|
{
|
|
|
|
if (ULyraInventoryItemDefinition* WeaponItemCDO = WeaponItemClass->GetDefaultObject<ULyraInventoryItemDefinition>())
|
|
|
|
{
|
|
|
|
if (const UInventoryFragment_SetStats* ItemStatsFragment = Cast<UInventoryFragment_SetStats>( WeaponItemCDO->FindFragmentByClass(UInventoryFragment_SetStats::StaticClass()) ))
|
|
|
|
{
|
|
|
|
return ItemStatsFragment->GetItemStatByTag(StatTag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|