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