Added Source files from svn repo

master
Shariq Shah 3 years ago
parent 63686e9e8b
commit 894844c5e9
  1. 23
      MechDefence.Build.cs
  2. 6
      MechDefence.cpp
  3. 18
      MechDefence.h
  4. 5
      MechDefenceGameModeBase.cpp
  5. 17
      MechDefenceGameModeBase.h
  6. 181
      Private/MAICharacterBase.cpp
  7. 49
      Private/MAIEnemyShooter.cpp
  8. 770
      Private/MCharacter.cpp
  9. 215
      Private/MConstructableBuilding.cpp
  10. 32
      Private/MEnemySpawnPoint.cpp
  11. 171
      Private/MGameMode.cpp
  12. 28
      Private/MGameplayActor.cpp
  13. 65
      Private/MHealthComponent.cpp
  14. 6
      Private/MInteractableActor.cpp
  15. 69
      Private/MPlayerController.cpp
  16. 188
      Private/MPlayerMech.cpp
  17. 48
      Private/MProjectile.cpp
  18. 6
      Private/MTargetableActor.cpp
  19. 419
      Private/MWeapon.cpp
  20. 97
      Public/MAICharacterBase.h
  21. 44
      Public/MAIEnemyShooter.h
  22. 283
      Public/MCharacter.h
  23. 117
      Public/MConstructableBuilding.h
  24. 31
      Public/MEnemySpawnPoint.h
  25. 96
      Public/MGameMode.h
  26. 33
      Public/MGameplayActor.h
  27. 63
      Public/MHealthComponent.h
  28. 26
      Public/MInteractableActor.h
  29. 52
      Public/MPlayerController.h
  30. 90
      Public/MPlayerMech.h
  31. 45
      Public/MProjectile.h
  32. 27
      Public/MTargetableActor.h
  33. 204
      Public/MWeapon.h
  34. 1
      Test.txt

@ -0,0 +1,23 @@
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class MechDefence : ModuleRules
{
public MechDefence(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "GameplayTags", "NavigationSystem", "UMG", "AIModule", "Niagara", "PhysicsCore" });
PrivateDependencyModuleNames.AddRange(new string[] { });
// Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}
}

@ -0,0 +1,6 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "MechDefence.h"
#include "Modules/ModuleManager.h"
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, MechDefence, "MechDefence" );

@ -0,0 +1,18 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#define TRACE_CHANNEL_Interactable ECC_GameTraceChannel1
#define TRACE_CHANNEL_PlayerTeam ECC_GameTraceChannel2
#define TRACE_CHANNEL_Weapon ECC_GameTraceChannel3
#define TRACE_CHANNEL_BuildingPlacement ECC_GameTraceChannel4
#define TRACE_CHANNEL_ConstructedBuilding ECC_GameTraceChannel5
#define SurfaceType_HighFriction SurfaceType1
#define SurfaceType_MediumFriction SurfaceType2
#define SurfaceType_Vulnerable SurfaceType3
#define COLLISION_OBJECT_UIEnablingVolume ECC_GameTraceChannel6

@ -0,0 +1,5 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "MechDefenceGameModeBase.h"

@ -0,0 +1,17 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MechDefenceGameModeBase.generated.h"
/**
*
*/
UCLASS()
class MECHDEFENCE_API AMechDefenceGameModeBase : public AGameModeBase
{
GENERATED_BODY()
};

@ -0,0 +1,181 @@
#include "MAICharacterBase.h"
#include "MCharacter.h"
#include "Components/CapsuleComponent.h"
#include "Components/ShapeComponent.h"
#include "Components/WidgetComponent.h"
#include "MGameplayActor.h"
#include "MHealthComponent.h"
#include "Misc/OutputDeviceDebug.h"
AMAICharacterBase::AMAICharacterBase()
{
PrimaryActorTick.bCanEverTick = true;
HealthComponent = CreateDefaultSubobject<UMHealthComponent>(TEXT("HealthComponent"));
HealthComponent->OnActorDeath.AddDynamic(this, &AMAICharacterBase::HandleCharacterDeath);
static ConstructorHelpers::FClassFinder<AMGameplayActor> SafeZoneClassFinder(TEXT("/Game/MechDefence/Blueprints/Buildings/BP_Beacon"));
SafeZoneClass = SafeZoneClassFinder.Class;
SafeZoneTags.AddTag(FGameplayTag::RequestGameplayTag("PlayerTeam.Beacon"));
StatusIndicatorWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("Status Indicator"));
StatusIndicatorWidget->SetupAttachment(RootComponent);
bIsInSafeZone = true;
}
void AMAICharacterBase::BeginPlay()
{
Super::BeginPlay();
UShapeComponent* OverlappingShape = GetSafeZoneOverlappingShape();
if(OverlappingShape)
{
OverlappingShape->OnComponentBeginOverlap.AddDynamic(this, &AMAICharacterBase::HandleSafeZoneBeginOverlap);
OverlappingShape->OnComponentEndOverlap.AddDynamic(this, &AMAICharacterBase::HandleSafeZoneEndOverlap);
}
// Perform these in the next tick to make sure everything is spawned and assigned before we go further
GetWorldTimerManager().SetTimerForNextTick(this, &AMAICharacterBase::CheckSafeZone);
GetWorldTimerManager().SetTimerForNextTick(this, &AMAICharacterBase::SetWidgetHealthComponentProperty);
}
void AMAICharacterBase::SetWidgetHealthComponentProperty()
{
UUserWidget* IndicatorWidget = StatusIndicatorWidget->GetWidget();
if(IndicatorWidget)
{
FObjectProperty* ObjProp = FindFProperty<FObjectProperty>(IndicatorWidget->GetClass(), TEXT("HealthComponent"));
if(ObjProp)
{
ObjProp->SetObjectPropertyValue_InContainer(IndicatorWidget, HealthComponent, 0);
//ObjProp->SetPropertyValue_InContainer(IndicatorWidget, HealthComponent, 0);
}
const FString Command = FString::Printf(TEXT("SetupHealthChangeAnims"));
FOutputDeviceDebug DebugOutputDevice;
if(!IndicatorWidget->CallFunctionByNameWithArguments(*Command, DebugOutputDevice, IndicatorWidget, true))
{
UE_LOG(LogTemp, Log, TEXT("Failed to setup health change anims"));
}
}
}
UShapeComponent* AMAICharacterBase::GetSafeZoneOverlappingShape()
{
return GetCapsuleComponent();
}
void AMAICharacterBase::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMAICharacterBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}
void AMAICharacterBase::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const
{
TagContainer = ActorTags;
}
void AMAICharacterBase::HandleSafeZoneBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
IGameplayTagAssetInterface* GameplayTagAsset = Cast<IGameplayTagAssetInterface>(OtherActor);
if(GameplayTagAsset && GameplayTagAsset->HasAnyMatchingGameplayTags(SafeZoneTags) && !bIsInSafeZone)
{
EnterSafeZone(OtherActor);
}
}
void AMAICharacterBase::HandleSafeZoneEndOverlap(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
CheckSafeZone();
}
}
void AMAICharacterBase::HandleCharacterDeath(UMHealthComponent* ActorHealthComponent, AActor* DeadActor, AActor* KillerActor)
{
UShapeComponent* ShapeMesh = GetSafeZoneOverlappingShape();
ShapeMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
DeadActor->SetActorEnableCollision(false);
}
void AMAICharacterBase::AddGameplayTags(const FGameplayTagContainer& TagContainer)
{
ActorTags.AppendTags(TagContainer);
}
void AMAICharacterBase::AddGameplayTag(const FGameplayTag& Tag)
{
ActorTags.AddTag(Tag);
}
void AMAICharacterBase::LeaveSafeZone()
{
bIsInSafeZone = false;
OnAILeaveSafeZone.Broadcast(this);
if(StatusIndicatorWidget)
{
StatusIndicatorWidget->SetVisibility(false, true);
}
}
void AMAICharacterBase::EnterSafeZone(AActor* SafeZoneActor)
{
OnAIEnterSafeZone.Broadcast(this, SafeZoneActor);
bIsInSafeZone = true;
if(StatusIndicatorWidget)
{
StatusIndicatorWidget->SetVisibility(true, true);
}
}
void AMAICharacterBase::CheckSafeZone()
{
TArray<AActor*> OverlappingActors;
UShapeComponent* SafeZoneOverlappingShape = GetSafeZoneOverlappingShape();
if(SafeZoneOverlappingShape)
{
SafeZoneOverlappingShape->GetOverlappingActors(OverlappingActors, SafeZoneClass);
if(OverlappingActors.Num() == 0)
{
if(bIsInSafeZone)
LeaveSafeZone();
}
else if(OverlappingActors.Num() > 0)
{
IGameplayTagAssetInterface* GameplayTagAsset = Cast<IGameplayTagAssetInterface>(OverlappingActors[0]);
if(GameplayTagAsset && GameplayTagAsset->HasAnyMatchingGameplayTags(SafeZoneTags))
{
if(!bIsInSafeZone) // If we're not in safe zone yet, enter it
{
EnterSafeZone(OverlappingActors[0]);
}
else
{
// If we're already in safe zone, check if we need healing
}
}
}
}
}

@ -0,0 +1,49 @@
#include "MAIEnemyShooter.h"
#include "MWeapon.h"
#include "MHealthComponent.h"
#include "NavigationSystem.h"
#include "Components/CapsuleComponent.h"
AMAIEnemyShooter::AMAIEnemyShooter()
{
WeaponSocketName = "WeaponSocket";
bCanIdle = true;
PatrolRadius = 500;
}
void AMAIEnemyShooter::BeginPlay()
{
Super::BeginPlay();
StartingLocation = GetActorLocation();
//Spawn Default Weapon
FActorSpawnParameters SpawnParameters;
SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
CurrentWeapon = GetWorld()->SpawnActor<AMWeapon>(StarterWeaponClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParameters);
if(CurrentWeapon)
{
CurrentWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponSocketName);
CurrentWeapon->SetOwner(this);
}
}
UShapeComponent* AMAIEnemyShooter::GetSafeZoneOverlappingShape()
{
return GetCapsuleComponent();
}
FVector AMAIEnemyShooter::PickNextPartrolDestination()
{
FNavLocation ResultLocation;
UNavigationSystemV1* NavigationSystem = UNavigationSystemV1::GetNavigationSystem(GetController());
if(NavigationSystem && NavigationSystem->GetRandomPointInNavigableRadius(StartingLocation, PatrolRadius, ResultLocation))
{
return ResultLocation.Location;
}
return FVector();
}

