Start of game mode
This commit is contained in:
parent
8ac4271602
commit
1fc53e9e62
@ -1,3 +1,8 @@
|
|||||||
[/Script/EngineSettings.GeneralProjectSettings]
|
[/Script/EngineSettings.GeneralProjectSettings]
|
||||||
ProjectID=EAE41D29471FBD447E5454B1D01C9ECF
|
ProjectID=EAE41D29471FBD447E5454B1D01C9ECF
|
||||||
ProjectName=Third Person Game Template
|
ProjectName=Gravity Stomp
|
||||||
|
Description=Multiplayer competitive platformer where you manipulate gravity
|
||||||
|
CopyrightNotice=Gravity Stomp Copyright Kevin Poretti
|
||||||
|
ProjectVersion=0.0.1
|
||||||
|
ProjectDisplayedTitle=NSLOCTEXT("[/Script/EngineSettings]", "79D56CC04C9E98AB5B2981808A14FB31", "Gravity Stomp")
|
||||||
|
|
||||||
|
BIN
GravityStomp/Content/Core/BP_GSDefaultGameMode.uasset
(Stored with Git LFS)
BIN
GravityStomp/Content/Core/BP_GSDefaultGameMode.uasset
(Stored with Git LFS)
Binary file not shown.
BIN
GravityStomp/Content/Maps/Debug/Test.umap
(Stored with Git LFS)
BIN
GravityStomp/Content/Maps/Debug/Test.umap
(Stored with Git LFS)
Binary file not shown.
@ -1,4 +1,4 @@
|
|||||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
#include "GSCharacter.h"
|
#include "GSCharacter.h"
|
||||||
#include "Camera/CameraComponent.h"
|
#include "Camera/CameraComponent.h"
|
||||||
@ -57,6 +57,11 @@ AGSCharacter::AGSCharacter(const FObjectInitializer& ObjectInitializer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSCharacter::ChangeTeamColor(bool bIsPlayerFriendly)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void AGSCharacter::BeginPlay()
|
void AGSCharacter::BeginPlay()
|
||||||
{
|
{
|
||||||
// Call the base class
|
// Call the base class
|
||||||
@ -98,6 +103,7 @@ void AGSCharacter::Move(const FInputActionValue& Value)
|
|||||||
AddMovementInput(FVector::RightVector, MovementVector.X);
|
AddMovementInput(FVector::RightVector, MovementVector.X);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AGSCharacter::ChangeGravityDirection(const FInputActionValue& Value)
|
void AGSCharacter::ChangeGravityDirection(const FInputActionValue& Value)
|
||||||
{
|
{
|
||||||
FVector2D GravityDirection = Value.Get<FVector2D>();
|
FVector2D GravityDirection = Value.Get<FVector2D>();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -35,6 +35,8 @@ class AGSCharacter : public ACharacter
|
|||||||
public:
|
public:
|
||||||
AGSCharacter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
AGSCharacter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||||
|
|
||||||
|
void ChangeTeamColor(bool bIsPlayerFriendly);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/** Called for movement input */
|
/** Called for movement input */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
#include "Character/GSCharacterMovementComponent.h"
|
#include "Character/GSCharacterMovementComponent.h"
|
||||||
|
|
||||||
@ -856,15 +856,9 @@ void UGSCharacterMovementComponent::PhysicsRotation(float DeltaTime)
|
|||||||
|
|
||||||
if (ShouldRemainVertical())
|
if (ShouldRemainVertical())
|
||||||
{
|
{
|
||||||
FString DebugDesiredRotation = FString::Printf(TEXT("Desired Rotation: %s"), *DesiredRotation.ToString());
|
|
||||||
GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Cyan, DebugDesiredRotation);
|
|
||||||
|
|
||||||
DesiredRotation.Pitch = IsCharacterUpAlignedToWorldUp() ? 0.f : FRotator::NormalizeAxis(DesiredRotation.Pitch);
|
DesiredRotation.Pitch = IsCharacterUpAlignedToWorldUp() ? 0.f : FRotator::NormalizeAxis(DesiredRotation.Pitch);
|
||||||
DesiredRotation.Yaw = IsCharacterUpAlignedToWorldUp() ? FRotator::NormalizeAxis(DesiredRotation.Yaw) : 0.f;
|
DesiredRotation.Yaw = IsCharacterUpAlignedToWorldUp() ? FRotator::NormalizeAxis(DesiredRotation.Yaw) : 0.f;
|
||||||
DesiredRotation.Roll = GetRollFromCharacterUpDir(CharacterUpDirection);
|
DesiredRotation.Roll = GetRollFromCharacterUpDir(CharacterUpDirection);
|
||||||
|
|
||||||
DebugDesiredRotation = FString::Printf(TEXT("Desired Rotation after keeping it vertical: %s"), *DesiredRotation.ToString());
|
|
||||||
GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Cyan, DebugDesiredRotation);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Fill out your copyright notice in the Description page of Project Settings.
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
@ -1,7 +1,309 @@
|
|||||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
#include "GSGameModeBase.h"
|
#include "GSGameModeBase.h"
|
||||||
|
|
||||||
|
// GS includes
|
||||||
|
#include "EngineUtils.h"
|
||||||
|
#include "GSGameState.h"
|
||||||
|
#include "Player/GSPlayerController.h"
|
||||||
|
#include "Player/GSPlayerState.h"
|
||||||
|
|
||||||
AGSGameModeBase::AGSGameModeBase()
|
AGSGameModeBase::AGSGameModeBase()
|
||||||
{
|
{
|
||||||
|
ScoreLimit = 25;
|
||||||
|
MaxNumTeams = 2;
|
||||||
|
TimeBetweenRounds = 20.0f;
|
||||||
|
RoundTime = 600.0f;
|
||||||
|
WarmupTime = 15.0f;
|
||||||
|
bWarmupEnabled = true;
|
||||||
|
bDelayedStart = bWarmupEnabled;
|
||||||
|
|
||||||
|
PlayerControllerClass = AGSPlayerController::StaticClass();
|
||||||
|
PlayerStateClass = AGSPlayerState::StaticClass();
|
||||||
|
GameStateClass = AGSGameState::StaticClass();
|
||||||
|
|
||||||
|
MinRespawnDelay = 5.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::PreInitializeComponents()
|
||||||
|
{
|
||||||
|
Super::PreInitializeComponents();
|
||||||
|
|
||||||
|
// Setup timer for match tick
|
||||||
|
GetWorldTimerManager().SetTimer(TimerHandle_DefaultTimer, this, &AGSGameModeBase::DefaultTimer, GetWorldSettings()->GetEffectiveTimeDilation(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
|
||||||
|
{
|
||||||
|
Super::InitGame(MapName, Options, ErrorMessage);
|
||||||
|
|
||||||
|
bUseSeamlessTravel = !GetWorld()->IsPlayInEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::InitGameState()
|
||||||
|
{
|
||||||
|
Super::InitGameState();
|
||||||
|
|
||||||
|
AGSGameState* GS = GetGameState<AGSGameState>();
|
||||||
|
check(GS)
|
||||||
|
GS->SetNumTeams(MaxNumTeams);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId,
|
||||||
|
FString& ErrorMessage)
|
||||||
|
{
|
||||||
|
// reject player is match has ended
|
||||||
|
AGSGameState* GS = GetGameState<AGSGameState>();
|
||||||
|
bool bMatchEnded = GS && GS->HasMatchEnded();
|
||||||
|
if(bMatchEnded)
|
||||||
|
{
|
||||||
|
ErrorMessage = TEXT("Can't join a match that has ended.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Super::PreLogin(Options, Address, UniqueId, ErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::PostLogin(APlayerController* NewPlayer)
|
||||||
|
{
|
||||||
|
Super::PostLogin(NewPlayer);
|
||||||
|
|
||||||
|
PickTeam(NewPlayer->GetPlayerState<AGSPlayerState>());
|
||||||
|
|
||||||
|
AGSPlayerController* PC = Cast<AGSPlayerController>(NewPlayer);
|
||||||
|
|
||||||
|
if(PC && IsMatchInProgress())
|
||||||
|
{
|
||||||
|
PC->Client_GameStarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::RestartPlayer(AController* NewPlayer)
|
||||||
|
{
|
||||||
|
Super::RestartPlayer(NewPlayer);
|
||||||
|
|
||||||
|
// inform player controller that game has started using client RPC
|
||||||
|
AGSPlayerController* PC = Cast<AGSPlayerController>(NewPlayer);
|
||||||
|
if(PC)
|
||||||
|
{
|
||||||
|
PC->Client_GameStarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AGSGameModeBase::ShouldSpawnAtStartSpot(AController* Player)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AGSGameModeBase::CanDamagePlayer(AGSPlayerController* Damager, AGSPlayerController* Victim)
|
||||||
|
{
|
||||||
|
AGSPlayerState* DamagerPS = Damager ? Damager->GetPlayerState<AGSPlayerState>() : nullptr;
|
||||||
|
AGSPlayerState* VictimPS = Victim ? Victim->GetPlayerState<AGSPlayerState>() : nullptr;
|
||||||
|
|
||||||
|
// can damage if both instigator and victim are valid and are on different teams
|
||||||
|
return DamagerPS && VictimPS && (DamagerPS->GetTeamNum() != VictimPS->GetTeamNum());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::PickTeam(AGSPlayerState* PlayerState)
|
||||||
|
{
|
||||||
|
// place player on the team with the fewest players, or on the first team if all teams have the same number of players
|
||||||
|
TArray<int32> NumTeamPlayers;
|
||||||
|
AGSGameState* GS = GetGameState<AGSGameState>();
|
||||||
|
NumTeamPlayers.AddZeroed(GS->GetNumTeams());
|
||||||
|
|
||||||
|
check(NumTeamPlayers.Num() > 0);
|
||||||
|
|
||||||
|
for(int32 PlayerIdx = 0; PlayerIdx < GS->PlayerArray.Num(); ++PlayerIdx)
|
||||||
|
{
|
||||||
|
AGSPlayerState* CurrPS = Cast<AGSPlayerState>(GS->PlayerArray[PlayerIdx]);
|
||||||
|
if(CurrPS && CurrPS->GetTeamNum() >= 0 && CurrPS->GetTeamNum() < NumTeamPlayers.Num())
|
||||||
|
{
|
||||||
|
NumTeamPlayers[CurrPS->GetTeamNum()]++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Error, TEXT("Player %s is on team number %d but only %d teams exist"), *CurrPS->GetPlayerName(), CurrPS->GetTeamNum(), NumTeamPlayers.Num())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 LowestTeamIdx = 0;
|
||||||
|
for(int32 TeamIdx = 0; TeamIdx < NumTeamPlayers.Num(); TeamIdx++)
|
||||||
|
{
|
||||||
|
if(NumTeamPlayers[TeamIdx] < NumTeamPlayers[LowestTeamIdx])
|
||||||
|
{
|
||||||
|
LowestTeamIdx = TeamIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerState->SetTeamNum(LowestTeamIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::OnPlayerKilled(AGSPlayerController* Killer, AGSPlayerController* Victim)
|
||||||
|
{
|
||||||
|
// Only keep track of kills in an active round
|
||||||
|
if(MatchState != MatchState::InProgress)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AGSPlayerState* KillerPS = Killer ? Killer->GetPlayerState<AGSPlayerState>() : nullptr;
|
||||||
|
AGSPlayerState* VictimPS = Victim ? Victim->GetPlayerState<AGSPlayerState>() : nullptr;
|
||||||
|
|
||||||
|
if(KillerPS && VictimPS)
|
||||||
|
{
|
||||||
|
// keep track of player's K/D
|
||||||
|
KillerPS->IncrementNumKills();
|
||||||
|
Killer->Client_OnKill(KillerPS, VictimPS);
|
||||||
|
|
||||||
|
VictimPS->IncrementNumDeaths();
|
||||||
|
|
||||||
|
// inform all players of the death
|
||||||
|
for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
|
||||||
|
{
|
||||||
|
AGSPlayerController* PC = Cast<AGSPlayerController>(*It);
|
||||||
|
if(PC)
|
||||||
|
{
|
||||||
|
PC->Client_OnDeath(KillerPS, VictimPS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// give killer's team a point and check if score limit is reached
|
||||||
|
AGSGameState* GS = GetGameState<AGSGameState>();
|
||||||
|
check(GS);
|
||||||
|
int32 NewScore = GS->GiveTeamPoint(KillerPS->GetTeamNum());
|
||||||
|
if(NewScore >= ScoreLimit)
|
||||||
|
{
|
||||||
|
FinishMatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::DefaultTimer()
|
||||||
|
{
|
||||||
|
AGSGameState* GS = GetGameState<AGSGameState>();
|
||||||
|
if(GS && GS->GetRemainingTime() > 0)
|
||||||
|
{
|
||||||
|
GS->SetRemainingTime(GS->GetRemainingTime() - 1);
|
||||||
|
if(GS->GetRemainingTime() <= 0)
|
||||||
|
{
|
||||||
|
if(GetMatchState() == MatchState::WaitingPostMatch)
|
||||||
|
{
|
||||||
|
RestartGame();
|
||||||
|
}
|
||||||
|
else if (GetMatchState() == MatchState::InProgress)
|
||||||
|
{
|
||||||
|
FinishMatch();
|
||||||
|
}
|
||||||
|
else if (GetMatchState() == MatchState::WaitingToStart)
|
||||||
|
{
|
||||||
|
StartMatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::FinishMatch()
|
||||||
|
{
|
||||||
|
if(IsMatchInProgress())
|
||||||
|
{
|
||||||
|
EndMatch();
|
||||||
|
|
||||||
|
// determine match winner
|
||||||
|
AGSGameState* GS = GetGameState<AGSGameState>();
|
||||||
|
check(GS);
|
||||||
|
int32 WinningTeamIdx = GS->GetWinningTeam();
|
||||||
|
|
||||||
|
// notify players that game has ended and notify winning players that they won
|
||||||
|
for(FConstPlayerControllerIterator PCIt = GetWorld()->GetPlayerControllerIterator(); PCIt; ++PCIt)
|
||||||
|
{
|
||||||
|
// notify player if they won
|
||||||
|
APlayerController* PC = PCIt->Get();
|
||||||
|
PC->GameHasEnded(nullptr, PC->GetPlayerState<AGSPlayerState>()->GetTeamNum() == WinningTeamIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn off all pawns
|
||||||
|
for (APawn* Pawn : TActorRange<APawn>(GetWorld()))
|
||||||
|
{
|
||||||
|
Pawn->TurnOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set game state remaining time to time between matches
|
||||||
|
GS->SetRemainingTime(TimeBetweenRounds);
|
||||||
|
GS->ResetTeamScores();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::HandleMatchIsWaitingToStart()
|
||||||
|
{
|
||||||
|
Super::HandleMatchIsWaitingToStart();
|
||||||
|
|
||||||
|
// setup warmup
|
||||||
|
AGSGameState* GS = GetGameState<AGSGameState>();
|
||||||
|
if(GS && GS->GetRemainingTime() <= 0)
|
||||||
|
{
|
||||||
|
if(bWarmupEnabled)
|
||||||
|
{
|
||||||
|
GS->SetRemainingTime(WarmupTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify players warmup period has started
|
||||||
|
for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
|
||||||
|
{
|
||||||
|
AGSPlayerController* PC = Cast<AGSPlayerController>(*It);
|
||||||
|
if(PC)
|
||||||
|
{
|
||||||
|
PC->Client_WarmupStarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameModeBase::HandleMatchHasStarted()
|
||||||
|
{
|
||||||
|
Super::HandleMatchHasStarted();
|
||||||
|
|
||||||
|
// setup in progress match
|
||||||
|
AGSGameState* GS = GetGameState<AGSGameState>();
|
||||||
|
if(GS && GS->GetRemainingTime() <= 0)
|
||||||
|
{
|
||||||
|
GS->SetRemainingTime(RoundTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify players match has started
|
||||||
|
for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
|
||||||
|
{
|
||||||
|
AGSPlayerController* PC = Cast<AGSPlayerController>(*It);
|
||||||
|
if(PC)
|
||||||
|
{
|
||||||
|
PC->Client_GameStarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AGSGameModeBase::RestartGame()
|
||||||
|
{
|
||||||
|
// notify players game has restarted
|
||||||
|
for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
|
||||||
|
{
|
||||||
|
AGSPlayerController* PC = Cast<AGSPlayerController>(*It);
|
||||||
|
if(PC)
|
||||||
|
{
|
||||||
|
PC->Client_GameRestarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Super::RestartGame();
|
||||||
}
|
}
|
@ -1,18 +1,129 @@
|
|||||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// GS includes
|
||||||
|
#include "Player/GSPlayerController.h"
|
||||||
|
#include "Player/GSPlayerState.h"
|
||||||
|
|
||||||
|
// UE includes
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "GameFramework/GameModeBase.h"
|
#include "GameFramework/GameMode.h"
|
||||||
|
|
||||||
#include "GSGameModeBase.generated.h"
|
#include "GSGameModeBase.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game mode for a team deathmatch/free for all game mode
|
||||||
|
*/
|
||||||
UCLASS(minimalapi)
|
UCLASS(minimalapi)
|
||||||
class AGSGameModeBase : public AGameModeBase
|
class AGSGameModeBase : public AGameMode
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AGSGameModeBase();
|
AGSGameModeBase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up default timer
|
||||||
|
*/
|
||||||
|
virtual void PreInitializeComponents() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the game. This is called before actors' PreInitializeComponents.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the game state. Sets the number of teams based on this game mode's settings.
|
||||||
|
*/
|
||||||
|
virtual void InitGameState() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept or reject a player attempting to join the server. Fails login if you set the ErrorMessage to a non-empty string.
|
||||||
|
*/
|
||||||
|
virtual void PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picks a player team and informs them to start if the match is in progress
|
||||||
|
*/
|
||||||
|
virtual void PostLogin(APlayerController* NewPlayer) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to spawn the player's pawn
|
||||||
|
*/
|
||||||
|
virtual void RestartPlayer(AController* NewPlayer) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose a random spawn
|
||||||
|
*/
|
||||||
|
virtual bool ShouldSpawnAtStartSpot(AController* Player) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if one player can damage another. For example, players on the same team should not be able to damage one another.
|
||||||
|
*/
|
||||||
|
virtual bool CanDamagePlayer(AGSPlayerController* Damager, AGSPlayerController* Victim);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decides on a team for a player based on current player distribution
|
||||||
|
*/
|
||||||
|
virtual void PickTeam(AGSPlayerState* PlayerState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles updating score and informing appropriate clients when one player kills another
|
||||||
|
*/
|
||||||
|
virtual void OnPlayerKilled(AGSPlayerController* Killer, AGSPlayerController* Victim);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decides which team wins and then informs players the match has ended and if they won or not
|
||||||
|
*/
|
||||||
|
void FinishMatch();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up match warmup timer
|
||||||
|
*/
|
||||||
|
virtual void HandleMatchIsWaitingToStart() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up match round timer
|
||||||
|
*/
|
||||||
|
virtual void HandleMatchHasStarted() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies players
|
||||||
|
*/
|
||||||
|
virtual void RestartGame() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that acts as the game mode "tick" and does match state transition logic
|
||||||
|
*/
|
||||||
|
virtual void DefaultTimer();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Maximum number of teams allowed in this gamemode
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=Settings, meta = (ClampMin = 0))
|
||||||
|
int32 MaxNumTeams;
|
||||||
|
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=Settings, meta = (ClampMin = 0))
|
||||||
|
int32 ScoreLimit;
|
||||||
|
|
||||||
|
// time in between rounds, in seconds
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=Settings, meta = (ClampMin = 0))
|
||||||
|
float TimeBetweenRounds;
|
||||||
|
|
||||||
|
// duration of a round, in seconds
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=Settings, meta = (ClampMin = 0))
|
||||||
|
float RoundTime;
|
||||||
|
|
||||||
|
// duration of the pre-match/warmup period, in seconds
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=Settings, meta = (ClampMin = 0))
|
||||||
|
float WarmupTime;;
|
||||||
|
|
||||||
|
// whether or not there is a warmup or the round should immediately start
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=Settings)
|
||||||
|
bool bWarmupEnabled;
|
||||||
|
|
||||||
|
FTimerHandle TimerHandle_DefaultTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
|
#include "GameModes/GSGameState.h"
|
||||||
|
|
||||||
|
// UE includes
|
||||||
|
#include "Net/UnrealNetwork.h"
|
||||||
|
|
||||||
|
void AGSGameState::SetNumTeams(int32 InNumTeams)
|
||||||
|
{
|
||||||
|
NumTeams = InNumTeams;
|
||||||
|
|
||||||
|
// Grow team score array if num teams is greater than current capacity
|
||||||
|
if(TeamScores.Num() < NumTeams)
|
||||||
|
{
|
||||||
|
TeamScores.AddZeroed(NumTeams - TeamScores.Num());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32 AGSGameState::GiveTeamPoint(int32 InTeamIdx)
|
||||||
|
{
|
||||||
|
if(InTeamIdx < 0 || InTeamIdx >= TeamScores.Num())
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Error, TEXT("Tried to give team %d a point but the team index is out of range"), InTeamIdx);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TeamScores[InTeamIdx]++;
|
||||||
|
OnTeamScoresUpdated.Broadcast(TeamScores);
|
||||||
|
return TeamScores[InTeamIdx];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameState::ResetTeamScores()
|
||||||
|
{
|
||||||
|
for(int32 TeamIdx = 0; TeamIdx < TeamScores.Num(); TeamIdx++)\
|
||||||
|
{
|
||||||
|
TeamScores[TeamIdx] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnTeamScoresUpdated.Broadcast(TeamScores);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32 AGSGameState::GetWinningTeam()
|
||||||
|
{
|
||||||
|
int32 WinningTeamIdx = 0;
|
||||||
|
int32 WinningTeamScore = 0;
|
||||||
|
for(int32 TeamIdx = 0; TeamIdx < TeamScores.Num(); TeamIdx++)\
|
||||||
|
{
|
||||||
|
if(TeamScores[TeamIdx] > WinningTeamScore)
|
||||||
|
{
|
||||||
|
WinningTeamIdx = TeamIdx;
|
||||||
|
WinningTeamScore = TeamScores[TeamIdx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WinningTeamIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameState::SetRemainingTime(float InRemainingTime)
|
||||||
|
{
|
||||||
|
RemainingTime = InRemainingTime;
|
||||||
|
|
||||||
|
OnRemainingTimeUpdated.Broadcast(RemainingTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AGSGameState::OnRep_MatchState()
|
||||||
|
{
|
||||||
|
Super::OnRep_MatchState();
|
||||||
|
|
||||||
|
OnMatchStateUpdated.Broadcast(GetMatchState());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameState::OnRep_TeamScores()
|
||||||
|
{
|
||||||
|
OnTeamScoresUpdated.Broadcast(TeamScores);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameState::OnRep_RemainingTime()
|
||||||
|
{
|
||||||
|
OnRemainingTimeUpdated.Broadcast(RemainingTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSGameState::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
|
||||||
|
{
|
||||||
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||||
|
|
||||||
|
DOREPLIFETIME(AGSGameState, NumTeams);
|
||||||
|
DOREPLIFETIME(AGSGameState, TeamScores);
|
||||||
|
DOREPLIFETIME(AGSGameState, RemainingTime);
|
||||||
|
}
|
73
GravityStomp/Source/GravityStompGame/GameModes/GSGameState.h
Normal file
73
GravityStomp/Source/GravityStompGame/GameModes/GSGameState.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// UE includes
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "GameFramework/GameState.h"
|
||||||
|
|
||||||
|
#include "GSGameState.generated.h"
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FTeamScoresUpdatedSignature, const TArray<int32>&, TeamScores);
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FRemainingTimeUpdatedDelegate, float, RemainingTime);
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMatchStateUpdatedSignature, FName, MatchState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game state for a team deathmatch/free for all game mode
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class GRAVITYSTOMPGAME_API AGSGameState : public AGameState
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
void SetNumTeams(int32 InNumTeams);
|
||||||
|
|
||||||
|
FORCEINLINE int32 GetNumTeams() { return NumTeams; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives team of the supplied team index a point. Returns the new score for that team.
|
||||||
|
*/
|
||||||
|
int32 GiveTeamPoint(int32 InTeamIdx);
|
||||||
|
|
||||||
|
void ResetTeamScores();
|
||||||
|
|
||||||
|
int32 GetWinningTeam();
|
||||||
|
|
||||||
|
FORCEINLINE float GetRemainingTime() { return RemainingTime; }
|
||||||
|
|
||||||
|
void SetRemainingTime(float InRemainingTime);
|
||||||
|
|
||||||
|
// Begin AGameState interface
|
||||||
|
virtual void OnRep_MatchState() override;
|
||||||
|
// End AGameState interface
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// number of teams currently in the game
|
||||||
|
UPROPERTY(Replicated, BlueprintReadOnly)
|
||||||
|
int32 NumTeams;
|
||||||
|
|
||||||
|
// array of team scores, index by the team number
|
||||||
|
UPROPERTY(ReplicatedUsing=OnRep_TeamScores, BlueprintReadOnly)
|
||||||
|
TArray<int32> TeamScores;
|
||||||
|
|
||||||
|
// time remaining for the current match state (i.e. warmup, the round itself, post round)
|
||||||
|
UPROPERTY(ReplicatedUsing=OnRep_RemainingTime, BlueprintReadOnly)
|
||||||
|
float RemainingTime;
|
||||||
|
|
||||||
|
/** Triggered when this weapon is fired. */
|
||||||
|
UPROPERTY(BlueprintAssignable, Category="Events")
|
||||||
|
FTeamScoresUpdatedSignature OnTeamScoresUpdated;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category="Events")
|
||||||
|
FRemainingTimeUpdatedDelegate OnRemainingTimeUpdated;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintAssignable, Category="Events")
|
||||||
|
FMatchStateUpdatedSignature OnMatchStateUpdated;
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRep_TeamScores();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRep_RemainingTime();
|
||||||
|
};
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
|
#include "Player/GSPlayerController.h"
|
||||||
|
|
||||||
|
void AGSPlayerController::Client_GameStarted_Implementation()
|
||||||
|
{
|
||||||
|
SetIgnoreMoveInput(false);
|
||||||
|
|
||||||
|
// enable all actions on pawn
|
||||||
|
OnGameStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSPlayerController::Client_OnKill_Implementation(AGSPlayerState* KillerPlayerState,
|
||||||
|
AGSPlayerState* VictimPlayerState)
|
||||||
|
{
|
||||||
|
OnKill(KillerPlayerState, VictimPlayerState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSPlayerController::Client_OnDeath_Implementation(AGSPlayerState* KillerPlayerState,
|
||||||
|
AGSPlayerState* VictimPlayerState)
|
||||||
|
{
|
||||||
|
OnDeath(KillerPlayerState, VictimPlayerState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSPlayerController::Client_WarmupStarted_Implementation()
|
||||||
|
{
|
||||||
|
OnWarmupStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AGSPlayerController::Client_GameRestarted_Implementation()
|
||||||
|
{
|
||||||
|
OnGameRestarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AGSPlayerController::ClientGameEnded_Implementation(AActor* EndGameFocus, bool bIsWinner)
|
||||||
|
{
|
||||||
|
Super::ClientGameEnded_Implementation(EndGameFocus, bIsWinner);
|
||||||
|
|
||||||
|
SetIgnoreMoveInput(true);
|
||||||
|
|
||||||
|
APawn* MyPawn = GetPawn();
|
||||||
|
if(MyPawn) // pawn may be null if we died and then the round ended
|
||||||
|
{
|
||||||
|
// disable all actions on character
|
||||||
|
MyPawn->TurnOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
OnGameEnded(bIsWinner);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSPlayerController::UnFreeze()
|
||||||
|
{
|
||||||
|
Super::UnFreeze();
|
||||||
|
|
||||||
|
ServerRestartPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSPlayerController::AcknowledgePossession(APawn* P)
|
||||||
|
{
|
||||||
|
OnAcknowledgePossession();
|
||||||
|
|
||||||
|
Super::AcknowledgePossession(P);
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// UE includes
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "GameFramework/PlayerController.h"
|
||||||
|
|
||||||
|
#include "GSPlayerController.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Player controller for a team deathmatch/free for all game mode
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class GRAVITYSTOMPGAME_API AGSPlayerController : public APlayerController
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Notify player that a game has started
|
||||||
|
*/
|
||||||
|
UFUNCTION(Reliable, Client)
|
||||||
|
void Client_GameStarted();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify player when the warmup period has started
|
||||||
|
*/
|
||||||
|
UFUNCTION(Reliable, Client)
|
||||||
|
void Client_WarmupStarted();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category="Events")
|
||||||
|
void OnWarmupStarted();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify a player that a game has started
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category="Events")
|
||||||
|
void OnGameStarted();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintImplementableEvent)
|
||||||
|
void OnAcknowledgePossession();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintImplementableEvent)
|
||||||
|
void OnPawnInitialized();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RPC when this player kills someone
|
||||||
|
*/
|
||||||
|
UFUNCTION(Reliable, Client)
|
||||||
|
void Client_OnKill(AGSPlayerState* KillerPlayerState, AGSPlayerState* VictimPlayerState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blueprint event when the OnKill RPC is called
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category="Events")
|
||||||
|
void OnKill(AGSPlayerState* KillerPlayerState, AGSPlayerState* VictimPlayerState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RPC when a death occurs so the player can update their UI
|
||||||
|
*/
|
||||||
|
UFUNCTION(Reliable, Client)
|
||||||
|
void Client_OnDeath(AGSPlayerState* KillerPlayerState, AGSPlayerState* VictimPlayerState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blueprint event when the OnDeath RPC is called
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category="Events")
|
||||||
|
void OnDeath(AGSPlayerState* KillerPlayerState, AGSPlayerState* VictimPlayerState);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blueprint event when the game ended RPC is called
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintImplementableEvent, Category="Events")
|
||||||
|
void OnGameEnded(bool bIsWinner);
|
||||||
|
|
||||||
|
UFUNCTION(Reliable, Client)
|
||||||
|
void Client_GameRestarted();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintImplementableEvent)
|
||||||
|
void OnGameRestarted();
|
||||||
|
|
||||||
|
// APlayerController interface
|
||||||
|
/**
|
||||||
|
* Does character cleanup when the game ends
|
||||||
|
*/
|
||||||
|
virtual void ClientGameEnded_Implementation(AActor* EndGameFocus, bool bIsWinner) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request respawn from server when we are allowed
|
||||||
|
*/
|
||||||
|
virtual void UnFreeze() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls OnAcknowledgePossession so the UI can bind itself to events like health updates
|
||||||
|
*/
|
||||||
|
virtual void AcknowledgePossession(APawn* P) override;
|
||||||
|
// End APlayerController interface
|
||||||
|
};
|
@ -0,0 +1,94 @@
|
|||||||
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
|
#include "Player/GSPlayerState.h"
|
||||||
|
|
||||||
|
// GS includes
|
||||||
|
#include "Character/GSCharacter.h"
|
||||||
|
|
||||||
|
// UE includes
|
||||||
|
#include "Kismet/GameplayStatics.h"
|
||||||
|
#include "Net/UnrealNetwork.h"
|
||||||
|
|
||||||
|
AGSPlayerState::AGSPlayerState()
|
||||||
|
{
|
||||||
|
TeamNum = 0;
|
||||||
|
NumKills = 0;
|
||||||
|
NumDeaths = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSPlayerState::SetTeamNum(int32 InTeamNum)
|
||||||
|
{
|
||||||
|
TeamNum = InTeamNum;
|
||||||
|
UpdateTeamColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSPlayerState::UpdateTeamColor()
|
||||||
|
{
|
||||||
|
AGSPlayerState* LocalPS = GetWorld()->GetFirstPlayerController()->GetPlayerState<AGSPlayerState>();
|
||||||
|
// if this player state belongs to us then we need to update all other characters
|
||||||
|
// rendering of team will essentially flip (all enemies are now rendered as friendly and vice versa)
|
||||||
|
if((GetPlayerController() && (GetNetMode() == ENetMode::NM_Client)) || ((GetPlayerController() == GetWorld()->GetFirstPlayerController()) && (GetNetMode() == ENetMode::NM_ListenServer)))
|
||||||
|
{
|
||||||
|
for(int32 PSIdx = 0; PSIdx < UGameplayStatics::GetNumPlayerStates(GetWorld()); ++PSIdx)
|
||||||
|
{
|
||||||
|
AGSPlayerState* CurrPS = Cast<AGSPlayerState>(UGameplayStatics::GetPlayerState(GetWorld(), PSIdx));
|
||||||
|
if(CurrPS != LocalPS) // only update team colors for remote characters
|
||||||
|
{
|
||||||
|
AGSCharacter* CurrChar = CurrPS->GetPawn<AGSCharacter>();
|
||||||
|
if(CurrChar && CurrPS && LocalPS)
|
||||||
|
{
|
||||||
|
CurrChar->ChangeTeamColor(IsPlayerFriendly(CurrPS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // this PS represents a remote player so we just need to switch how we are rendered to the local player
|
||||||
|
{
|
||||||
|
AGSCharacter* Char = GetPawn<AGSCharacter>();
|
||||||
|
if(Char && LocalPS)
|
||||||
|
{
|
||||||
|
Char->ChangeTeamColor(IsPlayerFriendly(LocalPS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSPlayerState::OnRep_TeamNum()
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("%s is on team %d"), *GetPlayerName(), TeamNum);
|
||||||
|
|
||||||
|
UpdateTeamColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AGSPlayerState::IsPlayerFriendly(const AGSPlayerState* PS) const
|
||||||
|
{
|
||||||
|
return TeamNum == PS->GetTeamNum();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSPlayerState::Reset()
|
||||||
|
{
|
||||||
|
NumKills = 0;
|
||||||
|
NumDeaths = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSPlayerState::ClientInitialize(AController* InController)
|
||||||
|
{
|
||||||
|
Super::ClientInitialize(InController);
|
||||||
|
|
||||||
|
UpdateTeamColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AGSPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||||
|
{
|
||||||
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||||
|
|
||||||
|
DOREPLIFETIME(AGSPlayerState, NumKills);
|
||||||
|
DOREPLIFETIME(AGSPlayerState, NumDeaths);
|
||||||
|
DOREPLIFETIME(AGSPlayerState, TeamNum);
|
||||||
|
}
|
73
GravityStomp/Source/GravityStompGame/Player/GSPlayerState.h
Normal file
73
GravityStomp/Source/GravityStompGame/Player/GSPlayerState.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// Gravity Stomp Copyright Kevin Poretti
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// UE includes
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "GameFramework/PlayerState.h"
|
||||||
|
|
||||||
|
#include "GSPlayerState.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Player state for a team deathmatch/free for all game mode
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class GRAVITYSTOMPGAME_API AGSPlayerState : public APlayerState
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
AGSPlayerState();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintPure)
|
||||||
|
FORCEINLINE int32 GetTeamNum() const { return TeamNum; }
|
||||||
|
|
||||||
|
void SetTeamNum(int32 InTeamNum);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintPure)
|
||||||
|
FORCEINLINE int32 GetNumKills() const { return NumKills; }
|
||||||
|
|
||||||
|
FORCEINLINE void SetNumKills(int32 InNumKills) { NumKills = InNumKills; }
|
||||||
|
|
||||||
|
FORCEINLINE void IncrementNumKills() { NumKills++; }
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintPure)
|
||||||
|
FORCEINLINE int32 GetNumDeaths() const { return NumDeaths; }
|
||||||
|
|
||||||
|
FORCEINLINE void SetNumDeaths(int32 InNumDeaths) { NumDeaths = InNumDeaths; }
|
||||||
|
|
||||||
|
FORCEINLINE void IncrementNumDeaths() { NumDeaths++; }
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintPure)
|
||||||
|
bool IsPlayerFriendly(const AGSPlayerState* PS) const;
|
||||||
|
|
||||||
|
// Begin APlayerState interface
|
||||||
|
/**
|
||||||
|
* Clear kills and deaths
|
||||||
|
*/
|
||||||
|
virtual void Reset() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates team color on init
|
||||||
|
*/
|
||||||
|
virtual void ClientInitialize(class AController* InController) override;
|
||||||
|
// End APlayerState interface
|
||||||
|
|
||||||
|
protected:
|
||||||
|
UPROPERTY(Replicated)
|
||||||
|
int32 NumKills;
|
||||||
|
|
||||||
|
UPROPERTY(Replicated)
|
||||||
|
int32 NumDeaths;
|
||||||
|
|
||||||
|
UPROPERTY(ReplicatedUsing=OnRep_TeamNum)
|
||||||
|
int32 TeamNum;
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRep_TeamNum();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes how this player is rendered to a client
|
||||||
|
*/
|
||||||
|
void UpdateTeamColor();
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user