2022-12-13 05:11:56 +01:00
|
|
|
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
|
|
|
|
|
|
#include "PlayerCharacter.h"
|
|
|
|
// You can remove these, this is just to get intellisense to work
|
|
|
|
#include "InputTriggers.h"
|
|
|
|
#include "EnhancedInputComponent.h"
|
|
|
|
#include "EnhancedInputSubsystems.h"
|
|
|
|
#include "GameFramework/CharacterMovementComponent.h"
|
|
|
|
#include "InputMappingContext.h"
|
2023-01-17 00:14:03 +01:00
|
|
|
#include "Destructable.h"
|
2023-02-03 02:31:23 +01:00
|
|
|
#include "EnemyCharacter.h"
|
|
|
|
|
2022-12-13 05:11:56 +01:00
|
|
|
#define COLLISION_WEAPON ECC_GameTraceChannel1
|
|
|
|
|
|
|
|
// Sets default values
|
|
|
|
APlayerCharacter::APlayerCharacter()
|
|
|
|
{
|
|
|
|
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
|
|
|
PrimaryActorTick.bCanEverTick = true;
|
|
|
|
PrimaryActorTick.SetTickFunctionEnable(true);
|
|
|
|
PrimaryActorTick.bStartWithTickEnabled = true;
|
|
|
|
|
|
|
|
//bUseControllerRotationPitch = true;
|
|
|
|
//bUseControllerRotationYaw = true;
|
|
|
|
//bUseControllerRotationRoll = false;
|
|
|
|
|
|
|
|
// Setup the camera boom
|
|
|
|
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
|
|
|
|
CameraBoom->SetupAttachment(RootComponent);
|
|
|
|
CameraBoom->bDoCollisionTest = true;
|
|
|
|
CameraBoom->bUsePawnControlRotation = true;
|
2023-03-13 02:39:43 +01:00
|
|
|
CameraBoom->TargetArmLength = 350.0f;
|
2022-12-13 05:11:56 +01:00
|
|
|
CameraBoom->bEnableCameraLag = true;
|
|
|
|
CameraBoom->CameraLagSpeed = 10.0f;
|
2023-03-13 02:39:43 +01:00
|
|
|
CameraBoom->SocketOffset = { 0.0f, 75.0f, 110.0f };
|
2022-12-13 05:11:56 +01:00
|
|
|
|
|
|
|
// Setup the camera component
|
|
|
|
CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
|
|
|
|
CameraComponent->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
|
|
|
|
CameraComponent->bUsePawnControlRotation = false;
|
2023-03-13 02:39:43 +01:00
|
|
|
CameraComponent->SetRelativeRotation({ -5.0f,0.0f,0.0f });
|
2022-12-13 05:11:56 +01:00
|
|
|
|
|
|
|
// Setup the character movement
|
|
|
|
UCharacterMovementComponent* CharacterMovementComponent = GetCharacterMovement();
|
|
|
|
CharacterMovementComponent->AirControl = 1.0f;
|
|
|
|
CharacterMovementComponent->bOrientRotationToMovement = true;
|
2023-01-17 00:13:24 +01:00
|
|
|
|
2023-02-16 19:27:34 +01:00
|
|
|
// Setup the character perception component
|
|
|
|
PerceptionSource = CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("Perception Source Stimuli"));
|
|
|
|
PerceptionSource->bAutoRegister = true;
|
|
|
|
|
2023-01-17 00:13:24 +01:00
|
|
|
this->Tags.Add(FName("Player"));
|
2022-12-13 05:11:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Called when the game starts or when spawned
|
|
|
|
void APlayerCharacter::BeginPlay()
|
|
|
|
{
|
|
|
|
Super::BeginPlay();
|
|
|
|
|
|
|
|
DefaultMovementSpeed = GetCharacterMovement()->MaxWalkSpeed;
|
2023-01-17 00:13:24 +01:00
|
|
|
|
|
|
|
if (!this->ActorHasTag(FName("Player")))
|
|
|
|
{
|
|
|
|
this->Tags.Add(FName("Player"));
|
|
|
|
}
|
|
|
|
|
2023-02-06 02:07:01 +01:00
|
|
|
if (PlayerHUD)
|
|
|
|
{
|
|
|
|
currentPlayerHUD = UUserWidget::CreateWidgetInstance(*GetWorld(), PlayerHUD, FName("PLayer HUD"));
|
|
|
|
|
|
|
|
if (currentPlayerHUD)
|
|
|
|
{
|
|
|
|
currentPlayerHUD->AddToViewport();
|
|
|
|
}
|
|
|
|
}
|
2022-12-13 05:11:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Called every frame
|
|
|
|
void APlayerCharacter::Tick(float DeltaTime)
|
|
|
|
{
|
|
|
|
Super::Tick(DeltaTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called to bind functionality to input
|
|
|
|
void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
|
|
|
{
|
|
|
|
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
|
|
|
|
|
|
|
if (APlayerController* PC = Cast<APlayerController>(GetController()))
|
|
|
|
{
|
|
|
|
if (UEnhancedInputLocalPlayerSubsystem* InputSystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
|
|
|
|
{
|
|
|
|
if (!InputMappingContext.IsNull())
|
|
|
|
{
|
|
|
|
InputSystem->AddMappingContext(InputMappingContext.LoadSynchronous(), MappingPriority);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (UEnhancedInputComponent* Input = Cast<UEnhancedInputComponent>(PlayerInputComponent))
|
|
|
|
{
|
|
|
|
if (MovementAction)
|
|
|
|
{
|
|
|
|
Input->BindAction(MovementAction, ETriggerEvent::Triggered, this, &APlayerCharacter::MovementCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (LookAction)
|
|
|
|
{
|
|
|
|
Input->BindAction(LookAction, ETriggerEvent::Triggered, this, &APlayerCharacter::LookCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (JumpAction)
|
|
|
|
{
|
|
|
|
Input->BindAction(JumpAction, ETriggerEvent::Triggered, this, &APlayerCharacter::JumpCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FireAction)
|
|
|
|
{
|
2023-01-13 22:11:07 +01:00
|
|
|
Input->BindAction(FireAction, ETriggerEvent::Started, this, &APlayerCharacter::BeginFireCallback);
|
|
|
|
Input->BindAction(FireAction, ETriggerEvent::Completed, this, &APlayerCharacter::EndFireCallback);
|
2022-12-13 05:11:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (QuitAction)
|
|
|
|
{
|
2023-02-06 02:30:57 +01:00
|
|
|
Input->BindAction(QuitAction, ETriggerEvent::Completed, this, &APlayerCharacter::QuitCallback);
|
2022-12-13 05:11:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (SprintAction)
|
|
|
|
{
|
|
|
|
Input->BindAction(SprintAction, ETriggerEvent::Started, this, &APlayerCharacter::SetSprintingCallback);
|
|
|
|
Input->BindAction(SprintAction, ETriggerEvent::Completed, this, &APlayerCharacter::SetWalkingCallback);
|
|
|
|
}
|
2023-01-05 02:53:32 +01:00
|
|
|
|
|
|
|
if (WeaponSwitchingAction)
|
|
|
|
{
|
|
|
|
Input->BindAction(WeaponSwitchingAction, ETriggerEvent::Triggered, this, &APlayerCharacter::WeaponSwitchingCallback);
|
|
|
|
}
|
2022-12-13 05:11:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerCharacter::MovementCallback(const FInputActionInstance& Instance)
|
|
|
|
{
|
|
|
|
FVector2D vec2 = Instance.GetValue().Get<FVector2D>();
|
|
|
|
|
|
|
|
if (vec2.Size() != 0.0f)
|
|
|
|
{
|
|
|
|
AddMovementInput(GetActorForwardVector(), vec2.Y);
|
|
|
|
AddMovementInput(GetActorRightVector(), vec2.X);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerCharacter::LookCallback(const FInputActionInstance& Instance)
|
|
|
|
{
|
|
|
|
FVector2D vec2 = Instance.GetValue().Get<FVector2D>();
|
|
|
|
|
|
|
|
if (vec2.Size() != 0.0f)
|
|
|
|
{
|
|
|
|
AddControllerYawInput(vec2.X * 45.0f * GetWorld()->GetDeltaSeconds());
|
2023-01-11 02:48:29 +01:00
|
|
|
AddControllerPitchInput((vec2.Y * (-1.0f)) * 45.0f * GetWorld()->GetDeltaSeconds());
|
2022-12-13 05:11:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerCharacter::JumpCallback(const FInputActionInstance& Instance)
|
|
|
|
{
|
|
|
|
Jump();
|
|
|
|
}
|
|
|
|
|
2023-01-13 22:11:07 +01:00
|
|
|
void APlayerCharacter::BeginFireCallback(const FInputActionInstance& Instance)
|
2022-12-13 05:11:56 +01:00
|
|
|
{
|
2023-01-13 22:11:07 +01:00
|
|
|
if (CurrentWeapon == nullptr || CurrentWeapon->GetCurrentWeaponStatus()->GetValue() != WeaponState::Idle)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2022-12-13 05:11:56 +01:00
|
|
|
|
2023-01-13 22:11:07 +01:00
|
|
|
IsFiring = true;
|
|
|
|
|
|
|
|
OnFire();
|
|
|
|
|
2023-01-24 03:54:17 +01:00
|
|
|
if (CurrentWeapon == nullptr)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2023-01-13 22:11:07 +01:00
|
|
|
|
|
|
|
if (CurrentWeapon->GetWeaponProperties()->IsAutomatic)
|
|
|
|
{
|
|
|
|
GetWorldTimerManager().SetTimer(CooldownTimerHandle, this, &APlayerCharacter::WeaponCooldownHandler, CurrentWeapon->GetWeaponProperties()->WeaponCooldown, true);
|
|
|
|
GetWorldTimerManager().SetTimer(FireTimerHandle, this, &APlayerCharacter::OnFire, CurrentWeapon->GetWeaponProperties()->WeaponCooldown, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GetWorldTimerManager().SetTimer(CooldownTimerHandle, this, &APlayerCharacter::WeaponCooldownHandler, CurrentWeapon->GetWeaponProperties()->WeaponCooldown, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerCharacter::EndFireCallback(const FInputActionInstance& Instance)
|
|
|
|
{
|
|
|
|
IsFiring = false;
|
2022-12-13 05:11:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerCharacter::QuitCallback(const FInputActionInstance& Instance)
|
|
|
|
{
|
2023-02-06 02:30:57 +01:00
|
|
|
// TODO: Add platform specific Exit requests
|
|
|
|
// This is not a bit deal for the moment as we are only building for windows
|
|
|
|
// For some reason the generic version does not work the same as FWindowsPlatformMisc
|
|
|
|
FWindowsPlatformMisc::RequestExit(false);
|
2022-12-13 05:11:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerCharacter::SetSprintingCallback(const FInputActionInstance& Instance)
|
|
|
|
{
|
|
|
|
GetCharacterMovement()->MaxWalkSpeed = DefaultMovementSpeed * SprintSpeedMultiplier;
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerCharacter::SetWalkingCallback(const FInputActionInstance& Instance)
|
|
|
|
{
|
|
|
|
GetCharacterMovement()->MaxWalkSpeed = DefaultMovementSpeed;
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerCharacter::CalculateHits(TArray<FHitResult>* hits)
|
|
|
|
{
|
|
|
|
// Set up randomness
|
|
|
|
const int32 RandomSeed = FMath::Rand();
|
|
|
|
FRandomStream WeaponRandomStream(RandomSeed);
|
2023-01-13 22:21:56 +01:00
|
|
|
const float Spread = CurrentWeapon->GetWeaponProperties()->WeaponSpread;
|
|
|
|
const float Range = CurrentWeapon->GetWeaponProperties()->ProjectileRange;
|
2022-12-13 05:11:56 +01:00
|
|
|
|
|
|
|
// Calculate starting position and direction
|
|
|
|
FVector TraceStart;
|
|
|
|
FRotator PlayerRot;
|
|
|
|
GetController<APlayerController>()->GetPlayerViewPoint(TraceStart, PlayerRot);
|
|
|
|
TraceStart = GetRootComponent()->GetComponentLocation();
|
|
|
|
FVector AimDir = PlayerRot.Vector();
|
|
|
|
AimDir.Z = 0.0;
|
|
|
|
TraceStart = TraceStart + AimDir * ((GetInstigator()->GetActorLocation() - TraceStart) | AimDir);
|
|
|
|
|
|
|
|
// Calculate the hit results from the trace
|
|
|
|
TArray<FHitResult> HitResults;
|
|
|
|
|
2023-01-13 22:21:56 +01:00
|
|
|
// Set up the collision query params, use the Weapon trace settings, Ignore the actor firing this trace
|
2022-12-13 05:11:56 +01:00
|
|
|
FCollisionQueryParams TraceParams(SCENE_QUERY_STAT(WeaponTrace), true, GetInstigator());
|
|
|
|
TraceParams.bReturnPhysicalMaterial = true;
|
|
|
|
|
2023-01-13 22:21:56 +01:00
|
|
|
for (size_t i = 0; i < CurrentWeapon->GetWeaponProperties()->ProjectilesPerShot; i++)
|
2022-12-13 05:11:56 +01:00
|
|
|
{
|
2023-01-13 22:21:56 +01:00
|
|
|
// Calculate the maximum distance the weapon can fire
|
|
|
|
FVector ShootDir = WeaponRandomStream.VRandCone(AimDir, FMath::DegreesToRadians(Spread), FMath::DegreesToRadians(Spread));
|
|
|
|
FVector MaxHitLoc = TraceStart + (ShootDir * Range);
|
|
|
|
|
|
|
|
GetWorld()->LineTraceMultiByChannel(HitResults, TraceStart, MaxHitLoc, COLLISION_WEAPON, TraceParams);
|
2022-12-13 05:11:56 +01:00
|
|
|
|
2023-01-13 22:21:56 +01:00
|
|
|
for (FHitResult Result : HitResults)
|
|
|
|
{
|
|
|
|
hits->Add(Result);
|
|
|
|
DrawDebugLine(GetWorld(), TraceStart, Result.ImpactPoint, FColor::Blue, true, 500, 0U, 0);
|
|
|
|
|
|
|
|
// TODO: Handle hits in a meaningful way
|
2023-01-31 22:27:36 +01:00
|
|
|
FActorSpawnParameters SpawnParameters;
|
|
|
|
SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
|
|
|
|
|
|
|
// Spawn field actor
|
|
|
|
FTransform transform;
|
|
|
|
transform.SetLocation(Result.ImpactPoint);
|
2023-02-03 02:31:23 +01:00
|
|
|
auto field = GetWorld()->SpawnActor<AFieldSystemActor>(CurrentWeapon->GetFieldSystemActor(), transform, SpawnParameters);
|
|
|
|
|
|
|
|
if (Result.GetActor())
|
2023-01-17 00:14:03 +01:00
|
|
|
{
|
2023-02-03 02:31:23 +01:00
|
|
|
if (Result.GetActor()->ActorHasTag("Enemy"))
|
|
|
|
{
|
|
|
|
// TODO: Do thing
|
|
|
|
auto enemy = Cast<AEnemyCharacter>(Result.GetActor());
|
|
|
|
enemy->GetHealthComponent()->TakeDamage(Result.GetActor(),CurrentWeapon->GetWeaponProperties()->WeaponDamage, nullptr, GetController(), this);
|
|
|
|
}
|
2023-01-17 00:14:03 +01:00
|
|
|
}
|
2023-01-13 22:21:56 +01:00
|
|
|
}
|
2022-12-13 05:11:56 +01:00
|
|
|
}
|
2023-01-02 00:59:59 +01:00
|
|
|
}
|
|
|
|
|
2023-01-05 02:53:32 +01:00
|
|
|
void APlayerCharacter::WeaponSwitchingCallback(const FInputActionInstance& Instance)
|
2023-01-02 00:59:59 +01:00
|
|
|
{
|
2023-01-05 02:53:32 +01:00
|
|
|
float value = Instance.GetValue().Get<float>();
|
|
|
|
|
|
|
|
if (value > 0)
|
|
|
|
{
|
|
|
|
InventoryIncrement();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
InventoryDecrement();
|
|
|
|
}
|
2023-01-02 00:59:59 +01:00
|
|
|
}
|
|
|
|
|
2023-01-13 22:11:07 +01:00
|
|
|
void APlayerCharacter::OnFire()
|
|
|
|
{
|
|
|
|
if (!IsFiring)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CurrentWeapon->SetCurrentWeaponStatus(WeaponState::Firing);
|
|
|
|
|
|
|
|
TArray<FHitResult> Hits = TArray<FHitResult>();
|
|
|
|
CalculateHits(&Hits);
|
|
|
|
|
2023-01-18 01:10:39 +01:00
|
|
|
CurrentWeapon->DecrementAmmoCount(1);
|
|
|
|
|
2023-01-18 01:35:06 +01:00
|
|
|
CurrentWeapon->PlayFireSoundAtLocation(GetActorLocation());
|
|
|
|
|
|
|
|
// TODO: Play some animation here
|
2023-01-13 22:11:07 +01:00
|
|
|
|
|
|
|
CurrentWeapon->SetCurrentWeaponStatus(WeaponState::Cooldown);
|
2023-01-24 03:54:17 +01:00
|
|
|
|
|
|
|
if (CurrentWeapon->GetAmmoCount() == 0)
|
|
|
|
{
|
|
|
|
RemoveCurrentWeaponFromInventory();
|
|
|
|
}
|
2023-01-13 22:11:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerCharacter::WeaponCooldownHandler()
|
|
|
|
{
|
|
|
|
if (CurrentWeapon->GetCurrentWeaponStatus()->GetValue() != WeaponState::Idle)
|
|
|
|
{
|
|
|
|
CurrentWeapon->SetCurrentWeaponStatus(WeaponState::Idle);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!IsFiring)
|
|
|
|
{
|
|
|
|
GetWorldTimerManager().ClearTimer(FireTimerHandle);
|
|
|
|
GetWorldTimerManager().ClearTimer(CooldownTimerHandle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void APlayerCharacter::ClearAllTimers()
|
|
|
|
{
|
|
|
|
GetWorldTimerManager().ClearTimer(FireTimerHandle);
|
|
|
|
GetWorldTimerManager().ClearTimer(CooldownTimerHandle);
|
|
|
|
}
|
2023-02-06 02:07:01 +01:00
|
|
|
|
|
|
|
int APlayerCharacter::GetCurrentAmmoCount()
|
|
|
|
{
|
|
|
|
if (CurrentWeapon == nullptr)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CurrentWeapon->GetAmmoCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
float APlayerCharacter::GetCurrentHealthCount()
|
|
|
|
{
|
|
|
|
if (!GetHealthComponent())
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return GetHealthComponent()->GetCurrentHealth();
|
|
|
|
}
|