@ -0,0 +1,770 @@
// 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);
}

@ -0,0 +1,215 @@
#include "MConstructableBuilding.h"
#include "MHealthComponent.h"
#include "Components/BoxComponent.h"
#include "Components/ShapeComponent.h"
#include "../MechDefence.h"
#include "Blueprint/UserWidget.h"
#include "Components/ArrowComponent.h"
#include "Components/WidgetComponent.h"
#include "Misc/OutputDeviceDebug.h"
AMConstructableBuilding::AMConstructableBuilding()
{
PrimaryActorTick.bCanEverTick = true;
BaseBuildingMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Base Mesh"));
RootComponent = BaseBuildingMesh;
BaseBuildingMesh->SetCollisionResponseToChannel(TRACE_CHANNEL_ConstructedBuilding, ECR_Block);
BaseBuildingMesh->SetCollisionResponseToChannel(TRACE_CHANNEL_Weapon, ECR_Ignore);
BaseBuildingMesh->SetCollisionResponseToChannel(COLLISION_OBJECT_UIEnablingVolume, ECR_Overlap);
BaseBuildingMesh->OnComponentBeginOverlap.AddDynamic(this, &AMConstructableBuilding::HandleMeshComponentBeginOverlap);
BaseBuildingMesh->OnComponentEndOverlap.AddDynamic(this, &AMConstructableBuilding::HandleMeshComponentEndOverlap);
BaseBuildingMesh->SetGenerateOverlapEvents(true);
PlacementOverlapChecker = CreateDefaultSubobject<UBoxComponent>(TEXT("PlacementOverlapChecker"));
PlacementOverlapChecker->SetupAttachment(RootComponent);
PlacementOverlapChecker->SetCollisionResponseToAllChannels(ECR_Overlap);
PlacementOverlapChecker->SetCollisionResponseToChannel(TRACE_CHANNEL_Weapon, ECR_Ignore);
ArrowMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ArrowMesh"));
ArrowMesh->SetupAttachment(RootComponent);
ArrowMesh->SetHiddenInGame(false);
ArrowMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
InvalidActorsFilter.AddUnique(GetClass());
HealthComponent = CreateDefaultSubobject<UMHealthComponent>(TEXT("HealthComp"));
StatusIndicatorWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("Health Indicator"));
StatusIndicatorWidget->SetupAttachment(RootComponent);
StatusIndicatorWidget->SetHiddenInGame(true);
BuildingState = EBuildingState::UnderConstruction;
}
void AMConstructableBuilding::HandleMeshComponentEndOverlap(UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if(OtherComp->GetCollisionObjectType() == COLLISION_OBJECT_UIEnablingVolume)
{
StatusIndicatorWidget->SetHiddenInGame(true);
}
}
void AMConstructableBuilding::HandleMeshComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep,
const FHitResult& SweepResult)
{
if(OtherComp->GetCollisionObjectType() == COLLISION_OBJECT_UIEnablingVolume)
{
StatusIndicatorWidget->SetHiddenInGame(false);
}
}
void AMConstructableBuilding::SetupHealthIndicator()
{
UUserWidget* IndicatorWidget = StatusIndicatorWidget->GetWidget();
if(IndicatorWidget)
{
FObjectProperty* ObjProp = FindFProperty<FObjectProperty>(IndicatorWidget->GetClass(), TEXT("HealthComponent"));
if(ObjProp)
{
ObjProp->SetObjectPropertyValue_InContainer(IndicatorWidget, HealthComponent, 0);
}
const FString Command = FString::Printf(TEXT("SetupHealthChangeAnims"));
FOutputDeviceDebug DebugOutputDevice;
if(!IndicatorWidget->CallFunctionByNameWithArguments(*Command, DebugOutputDevice, IndicatorWidget, true))
{
UE_LOG(LogTemp, Log, TEXT("Failed to setup health change anims"));
}
}
}
void AMConstructableBuilding::BeginPlay()
{
Super::BeginPlay();
DefaultMaterials = BaseBuildingMesh->GetMaterials();
if(bNetStartup)
SetBuildingState(EBuildingState::Constructed);
GetWorldTimerManager().SetTimerForNextTick(this, &AMConstructableBuilding::SetupHealthIndicator);
HealthComponent->Deactivate();
}
void AMConstructableBuilding::ConstructionCompleted()
{
SetBuildingState(EBuildingState::Constructed);
}
void AMConstructableBuilding::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMConstructableBuilding::SetBuildingState(TEnumAsByte<EBuildingState> NewState)
{
// if(NewState == BuildingState)
// return;
const EBuildingState PreviousState = BuildingState;
BuildingState = NewState;
if(NewState != EBuildingState::Constructed)
{
CurrentBuildingStateMaterial = UMaterialInstanceDynamic::Create(BuildingDynamicStateMaterial, this);
//Set instance color param according to the building state
FLinearColor ColorValue;
switch(NewState)
{
case EBuildingState::Constructable: ColorValue = ConstructableColor; break;
case EBuildingState::UnConstructable: ColorValue = UnConstructableColor; break;
case EBuildingState::UnderConstruction: ColorValue = UnderConstructionColor; break;
default: ColorValue = FLinearColor(0, 0, 0); break;
}
CurrentBuildingStateMaterial->SetVectorParameterValue("Color", ColorValue);
for(int i = 0; i < DefaultMaterials.Num(); i++)
BaseBuildingMesh->SetMaterial(i, CurrentBuildingStateMaterial);
}
if(BuildingState == EBuildingState::Constructed)
{
for(int i = 0; i < DefaultMaterials.Num(); i++)
BaseBuildingMesh->SetMaterial(i, DefaultMaterials[i]);
BaseBuildingMesh->SetCollisionResponseToChannel(TRACE_CHANNEL_Weapon, ECR_Block);
HealthComponent->Activate();
ArrowMesh->SetHiddenInGame(true);
}
BuildingStateSet(NewState, PreviousState);
OnBuildingStateChanged.Broadcast(this, NewState, PreviousState);
}
void AMConstructableBuilding::StartBuildingConstruction(float TimeToConstruct)
{
GetWorldTimerManager().SetTimer(TimerHandle_UnderConstructionTimer, this, &AMConstructableBuilding::ConstructionCompleted, TimeToConstruct);
}
void AMConstructableBuilding::SetHighlighted_Implementation(bool Highlighted)
{
BaseBuildingMesh->SetRenderCustomDepth(Highlighted);
}
bool AMConstructableBuilding::IsAtValidLocation_Implementation()
{
bool PositionIsValid = true;
if(BaseBuildingMesh)
{
TArray<AActor*> OverlappingActors;
for(const TSubclassOf<AActor> FilteredActorClass : InvalidActorsFilter)
{
PlacementOverlapChecker->GetOverlappingActors(OverlappingActors, FilteredActorClass);
if(FilteredActorClass == GetClass())
{
// Check overlapping against ourselves
for(const AActor* OverlappingActor : OverlappingActors)
{
if(OverlappingActor != this)
{
PositionIsValid = false;
break;
}
}
}
else if(OverlappingActors.Num() != 0)
{
PositionIsValid = false;
}
if(!PositionIsValid)
break;
}
// We are not overlapping with any unwanted actors, now to check if we are overlapping against
// required actors
if(PositionIsValid)
{
for(const TSubclassOf<AActor> RequiredActorClass : RequiredOverlappingActorsFilter)
{
PlacementOverlapChecker->GetOverlappingActors(OverlappingActors, RequiredActorClass);
if(OverlappingActors.Num() == 0)
{
PositionIsValid = false;
break;
}
}
}
}
return PositionIsValid;
}

@ -0,0 +1,32 @@
#include "MEnemySpawnPoint.h"
// Sets default values
AMEnemySpawnPoint::AMEnemySpawnPoint()
{
// 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;
}
// Called when the game starts or when spawned
void AMEnemySpawnPoint::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMEnemySpawnPoint::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMEnemySpawnPoint::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const
{
TagContainer = ActorTags;
}

