Start of game mode
This commit is contained in:
		
							parent
							
								
									8ac4271602
								
							
						
					
					
						commit
						1fc53e9e62
					
				@ -1,3 +1,8 @@
 | 
			
		||||
[/Script/EngineSettings.GeneralProjectSettings]
 | 
			
		||||
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 "Camera/CameraComponent.h"
 | 
			
		||||
@ -57,6 +57,11 @@ AGSCharacter::AGSCharacter(const FObjectInitializer& ObjectInitializer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void AGSCharacter::ChangeTeamColor(bool bIsPlayerFriendly)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void AGSCharacter::BeginPlay()
 | 
			
		||||
{
 | 
			
		||||
	// Call the base class  
 | 
			
		||||
@ -98,6 +103,7 @@ void AGSCharacter::Move(const FInputActionValue& Value)
 | 
			
		||||
	AddMovementInput(FVector::RightVector, MovementVector.X);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void AGSCharacter::ChangeGravityDirection(const FInputActionValue& Value)
 | 
			
		||||
{
 | 
			
		||||
	FVector2D GravityDirection = Value.Get<FVector2D>();
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
// Copyright Epic Games, Inc. All Rights Reserved.
 | 
			
		||||
// Gravity Stomp Copyright Kevin Poretti
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
@ -35,6 +35,8 @@ class AGSCharacter : public ACharacter
 | 
			
		||||
public:
 | 
			
		||||
	AGSCharacter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
 | 
			
		||||
 | 
			
		||||
	void ChangeTeamColor(bool bIsPlayerFriendly);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
 | 
			
		||||
	/** 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"
 | 
			
		||||
 | 
			
		||||
@ -856,15 +856,9 @@ void UGSCharacterMovementComponent::PhysicsRotation(float DeltaTime)
 | 
			
		||||
 | 
			
		||||
	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.Yaw = IsCharacterUpAlignedToWorldUp() ? FRotator::NormalizeAxis(DesiredRotation.Yaw) : 0.f;
 | 
			
		||||
	    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
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
// Fill out your copyright notice in the Description page of Project Settings.
 | 
			
		||||
// Gravity Stomp Copyright Kevin Poretti
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,309 @@
 | 
			
		||||
// Copyright Epic Games, Inc. All Rights Reserved.
 | 
			
		||||
// Gravity Stomp Copyright Kevin Poretti
 | 
			
		||||
 | 
			
		||||
#include "GSGameModeBase.h"
 | 
			
		||||
 | 
			
		||||
// GS includes
 | 
			
		||||
#include "EngineUtils.h"
 | 
			
		||||
#include "GSGameState.h"
 | 
			
		||||
#include "Player/GSPlayerController.h"
 | 
			
		||||
#include "Player/GSPlayerState.h"
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
// GS includes
 | 
			
		||||
#include "Player/GSPlayerController.h"
 | 
			
		||||
#include "Player/GSPlayerState.h"
 | 
			
		||||
 | 
			
		||||
// UE includes
 | 
			
		||||
#include "CoreMinimal.h"
 | 
			
		||||
#include "GameFramework/GameModeBase.h"
 | 
			
		||||
#include "GameFramework/GameMode.h"
 | 
			
		||||
 | 
			
		||||
#include "GSGameModeBase.generated.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *	Game mode for a team deathmatch/free for all game mode
 | 
			
		||||
 */
 | 
			
		||||
UCLASS(minimalapi)
 | 
			
		||||
class AGSGameModeBase : public AGameModeBase
 | 
			
		||||
class AGSGameModeBase : public AGameMode
 | 
			
		||||
{
 | 
			
		||||
	GENERATED_BODY()
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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