Protptype of a wave based, first person shooter game made in Unreal Engine 4
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
MechDefence/Private/MCharacter.cpp

770 lines
24 KiB

// Fill out your copyright notice in the Description page of Project Settings.
#include "MCharacter.h"
#include "DrawDebugHelpers.h"
#include "MPlayerMech.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/PawnMovementComponent.h"
#include "MechDefence/MechDefence.h"
#include "MWeapon.h"
#include "MHealthComponent.h"
#include "HAL/IConsoleManager.h"
#include "EngineUtils.h"
#include "MConstructableBuilding.h"
#include "Kismet/GameplayStatics.h"
#include "Components/AudioComponent.h"
#include "MGameplayActor.h"
#include "MInteractableActor.h"
#include "GameFramework/CharacterMovementComponent.h"
static TAutoConsoleVariable<int32> CVAR_DebugPlayerDrawing(
TEXT("MDEF.DebugPlayerDrawing"),
0,
TEXT("Draw debug visualizations for the player character"),
ECVF_Cheat);
static TAutoConsoleVariable<int32> CVAR_GodMode(
TEXT("MDEF.GodMode"),
0,
TEXT("Make the player character invincible"),
ECVF_Cheat);
AMCharacter::AMCharacter()
{
PrimaryActorTick.bCanEverTick = true;
CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
CameraComponent->SetupAttachment(GetCapsuleComponent());
CameraComponent->SetRelativeLocation(FVector(0.f, 0.f, BaseEyeHeight));
CameraComponent->bUsePawnControlRotation = true;
PlayerArms = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("PlayerArms"));
PlayerArms->SetupAttachment(CameraComponent);
PlayerArms->SetRelativeLocation(FVector(0.f, 0.f, -155.f));
PlayerArms->SetRelativeRotation(FRotator(2.f, -15.f, 5.f));
GetMovementComponent()->GetNavAgentPropertiesRef().bCanCrouch = true;
HealthComponent = CreateDefaultSubobject<UMHealthComponent>(TEXT("HealthComponent"));
AudioComponent = CreateDefaultSubobject<UAudioComponent>(TEXT("AudioComponent"));
AudioComponent->SetupAttachment(RootComponent);
MaxInteractableDistance = 1000.f;
HoveredInteractableActor = nullptr;
bDied = false;
CurrentWeapon = nullptr;
bWantsToZoom = false;
TargetFOV = 45.f;
ZoomInterpolateSpeed = 20.f;
DefaultFOV = CameraComponent->FieldOfView;
OutOfSafeZoneDamageInterval = 1.f;
OutOfSafeZoneDamage = 10.f;
SafeZoneHealAmount = 10.f;
SafeZoneHealInterval = 0.5f;
SafeZoneHealDelay = 5.f;
bIsInSafeZone = false;
bIsFirstTimeHeal = true;
MinimumMovementVelocityThreshold = 100.f;
MaxStamina = 100.f;
CurrentStamina = MaxStamina;
StaminaConsumeAmount = 5.f;
StaminaConsumeInterval = 0.5f;
StaminaRestoreAmount = 10.f;
StaminaRestoreInterval = 0.5f;
StaminaRestoreDelay = 2.f;
SprintMultiplier = 2.f;
bIsSprinting = false;
bCanSprint = true;
ArmRotateSpeed = 5.f;
HiddenArmPitch = -60.f;
HiddenArmLocation = FVector(-20, -5, -200);
FAutoConsoleVariableSink CVarConsoleVarSink(FConsoleCommandDelegate::CreateUObject(this, &AMCharacter::HandleCVARChanged));
}
void AMCharacter::BeginPlay()
{
Super::BeginPlay();
DefaultArmPitch = PlayerArms->GetRelativeRotation().Pitch;
DefaultArmLocation = PlayerArms->GetRelativeLocation();
HealthComponent->OnHealthChanged.AddDynamic(this, &AMCharacter::HandleHealthChanged);
if(CVAR_GodMode.GetValueOnGameThread() > 0)
HealthComponent->bIsInvincible = true;
// Spawn weapons
FActorSpawnParameters SpawnParameters;
SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
Weapons.SetNumZeroed((int32)EWeaponType::MaxWeaponType);
if(PrimaryWeaponClass)
{
AMWeapon* PrimaryWeapon = GetWorld()->SpawnActor<AMWeapon>(PrimaryWeaponClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParameters);
if(PrimaryWeapon)
{
PrimaryWeapon->AttachToComponent(PlayerArms, FAttachmentTransformRules::SnapToTargetNotIncludingScale, PrimaryWeapon->GetPlayerAttachmentSocketName());
Weapons[(int)EWeaponType::PrimaryWeapon] = PrimaryWeapon;
PrimaryWeapon->SetOwner(this);
PrimaryWeapon->SetActorEnableCollision(false);
OnWeaponEquipped.AddDynamic(PrimaryWeapon, &AMWeapon::HandleWeaponEquipped);
}
}
else
{
UE_LOG(LogTemp, Log, TEXT("Primary Weapon Class not specified. Please set primary weapon class"));
}
if(ScannerClass)
{
AMWeapon* Scanner = GetWorld()->SpawnActor<AMWeapon>(ScannerClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParameters);
if(Scanner)
{
Scanner->AttachToComponent(PlayerArms, FAttachmentTransformRules::SnapToTargetNotIncludingScale, Scanner->GetPlayerAttachmentSocketName());
Weapons[(int)EWeaponType::Scanner] = Scanner;
Scanner->SetOwner(this);
Scanner->SetActorHiddenInGame(true);
Scanner->SetActorTickEnabled(false);
Scanner->SetActorEnableCollision(false);
OnWeaponEquipped.AddDynamic(Scanner, &AMWeapon::HandleWeaponEquipped);
}
}
else
{
UE_LOG(LogTemp, Log, TEXT("Scanner Class not specified. Please set scanner class"));
}
if(BlowTorchClass)
{
AMWeapon* BlowTorch = GetWorld()->SpawnActor<AMWeapon>(BlowTorchClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParameters);
if(BlowTorch)
{
BlowTorch->AttachToComponent(PlayerArms, FAttachmentTransformRules::SnapToTargetNotIncludingScale, BlowTorch->GetPlayerAttachmentSocketName());
Weapons[(int)EWeaponType::BlowTorch] = BlowTorch;
BlowTorch->SetOwner(this);
BlowTorch->SetActorHiddenInGame(true);
BlowTorch->SetActorTickEnabled(false);
BlowTorch->SetActorEnableCollision(false);
OnWeaponEquipped.AddDynamic(BlowTorch, &AMWeapon::HandleWeaponEquipped);
}
}
else
{
UE_LOG(LogTemp, Log, TEXT("BlowTorch Class not specified. Please set blowtorch class"));
}
if(TabletClass)
{
AMWeapon* Tablet = GetWorld()->SpawnActor<AMWeapon>(TabletClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParameters);
if(Tablet)
{
Tablet->AttachToComponent(PlayerArms, FAttachmentTransformRules::SnapToTargetNotIncludingScale, Tablet->GetPlayerAttachmentSocketName());
Weapons[(int)EWeaponType::Tablet] = Tablet;
Tablet->SetOwner(this);
Tablet->SetActorHiddenInGame(true);
Tablet->SetActorTickEnabled(false);
Tablet->SetActorEnableCollision(false);
OnWeaponEquipped.AddDynamic(Tablet, &AMWeapon::HandleWeaponEquipped);
}
}
else
{
UE_LOG(LogTemp, Log, TEXT("Tablet Class not specified. Please set tablet class"));
}
EquipWeapon((uint8)EWeaponType::PrimaryWeapon);
// Safezone checks
UCapsuleComponent* PlayerCapsuleComponent = GetCapsuleComponent();
if(PlayerCapsuleComponent)
{
PlayerCapsuleComponent->OnComponentBeginOverlap.AddDynamic(this, &AMCharacter::HandleCapsuleBeginOverlap);
PlayerCapsuleComponent->OnComponentEndOverlap.AddDynamic(this, &AMCharacter::HandleCapsuleEndOverlap);
}
// GetWorldTimerManager().SetTimerForNextTick(this, &AMCharacter::CheckIfInSafeZone);
GetWorldTimerManager().SetTimer(TimerHandle_ApplySafeZoneHeal, this, &AMCharacter::CheckIfInSafeZone, 2.f);
}
void AMCharacter::HandleCVARChanged()
{
bool GodMode = CVAR_GodMode.GetValueOnGameThread() > 0;
HealthComponent->bIsInvincible = GodMode;
UWorld* CurrentWorld = GetWorld();
if(CurrentWorld)
{
for(TActorIterator<AMPlayerMech> It(CurrentWorld); It; ++It)
{
AMPlayerMech* PlayerVehicle = *It;
UMHealthComponent* HC = Cast<UMHealthComponent>(PlayerVehicle->GetComponentByClass(UMHealthComponent::StaticClass()));
if(HC)
{
HC->bIsInvincible = GodMode;
}
}
}
}
void AMCharacter::CheckIfInSafeZone()
{
TArray<AActor*> OverlappingActors;
UCapsuleComponent* ActorCapsule = GetCapsuleComponent();
if(ActorCapsule)
{
ActorCapsule->GetOverlappingActors(OverlappingActors, SafeZoneClass);
if(OverlappingActors.Num() == 0)
{
if(bIsInSafeZone)
LeaveSafeZone();
}
else if(OverlappingActors.Num() > 0)
{
for(AActor* OverlappedActor : OverlappingActors)
{
IGameplayTagAssetInterface* GameplayTagAsset = Cast<IGameplayTagAssetInterface>(OverlappingActors[0]);
if(GameplayTagAsset && GameplayTagAsset->HasAnyMatchingGameplayTags(SafeZoneTags))
{
//Check if the safezone is constructed then determine if we are inside a constructed or underconstruction safezone
AMConstructableBuilding* ConstructableBuilding = Cast<AMConstructableBuilding>(OverlappedActor);
if(ConstructableBuilding)
{
if(ConstructableBuilding->GetBuildingState() == EBuildingState::Constructed)
{
if(!bIsInSafeZone) // If we're not in safe zone yet, enter it
{
EnterSafeZone(OverlappedActor);
}
else
{
// If we're already in safe zone, check if we need healing
FTimerManager& TimerManager = GetWorldTimerManager();
if(!HealthComponent->IsAtMaxHealth() && !TimerManager.IsTimerActive(TimerHandle_ApplySafeZoneHeal))
{
TimerManager.SetTimer(TimerHandle_ApplySafeZoneHeal, this, &AMCharacter::ApplySafeZoneHealing, SafeZoneHealInterval, false, SafeZoneHealDelay);
}
}
break;
}
else
{
// We are inside a safezone but it isnt constructed
if(bIsInSafeZone)
LeaveSafeZone();
}
}
}
}
}
}
}
void AMCharacter::MoveForward(float ScaleValue)
{
AddMovementInput(GetActorForwardVector(), ScaleValue);
}
void AMCharacter::MoveRight(float ScaleValue)
{
AddMovementInput(GetActorRightVector(), ScaleValue);
}
void AMCharacter::StartCrouch()
{
Crouch();
}
void AMCharacter::StopCrouch()
{
UnCrouch();
}
void AMCharacter::StartFire()
{
if(CurrentWeapon)
{
if(CurrentWeapon->StartFire())
{
UAnimInstance* AnimInstance = PlayerArms->GetAnimInstance();
if(AnimInstance && FireAnimation)
{
AnimInstance->PlaySlotAnimationAsDynamicMontage(FireAnimation, "Arms", 0.f);
}
}
}
}
void AMCharacter::StopFire()
{
if(CurrentWeapon)
{
CurrentWeapon->StopFire();
}
}
void AMCharacter::Reload()
{
if(CurrentWeapon && !CurrentWeapon->IsReloading())
{
CurrentWeapon->Reload();
UAnimInstance* AnimInstance = PlayerArms->GetAnimInstance();
if(AnimInstance && ReloadAnimation)
{
AnimInstance->PlaySlotAnimationAsDynamicMontage(ReloadAnimation, "Arms", 0.f);
}
}
}
void AMCharacter::StartZoom()
{
bWantsToZoom = true;
}
void AMCharacter::StopZoom()
{
bWantsToZoom = false;
}
void AMCharacter::StartSprint()
{
if(bCanSprint && !bIsSprinting)
{
UCharacterMovementComponent* MovementComponent = GetCharacterMovement();
if(MovementComponent)
{
MovementComponent->MaxWalkSpeed *= SprintMultiplier;
GetWorldTimerManager().ClearTimer(TimerHandle_RestoreStamina);
GetWorldTimerManager().SetTimer(TimerHandle_ConsumeStamina, this, &AMCharacter::ConsumeStamina, StaminaConsumeInterval);
bIsSprinting = true;
const FVector CurrentLocation = GetActorLocation();
PreviousLocation = FVector2D(CurrentLocation.X, CurrentLocation.Y);
}
}
}
void AMCharacter::StopSprint()
{
if(bIsSprinting)
{
bIsSprinting = false;
bCanSprint = CurrentStamina > 0.f ? true : false;
UCharacterMovementComponent* MovementComponent = GetCharacterMovement();
if(MovementComponent)
{
MovementComponent->MaxWalkSpeed /= SprintMultiplier;
GetWorldTimerManager().ClearTimer(TimerHandle_ConsumeStamina);
GetWorldTimerManager().SetTimer(TimerHandle_RestoreStamina, this, &AMCharacter::RestoreStamina, StaminaRestoreInterval, false, StaminaRestoreDelay);
}
}
}
void AMCharacter::ConsumeStamina()
{
const FVector CurrentLocation = GetActorLocation();
const FVector2D CurrentLocation2D = FVector2D(CurrentLocation.X, CurrentLocation.Y);
if(PreviousLocation != CurrentLocation2D)
{
CurrentStamina = FMath::Max(0.f, CurrentStamina - StaminaConsumeAmount);
PreviousLocation = CurrentLocation2D;
}
if(CurrentStamina > 0.f)
{
GetWorldTimerManager().SetTimer(TimerHandle_ConsumeStamina, this, &AMCharacter::ConsumeStamina, StaminaConsumeInterval);
}
else
{
StopSprint();
}
}
void AMCharacter::RestoreStamina()
{
CurrentStamina = FMath::Min(MaxStamina, CurrentStamina + StaminaRestoreAmount);
if(CurrentStamina < MaxStamina)
{
GetWorldTimerManager().SetTimer(TimerHandle_RestoreStamina, this, &AMCharacter::RestoreStamina, StaminaRestoreInterval);
}
else
{
bCanSprint = true;
}
}
void AMCharacter::EnterSafeZone(AActor* SafeZoneActor)
{
OnCharacterEnterSafeZone.Broadcast(this, SafeZoneActor);
FTimerManager& TimerManager = GetWorldTimerManager();
if(!HealthComponent->IsAtMaxHealth())
TimerManager.SetTimer(TimerHandle_ApplySafeZoneHeal, this, &AMCharacter::ApplySafeZoneHealing, SafeZoneHealInterval, false, SafeZoneHealDelay);
bIsInSafeZone = true;
if(SafeZoneEnterSound)
UGameplayStatics::PlaySound2D(this, SafeZoneEnterSound);
if(TimerManager.IsTimerActive(TimerHandle_ApplyOutOfSafeZoneDamage))
TimerManager.ClearTimer(TimerHandle_ApplyOutOfSafeZoneDamage);
}
void AMCharacter::LeaveSafeZone()
{
if(bIsMounted)
return;
bIsInSafeZone = false;
GetWorldTimerManager().SetTimer(TimerHandle_ApplyOutOfSafeZoneDamage, this, &AMCharacter::ApplyOutOfSafeZoneDamage, OutOfSafeZoneDamageInterval, true);
GetWorldTimerManager().ClearTimer(TimerHandle_ApplySafeZoneHeal);
OnCharacterLeaveSafeZone.Broadcast(this);
if(SafeZoneLeaveSound)
UGameplayStatics::PlaySound2D(this, SafeZoneLeaveSound);
}
void AMCharacter::StartHidingEquippedItem()
{
bWantsToHideEquippedItem = true;
}
void AMCharacter::StopHidingEquippedItem()
{
bWantsToHideEquippedItem = false;
}
void AMCharacter::SetTargetFOV(float NewTargetFOV)
{
if(NewTargetFOV != DefaultFOV)
{
TargetFOV = NewTargetFOV;
bWantsToZoom = true;
}
}
void AMCharacter::ResetToDefaultFOV()
{
TargetFOV = DefaultFOV;
bWantsToZoom = false;
}
void AMCharacter::ApplyOutOfSafeZoneDamage()
{
if(!bIsInSafeZone)
{
if(!HealthComponent->IsDead())
{
UGameplayStatics::ApplyDamage(this, OutOfSafeZoneDamage, GetController(), this, OutOfSafeZoneDamageClass);
GetWorldTimerManager().SetTimer(TimerHandle_ApplyOutOfSafeZoneDamage, this, &AMCharacter::ApplyOutOfSafeZoneDamage, OutOfSafeZoneDamageInterval);
}
else
{
GetWorldTimerManager().ClearTimer(TimerHandle_ApplyOutOfSafeZoneDamage);
}
}
}
void AMCharacter::ApplySafeZoneHealing()
{
if(!HealthComponent->IsAtMaxHealth())
{
HealthComponent->Heal(SafeZoneHealAmount);
if(bIsFirstTimeHeal)
{
if(HealingStartSound)
UGameplayStatics::PlaySound2D(this, HealingStartSound);
bIsFirstTimeHeal = false;
}
else
{
if(HealingInProgressSound)
UGameplayStatics::PlaySound2D(this, HealingInProgressSound);
}
}
if(HealthComponent->IsAtMaxHealth())
{
GetWorldTimerManager().ClearTimer(TimerHandle_ApplySafeZoneHeal);
bIsFirstTimeHeal = true;
if(HealingCompletedSound)
UGameplayStatics::PlaySound2D(this, HealingCompletedSound);
}
else
{
GetWorldTimerManager().SetTimer(TimerHandle_ApplySafeZoneHeal, this, &AMCharacter::ApplySafeZoneHealing, SafeZoneHealInterval);
}
}
void AMCharacter::Interact()
{
if(!HoveredInteractableActor)
return;
IGameplayTagAssetInterface* GameplayTagAsset = Cast<IGameplayTagAssetInterface>(HoveredInteractableActor);
if(GameplayTagAsset && GameplayTagAsset->HasMatchingGameplayTag(FGameplayTag::RequestGameplayTag("Interactable.Mountable")))
{
APlayerController* PC = Cast<APlayerController>(GetController());
if(PC)
{
PC->Possess(Cast<APawn>(HoveredInteractableActor));
AMPlayerMech* Mech = Cast<AMPlayerMech>(HoveredInteractableActor);
if(Mech)
{
bIsMounted = true;
EnterSafeZone(Mech);
Mech->MountPlayerCharacter(this);
// Disable outline
IMInteractableActor* InteractableActor = Cast<IMInteractableActor>(HoveredInteractableActor);
if(InteractableActor)
{
UMeshComponent* InteractableActorMesh = InteractableActor->GetHoveredMeshComponent();
if(InteractableActorMesh)
{
InteractableActorMesh->SetRenderCustomDepth(false);
}
}
}
UE_LOG(LogTemp, Log, TEXT("Player Possessed %s"), *HoveredInteractableActor->GetName());
}
}
}
void AMCharacter::HandleCapsuleBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
IGameplayTagAssetInterface* GameplayTagAsset = Cast<IGameplayTagAssetInterface>(OtherActor);
if(GameplayTagAsset && GameplayTagAsset->HasAnyMatchingGameplayTags(SafeZoneTags) && !bIsInSafeZone)
{
AMConstructableBuilding* ConstructableBuilding = Cast<AMConstructableBuilding>(OtherActor);
if(ConstructableBuilding && ConstructableBuilding->GetBuildingState() == EBuildingState::Constructed)
{
EnterSafeZone(OtherActor);
}
}
}
void AMCharacter::HandleCapsuleEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
IGameplayTagAssetInterface* GameplayTagAsset = Cast<IGameplayTagAssetInterface>(OtherActor);
if(GameplayTagAsset && GameplayTagAsset->HasAnyMatchingGameplayTags(SafeZoneTags) && bIsInSafeZone)
{
// Check again just to make sure, we might have left one safe zone but might still be inside
// another. This can happen if the perimeters of both safezone overlap. We're still technically
// inside 'A' safe zone even if we might have left this one. This check is to make sure this
// scenario is correctly handled.
AMConstructableBuilding* ConstructableBuilding = Cast<AMConstructableBuilding>(OtherActor);
if(ConstructableBuilding && ConstructableBuilding->GetBuildingState() == EBuildingState::Constructed)
{
CheckIfInSafeZone();
}
}
}
void AMCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// Check for hovered interactable objects
FHitResult TraceHit;
FVector TraceStartLocation = CameraComponent->GetComponentLocation();
FVector TraceEndLocation;
TraceEndLocation = TraceStartLocation + CameraComponent->GetComponentRotation().Vector() * MaxInteractableDistance;
FCollisionQueryParams CollisionParams;
CollisionParams.AddIgnoredActor(this);
AActor* PreviousHoveredInteractableActor = HoveredInteractableActor;
bool bShouldUnHover = false;
if(GetWorld()->LineTraceSingleByChannel(TraceHit, TraceStartLocation, TraceEndLocation, TRACE_CHANNEL_Interactable, CollisionParams))
{
AActor* NewHoveredInteractableActor = TraceHit.Actor.Get();
if(NewHoveredInteractableActor && NewHoveredInteractableActor != HoveredInteractableActor)
{
//Hover new actor
HoveredInteractableActor = NewHoveredInteractableActor;
bShouldUnHover = true;
IMInteractableActor* InteractableActor = Cast<IMInteractableActor>(HoveredInteractableActor);
if(InteractableActor)
{
UMeshComponent* InteractableActorMesh = InteractableActor->GetHoveredMeshComponent();
if(InteractableActorMesh)
{
InteractableActorMesh->SetRenderCustomDepth(true);
}
}
}
}
else
{
HoveredInteractableActor = nullptr;
bShouldUnHover = true;
}
//Unhover previous interactable actor
if(PreviousHoveredInteractableActor && bShouldUnHover)
{
IMInteractableActor* InteractableActor = Cast<IMInteractableActor>(PreviousHoveredInteractableActor);
if(InteractableActor)
{
UMeshComponent* InteractableActorMesh = InteractableActor->GetHoveredMeshComponent();
if(InteractableActorMesh)
{
InteractableActorMesh->SetRenderCustomDepth(false);
}
}
}
if(CVAR_DebugPlayerDrawing.GetValueOnGameThread() > 0)
DrawDebugLine(GetWorld(), TraceStartLocation, TraceEndLocation, FColor::Green, false, 1.f, 0, 1.f);
// Zoom in/out
float NewTargetFOV = bWantsToZoom ? TargetFOV : DefaultFOV;
if(CameraComponent->FieldOfView != NewTargetFOV)
{
float NewFOV = FMath::FInterpTo(CameraComponent->FieldOfView, NewTargetFOV, DeltaTime, ZoomInterpolateSpeed);
CameraComponent->SetFieldOfView(NewFOV);
}
// Hide/Show equipped item
float TargetPitch = bWantsToHideEquippedItem ? HiddenArmPitch : DefaultArmPitch;
float CurrentPitch = PlayerArms->GetRelativeRotation().Pitch;
if(CurrentPitch != TargetPitch)
{
float NewPitch = FMath::FInterpTo(CurrentPitch, TargetPitch, DeltaTime, ArmRotateSpeed);
FRotator NewRotation = PlayerArms->GetRelativeRotation();
NewRotation.Pitch = NewPitch;
PlayerArms->SetRelativeRotation(NewRotation);
}
FVector TargetArmLocation = bWantsToHideEquippedItem ? HiddenArmLocation : DefaultArmLocation;
FVector CurrentLocation = PlayerArms->GetRelativeLocation();
if(CurrentLocation != TargetArmLocation)
{
FVector NewLocation = FMath::VInterpTo(CurrentLocation, TargetArmLocation, DeltaTime, ArmRotateSpeed);
PlayerArms->SetRelativeLocation(NewLocation);
}
// Movement Sounds
float CurrentVelocity = GetVelocity().Size();
UPawnMovementComponent* MC = GetMovementComponent();
if(MC && CurrentVelocity > MinimumMovementVelocityThreshold && MC->IsMovingOnGround())
{
if(MC->IsCrouching() && CrouchWalkSound && !AudioComponent->IsPlaying())
{
AudioComponent->Stop();
AudioComponent->SetSound(CrouchWalkSound);
AudioComponent->Play();
}
else if(WalkSound && !AudioComponent->IsPlaying())
{
AudioComponent->Stop();
AudioComponent->SetSound(WalkSound);
AudioComponent->Play();
}
}
}
void AMCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis("MoveForward", this, &AMCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AMCharacter::MoveRight);
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Crouch", IE_Pressed, this, &AMCharacter::StartCrouch);
PlayerInputComponent->BindAction("Crouch", IE_Released, this, &AMCharacter::StopCrouch);
PlayerInputComponent->BindAction("Dismount", IE_Pressed, this, &AMCharacter::Interact);
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AMCharacter::StartFire);
PlayerInputComponent->BindAction("Fire", IE_Released, this, &AMCharacter::StopFire);
// PlayerInputComponent->BindAction("SecondaryFire", IE_Pressed, this, &AMCharacter::StartZoom);
// PlayerInputComponent->BindAction("SecondaryFire", IE_Released, this, &AMCharacter::StopZoom);
PlayerInputComponent->BindAction("Sprint", IE_Pressed, this, &AMCharacter::StartSprint);
PlayerInputComponent->BindAction("Sprint", IE_Released, this, &AMCharacter::StopSprint);
PlayerInputComponent->BindAction("Reload", IE_Pressed, this, &AMCharacter::Reload);
}
void AMCharacter::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const
{
TagContainer = ActorTags;
}
void AMCharacter::EquipWeapon(uint8 Weapon)
{
EWeaponType WeaponType = (EWeaponType)Weapon;
if((CurrentWeapon && CurrentWeapon->WeaponType == WeaponType) || WeaponType >= EWeaponType::MaxWeaponType)
return;
if(Weapons[Weapon])
{
AMWeapon* PreviousWeapon = nullptr;
if(CurrentWeapon)
{
if(CurrentWeapon->IsReloading())
CurrentWeapon->CancelReload();
CurrentWeapon->SetActorHiddenInGame(true);
CurrentWeapon->SetActorTickEnabled(false);
PreviousWeapon = CurrentWeapon;
}
CurrentWeapon = Weapons[Weapon];
CurrentWeapon->SetActorHiddenInGame(false);
CurrentWeapon->SetActorTickEnabled(true);
OnWeaponEquipped.Broadcast(this, CurrentWeapon, PreviousWeapon);
}
}
void AMCharacter::HandleHealthChanged(UMHealthComponent* HealthComp, float Health, float HealthDelta, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if(Health <= 0.f && !bDied)
{
bDied = true;
GetMovementComponent()->StopMovementImmediately();
GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
DetachFromControllerPendingDestroy();
SetLifeSpan(10.f);
}
else
{
// If we received damage, reset the healing timer
if(HealthDelta < 0.f)
{
FTimerManager& TimerManager = GetWorldTimerManager();
if(TimerManager.IsTimerActive(TimerHandle_ApplySafeZoneHeal))
TimerManager.SetTimer(TimerHandle_ApplySafeZoneHeal, this, &AMCharacter::ApplySafeZoneHealing, SafeZoneHealInterval, false, SafeZoneHealDelay);
if(HitByBulletSound)
UGameplayStatics::PlaySound2D(this, HitByBulletSound);
}
CheckIfInSafeZone();
}
}
float AMCharacter::GetStaminaRemappedInRange(float Min, float Max)
{
const FVector2D InputRange(0.f, MaxStamina);
const FVector2D OutputRange(Min, Max);
return FMath::GetMappedRangeValueClamped(InputRange, OutputRange, CurrentStamina);
}