parent
63686e9e8b
commit
894844c5e9
@ -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, &layerController::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, &layerMech::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, &layerMech::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, &layerMech::MoveForward); |
||||
PlayerInputComponent->BindAxis("MoveRight", this, &layerMech::MoveRight); |
||||
|
||||
//PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
|
||||
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput); |
||||
|
||||
PlayerInputComponent->BindAction("Dismount", IE_Pressed, this, &layerMech::RequestDismount); |
||||
|
||||
PlayerInputComponent->BindAction("ResetVehicle", IE_Pressed, this, &layerMech::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, &rojectile::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; |
||||
|
||||
|
||||
}; |
Loading…
Reference in new issue