super-duper-spoon/Source/Shooter/Item.cpp

489 lines
13 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
GlowAmount(150.f),
FresnelExponent(3.f),
FresnelReflectFraction(4.f),
PulseCurveTime(5.f)
{
// 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();
}
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()
{
if (Character)
{
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()
{
if (Character)
{
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;
}
}
void AItem::StartItemCurve(AShooterCharacter* Char)
{
// 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();
// 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);
SetItemState(EItemState::EIS_PickedUp);
}
// Set scale back to normal
SetActorScale3D(FVector(1.f));
bCanChangeCustomDepth = true;
DisableCustomDepth();
}