@ -0,0 +1,171 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "MGameMode.h"
#include "Kismet/GameplayStatics.h"
#include "MPlayerController.h"
#include "EngineUtils.h"
#include "GameFramework/Character.h"
#include "MAICharacterBase.h"
#include "GameplayTagsManager.h"
#include "MHealthComponent.h"
AMGameMode::AMGameMode()
{
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.TickInterval = 1.f;
WaveCount = 0;
LevelEndViewTargetBlendTime = 2.f;
TimeBetweenWaves = 60.f;
NumEnemiesKilled = 0;
MaxEnemiesInWave = 10;
MinEnemiesInWave = 3;
}
void AMGameMode::StartPlay()
{
Super::StartPlay();
PrepareForNextWave();
}
void AMGameMode::HandlePlayerKilled()
{
GameOver();
}
void AMGameMode::HandlePlayerVehicleDestroyed(AActor* Vehicle)
{
GameOver();
}
void AMGameMode::GameOver()
{
if(SpectatingViewPointClass)
{
TArray<AActor*> ReturnedActors;
UGameplayStatics::GetAllActorsOfClass(this, SpectatingViewPointClass, ReturnedActors);
AActor* NewViewTarget = nullptr;
if(ReturnedActors.Num() > 0)
{
NewViewTarget = ReturnedActors[0];
for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; It++)
{
AMPlayerController* PC = Cast<AMPlayerController>(It->Get());
if(PC)
{
PC->SetViewTargetWithBlend(NewViewTarget, LevelEndViewTargetBlendTime, EViewTargetBlendFunction::VTBlend_Cubic);
PC->OnGameOver();
SetWaveState(EWaveState::GameOver);
}
}
}
}
}
void AMGameMode::LevelCompleted()
{
if(SpectatingViewPointClass)
{
TArray<AActor*> ReturnedActors;
UGameplayStatics::GetAllActorsOfClass(this, SpectatingViewPointClass, ReturnedActors);
AActor* NewViewTarget = nullptr;
if(ReturnedActors.Num() > 0)
{
NewViewTarget = ReturnedActors[0];
for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; It++)
{
AMPlayerController* PC = Cast<AMPlayerController>(It->Get());
if(PC)
{
PC->SetViewTargetWithBlend(NewViewTarget, LevelEndViewTargetBlendTime, EViewTargetBlendFunction::VTBlend_Cubic);
PC->OnLevelComplete();
SetWaveState(EWaveState::LevelComplete);
}
}
}
}
}
void AMGameMode::PrepareForNextWave()
{
GetWorldTimerManager().SetTimer(TimerHandle_NextWaveStart, this, &AMGameMode::StartWave, TimeBetweenWaves, false);
SetWaveState(EWaveState::WaitingToStart);
}
void AMGameMode::StartWave()
{
FTimerManager& TimerManager = GetWorldTimerManager();
// Cancel Timer if it is still active. This needs to be done if
// the wave is triggered manually by a console command to make sure wave isn't
// triggered twice
if(TimerManager.IsTimerActive(TimerHandle_NextWaveStart))
TimerManager.ClearTimer(TimerHandle_NextWaveStart);
WaveCount++;
NumEnemiesToSpawn = FMath::RandRange(MinEnemiesInWave, MaxEnemiesInWave);
NumEnemiesToSpawn = WaveCount * NumEnemiesToSpawn;
TimerManager.SetTimer(TimerHandle_EnemySpawn, this, &AMGameMode::SpawnEnemyTimerElapsed, 1.f, true, 0.f);
SetWaveState(EWaveState::WaveInProgress);
UE_LOG(LogTemp, Log, TEXT("Enemies to spawn %d"), NumEnemiesToSpawn);
}
void AMGameMode::EndWave()
{
GetWorldTimerManager().ClearTimer(TimerHandle_EnemySpawn);
SetWaveState(EWaveState::WaitingToComplete);
}
void AMGameMode::CheckWaveState()
{
bool IsPreparingForNextWave = GetWorldTimerManager().IsTimerActive(TimerHandle_NextWaveStart);
if(NumEnemiesToSpawn > 0 || IsPreparingForNextWave)
return;
bool IsAnyEnemyAlive = false;
for(TActorIterator<AMAICharacterBase> It(GetWorld()); It; ++It)
{
AMAICharacterBase* EnemyChar = *It;
if(EnemyChar->HasAllMatchingGameplayTags(SpawnedEnemyTags))
{
UMHealthComponent* HealthComponent = Cast<UMHealthComponent>(EnemyChar->GetComponentByClass(UMHealthComponent::StaticClass()));
if(HealthComponent && HealthComponent->GetHealth() > 0.f)
{
IsAnyEnemyAlive = true;
break;
}
}
}
if(!IsAnyEnemyAlive)
{
SetWaveState(EWaveState::WaveComplete);
PrepareForNextWave();
}
}
void AMGameMode::SetWaveState(EWaveState NewWaveState)
{
WaveStateChanged(NewWaveState, WaveState);
WaveState = NewWaveState;
}
void AMGameMode::SpawnEnemyTimerElapsed()
{
SpawnNewEnemy();
NumEnemiesToSpawn--;
if(NumEnemiesToSpawn <= 0)
{
EndWave();
}
}
void AMGameMode::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
CheckWaveState();
}

@ -0,0 +1,28 @@
#include "MGameplayActor.h"
AMGameplayActor::AMGameplayActor()
{
PrimaryActorTick.bCanEverTick = true;
}
void AMGameplayActor::BeginPlay()
{
Super::BeginPlay();
}
void AMGameplayActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMGameplayActor::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const
{
TagContainer = ActorTags;
}

@ -0,0 +1,65 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "MHealthComponent.h"
#include "MGameMode.h"
UMHealthComponent::UMHealthComponent()
{
DefaultHealth = 100.f;
bIsDead = false;
bIsInvincible = false;
}
void UMHealthComponent::BeginPlay()
{
Super::BeginPlay();
AActor* ComponentOwner = GetOwner();
if(ComponentOwner)
ComponentOwner->OnTakeAnyDamage.AddDynamic(this, &UMHealthComponent::HandleTakeAnyDamage);
Health = DefaultHealth;
}
void UMHealthComponent::HandleTakeAnyDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if(bIsDead)
return;
if(!bIsInvincible)
Health = FMath::Clamp(Health - Damage, 0.f, DefaultHealth);
bIsDead = Health <= 0.f;
OnHealthChanged.Broadcast(this, Health, -(Health - Damage), DamageType, InstigatedBy, DamageCauser);
if(bIsDead)
{
AMGameMode* GM = Cast<AMGameMode>(GetWorld()->GetAuthGameMode());
if(GM)
{
GM->OnActorKilled.Broadcast(DamagedActor, DamageCauser, InstigatedBy);
}
OnActorDeath.Broadcast(this, DamagedActor, DamageCauser);
}
}
float UMHealthComponent::GetHealthRemappedInRange(float Min, float Max)
{
const FVector2D InputRange(0.f, DefaultHealth);
const FVector2D OutputRange(Min, Max);
return FMath::GetMappedRangeValueClamped(InputRange, OutputRange, Health);
}
void UMHealthComponent::Heal(float HealAmount)
{
if(HealAmount <= 0.f || bIsDead || Health <= 0.f)
return;
Health = FMath::Clamp(Health + HealAmount, 0.f, DefaultHealth);
OnHealthChanged.Broadcast(this, Health, HealAmount, nullptr, nullptr, nullptr);
}

@ -0,0 +1,6 @@
#include "MInteractableActor.h"
// Add default functionality here for any IMInteractableActor functions that are not pure virtual.

@ -0,0 +1,69 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "MPlayerController.h"
#include "Blueprint/UserWidget.h"
#include "MWeapon.h"
AMPlayerController::AMPlayerController()
{
bShouldPerformFullTickWhenPaused = true;
}
void AMPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
InputComponent->BindAction("Pause", IE_Pressed, this, &AMPlayerController::TogglePause);
}
void AMPlayerController::TogglePause()
{
//SetPause(IsPaused() ? false : true);
UE_LOG(LogTemp, Log, TEXT("Pause"));
Pause();
APawn* ControlledPawn = GetPawn();
if(IsPaused())
{
ControlledPawn->DisableInput(this);
if(PauseWidget)
PauseWidget->AddToViewport();
if(HudWidget)
HudWidget->RemoveFromViewport();
FInputModeGameAndUI InputMode;
SetInputMode(InputMode);
bShowMouseCursor = true;
}
else
{
ControlledPawn->EnableInput(this);
if(HudWidget)
HudWidget->AddToViewport();
if(PauseWidget)
PauseWidget->RemoveFromViewport();
FInputModeGameOnly InputMode;
SetInputMode(InputMode);
bShowMouseCursor = false;
}
}
void AMPlayerController::BeginPlay()
{
Super::BeginPlay();
if(HudWidgetClass)
{
HudWidget = CreateWidget(this, HudWidgetClass, "PlayerHud");
if(HudWidget)
HudWidget->AddToViewport();
}
if(PauseWidgetClass)
{
PauseWidget = CreateWidget(this, PauseWidgetClass, "PauseWidget");
}
}

