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