530 lines
15 KiB
C++
530 lines
15 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
#include "Item.h"
|
|
|
|
#include "ShooterCharacter.h"
|
|
#include "Components/BoxComponent.h"
|
|
#include "Components/SphereComponent.h"
|
|
#include "Components/WidgetComponent.h"
|
|
#include "Camera/CameraComponent.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
#include "Sound/SoundCue.h"
|
|
#include "Curves/CurveVector.h"
|
|
|
|
// Sets default values
|
|
AItem::AItem() :
|
|
ItemName(FString("Default")),
|
|
ItemCount(0),
|
|
ItemRarity(EItemRarity::EIR_Common),
|
|
ItemState(EItemState::EIS_Pickup),
|
|
// Item interp variables
|
|
ItemInterpStartLocation(FVector(0.f)),
|
|
CameraTargetLocation(FVector(0.f)),
|
|
bInterping(false),
|
|
ZCurveTime(0.7f),
|
|
ItemIntepX(0.f),
|
|
ItemIntepY(0.f),
|
|
InterpInitialYawOffset(0.f),
|
|
ItemType(EItemType::EIT_MAX),
|
|
InterpLocIndex(0),
|
|
MaterialIndex(0),
|
|
bCanChangeCustomDepth(true),
|
|
// Dynamic Material Parameters
|
|
PulseCurveTime(5.f),
|
|
GlowAmount(150.f),
|
|
FresnelExponent(3.f),
|
|
FresnelReflectFraction(4.f),
|
|
SlotIndex(0),
|
|
bCharacterInventoryFull(false)
|
|
{
|
|
// 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;
|
|
|
|
ItemMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("ItemMesh"));
|
|
SetRootComponent(ItemMesh);
|
|
|
|
CollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("CollisionBox"));
|
|
CollisionBox->SetupAttachment(ItemMesh);
|
|
CollisionBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
CollisionBox->SetCollisionResponseToChannel(ECollisionChannel::ECC_Visibility, ECollisionResponse::ECR_Block);
|
|
|
|
PickupWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("PickupWidget"));
|
|
PickupWidget->SetupAttachment(GetRootComponent());
|
|
|
|
AreaSphere = CreateDefaultSubobject<USphereComponent>(TEXT("AreaSphere"));
|
|
AreaSphere->SetupAttachment(GetRootComponent());
|
|
}
|
|
|
|
// Called when the game starts or when spawned
|
|
void AItem::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
// Hide Pickup Widget
|
|
if (PickupWidget)
|
|
PickupWidget->SetVisibility(false);
|
|
|
|
// Set ActiveStars array based on item rarity
|
|
setActiveStars();
|
|
|
|
// Setup overlap for AreaSphere
|
|
AreaSphere->OnComponentBeginOverlap.AddDynamic(this, &AItem::OnSphereOverlap);
|
|
AreaSphere->OnComponentEndOverlap.AddDynamic(this, &AItem::OnSphereEndOverlap);
|
|
|
|
//Set Item properties based on ItemState
|
|
SetItemProperties(ItemState);
|
|
|
|
// Set custom depth to disabled
|
|
InitializeCustomDepth();
|
|
|
|
StartPulseTimer();
|
|
|
|
// We wait a small delay to make sure all actors are spawned,
|
|
// then we set a small sphere radius and reset it again,
|
|
// this generates any overlap events that weren't accounted for on spawn
|
|
FTimerHandle TimerHandle;
|
|
GetWorldTimerManager().SetTimer(TimerHandle, [&]()
|
|
{
|
|
AreaSphereRadius = AreaSphere->GetUnscaledSphereRadius();
|
|
AreaSphere->SetSphereRadius(1.f);
|
|
AreaSphere->SetSphereRadius(AreaSphereRadius);
|
|
}, 0.5, false);
|
|
}
|
|
|
|
void AItem::setActiveStars()
|
|
{
|
|
// The 0 element isn't used
|
|
for (int32 i = 0; i <= 5; ++i)
|
|
{
|
|
ActiveStars.Add(false);
|
|
}
|
|
|
|
switch (ItemRarity)
|
|
{
|
|
case EItemRarity::EIR_Damaged:
|
|
ActiveStars[1] = true;
|
|
break;
|
|
case EItemRarity::EIR_Common:
|
|
ActiveStars[1] = true;
|
|
ActiveStars[2] = true;
|
|
break;
|
|
case EItemRarity::EIR_Uncommon:
|
|
ActiveStars[1] = true;
|
|
ActiveStars[2] = true;
|
|
ActiveStars[3] = true;
|
|
break;
|
|
case EItemRarity::EIR_Rare:
|
|
ActiveStars[1] = true;
|
|
ActiveStars[2] = true;
|
|
ActiveStars[3] = true;
|
|
ActiveStars[4] = true;
|
|
break;
|
|
case EItemRarity::EIR_Legendary:
|
|
ActiveStars[1] = true;
|
|
ActiveStars[2] = true;
|
|
ActiveStars[3] = true;
|
|
ActiveStars[4] = true;
|
|
ActiveStars[5] = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AItem::PlayPickupSound(bool bForcePlaySound)
|
|
{
|
|
if (Character)
|
|
{
|
|
if (bForcePlaySound)
|
|
{
|
|
if (PickupSound)
|
|
{
|
|
UGameplayStatics::PlaySound2D(this, PickupSound);
|
|
}
|
|
}
|
|
else if (Character->ShouldPlayPickupSound())
|
|
{
|
|
Character->StartPickupSoundTimer();
|
|
if (PickupSound)
|
|
{
|
|
UGameplayStatics::PlaySound2D(this, PickupSound);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AItem::EnableCustomDepth()
|
|
{
|
|
if (bCanChangeCustomDepth)
|
|
ItemMesh->SetRenderCustomDepth(true);
|
|
}
|
|
|
|
void AItem::DisableCustomDepth()
|
|
{
|
|
if (bCanChangeCustomDepth)
|
|
ItemMesh->SetRenderCustomDepth(false);
|
|
}
|
|
|
|
void AItem::InitializeCustomDepth()
|
|
{
|
|
DisableCustomDepth();
|
|
}
|
|
|
|
void AItem::OnConstruction(const FTransform& Transform)
|
|
{
|
|
if (MaterialInstance)
|
|
{
|
|
DynamicMaterialInstance = UMaterialInstanceDynamic::Create(MaterialInstance, this);
|
|
ItemMesh->SetMaterial(MaterialIndex, DynamicMaterialInstance);
|
|
}
|
|
|
|
EnableGlowMaterial();
|
|
}
|
|
|
|
void AItem::UpdatePulse()
|
|
{
|
|
float ElapsedTime{};
|
|
FVector CurveValue{};
|
|
|
|
switch (ItemState)
|
|
{
|
|
case EItemState::EIS_Pickup:
|
|
if (PulseCurve)
|
|
{
|
|
ElapsedTime = GetWorldTimerManager().GetTimerElapsed(PulseTimer);
|
|
CurveValue = PulseCurve->GetVectorValue(ElapsedTime);
|
|
}
|
|
break;
|
|
case EItemState::EIS_EquipInterping:
|
|
if (InterpPulseCurve)
|
|
{
|
|
ElapsedTime = GetWorldTimerManager().GetTimerElapsed(ItemInterpTimer);
|
|
CurveValue = InterpPulseCurve->GetVectorValue(ElapsedTime);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (DynamicMaterialInstance)
|
|
{
|
|
DynamicMaterialInstance->SetScalarParameterValue(TEXT("GlowAmount"), CurveValue.X * GlowAmount);
|
|
DynamicMaterialInstance->SetScalarParameterValue(TEXT("FresnelExponent"), CurveValue.Y * FresnelExponent);
|
|
DynamicMaterialInstance->SetScalarParameterValue(TEXT("FresnelReflectFraction"), CurveValue.Z * FresnelReflectFraction);
|
|
}
|
|
}
|
|
|
|
void AItem::EnableGlowMaterial()
|
|
{
|
|
if (DynamicMaterialInstance)
|
|
{
|
|
DynamicMaterialInstance->SetScalarParameterValue(TEXT("GlowBlendAlpha"), 0);
|
|
}
|
|
}
|
|
|
|
void AItem::DisableGlowMaterial()
|
|
{
|
|
if (DynamicMaterialInstance)
|
|
{
|
|
DynamicMaterialInstance->SetScalarParameterValue(TEXT("GlowBlendAlpha"), 1);
|
|
}
|
|
}
|
|
|
|
void AItem::PlayEquipSound(bool bForcePlaySound)
|
|
{
|
|
if (Character)
|
|
{
|
|
if (bForcePlaySound)
|
|
{
|
|
UGameplayStatics::PlaySound2D(this, EquipSound);
|
|
}
|
|
else if (Character->ShouldPlayEquipSound())
|
|
{
|
|
Character->StartEquipSoundTimer();
|
|
if (EquipSound)
|
|
{
|
|
UGameplayStatics::PlaySound2D(this, EquipSound);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called every frame
|
|
void AItem::Tick(float DeltaTime)
|
|
{
|
|
Super::Tick(DeltaTime);
|
|
|
|
// Handle Item interping when in the EquipInterping state
|
|
ItemInterp(DeltaTime);
|
|
|
|
// Get curve values from PulseCurve and set dynamic material parameters
|
|
UpdatePulse();
|
|
}
|
|
|
|
void AItem::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp,
|
|
int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
|
|
{
|
|
if (OtherActor)
|
|
{
|
|
AShooterCharacter* ShooterCharacter = Cast<AShooterCharacter>(OtherActor);
|
|
|
|
if (ShooterCharacter)
|
|
{
|
|
ShooterCharacter->IncrementOverlappedItemCount(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AItem::OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
|
|
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
|
|
{
|
|
if (OtherActor)
|
|
{
|
|
AShooterCharacter* ShooterCharacter = Cast<AShooterCharacter>(OtherActor);
|
|
|
|
if (ShooterCharacter)
|
|
{
|
|
ShooterCharacter->IncrementOverlappedItemCount(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AItem::ItemInterp(float DeltaTime)
|
|
{
|
|
if (!bInterping) return;
|
|
|
|
if (Character && ItemZCurve)
|
|
{
|
|
// Elapsed time since we started ItemInterpTimer
|
|
const float ElapsedTime = GetWorldTimerManager().GetTimerElapsed(ItemInterpTimer);
|
|
|
|
// Get curve value corresponding to ElapsedTime
|
|
const float CurveValue = ItemZCurve->GetFloatValue(ElapsedTime);
|
|
|
|
// Get the item's initial location when the curve started
|
|
FVector ItemLocation = ItemInterpStartLocation;
|
|
|
|
// Get location in front of the camera
|
|
const FVector CameraInterpLocation{ GetInterpLocation() };
|
|
|
|
// Vector from Item to Camera Interp Location, X and Y are zeroed out
|
|
const FVector ItemToCamera{ FVector(0.f, 0.f, (CameraInterpLocation - ItemLocation).Z) };
|
|
|
|
// SCale factor to multiply with CurveValue
|
|
const float DeltaZ = ItemToCamera.Size();
|
|
|
|
const FVector CurrentLocation{ GetActorLocation() };
|
|
|
|
// Interpolated X Value
|
|
const float InterpXValue = FMath::FInterpTo(CurrentLocation.X,
|
|
CameraInterpLocation.X,
|
|
DeltaTime, 30.f);
|
|
|
|
// Interpolated Y Value
|
|
const float InterpYValue = FMath::FInterpTo(CurrentLocation.Y,
|
|
CameraInterpLocation.Y,
|
|
DeltaTime, 30.f);
|
|
|
|
// Set X and Y of ItemLocation to Interped values
|
|
ItemLocation.X = InterpXValue;
|
|
ItemLocation.Y = InterpYValue;
|
|
|
|
// Adding curve value to the Z component of the Initial Location (Scaled by DeltaZ)
|
|
ItemLocation.Z += CurveValue * DeltaZ;
|
|
SetActorLocation(ItemLocation, true,
|
|
nullptr, ETeleportType::TeleportPhysics);
|
|
|
|
// Camera rotation this frame
|
|
const FRotator CameraRotation{Character->GetFollowCamera()->GetComponentRotation()};
|
|
// Camera rotation plus initial Yaw Offset
|
|
const FRotator ItemRotation{0.f, CameraRotation.Yaw + InterpInitialYawOffset, 0.f};
|
|
SetActorRotation(ItemRotation, ETeleportType::TeleportPhysics);
|
|
|
|
if (ItemScaleCurve)
|
|
{
|
|
const float ScaleCurveValue = ItemScaleCurve->GetFloatValue(ElapsedTime);
|
|
SetActorScale3D(FVector(ScaleCurveValue, ScaleCurveValue, ScaleCurveValue));
|
|
}
|
|
}
|
|
}
|
|
|
|
FVector AItem::GetInterpLocation()
|
|
{
|
|
if (!Character) return FVector(0.f);
|
|
|
|
switch (ItemType)
|
|
{
|
|
case EItemType::EIT_Ammo:
|
|
return Character->GetInterpLocation(InterpLocIndex).SceneComponent->GetComponentLocation();
|
|
break;
|
|
case EItemType::EIT_Weapon:
|
|
return Character->GetInterpLocation(0).SceneComponent->GetComponentLocation();
|
|
break;
|
|
}
|
|
|
|
return FVector(0.f);
|
|
}
|
|
|
|
void AItem::ResetPulseTimer()
|
|
{
|
|
StartPulseTimer();
|
|
}
|
|
|
|
void AItem::StartPulseTimer()
|
|
{
|
|
if (ItemState == EItemState::EIS_Pickup)
|
|
{
|
|
GetWorldTimerManager().SetTimer(PulseTimer, this, &AItem::ResetPulseTimer, PulseCurveTime);
|
|
}
|
|
}
|
|
|
|
void AItem::SetItemState(EItemState State)
|
|
{
|
|
ItemState = State;
|
|
SetItemProperties(State);
|
|
}
|
|
|
|
void AItem::SetItemProperties(EItemState State)
|
|
{
|
|
switch (State)
|
|
{
|
|
case EItemState::EIS_Pickup:
|
|
// Set Mesh properties
|
|
ItemMesh->SetSimulatePhysics(false);
|
|
ItemMesh->SetEnableGravity(false);
|
|
ItemMesh->SetVisibility(true);
|
|
ItemMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
ItemMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
|
|
// Set AreaSphere properties
|
|
AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap);
|
|
AreaSphere->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
|
|
|
|
// Set CollisionBox properties
|
|
CollisionBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
CollisionBox->SetCollisionResponseToChannel(ECollisionChannel::ECC_Visibility, ECollisionResponse::ECR_Block);
|
|
CollisionBox->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
|
|
break;
|
|
case EItemState::EIS_Equipped:
|
|
// Set Mesh properties
|
|
ItemMesh->SetSimulatePhysics(false);
|
|
ItemMesh->SetEnableGravity(false);
|
|
ItemMesh->SetVisibility(true);
|
|
ItemMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
ItemMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
|
|
// Set AreaSphere properties
|
|
AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
|
|
// Set CollisionBox properties
|
|
CollisionBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
CollisionBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
break;
|
|
case EItemState::EIS_Falling:
|
|
// Set Mesh properties
|
|
ItemMesh->SetSimulatePhysics(true);
|
|
ItemMesh->SetEnableGravity(true);
|
|
ItemMesh->SetVisibility(true);
|
|
ItemMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
ItemMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block);
|
|
ItemMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
|
|
|
|
// Set AreaSphere properties
|
|
AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
|
|
// Set CollisionBox properties
|
|
CollisionBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
CollisionBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
break;
|
|
case EItemState::EIS_EquipInterping:
|
|
PickupWidget->SetVisibility(false);
|
|
|
|
// Set Mesh properties
|
|
ItemMesh->SetSimulatePhysics(false);
|
|
ItemMesh->SetEnableGravity(false);
|
|
ItemMesh->SetVisibility(true);
|
|
ItemMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
ItemMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
|
|
// Set AreaSphere properties
|
|
AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
|
|
// Set CollisionBox properties
|
|
CollisionBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
CollisionBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
break;
|
|
case EItemState::EIS_PickedUp:
|
|
PickupWidget->SetVisibility(false);
|
|
|
|
// Set Mesh properties
|
|
ItemMesh->SetSimulatePhysics(false);
|
|
ItemMesh->SetEnableGravity(false);
|
|
ItemMesh->SetVisibility(false);
|
|
ItemMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
ItemMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
|
|
// Set AreaSphere properties
|
|
AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
|
|
// Set CollisionBox properties
|
|
CollisionBox->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
|
|
CollisionBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AItem::StartItemCurve(AShooterCharacter* Char, bool bForcePlaySound)
|
|
{
|
|
// Store a handle to the Character
|
|
Character = Char;
|
|
|
|
// Get array index in InterpLocations with the lowest ItemCount
|
|
InterpLocIndex = Character->GetInterpLocationIndex();
|
|
|
|
// Add 1 to the Item Count for this interp location struct
|
|
Character->IncrementInterpLocItemCount(InterpLocIndex, 1);
|
|
|
|
PlayPickupSound(bForcePlaySound);
|
|
|
|
// Store the initial locatino of the Item
|
|
ItemInterpStartLocation = GetActorLocation();
|
|
|
|
bInterping = true;
|
|
SetItemState(EItemState::EIS_EquipInterping);
|
|
GetWorldTimerManager().ClearTimer(PulseTimer);
|
|
|
|
GetWorldTimerManager().SetTimer(ItemInterpTimer, this,
|
|
&AItem::FinishInterping, ZCurveTime);
|
|
|
|
// Get initial Yaw of the Camera
|
|
const float CameraRotationYaw{
|
|
static_cast<float>(Character->GetFollowCamera()->GetComponentRotation().Yaw)
|
|
};
|
|
|
|
// Get initial Yaw of the Item
|
|
const float ItemRotationYaw{ static_cast<float>(GetActorRotation().Yaw) };
|
|
|
|
// Initial Yaw Offset between Camera and Item
|
|
InterpInitialYawOffset = ItemRotationYaw - CameraRotationYaw;
|
|
|
|
bCanChangeCustomDepth = false;
|
|
}
|
|
|
|
void AItem::FinishInterping()
|
|
{
|
|
bInterping = false;
|
|
if (Character)
|
|
{
|
|
// Subtract 1 from the Item Count of the interp location struct
|
|
Character->IncrementInterpLocItemCount(InterpLocIndex, -1);
|
|
Character->GetPickupItem(this);
|
|
}
|
|
// Set scale back to normal
|
|
SetActorScale3D(FVector(1.f));
|
|
|
|
bCanChangeCustomDepth = true;
|
|
DisableCustomDepth();
|
|
}
|
|
|