@ -0,0 +1,188 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "MPlayerMech.h"
#include "DrawDebugHelpers.h"
#include "MCharacter.h"
#include "NavigationSystem.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/BoxComponent.h"
#include "Components/ArrowComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "MHealthComponent.h"
#include "Components/WidgetComponent.h"
#include "EnvironmentQuery/EnvQueryManager.h"
#include "EnvironmentQuery/EnvQueryTypes.h"
#include "Kismet/GameplayStatics.h"
#include "Misc/OutputDeviceDebug.h"
int32 AMPlayerMech::DebugMechDrawing = 0;
FAutoConsoleVariableRef CVAR_DebugMechDrawing(
TEXT("MDEF.DebugMechDrawing"),
AMPlayerMech::DebugMechDrawing,
TEXT("Draw debug visualizations for the player mech"),
ECVF_Cheat);
AMPlayerMech::AMPlayerMech()
{
PrimaryActorTick.bCanEverTick = true;
MeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("MeshComponent");
RootComponent = MeshComponent;
SpringArmComponent = CreateDefaultSubobject<USpringArmComponent>("SpringArmComponent");
SpringArmComponent->SetupAttachment(MeshComponent);
SpringArmComponent->TargetArmLength = 1500.f;
CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
CameraComponent->SetupAttachment(SpringArmComponent);
CameraComponent->SetRelativeLocation(FVector(0.f, 0.f, BaseEyeHeight));
HealthComponent = CreateDefaultSubobject<UMHealthComponent>(TEXT("HealthComponent"));
StatusIndicatorWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("StatusIndicator"));
StatusIndicatorWidget->SetupAttachment(RootComponent);
PlayerDismountRadius = 500.f;
PlayerDismountGroundOffset = 100.f;
ResetZOffset = 200.f;
MountedPlayerCharacter = nullptr;
}
void AMPlayerMech::BeginPlay()
{
Super::BeginPlay();
GetWorldTimerManager().SetTimerForNextTick(this, &AMPlayerMech::SetupHealthIndicator);
}
void AMPlayerMech::SetupHealthIndicator()
{
UUserWidget* IndicatorWidget = StatusIndicatorWidget->GetWidget();
if(IndicatorWidget)
{
FObjectProperty* ObjProp = FindFProperty<FObjectProperty>(IndicatorWidget->GetClass(), TEXT("HealthComponent"));
if(ObjProp)
{
ObjProp->SetObjectPropertyValue_InContainer(IndicatorWidget, HealthComponent, 0);
}
const FString Command = FString::Printf(TEXT("SetupHealthChangeAnims"));
FOutputDeviceDebug DebugOutputDevice;
if(!IndicatorWidget->CallFunctionByNameWithArguments(*Command, DebugOutputDevice, IndicatorWidget, true))
{
UE_LOG(LogTemp, Log, TEXT("Failed to setup health change anims"));
}
}
}
void AMPlayerMech::MoveForward(float ScaleValue)
{
//AddMovementInput(GetActorForwardVector(), ScaleValue);
}
void AMPlayerMech::MoveRight(float ScaleValue)
{
//AddMovementInput(GetActorRightVector(), ScaleValue);
//AddControllerYawInput(ScaleValue);
}
void AMPlayerMech::RequestDismount()
{
FEnvQueryRequest DismountQueryRequest = FEnvQueryRequest(DismountEQS);
DismountQueryRequest.Execute(EEnvQueryRunMode::SingleResult, this, &AMPlayerMech::HandleDismountResponse);
}
void AMPlayerMech::RevertOrientation()
{
const FRotator CameraOrientation = CameraComponent->GetComponentRotation();
const FRotator NewRotation(0.f, CameraOrientation.Yaw, 0.f);
const FVector NewLocation = GetActorLocation() + FVector(0.f, 0.f, ResetZOffset);
SetActorLocationAndRotation(NewLocation, NewRotation, false, nullptr, ETeleportType::TeleportPhysics);
}
void AMPlayerMech::HandleDismountResponse(TSharedPtr<FEnvQueryResult> QueryResult)
{
if(QueryResult->IsSuccsessful())
{
FVector DismountLocation = QueryResult->GetItemAsLocation(0);
if(DebugMechDrawing > 0)
DrawDebugSphere(GetWorld(), DismountLocation, 50.f, 20, FColor::Green, false, 5.f, 0, 1.f);
if(MountedPlayerCharacter)
{
MountedPlayerCharacter->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
MountedPlayerCharacter->SetActorHiddenInGame(false);
MountedPlayerCharacter->SetActorEnableCollision(true);
MountedPlayerCharacter->SetActorTickEnabled(true);
//DismountLocation.Z += PlayerDismountGroundOffset; // Add a bit of offset from the ground to avoid player falling from the ground when dismounting
MountedPlayerCharacter->SetActorLocationAndRotation(DismountLocation, FRotator(0.f, GetActorRotation().Yaw, 0.f), false, nullptr, ETeleportType::ResetPhysics);
APlayerController* PC = Cast<APlayerController>(GetController());
if(PC)
{
PC->Possess(Cast<APawn>(MountedPlayerCharacter));
}
MountedPlayerCharacter->SetMounted(false);
MountedPlayerCharacter->CheckIfInSafeZone();
MountedPlayerCharacter = nullptr;
if(MountSound)
UGameplayStatics::PlaySound2D(this, MountSound);
}
}
else
{
UE_LOG(LogTemp, Log, TEXT("Could not find suitable location to dismount"));
}
}
void AMPlayerMech::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMPlayerMech::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis("MoveForward", this, &AMPlayerMech::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AMPlayerMech::MoveRight);
//PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAction("Dismount", IE_Pressed, this, &AMPlayerMech::RequestDismount);
PlayerInputComponent->BindAction("ResetVehicle", IE_Pressed, this, &AMPlayerMech::RevertOrientation);
}
void AMPlayerMech::MountPlayerCharacter(AMCharacter* PlayerCharacter)
{
if(!PlayerCharacter)
return;
MountedPlayerCharacter = PlayerCharacter;
MountedPlayerCharacter->SetActorHiddenInGame(true);
MountedPlayerCharacter->SetActorEnableCollision(false);
MountedPlayerCharacter->SetActorTickEnabled(false);
PlayerCharacter->AttachToActor(this, FAttachmentTransformRules::SnapToTargetNotIncludingScale, PlayerAttachmentSocket);
if(MountSound)
UGameplayStatics::PlaySound2D(this, MountSound);
}
void AMPlayerMech::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const
{
TagContainer = ActorTags;
}
UMeshComponent* AMPlayerMech::GetHoveredMeshComponent()
{
return MeshComponent;
}

@ -0,0 +1,48 @@
#include "MProjectile.h"
#include "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
AMProjectile::AMProjectile()
{
PrimaryActorTick.bCanEverTick = true;
CapsuleComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleComponent"));
RootComponent = CapsuleComponent;
MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
MeshComponent->SetupAttachment(RootComponent);
MeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
ParticleSystem = CreateDefaultSubobject<UNiagaraComponent>(TEXT("ParticleSystem"));
ParticleSystem->SetupAttachment(RootComponent);
ProjectileComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
ProjectileComponent->InitialSpeed = 12000.f;
ProjectileComponent->MaxSpeed = 18000.f;
ProjectileComponent->ProjectileGravityScale = 0.f;
}
void AMProjectile::BeginPlay()
{
Super::BeginPlay();
CapsuleComponent->OnComponentBeginOverlap.AddDynamic(this, &AMProjectile::HandleBeginOverlap);
SetLifeSpan(5.f);
}
void AMProjectile::HandleBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
AActor* ProjectileOwner = GetOwner();
if(ProjectileOwner && OtherActor != ProjectileOwner && !ActorClassesToIgnore.Contains(OtherActor->GetClass()))
{
if(ImpactEffect)
{
UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), ImpactEffect, SweepResult.ImpactPoint, SweepResult.ImpactNormal.Rotation());
}
Destroy();
}
}

@ -0,0 +1,6 @@
#include "MTargetableActor.h"
// Add default functionality here for any IMTargetableActor functions that are not pure virtual.

@ -0,0 +1,419 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "MWeapon.h"
#include "Kismet/GameplayStatics.h"
#include "MechDefence/MechDefence.h"
#include "DrawDebugHelpers.h"
#include "MAICharacterBase.h"
#include "MCharacter.h"
#include "Particles/ParticleSystemComponent.h"
#include "MPlayerController.h"
#include "NiagaraSystem.h"
#include "NiagaraFunctionLibrary.h"
#include "NiagaraComponent.h"
#include "Components/AudioComponent.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
#include "MProjectile.h"
static TAutoConsoleVariable<int32> CVAR_DebugWeaponDrawing(
TEXT("MDEF.DebugWeaponDraw"),
0,
TEXT("Draw debug lines for visualizing weapons"),
ECVF_Cheat);
AMWeapon::AMWeapon()
{
PrimaryActorTick.bCanEverTick = true;
WeaponMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Weapon Mesh"));
RootComponent = WeaponMesh;
MuzzleSocketName = "MuzzleSocket";
PlayerAttachmentSocketName = "WeaponSocket";
BaseDamage = 20.f;
VulnerableDamageMultiplier = 4.f;
RateOfFire = 100.f;
BulletSpread = 0.5f;
bFiring = false;
bReloading = false;
TimeLastFired = 0.f;
StopFireDelay = 0.1f;
MaxWeaponRange = 10000.f;
TracerParticleStartLocationName = "Target";
ClipSize = 5;
ReloadTime = 2.f;
ProjectileSpawnChance = 0.5f;
bCanZoom = true;
ZoomInFOV = 60.f;
bSecondaryFireBound = false;
}
void AMWeapon::BeginPlay()
{
Super::BeginPlay();
ClipRemainingBullets = ClipSize;
TimeBetweenShots = 60.f / RateOfFire;
}
void AMWeapon::Fire()
{
OnFire();
TimeLastFired = GetWorld()->GetTimeSeconds();
if(ClipSize != 0)
ClipRemainingBullets--;
bFiring = true;
}
void AMWeapon::PlayFireEffects(FVector ShotDirection, UParticleSystem* ImpactEffect, FVector TraceEffectEndLocation, const FRotator ImpactParticleRotation)
{
// Muzzle Particle Effect
if(MuzzleEffect)
UGameplayStatics::SpawnEmitterAttached(MuzzleEffect, WeaponMesh, MuzzleSocketName);
// Projectile
bool ProjectileSpawned = false;
if(ProjectileClass && ProjectileSpawnChance > 0.f)
{
const float SpawnProbability = FMath::FRand();
if(SpawnProbability > 0.f && SpawnProbability <= ProjectileSpawnChance)
{
const FVector MuzzleSocketLocation = WeaponMesh->GetSocketLocation(MuzzleSocketName);
FActorSpawnParameters SpawnParameters;
SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
AMProjectile* Projectile = GetWorld()->SpawnActor<AMProjectile>(ProjectileClass, MuzzleSocketLocation, ShotDirection.Rotation(), SpawnParameters);
Projectile->SetOwner(this);
ProjectileSpawned = true;
}
}
// Tracer Particle Effect
if(TracerEffect && !ProjectileSpawned)
{
const FVector MuzzleSocketLocation = WeaponMesh->GetSocketLocation(MuzzleSocketName);
//UParticleSystemComponent* TracerComp = UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), TracerEffect, MuzzleSocketLocation);
UNiagaraComponent* TracerComp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), TracerEffect, MuzzleSocketLocation);
if(TracerComp)
{
TracerComp->SetVectorParameter(TracerParticleStartLocationName, TraceEffectEndLocation);
}
}
// Impact Particle
if(ImpactEffect)
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ImpactEffect, TraceEffectEndLocation, ImpactParticleRotation);
}
void AMWeapon::OnFire_Implementation()
{
AActor* WeaponOwner = GetOwner();
if(!WeaponOwner)
{
UE_LOG(LogTemp, Log, TEXT("No weapon owner for weapon %s. Cannot Fire!"), *GetName());
return;
}
// try and play the sound if specified
if(FireSound)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}
APawn* WeaponOwnerPawn = Cast<APawn>(WeaponOwner);
MakeNoise(0.75f, WeaponOwnerPawn);
UParticleSystem* ImpactEffect = DefaultImpactEffect;
FVector EyeLocation;
FRotator EyeRotation;
WeaponOwner->GetActorEyesViewPoint(EyeLocation, EyeRotation);
FVector ShotDirection = EyeRotation.Vector();
// Bullet Spread if aiming from hip
bool bShouldApplyBulletSpread = true;
AMCharacter* PlayerChar = Cast<AMCharacter>(WeaponOwner);
if(PlayerChar && PlayerChar->IsZoomedIn())
{
bShouldApplyBulletSpread = false;
}
if(bShouldApplyBulletSpread)
{
float HalfAngleRadians = FMath::DegreesToRadians(BulletSpread);
ShotDirection = FMath::VRandCone(ShotDirection, HalfAngleRadians);
}
FVector TraceEndLocation = EyeLocation + (ShotDirection * MaxWeaponRange);
FVector TraceEffectEndLocation = TraceEndLocation; // Init Location where the Tracer effect should end
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(WeaponOwner);
QueryParams.AddIgnoredActor(this);
QueryParams.bTraceComplex = true;
QueryParams.bReturnPhysicalMaterial = true;
EPhysicalSurface HitSurfaceType = SurfaceType_Default;
FRotator ImpactRotation;
FHitResult Hit;
if(GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEndLocation, TRACE_CHANNEL_Weapon, QueryParams))
{
AActor* HitActor = Hit.GetActor();
TraceEffectEndLocation = Hit.ImpactPoint;
ImpactRotation = Hit.Normal.Rotation();
// Damage
float ActualDamage = BaseDamage;
HitSurfaceType = UPhysicalMaterial::DetermineSurfaceType(Hit.PhysMaterial.Get());
if(HitSurfaceType == SurfaceType_Vulnerable)
ActualDamage *= VulnerableDamageMultiplier;
// Check tags on hit actor to determine if it was a valid target so that we can apply damage and provide feedback if the owner of this weapon is player
IGameplayTagAssetInterface* GameplayTagAsset = Cast<IGameplayTagAssetInterface>(HitActor);
if(GameplayTagAsset && GameplayTagAsset->HasAnyMatchingGameplayTags(ValidTargetTags))
{
//Damage
UGameplayStatics::ApplyPointDamage(HitActor, ActualDamage, ShotDirection, Hit, WeaponOwner->GetInstigatorController(), WeaponOwner, DamageType);
// Notify player controller so that we can show feedback for the hit on the hud
AMPlayerController* PC = WeaponOwner->GetInstigatorController<AMPlayerController>();
if(PC)
{
OnTargetHit.Broadcast(this, HitActor, WeaponOwner);
PC->HandleTargetHit(this, HitActor, WeaponOwner);
}
//Determine the impact particle to spawn. If the hit target is an enemy and has the right particle effects,
//use those. Otherwise, we'll use the defaults
AMAICharacterBase* EnemyBaseChar = Cast<AMAICharacterBase>(HitActor);
if(EnemyBaseChar)
{
if(HitSurfaceType == SurfaceType_Vulnerable && EnemyBaseChar->GetVulnerableHitParticle())
ImpactEffect = EnemyBaseChar->GetVulnerableHitParticle();
else if(EnemyBaseChar->GetDefaultHitParticle())
ImpactEffect = EnemyBaseChar->GetDefaultHitParticle();
}
}
}
if(CVAR_DebugWeaponDrawing.GetValueOnGameThread() > 0)
DrawDebugLine(GetWorld(), EyeLocation, TraceEffectEndLocation, FColor::Red, false, 1.f, 0, 1.f);
// Fire Particle Effects
PlayFireEffects(ShotDirection, ImpactEffect, TraceEffectEndLocation, ImpactRotation);
// Camera Shake
APawn* OwnerPawn = Cast<APawn>(WeaponOwner);
if(OwnerPawn && CameraShakeClass)
{
APlayerController* PC = Cast<APlayerController>(OwnerPawn->GetController());
if(PC)
{
PC->ClientStartCameraShake(CameraShakeClass);
}
}
}
void AMWeapon::StopFire_Implementation()
{
if(bFiring)
{
bFiring = false;
}
GetWorldTimerManager().ClearTimer(TimerHandle_TimeBetweenShots);
if(ClipRemainingBullets == 0 && ClipSize != 0)
Reload();
}
void AMWeapon::StartSecondaryFire_Implementation()
{
if(bCanZoom)
{
AMCharacter* PlayerChar = Cast<AMCharacter>(GetOwner());
if(PlayerChar)
{
PlayerChar->SetTargetFOV(ZoomInFOV);
}
}
}
void AMWeapon::StopSecondaryFire_Implementation()
{
if(bCanZoom)
{
AMCharacter* PlayerChar = Cast<AMCharacter>(GetOwner());
if(PlayerChar)
{
PlayerChar->ResetToDefaultFOV();
}
}
}
void AMWeapon::Reload()
{
if(bReloading)
return;
bReloading = true;
bFiring = false;
GetWorldTimerManager().SetTimer(TimerHandle_Reload, this, &AMWeapon::HandleReloadComplete, ReloadTime);
OnWeaponReloadStarted.Broadcast(this);
if(ReloadSound)
TempReloadSoundHandle = UGameplayStatics::SpawnSoundAttached(ReloadSound, RootComponent);
}
void AMWeapon::HandleWeaponEquipped(AMCharacter* Character, AMWeapon* NewWeapon, AMWeapon* PreviousWeapon)
{
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
if(NewWeapon && NewWeapon == this)
{
if(PC)
{
EnableInput(PC);
if(!bSecondaryFireBound)
{
UInputComponent* IC = this->InputComponent;
if(IC)
{
FInputActionBinding& PressedBinding = IC->BindAction("SecondaryFire", IE_Pressed, this, &AMWeapon::StartSecondaryFire);
FInputActionBinding& ReleasedBinding = IC->BindAction("SecondaryFire", IE_Released, this, &AMWeapon::StopSecondaryFire);
PressedBinding.bConsumeInput = false;
ReleasedBinding.bConsumeInput = false;
bSecondaryFireBound = true;
}
}
}
}
else if(PreviousWeapon && PreviousWeapon == this)
{
if(PC)
{
DisableInput(PC);
}
}
}
void AMWeapon::HandleReloadComplete()
{
bReloading = false;
ClipRemainingBullets = ClipSize;
GetWorldTimerManager().ClearTimer(TimerHandle_Reload);
OnWeaponReloadCompleted.Broadcast(this);
TempReloadSoundHandle = nullptr;
}
void AMWeapon::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
bool AMWeapon::StartFire_Implementation()
{
if(bReloading)
return false;
if(ClipRemainingBullets == 0 && ClipSize != 0)
{
Reload();
return false;
}
float FirstFireDelay = FMath::Max(TimeLastFired + TimeBetweenShots - GetWorld()->GetTimeSeconds(), 0.f);
if(RateOfFire == 0.f)
{
Fire();
GetWorldTimerManager().SetTimer(TimerHandle_TimeBetweenShots, this, &AMWeapon::StopFire, StopFireDelay, false, 0.f);
}
else
{
GetWorldTimerManager().SetTimer(TimerHandle_TimeBetweenShots, this, &AMWeapon::Fire, TimeBetweenShots, true, FirstFireDelay);
}
return true;
}
float AMWeapon::GetReloadProgress()
{
float Progress = 1.f;
if(bReloading)
{
float TimeElapsed = GetWorldTimerManager().GetTimerElapsed(TimerHandle_Reload);
Progress = FMath::Clamp(TimeElapsed / ReloadTime, 0.f, 1.f);
}
return Progress;
}
void AMWeapon::CancelReload()
{
if(!bReloading)
return;
GetWorldTimerManager().ClearTimer(TimerHandle_Reload);
OnWeaponReloadCancelled.Broadcast(this);
bReloading = false;
if(TempReloadSoundHandle)
TempReloadSoundHandle->Stop();
TempReloadSoundHandle = nullptr;
}
FVector AMWeapon::CalculateTargetedWorldLocation(TSubclassOf<AActor> ValidTargetClass, ECollisionChannel TraceChannel) const
{
FVector Location;
AActor* WeaponOwner = GetOwner();
if(WeaponOwner)
{
FVector EyeLocation;
FRotator EyeRotation;
WeaponOwner->GetActorEyesViewPoint(EyeLocation, EyeRotation);
FVector ShotDirection = EyeRotation.Vector();
FVector TraceEndLocation = EyeLocation + (ShotDirection * MaxWeaponRange);
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(WeaponOwner);
QueryParams.AddIgnoredActor(this);
QueryParams.bTraceComplex = true;
QueryParams.bReturnPhysicalMaterial = true;
FHitResult Hit;
if(GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEndLocation, TraceChannel, QueryParams))
Location = Hit.Location;
}
return Location;
}
AActor* AMWeapon::GetTargetedActor(ECollisionChannel TraceChannel) const
{
AActor* TargetedActor = nullptr;
AActor* WeaponOwner = GetOwner();
if(WeaponOwner)
{
FVector EyeLocation;
FRotator EyeRotation;
WeaponOwner->GetActorEyesViewPoint(EyeLocation, EyeRotation);
FVector ShotDirection = EyeRotation.Vector();
FVector TraceEndLocation = EyeLocation + (ShotDirection * MaxWeaponRange);
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(WeaponOwner);
QueryParams.AddIgnoredActor(this);
QueryParams.bTraceComplex = true;
FHitResult Hit;
if(GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEndLocation, TraceChannel, QueryParams))
TargetedActor = Hit.GetActor();
}
return TargetedActor;
}

@ -0,0 +1,97 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "GameplayTagAssetInterfacE.h"
#include "MAICharacterBase.generated.h"
class UMHealthComponent;
class UWidgetComponent;
class AMGameplayActor;
class FOnCharacterEnterSafeZone;
class AMAICharacterBase;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAIEnterSafeZone, AMAICharacterBase*, AICharacter, AActor*, SafeZoneEnteredActor);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAILeaveSafeZone, AMAICharacterBase*, AICharacter);
UCLASS()
class MECHDEFENCE_API AMAICharacterBase : public ACharacter, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
AMAICharacterBase();
protected:
bool bIsInSafeZone;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "AICharacter SafeZone")
FGameplayTagContainer SafeZoneTags;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "AICharacter SafeZone")
TSubclassOf<AActor> SafeZoneClass;
UPROPERTY(EditDefaultsOnly, Category = "Tags")
FGameplayTagContainer ActorTags;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "AICharacter Base")
UWidgetComponent* StatusIndicatorWidget;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "AICharacter Base")
UMHealthComponent* HealthComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "AICharacter Base")
UParticleSystem* DefaultHitParticle;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "AICharacter Base")
UParticleSystem* VulnerableHitParticle;
UFUNCTION(BlueprintCallable)
virtual UShapeComponent* GetSafeZoneOverlappingShape();
virtual void BeginPlay() override;
void SetWidgetHealthComponentProperty();
public:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
UPROPERTY(BlueprintAssignable, Category = "AICharacter Events")
FOnAIEnterSafeZone OnAIEnterSafeZone;
UPROPERTY(BlueprintAssignable, Category = "AICharacter Events")
FOnAILeaveSafeZone OnAILeaveSafeZone;
UFUNCTION(BlueprintCallable, Category = "AICharacter")
void AddGameplayTags(const FGameplayTagContainer& TagContainer);
UFUNCTION(BlueprintCallable, Category = "AICharacter")
void AddGameplayTag(const FGameplayTag& Tag);
void LeaveSafeZone();
void EnterSafeZone(AActor* SafeZoneActor);
void CheckSafeZone();
bool IsInSafeZone() const { return bIsInSafeZone; }
UParticleSystem* GetVulnerableHitParticle() const { return VulnerableHitParticle; }
UParticleSystem* GetDefaultHitParticle() const { return DefaultHitParticle; }
private:
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override;
UFUNCTION()
void HandleSafeZoneBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void HandleSafeZoneEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
UFUNCTION()
void HandleCharacterDeath(UMHealthComponent* ActorHealthComponent, AActor* DeadActor, AActor* KillerActor);
};

@ -0,0 +1,44 @@
#pragma once
#include "CoreMinimal.h"
#include "MAICharacterBase.h"
#include "MAIEnemyShooter.generated.h"
class AMWeapon;
UCLASS()
class MECHDEFENCE_API AMAIEnemyShooter : public AMAICharacterBase
{
GENERATED_BODY()
public:
AMAIEnemyShooter();
protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadonly, Category = "Shooter")
TSubclassOf<AMWeapon> StarterWeaponClass;
UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category = "Shooter")
bool bCanIdle;
UPROPERTY(EditDefaultsOnly, BlueprintReadonly, Category = "Shooter")
float PatrolRadius;
UPROPERTY(VisibleAnywhere, BlueprintReadonly, Category = "Shooter", meta = (Tooltip = "The place where this actor was when the level began. Used to generate a random point within a radius when in patrol mode"))
FVector StartingLocation;
UPROPERTY(BlueprintReadonly, Category = "Shooter")
FName WeaponSocketName;
UPROPERTY(VisibleAnywhere, BlueprintReadonly, Category = "Shooter")
AMWeapon* CurrentWeapon;
UFUNCTION(BlueprintCallable, Category = "Shooter")
FVector PickNextPartrolDestination();
virtual void BeginPlay() override;
virtual UShapeComponent* GetSafeZoneOverlappingShape() override;
};

@ -0,0 +1,283 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagAssetInterface.h"
#include "GameFramework/Character.h"
#include "MCharacter.generated.h"
class AMGameplayActor;
class UCameraComponent;
class AMWeapon;
class UMHealthComponent;
class AMCharacter;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnWeaponEquipped, AMCharacter*, Character, AMWeapon*, NewWeapon, AMWeapon*, PreviousWeapon);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnCharacterEnterSafeZone, AMCharacter*, Character, AActor*, SafeZoneEnteredActor);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCharacterLeaveSafeZone, AMCharacter*, Character);
UCLASS()
class MECHDEFENCE_API AMCharacter : public ACharacter, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
AMCharacter();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UCameraComponent* CameraComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USkeletalMeshComponent* PlayerArms;
UPROPERTY(EditDefaultsOnly, Category = "Player", meta=(Tooltip = "Maximum Distance to Trace for Interactable Objects", ClampMin = 50.f))
float MaxInteractableDistance;
UPROPERTY(EditDefaultsOnly, Category = "Tags")
FGameplayTagContainer ActorTags;
UPROPERTY(EditDefaultsOnly, Category = "Player")
TSubclassOf<AMWeapon> PrimaryWeaponClass;
UPROPERTY(EditDefaultsOnly, Category = "Player")
TSubclassOf<AMWeapon> ScannerClass;
UPROPERTY(EditDefaultsOnly, Category = "Player")
TSubclassOf<AMWeapon> BlowTorchClass;
UPROPERTY(EditDefaultsOnly, Category = "Player")
TSubclassOf<AMWeapon> TabletClass;
UPROPERTY(EditDefaultsOnly, Category = "Player")
UAnimSequence* FireAnimation;
UPROPERTY(EditDefaultsOnly, Category = "Player")
UAnimSequenceBase* ReloadAnimation;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Player")
UMHealthComponent* HealthComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
UAudioComponent* AudioComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
USoundBase* HealingStartSound;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
USoundBase* HealingInProgressSound;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
USoundBase* HealingCompletedSound;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
USoundBase* SafeZoneEnterSound;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
USoundBase* SafeZoneLeaveSound;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
USoundBase* HitByBulletSound;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
USoundBase* WalkSound;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
USoundBase* CrouchWalkSound;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
float DefaultFOV;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
float ZoomInterpolateSpeed;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player", meta = (Tooltip = "The interval in seconds after which we check if player is in safe zone or not to determine whether to apply damage"))
float OutOfSafeZoneDamageInterval;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
float OutOfSafeZoneDamage;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player", meta = (Tooltip = "The interval in seconds after which healing is applied when the player is inside the safe zone is their health is low"))
float SafeZoneHealInterval;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player", meta = (Tooltip = "The amount of healing applied when the player is inside safe zone"))
float SafeZoneHealAmount;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player", meta = (Tooltip = "Time interval in seconds after which healing within safezone will begin"))
float SafeZoneHealDelay;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player", meta = (Tooltip = "Minimum velocity threshold after which player movement sounds will be played"))
float MinimumMovementVelocityThreshold;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
float MaxStamina;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Player")
float CurrentStamina;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
float StaminaConsumeAmount;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player", meta = (Min = 0.f))
float StaminaConsumeInterval;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player", meta = (Min = 0.f, Tooltip = "The amount of time in seconds to wait before we start recovering stamina"))
float StaminaRestoreDelay;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
float StaminaRestoreAmount;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player", meta = (Min = 0.f))
float StaminaRestoreInterval;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player", meta = (Min = 1.f))
float SprintMultiplier;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
float ArmRotateSpeed;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
float HiddenArmPitch;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
FVector HiddenArmLocation;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
TSubclassOf<AMGameplayActor> SafeZoneClass;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
TSubclassOf<UDamageType> OutOfSafeZoneDamageClass;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Player")
FGameplayTagContainer SafeZoneTags;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Player")
TArray<AMWeapon*> Weapons;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Player")
AMWeapon* CurrentWeapon;
FTimerHandle TimerHandle_ApplyOutOfSafeZoneDamage;
FTimerHandle TimerHandle_ApplySafeZoneHeal;
FTimerHandle TimerHandle_RestoreStamina;
FTimerHandle TimerHandle_ConsumeStamina;
bool bWantsToZoom;
bool bIsInSafeZone;
bool bIsFirstTimeHeal;
bool bIsMounted;
bool bCanSprint;
bool bIsSprinting;
bool bWantsToHideEquippedItem;
void MoveForward(float ScaleValue);
void MoveRight(float ScaleValue);
void StartCrouch();
void StopCrouch();
void StartFire();
void StopFire();
void Interact();
void Reload();
void StartZoom();
void StopZoom();
void StartSprint();
void StopSprint();
void ConsumeStamina();
void RestoreStamina();
void EnterSafeZone(AActor* SafeZoneActor);
void LeaveSafeZone();
UFUNCTION()
void ApplyOutOfSafeZoneDamage();
UFUNCTION()
void ApplySafeZoneHealing();
UFUNCTION()
void HandleCapsuleBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void HandleCapsuleEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
virtual void BeginPlay() override;
void HandleCVARChanged();
// The actor over which our cursor is currently hovering
AActor* HoveredInteractableActor;
// Used when sprinting to check whether the player has moved or not in order to determine if stamina should be consumed or not
// We only check for the x and y positions to not count jumping as change in location
FVector2D PreviousLocation;
float DefaultArmPitch;
FVector DefaultArmLocation;
float TargetFOV;
public:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override;
UFUNCTION(BlueprintCallable)
void EquipWeapon(uint8 Weapon);
UFUNCTION(BlueprintCallable)
void CheckIfInSafeZone();
UFUNCTION()
void HandleHealthChanged(UMHealthComponent* HealthComp, float Health, float HealthDelta, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
UFUNCTION(BlueprintCallable)
void SetMounted(bool Mounted) { bIsMounted = Mounted; }
UPROPERTY(BlueprintAssignable, Category = "Player Events")
FOnWeaponEquipped OnWeaponEquipped;
UPROPERTY(BlueprintAssignable, Category = "Character Events")
FOnCharacterEnterSafeZone OnCharacterEnterSafeZone;
UPROPERTY(BlueprintAssignable, Category = "Character Events")
FOnCharacterLeaveSafeZone OnCharacterLeaveSafeZone;
UFUNCTION(BlueprintCallable)
float GetStaminaRemappedInRange(float Min, float Max);
UFUNCTION(BlueprintCallable)
bool IsZoomedIn() const { return bWantsToZoom; }
UFUNCTION(BlueprintCallable)
bool IsInSafeZone() const { return bIsInSafeZone; }
UFUNCTION(BlueprintCallable)
bool IsMounted() const { return bIsMounted; }
UFUNCTION(BlueprintCallable)
void StartHidingEquippedItem();
UFUNCTION(BlueprintCallable)
void StopHidingEquippedItem();
UFUNCTION(BlueprintCallable)
void SetTargetFOV(float NewTargetFOV);
UFUNCTION(BlueprintCallable)
void ResetToDefaultFOV();
private:
bool bDied;
};

@ -0,0 +1,117 @@
#pragma once
#include "CoreMinimal.h"
#include "MGameplayActor.h"
#include "MConstructableBuilding.generated.h"
class UArrowComponent;
class UMHealthComponent;
class UWidgetComponent;
class AMConstructableBuilding;
UENUM(BlueprintType)
enum class EBuildingState : uint8
{
Constructable UMETA(DisplayName = "Constructable"),
UnConstructable UMETA(DisplayName = "UnConstructable"),
UnderConstruction UMETA(DisplayName = "UnderConstruction"),
Constructed UMETA(DisplayName = "Constructed")
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnBuildingStateChanged, AMConstructableBuilding*, Building, EBuildingState, NewState, EBuildingState, PreviousState);
UCLASS()
class MECHDEFENCE_API AMConstructableBuilding : public AMGameplayActor
{
GENERATED_BODY()
public:
AMConstructableBuilding();
protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
USkeletalMeshComponent* BaseBuildingMesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Building", meta = (Tooltip = "The colour that will be used to show the building when it is at a valid location and can be constructed"))
FLinearColor ConstructableColor;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Building", meta = (Tooltip = "The colour that will be used to show the building when it is at a location where it cannot be constructed"))
FLinearColor UnConstructableColor;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Building", meta = (Tooltip = "The colour that will be used to show the building when it has been placed in the world and is under construction"))
FLinearColor UnderConstructionColor;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Building", meta = (Tooltip = "The material that will be used on all the meshes of this building while it is in an unconstructed state."))
UMaterialInterface* BuildingDynamicStateMaterial;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Building")
EBuildingState BuildingState;
UPROPERTY(BlueprintReadWrite)
TArray<UMaterialInterface*> DefaultMaterials;
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Building", meta = (Tooltip = "Dynamically created material that represents the current state of the building"))
UMaterialInstanceDynamic* CurrentBuildingStateMaterial;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Building", meta = (Tooltip = "List of actor types that will be checked against to determine if the building can be placed at a particular location"))
TArray<TSubclassOf<AActor>> InvalidActorsFilter;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Building", meta = (Tooltip = "List of actor types that required to be overlapping with this building at a particular location if it is to be placed there"))
TArray<TSubclassOf<AActor>> RequiredOverlappingActorsFilter;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Building", meta = (Tooltip = "This shape is used alongside InvalidActors filter to determine whether the building can be placed at its current location or not"))
UShapeComponent* PlacementOverlapChecker;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Building")
UWidgetComponent* StatusIndicatorWidget;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Building")
UStaticMeshComponent* ArrowMesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Building")
UMHealthComponent* HealthComponent;
UFUNCTION()
void HandleMeshComponentEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
UFUNCTION()
void HandleMeshComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void SetupHealthIndicator();
virtual void BeginPlay() override;
UFUNCTION()
void ConstructionCompleted();
FTimerHandle TimerHandle_UnderConstructionTimer;
public:
virtual void Tick(float DeltaTime) override;
UFUNCTION(BlueprintCallable)
void SetBuildingState(TEnumAsByte<EBuildingState> NewState);
UFUNCTION(BlueprintImplementableEvent, meta = (Tooltip = "When building state is set, the base class only changes the state of the default mesh. Implement this function in blueprint to add other behaviour"))
void BuildingStateSet(const EBuildingState& NewState, const EBuildingState& PreviousState);
UFUNCTION(BlueprintCallable)
void StartBuildingConstruction(float TimeToConstruct);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
bool IsAtValidLocation();
UFUNCTION(BlueprintCallable)
EBuildingState GetBuildingState() const { return BuildingState; };
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void SetHighlighted(bool Highlighted);
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Events")
FOnBuildingStateChanged OnBuildingStateChanged;
};

@ -0,0 +1,31 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GameplayTagAssetInterfacE.h"
#include "MEnemySpawnPoint.generated.h"
UCLASS()
class MECHDEFENCE_API AMEnemySpawnPoint : public AActor, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
AMEnemySpawnPoint();
protected:
virtual void BeginPlay() override;
UPROPERTY(EditDefaultsOnly, Category = "Tags")
FGameplayTagContainer ActorTags;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
private:
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override;
};

@ -0,0 +1,96 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "GameplayTagAssetInterface.h"
#include "MGameMode.generated.h"
UENUM(BlueprintType)
enum class EWaveState : uint8
{
WaitingToStart,
WaveInProgress,
WaitingToComplete, // All the enemies have been spawned and we're waiting for them to be destroyed or for the player to die to move to next state
WaveComplete,
GameOver,
LevelComplete
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnActorKilledSignature, AActor*, VictimActor, AActor*, KillerActor, AController*, KillerController);
UCLASS()
class MECHDEFENCE_API AMGameMode : public AGameModeBase
{
GENERATED_BODY()
protected:
UPROPERTY(EditDefaultsOnly, Category = "GameMode")
TSubclassOf<AActor> SpectatingViewPointClass;
UPROPERTY(EditDefaultsOnly, Category = "GameMode")
float LevelEndViewTargetBlendTime;
UPROPERTY(EditDefaultsOnly, Category = "GameMode")
float TimeBetweenWaves;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "GameMode")
int WaveCount;
UPROPERTY(EditDefaultsOnly, Category = "GameMode")
int MaxEnemiesInWave;
UPROPERTY(EditDefaultsOnly, Category = "GameMode")
int MinEnemiesInWave;
UPROPERTY(EditDefaultsOnly, meta = (Tooltip = "These are the tags that are used to check how many enemies spawned in the current wave remain in the level"), Category = "GameMode")
FGameplayTagContainer SpawnedEnemyTags;
virtual void StartPlay() override;
UFUNCTION(BlueprintCallable)
void HandlePlayerKilled();
UFUNCTION(BlueprintCallable)
void HandlePlayerVehicleDestroyed(AActor* Vehicle);
UFUNCTION(BlueprintCallable)
void GameOver();
UFUNCTION(BlueprintCallable)
void LevelCompleted();
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "GameMode")
void SpawnNewEnemy();
UFUNCTION(BlueprintImplementableEvent, Category = "GameMode")
void WaveStateChanged(EWaveState NewState, EWaveState OldState);
UFUNCTION(BlueprintCallable, Category = "GameMode")
void StartWave();
void PrepareForNextWave();
void EndWave();
void CheckWaveState();
void SetWaveState(EWaveState NewWaveState);
void SpawnEnemyTimerElapsed();
int NumEnemiesToSpawn;
FTimerHandle TimerHandle_NextWaveStart;
FTimerHandle TimerHandle_EnemySpawn;
EWaveState WaveState;
public:
UPROPERTY(BlueprintAssignable, Category = "GameMode")
FOnActorKilledSignature OnActorKilled;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "GameMode")
int NumEnemiesKilled;
void Tick(float DeltaSeconds) override;
AMGameMode();
};

@ -0,0 +1,33 @@
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagAssetInterface.h"
#include "GameFramework/Actor.h"
#include "MGameplayActor.generated.h"
UCLASS()
class MECHDEFENCE_API AMGameplayActor : public APawn, public IGameplayTagAssetInterface
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMGameplayActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Gameplay")
FGameplayTagContainer ActorTags;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override;
};

@ -0,0 +1,63 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "MHealthComponent.generated.h"
class UMHealthComponent;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, UMHealthComponent*, HealthComp, float, Health, float, HealthDelta, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnActorDeath, UMHealthComponent*, HealthComponent, AActor*, DeadActor, AActor*, KillerActor);
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class MECHDEFENCE_API UMHealthComponent : public UActorComponent
{
GENERATED_BODY()
public:
UMHealthComponent();
protected:
virtual void BeginPlay() override;
bool bIsDead;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HealthComponent")
float Health;
UPROPERTY(EditDefaultsOnly, Category = "HealthComponent", meta = (ClampMin = 0.f))
float DefaultHealth;
UFUNCTION()
void HandleTakeAnyDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
public:
UFUNCTION(BlueprintCallable)
float GetHealth() const { return Health; }
UFUNCTION(BlueprintCallable)
float GetHealthRemappedInRange(float Min = 0.f, float Max = 1.f);
UFUNCTION(BlueprintCallable)
float GetDefaultHealth() const { return DefaultHealth; }
UFUNCTION(BlueprintCallable)
bool IsAtMaxHealth() const { return Health == DefaultHealth; }
UFUNCTION(BlueprintCallable)
bool IsDead() const { return bIsDead; };
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnHealthChangedSignature OnHealthChanged;
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnActorDeath OnActorDeath;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HealthComponent")
bool bIsInvincible;
UFUNCTION(BlueprintCallable, Category = "HealthComponent")
void Heal(float HealAmount);
};

@ -0,0 +1,26 @@
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "MInteractableActor.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UMInteractableActor : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class MECHDEFENCE_API IMInteractableActor
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
virtual UMeshComponent* GetHoveredMeshComponent() = 0;
};

@ -0,0 +1,52 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "MPlayerController.generated.h"
/**
*
*/
class UUserWidget;
class AMWeapon;
UCLASS()
class MECHDEFENCE_API AMPlayerController : public APlayerController
{
GENERATED_BODY()
public:
AMPlayerController();
UFUNCTION(BlueprintImplementableEvent, Category = "PlayerController")
void OnGameOver();
UFUNCTION(BlueprintImplementableEvent, Category = "PlayerController")
void OnLevelComplete();
UFUNCTION(BlueprintImplementableEvent)
void HandleTargetHit(AMWeapon* Weapon, AActor* Target, AActor* Shooter);
protected:
UPROPERTY(EditDefaultsOnly, Category = "UI")
TSubclassOf<UUserWidget> PauseWidgetClass;
UPROPERTY(EditDefaultsOnly, Category = "UI")
TSubclassOf<UUserWidget> HudWidgetClass;
void SetupInputComponent() override;
UFUNCTION(BlueprintCallable)
void TogglePause();
void BeginPlay() override;
UPROPERTY(BlueprintReadOnly, Category = "UI")
UUserWidget* PauseWidget;
UPROPERTY(BlueprintReadOnly, Category = "UI")
UUserWidget* HudWidget;
};

@ -0,0 +1,90 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagAssetInterfacE.h"
#include "MInteractableActor.h"
#include "GameFramework/Pawn.h"
#include "MPlayerMech.generated.h"
class UWidgetComponent;
class AMCharacter;
class UCameraComponent;
class UMHealthComponent;
class UBoxComponent;
class UEnvQuery;
class USpringArmComponent;
struct FEnvQueryResult;
UCLASS()
class MECHDEFENCE_API AMPlayerMech : public APawn, public IGameplayTagAssetInterface, public IMInteractableActor
{
GENERATED_BODY()
public:
AMPlayerMech();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UCameraComponent* CameraComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USpringArmComponent* SpringArmComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USkeletalMeshComponent* MeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Components")
UWidgetComponent* StatusIndicatorWidget;
UPROPERTY(EditDefaultsOnly, Category = "Tags")
FGameplayTagContainer ActorTags;
UPROPERTY(EditDefaultsOnly, Category = "Mech")
float PlayerDismountRadius;
UPROPERTY(EditDefaultsOnly, Category = "Mech")
FName PlayerAttachmentSocket;
UPROPERTY(EditDefaultsOnly, Category = "Mech", meta = (Tooltip = "Offset from the ground to add to the dismount location calculated for the player"))
float PlayerDismountGroundOffset;
UPROPERTY(EditDefaultsOnly, Category = "Mech", meta = (Tooltip = "Offset from the ground to add to the vehicle when it has been reset"));
float ResetZOffset;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Mech")
UMHealthComponent* HealthComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mech")
UEnvQuery* DismountEQS;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Mech")
USoundBase* MountSound;
// Reference to the player that has mounted this mech
AMCharacter* MountedPlayerCharacter;
virtual void BeginPlay() override;
void SetupHealthIndicator();
void MoveForward(float ScaleValue);
void MoveRight(float ScaleValue);
void RequestDismount();
void RevertOrientation();
void HandleDismountResponse(TSharedPtr<FEnvQueryResult> QueryResult);
public:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
void MountPlayerCharacter(AMCharacter* PlayerCharacter);
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override;
virtual UMeshComponent* GetHoveredMeshComponent() override;
static int32 DebugMechDrawing;
};

@ -0,0 +1,45 @@
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MProjectile.generated.h"
class UCapsuleComponent;
class UNiagaraComponent;
class UNiagaraSystem;
class UProjectileMovementComponent;
UCLASS()
class MECHDEFENCE_API AMProjectile : public AActor
{
GENERATED_BODY()
protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Components")
UProjectileMovementComponent* ProjectileComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Components")
UStaticMeshComponent* MeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Components")
UNiagaraComponent* ParticleSystem;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Components");
UCapsuleComponent* CapsuleComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Projectile");
UNiagaraSystem* ImpactEffect;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Projectile")
TArray<TSubclassOf<AActor>> ActorClassesToIgnore;
public:
AMProjectile();
protected:
virtual void BeginPlay() override;
UFUNCTION()
void HandleBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
};

@ -0,0 +1,27 @@
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "MTargetableActor.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UMTargetableActor : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class MECHDEFENCE_API IMTargetableActor
{
GENERATED_BODY()
public:
// Get the target points that will be used to aim at this actor by AI
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void GetTargetPoints(TArray<FVector>& TargetPoints);
};

@ -0,0 +1,204 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GameplayTagAssetInterface.h"
#include "MWeapon.generated.h"
class AMCharacter;
class AMWeapon;
UENUM(BlueprintType)
enum class EWeaponType : uint8
{
PrimaryWeapon = 0,
Scanner,
BlowTorch,
Tablet,
MaxWeaponType
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnTargetHit, AMWeapon*, Weapon, AActor*, TargetActor, AActor*, Shooter);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWeaponReloadStarted, AMWeapon*, Weapon);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWeaponReloadCompleted, AMWeapon*, Weapon);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWeaponReloadCancelled, AMWeapon*, Weapon);
class USoundBase;
class UMatineeCameraShake;
class UNiagaraSystem;
class AMProjectile;
UCLASS()
class MECHDEFENCE_API AMWeapon : public AActor
{
GENERATED_BODY()
public:
AMWeapon();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USkeletalMeshComponent* WeaponMesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
TSubclassOf<UDamageType> DamageType;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
FName MuzzleSocketName;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon", meta=(Tooltip="Name of socket on the player's skeleton where the weapon should be attached"))
FName PlayerAttachmentSocketName;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon", meta=(Tooltip="Name of the exposed variable in the beam particle system that will be updated to the location of the target when the weapon fires"))
FName TracerParticleStartLocationName;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
UParticleSystem* MuzzleEffect;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
UNiagaraSystem* TracerEffect;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
UParticleSystem* DefaultImpactEffect;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
TSubclassOf<UMatineeCameraShake> CameraShakeClass;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
TSubclassOf<AMProjectile> ProjectileClass;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Weapon", meta = (Tooltip = "When the bullet from this weapon hits a target, these tags will determine if the target is valid and if damage and other UI effects should be applied"))
FGameplayTagContainer ValidTargetTags;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
float BaseDamage;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
float VulnerableDamageMultiplier;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon", meta = (Tooltip = "Bullets fired per minute. Set it to 0 for weapons that should only fire once when fire button is pressed"))
float RateOfFire;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon", meta = (Tooltip = "Bullet spread angle in degrees", ClampMin = 0.f))
float BulletSpread;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon", meta = (Tooltip = "Number of seconds after which StopFire is automatically called. Only used when RateOfFire is also set to 0", ClampMin = 0.f))
float StopFireDelay;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon", meta = (Tooltip = "Number of bullets in one clip after which the weapon is going to be reloaded. Set zero to disable reloading altogether"))
uint8 ClipSize;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon", meta = (Tooltip = "Number of seconds it takes to reload the weapon"))
float ReloadTime;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon", meta = (Tooltip = "Chance for the projectile particle to spawn. 1 would mean always and 0 would mean never", ClampMin = 0.f, ClampMax = 1.f))
float ProjectileSpawnChance;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
UAnimSequence* FireAnimation;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
USoundBase* FireSound;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
USoundBase* ReloadSound;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
float MaxWeaponRange;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon|Zoom")
float ZoomInFOV;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon|Zoom")
bool bCanZoom;
float TimeBetweenShots;
float TimeLastFired;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Weapon")
uint8 ClipRemainingBullets;
bool bFiring;
bool bReloading;
FTimerHandle TimerHandle_TimeBetweenShots;
FTimerHandle TimerHandle_Reload;
bool bSecondaryFireBound;
UFUNCTION()
void HandleReloadComplete();
virtual void BeginPlay() override;
UFUNCTION(BlueprintCallable)
void Fire();
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void OnFire();
UFUNCTION(BlueprintCallable)
void PlayFireEffects(FVector ShotDirection, UParticleSystem* ImpactEffect, FVector TraceEffectEndLocation, const FRotator ImpactParticleRotation);
// This will hold the reload sound handle when it is played so that if the reload is cancelled
// we can stop the sound from playing
UAudioComponent* TempReloadSoundHandle;
public:
virtual void Tick(float DeltaTime) override;
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
bool StartFire();
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void StopFire();
UFUNCTION(BLueprintCallable, BlueprintNativeEvent)
void StartSecondaryFire();
UFUNCTION(BLueprintCallable, BlueprintNativeEvent)
void StopSecondaryFire();
UFUNCTION(BlueprintCallable)
virtual void Reload();
UFUNCTION(BlueprintCallable)
float GetReloadProgress();
UFUNCTION(BlueprintCallable)
bool IsFiring() { return bFiring; }
UFUNCTION(BlueprintCallable)
bool IsReloading() { return bReloading; }
UFUNCTION(BlueprintCallable)
void CancelReload();
UFUNCTION()
void HandleWeaponEquipped(AMCharacter* Character, AMWeapon* NewWeapon, AMWeapon* PreviousWeapon);
UFUNCTION(BlueprintCallable, meta = (Tooltip = "Return the world coordinates of the position of the this weapon is aiming at"))
FVector CalculateTargetedWorldLocation(TSubclassOf<AActor> ValidTargetClass, ECollisionChannel TraceChannel) const;
UFUNCTION(BlueprintCallable)
AActor* GetTargetedActor(ECollisionChannel TraceChannel) const;
FName GetPlayerAttachmentSocketName() const { return PlayerAttachmentSocketName; }
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnTargetHit OnTargetHit;
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnWeaponReloadStarted OnWeaponReloadStarted;
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnWeaponReloadCompleted OnWeaponReloadCompleted;
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnWeaponReloadCancelled OnWeaponReloadCancelled;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
EWeaponType WeaponType;
};

@ -1 +0,0 @@
This is a test document to see whether it works or not
Loading…
Cancel
Save