HAxis sos

This commit is contained in:
guus
2018-08-11 16:46:35 +02:00
commit 510654f8a1
480 changed files with 54126 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
#ifdef ABILITY_OP
ABILITY_ATTACK(0, slot0)
ABILITY_OP(1, slot1)
ABILITY_OP(2, slot2)
ABILITY_OP(3, slot3)
ABILITY_OP(4, slot4)
ABILITY_OP(5, slot5)
ABILITY_OP(6, slot6)
ABILITY_OP(7, slot7)
ABILITY_OP(8, slot8)
#undef ABILITY_OP
#endif
#ifdef ABILITY_KEYBINDING_OP
ABILITY_KEYBINDING_OP("Attack", 0)
ABILITY_KEYBINDING_OP("CastAbility1",1)
ABILITY_KEYBINDING_OP("CastAbility2", 2)
ABILITY_KEYBINDING_OP("CastAbility3", 3)
ABILITY_KEYBINDING_OP("CastAbility4", 4)
ABILITY_KEYBINDING_OP("CastAbility5", 5)
ABILITY_KEYBINDING_OP("CastAbility6", 6)
ABILITY_KEYBINDING_OP("CastAbility7", 7)
ABILITY_KEYBINDING_OP("CastAbility8", 8)
#undef ABILITY_KEYBINDING_OP
#endif

View File

@@ -0,0 +1,557 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "Runtime/Engine/Classes/Engine/LocalPlayer.h"
#if PLATFORM_SPECIFIC_WIN == 0
// Include input manager for windows to allow DS4 controllers
#include "InputManager.hpp"
using namespace Input;
#endif
#include "DefaultGameInstance.h"
#include "CharacterSettings.h"
#include "Prefs.h"
#include "PlayerControllerBase.h"
#include "ScreenOverlay.h"
#include "SessionManager.h"
#include "ScreenOverlay.h"
#include "MapData.h"
#include "MapList.h"
#include <regex>
// Replicates the exact same data structure as FWindowsCursor except with a public cursor handle member
#if PLATFORM_SPECIFIC_WIN == 0
class FWindowsCursor1 : public ICursor
{
public:
EMouseCursor::Type CurrentType;
HCURSOR CursorHandles[EMouseCursor::TotalCursorCount];
};
HCURSOR originalCursor;
void InitWindowsCursors()
{
// Store original application cursor for editor
FWindowsCursor1* windowsCursor = (FWindowsCursor1*)FSlateApplication::Get().GetPlatformCursor().Get();
originalCursor = windowsCursor->CursorHandles[EMouseCursor::Default];
}
void ShutdownWindowsCursors()
{
// Reset original cursor, mainly for editor
FWindowsCursor1* windowsCursor = (FWindowsCursor1*)FSlateApplication::Get().GetPlatformCursor().Get();
windowsCursor->CursorHandles[EMouseCursor::Default] = originalCursor;
}
HCURSOR LoadCustomCursor(const FString& path)
{
FString parentPath = FPaths::GameDir();
FString cursorPath = parentPath + "\\" + path;
HCURSOR cursorHandle = LoadCursorFromFileW(*cursorPath);
return cursorHandle;
}
void SetApplicationCursor(HCURSOR cursor)
{
FWindowsCursor1* windowsCursor = (FWindowsCursor1*)FSlateApplication::Get().GetPlatformCursor().Get();
if(windowsCursor)
{
HCURSOR handleDst;
if(cursor != INVALID_HANDLE_VALUE)
{
handleDst = cursor;
}
else
{
handleDst = LoadCursor(GetModuleHandle(0), IDC_ARROW);
}
// Overwrite default cursor that unreal sets hack
windowsCursor->CursorHandles[EMouseCursor::Default] = handleDst;
}
}
#endif
static UMapList* mapList;
UDefaultGameInstance::UDefaultGameInstance()
{
mapList = ConstructorHelpers::FObjectFinder<UMapList>(TEXT("/Game/Assets/Levels/MapList")).Object;
menuLevelPath = "/Game/Assets/Levels/Menu";
splashScreenShown = false;
}
void UDefaultGameInstance::Init()
{
m_characterSettings = nullptr;
LoadSettings();
#if PLATFORM_SPECIFIC_WIN == 0
InitWindowsCursors();
SetApplicationCursor(LoadCustomCursor("cursor.cur"));
#endif
Super::Init();
m_prefs = nullptr;
LoadPrefs();
m_LoadMaps();
#if PLATFORM_SPECIFIC_WIN == 0
// Initialize TEA Input
Input::InputManager::Initialize();
Input::InputManager::GetInstance()->SearchForJoystick();
m_tickDelegateHandle = FTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &UDefaultGameInstance::m_Tick), 0.016666667f);
#endif
m_localPlayer = nullptr;
// Create session manager
sessionManager = NewObject<USessionManager>(this);
sessionManager->m_instance = this;
}
bool UDefaultGameInstance::HandleOpenCommand(const TCHAR* Cmd, FOutputDevice& Ar, UWorld* InWorld)
{
GPRINT("Open command -> " + FString(Cmd) + " [" + InWorld->GetFullName() + "]");
APlayerControllerBase* pcb = Cast<APlayerControllerBase>(GetFirstLocalPlayerController());
if(pcb && Super::HandleOpenCommand(Cmd, Ar, InWorld))
{
FString msg = FString("Opening level [") + Cmd + "]";
pcb->overlay->ShowOverlay(msg);
return true;
}
return false;
}
bool UDefaultGameInstance::m_Tick(float DeltaSeconds)
{
#if PLATFORM_SPECIFIC_WIN == 0
InputManager* inputManager = Input::InputManager::GetInstance();
inputManager->Update();
if(!inputManager->joystick)
{
std::vector<std::wstring> names = inputManager->GetJoystickNames();
for(size_t i = 0; i < names.size(); i++)
{
inputManager->ConnectJoystickByIndex(i);
if(inputManager->joystick)
break;
}
}
#endif
return true;
}
void UDefaultGameInstance::Shutdown()
{
FTicker::GetCoreTicker().RemoveTicker(m_tickDelegateHandle);
Super::Shutdown();
#if PLATFORM_SPECIFIC_WIN == 0
ShutdownWindowsCursors();
Input::InputManager::Cleanup();
#endif
sessionManager->Shutdown();
}
const FString levelPath = TEXT("/Game/Assets/Levels");
void UDefaultGameInstance::m_LoadMaps()
{
if (mapList)
{
for (int32 i = 0; i < mapList->mapList.Num(); i++)
{
UMapData* mapData = mapList->mapList[i];
FString name = mapData->GetName();
wchar_t* nameStrPtr = name.GetCharArray().GetData();
std::wstring nameStr = std::wstring(nameStrPtr, nameStrPtr + name.Len());
//GWPRINT(mapData->GetName() + L"\n" + mapData->GetPathName() + L"\n" + mapData->GetFullGroupName(false));
static std::wregex nameRx = std::wregex(L"I_([_a-zA-Z0-9]+)");
std::wsmatch match;
if (std::regex_match(nameStr, match, nameRx))
{
std::wstring levelNameStr = match[1];
FString matchingLevelName = FString(levelNameStr.c_str());
mapData->pathToAsset = levelPath + FString(L"/") + matchingLevelName + FString(L".") + matchingLevelName;
mapData->pathToAsset = levelPath + FString(L"/") + matchingLevelName;
//GWPRINT(L"Level asset path: " + mapData->pathToAsset);
maps.Add(mapData);
mapsByLevelName.Add(matchingLevelName, mapData);
mapsByLevelPath.Add(mapData->pathToAsset, mapData);
}
else
{
GWERROR(L"Can't extract map name from level asset " + mapData->GetPathName());
}
}
}
}
ULocalPlayer* UDefaultGameInstance::CreateInitialPlayer(FString& OutError)
{
m_localPlayer = Super::CreateInitialPlayer(OutError);
check(m_localPlayer);
//m_localNetID = m_localPlayer->GetPreferredUniqueNetId();
//GWPRINT(L"Created local player " + m_localPlayer->GetName() +
// ", NetID = " + m_localNetID->ToDebugString());
//check(m_localNetID.IsValid());
// Retrieve player name
//IOnlineIdentityPtr identity = m_onlineSystem->GetIdentityInterface();
//playerID = identity->GetPlayerNickname(*m_localNetID);
//GWPRINT(L"Assigned player ID: " + playerID);
//IOnlineFriendsPtr friends = m_onlineSystem->GetFriendsInterface();
//if (friends.IsValid())
//{
// onReadFriendsListCompleteDelegate = FOnReadFriendsListComplete::CreateUObject(this, &UDefaultGameInstance::m_OnReadFriendsListCompleteDelegate);
// friends->ReadFriendsList(0, TEXT("Friends"), onReadFriendsListCompleteDelegate);
//}
//ISteamFriends* steamFriends = steamIdentity->GetSteamFriendsPtr();
//steamIdentity
//friends->GetLargeFriendAvatar()
//ISteamUser* user;
//FOnlineIdentitySteam* steam = (FOnlineIdentitySteam*)*identity;
//class ISteamUser* steamUser = identity->Get
return m_localPlayer;
}
void UDefaultGameInstance::OnSessionUserInviteAccepted(const bool bWasSuccess, const int32 ControllerId, TSharedPtr<const FUniqueNetId> Us, const FOnlineSessionSearchResult& res)
{
sessionManager->AcceptUserInvite(bWasSuccess, ControllerId, Us, res);
}
void UDefaultGameInstance::SaveSettings()
{
if (m_characterSettings)
{
UCharacterSettings::Save(m_characterSettings);
}
}
void UDefaultGameInstance::LoadSettings()
{
m_characterSettings = UCharacterSettings::Load();
}
UCharacterSettings* UDefaultGameInstance::GetCharacterSettings()
{
return m_characterSettings;
}
void UDefaultGameInstance::SavePrefs()
{
if (m_prefs)
{
UPrefs::Save(m_prefs);
}
}
void UDefaultGameInstance::LoadPrefs()
{
m_prefs = UPrefs::Load();
}
UPrefs* UDefaultGameInstance::GetPrefs()
{
return m_prefs;
}
UMapData* UDefaultGameInstance::GetCurrentLevelMapData()
{
UWorld* world = GetWorld();
check(world);
FString mapName = world->GetMapName();
FString name1 = world->GetName();
UMapData** res = mapsByLevelName.Find(name1);
if (res)
return *res;
return nullptr;
}
class UMapData* UDefaultGameInstance::GetMapData(FString levelPath)
{
FString first;
FString last;
if(!levelPath.Split("/", &first, &last, ESearchCase::IgnoreCase, ESearchDir::FromEnd))
return nullptr;
FString la, lb;
if(last.Split(".", &la, &lb))
{
last = lb;
}
levelPath = first + "/" + last;
UMapData** res = mapsByLevelPath.Find(levelPath);
if(res)
return *res;
return nullptr;
}
void UDefaultGameInstance::ShowNetworkError(const FString& msg)
{
APlayerControllerBase* controller = Cast<APlayerControllerBase>(GetFirstLocalPlayerController());
check(controller);
TArray<FString> options;
options.Add("OK");
controller->overlay->ShowMessageBox("Network Error", msg, options);
}
int32 UDefaultGameInstance::GetScalabilityQuality()
{
const Scalability::FQualityLevels current = GEngine->GetGameUserSettings()->ScalabilityQuality;
// Check if any we know
Scalability::FQualityLevels setting;
for (int32 i = 0; i < 4; i++)
{
setting.SetFromSingleQualityLevel(i);
if (current == setting)
return i;
}
// Custom
return -1;
}
void UDefaultGameInstance::SetScalabilityQuality(int32 scalability)
{
GEngine->GetGameUserSettings()->ScalabilityQuality.SetFromSingleQualityLevel(scalability);
GEngine->GetGameUserSettings()->ApplySettings(false);
GEngine->GetGameUserSettings()->SaveSettings();
m_prefs->usingCustomGraphicsSettings = false;
SavePrefs();
//if (GConfig)
//{
// GConfig->SetInt(
// TEXT("ScalabilityGroups"),
// TEXT("sg.Preset"),
// scalability,
// GGameUserSettingsIni);
// GConfig->Flush(false, GGameUserSettingsIni);
//}
}
static int32 GenerateResolutionQuality(int32 value)
{
Scalability::FQualityLevels setting;
setting.SetFromSingleQualityLevel(value);
return setting.ResolutionQuality;
}
static int32 RevertResolutionQuality(int32 value)
{
Scalability::FQualityLevels setting;
for (int32 i = 0; i < 4; i++)
{
setting.SetFromSingleQualityLevel(i);
if (setting.ResolutionQuality == value)
return i;
}
return 0;
}
void UDefaultGameInstance::SetScalabilityQualityValues(int32 ResolutionQuality, int32 AntiAliasingQuality, int32 ShadowQuality, int32 PostProcessQuality, int32 TextureQuality, int32 EffectsQuality)
{
Scalability::FQualityLevels setting;
setting.SetFromSingleQualityLevel(ResolutionQuality);
setting.AntiAliasingQuality = AntiAliasingQuality;
setting.ShadowQuality = ShadowQuality;
setting.PostProcessQuality = PostProcessQuality;
setting.TextureQuality = TextureQuality;
setting.EffectsQuality = EffectsQuality;
setting.ViewDistanceQuality = TextureQuality;
GEngine->GetGameUserSettings()->ScalabilityQuality = setting;
GEngine->GetGameUserSettings()->ApplySettings(false);
GEngine->GetGameUserSettings()->SaveSettings();
m_prefs->usingCustomGraphicsSettings = true;
m_prefs->customGraphicsSettings[0] = ResolutionQuality;
m_prefs->customGraphicsSettings[1] = AntiAliasingQuality;
m_prefs->customGraphicsSettings[2] = ShadowQuality;
m_prefs->customGraphicsSettings[3] = PostProcessQuality;
m_prefs->customGraphicsSettings[4] = TextureQuality;
m_prefs->customGraphicsSettings[5] = EffectsQuality;
SavePrefs();
}
int32 UDefaultGameInstance::GetScalabilityQualityValue(EGraphicsSetting setting)
{
const Scalability::FQualityLevels current = GEngine->GetGameUserSettings()->ScalabilityQuality;
switch (int32(setting))
{
case 0: return RevertResolutionQuality(current.ResolutionQuality);
case 1: return current.AntiAliasingQuality;
case 2: return current.ShadowQuality;
case 3: return current.PostProcessQuality;
case 4: return current.TextureQuality;
case 5: return current.EffectsQuality;
}
return 0;
}
void UDefaultGameInstance::SetScalabilityQualityValue(EGraphicsSetting setting, int32 value)
{
if (int32(setting) >= 6)
return;
Scalability::FQualityLevels current = GEngine->GetGameUserSettings()->ScalabilityQuality;
switch (int32(setting))
{
case 0: current.ResolutionQuality = GenerateResolutionQuality(value); break;
case 1: current.AntiAliasingQuality = value; break;
case 2: current.ShadowQuality = value; break;
case 3: current.PostProcessQuality = value; break;
case 4: current.TextureQuality = value; break;
case 5: current.EffectsQuality = value; break;
}
GEngine->GetGameUserSettings()->ScalabilityQuality = current;
GEngine->GetGameUserSettings()->ApplySettings(false);
GEngine->GetGameUserSettings()->SaveSettings();
m_prefs->usingCustomGraphicsSettings = true;
m_prefs->customGraphicsSettings[int32(setting)] = value;
SavePrefs();
}
void UDefaultGameInstance::SetupLoadingScreen()
{
m_loadingScreen.bAutoCompleteWhenLoadingCompletes = true;
m_loadingScreen.WidgetLoadingScreen = FLoadingScreenAttributes::NewTestLoadingScreenWidget(); // <-- test screen that comes with
m_loadingScreen.MinimumLoadingScreenDisplayTime = 1.0f;
GetMoviePlayer()->SetupLoadingScreen(m_loadingScreen);
}
/*
void UDefaultGameInstance::OnSessionUserInviteAccepted(const bool bWasSuccess, const int32 ControllerId, TSharedPtr< const FUniqueNetId > Us, const FOnlineSessionSearchResult& res)
{
if (bWasSuccess)
{
m_JoinOnlineSession(res);
}
else
{
GWWARNING(L"OnSessionUserInviteAccepted was not successful");
}
}
void UDefaultGameInstance::m_OnCreateSessionComplete(FName sessionName, bool successful)
{
JWPRINT(L"OnCreateSessionComplete " + sessionName.ToString().GetCharArray().GetData() + L", " + successful);
m_session->ClearOnCreateSessionCompleteDelegate_Handle(onCreateSessionCompleteDelegateHandle);
if (successful)
{
//onStartSessionCompleteDelegateHandle = m_session->AddOnStartSessionCompleteDelegate_Handle(onStartSessionCompleteDelegate);
//m_session->StartSession(sessionName);
FString options = FString("listen");
// Add additional game settings here
options += FString("?teamCount=") + FString::FromInt(hostMaxTeamCount);
SetupLoadingScreen();
UGameplayStatics::OpenLevel(GetWorld(), FName(hostLoadMap.GetCharArray().GetData()), true, options);
}
else
{
ShowNetworkError("Failed to create session");
}
}
void UDefaultGameInstance::m_OnStartOnlineGameComplete(FName sessionName, bool successful)
{
JWPRINT(L"OnStartOnlineGameComplete " + sessionName.ToString().GetCharArray().GetData() + L", " + successful);
m_session->ClearOnStartSessionCompleteDelegate_Handle(onStartSessionCompleteDelegateHandle);
if (successful)
{
FString options = FString("listen");
// Add additional game settings here
options += FString("?teamCount=") + FString::FromInt(hostMaxTeamCount);
SetupLoadingScreen();
UGameplayStatics::OpenLevel(GetWorld(), FName(hostLoadMap.GetCharArray().GetData()), true, options);
}
else
{
ShowNetworkError("Failed to start session");
}
}
void UDefaultGameInstance::m_OnFindSessionsComplete(bool successful) //__attribute__((optnone))
{
JWPRINT(L"OnFindSessionsComplete " + successful);
m_operationInProgress = false;
if(m_findingOverlay)
{
m_findingOverlay->Close();
m_findingOverlay = nullptr;
}
m_session->ClearOnFindSessionsCompleteDelegate_Handle(onFindSessionsCompleteDelegateHandle);
JPRINT("OnFindSessionsComplete: " + sessionSearch->SearchResults.Num() + " sessions");
if (sessionSearch->SearchResults.Num() > 0)
{
for (int32 SearchIdx = 0; SearchIdx < sessionSearch->SearchResults.Num(); SearchIdx++)
{
JWPRINT(SearchIdx + L", " + sessionSearch->SearchResults[SearchIdx].Session.OwningUserName);
}
}
}
void UDefaultGameInstance::m_OnJoinSessionComplete(FName sessionName, EOnJoinSessionCompleteResult::Type result)
{
JWPRINT(L"OnStartOnlineGameComplete " + sessionName.ToString().GetCharArray().GetData() + L", " + int32(result));
m_session->ClearOnJoinSessionCompleteDelegate_Handle(onJoinSessionCompleteDelegateHandle);
m_operationInProgress = false;
if(result != EOnJoinSessionCompleteResult::Success)
{
ShowNetworkError(FString("Failed to join session, Error Code ") + FString::FromInt(result));
SetGameJoined();
return;
}
APlayerController* const playerController = GetFirstLocalPlayerController();
check(playerController);
SetupLoadingScreen();
playerController->ClientTravel(m_travelURL, ETravelType::TRAVEL_Absolute);
}
void UDefaultGameInstance::m_OnDestroySessionComplete(FName sessionName, bool successful)
{
JWPRINT(L"OnDestroySessionComplete " + sessionName.ToString().GetCharArray().GetData() + L", " + successful);
m_session->ClearOnDestroySessionCompleteDelegate_Handle(onDestroySessionCompleteDelegateHandle);
if(m_travelOnSessionDestroy)
{
UGameplayStatics::OpenLevel(GetWorld(), "/Game/Assets/Levels/Menu", true);
}
}
void UDefaultGameInstance::m_OnReadFriendsListCompleteDelegate(int32, bool, const FString&, const FString&)
{
GPRINT("Got friends list");
}
*/

View File

@@ -0,0 +1,107 @@
// Project Lab - NHTV Igad
#pragma once
#include "Engine/GameInstance.h"
#include "MoviePlayer.h"
#include "DefaultGameInstance.generated.h"
UENUM(BlueprintType)
enum class EGraphicsSetting : uint8
{
ResolutionQuality = 0,
AntiAliasingQuality,
ShadowQuality,
PostProcessQuality,
TextureQuality,
EffectsQuality,
};
UCLASS()
class UNREALPROJECT_API UDefaultGameInstance : public UGameInstance
{
GENERATED_BODY()
friend class USessionManager;
public:
UDefaultGameInstance();
virtual void Init() override;
virtual void Shutdown() override;
virtual bool HandleOpenCommand(const TCHAR* Cmd, FOutputDevice& Ar, UWorld* InWorld);
virtual ULocalPlayer* CreateInitialPlayer(FString& OutError) override;
virtual void OnSessionUserInviteAccepted(const bool bWasSuccess, const int32 ControllerId,
TSharedPtr<const FUniqueNetId> Us, const FOnlineSessionSearchResult& res) override;
void ShowNetworkError(const FString& msg);
void SetupLoadingScreen();
UFUNCTION(BlueprintCallable, Category = "Save")
void SaveSettings();
UFUNCTION(BlueprintCallable, Category = "Save")
void LoadSettings();
UFUNCTION(BlueprintCallable, Category = "Save")
UCharacterSettings* GetCharacterSettings();
UFUNCTION(BlueprintCallable, Category = "Prefs")
void SavePrefs();
UFUNCTION(BlueprintCallable, Category = "Prefs")
void LoadPrefs();
UFUNCTION(BlueprintCallable, Category = "Prefs")
UPrefs* GetPrefs();
UFUNCTION(BlueprintCallable, Category = "Levels")
class UMapData* GetCurrentLevelMapData();
UFUNCTION(BlueprintCallable, Category = "Levels")
class UMapData* GetMapData(FString levelPath);
// Get overall graphics settings, returns -1 if the user is using custom
UFUNCTION(BlueprintCallable, Category = "Quality")
int32 GetScalabilityQuality();
// Set overall graphics settings
UFUNCTION(BlueprintCallable, Exec, Category = "Quality")
void SetScalabilityQuality(int32 scalability);
// Set graphics settings from custom values, calling this will enable custom
UFUNCTION(BlueprintCallable, Category = "Quality")
void SetScalabilityQualityValues(int32 ResolutionQuality, int32 AntiAliasingQuality, int32 ShadowQuality, int32 PostProcessQuality, int32 TextureQuality, int32 EffectsQuality);
// Get individual graphics value
UFUNCTION(BlueprintCallable, Exec, Category = "Quality")
int32 GetScalabilityQualityValue(EGraphicsSetting setting);
// Set individual graphics value
UFUNCTION(BlueprintCallable, Category = "Quality")
void SetScalabilityQualityValue(EGraphicsSetting setting, int32 value);
// The class that manages online sessions and establishing connections to other clients
UPROPERTY(BlueprintReadOnly, Category = "Network")
class USessionManager* sessionManager;
UPROPERTY(BlueprintReadOnly, Category = "Levels")
TArray<class UMapData*> maps;
UPROPERTY()
TMap<FString, class UMapData*> mapsByLevelName;
UPROPERTY()
TMap<FString, class UMapData*> mapsByLevelPath;
UPROPERTY(BlueprintReadOnly, Category = "Player")
bool splashScreenShown;
UPROPERTY(EditDefaultsOnly)
FString menuLevelPath;
UPROPERTY(BlueprintReadOnly, Category = "Player")
FString playerID;
private:
virtual bool m_Tick(float DeltaSeconds);
void m_LoadMaps();
FLoadingScreenAttributes m_loadingScreen;
FDelegateHandle m_tickDelegateHandle;
ULocalPlayer* m_localPlayer;
UPROPERTY()
class UCharacterSettings* m_characterSettings;
UPROPERTY()
class UPrefs* m_prefs;
};

View File

@@ -0,0 +1,381 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "DefaultGameMode.h"
#include "DefaultGameState.h"
#include "DefaultGameInstance.h"
#include "PlayerSpawn.h"
#include "NetworkPlayer.h"
#include "CreatureSpawn.h"
#include "DefaultPlayer.h"
#include "DefaultPlayerController.h"
#include "DefaultPlayerState.h"
#include "GameStateBase.h"
#include "SessionManager.h"
#if PLATFORM_SPECIFIC_WIN == 0
#include "HeatMapMetrics.h"
#endif
#if UE_INCLUDE_METRICS
#include "MiniMapVolume.h"
#include "IngameHUD.h"
#include "MiniMapWidget.h"
#endif
ADefaultGameMode::ADefaultGameMode()
{
PrimaryActorTick.bCanEverTick = true;
// Lobby system starts the match
bDelayedStart = true;
PlayerControllerClass = ADefaultPlayerController::StaticClass();
DefaultPawnClass = ConstructorHelpers::FClassFinder<APawn>(TEXT("/Game/Assets/Blueprints/BP_DefaultPlayer")).Class;
SpectatorClass = ConstructorHelpers::FClassFinder<ASpectatorPawn>(TEXT("/Game/Assets/Blueprints/BP_SpectatorPawn")).Class;
GameStateClass = ADefaultGameState::StaticClass();
PlayerStateClass = ADefaultPlayerState::StaticClass();
}
void ADefaultGameMode::PreInitializeComponents()
{
Super::PreInitializeComponents();
ADefaultGameState* gameState = GetGameState<ADefaultGameState>();
check(gameState);
m_playerSpawns.AddZeroed(4);
//FString str = UGameplayStatics::ParseOption(OptionsString, FString("teamCount"));
//int32 teamCountSetting = FCString::Atoi(*str);
//if(teamCountSetting > 1)
//{
// gameState->m_mapTeamCount = teamCountSetting;
// GWPRINT(L"Set map team count to " + gameState->m_mapTeamCount);
//}
}
void ADefaultGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
Super::InitGame(MapName, Options, ErrorMessage);
// Start game immediately in editor since we can't use seamless travel to obtain the players
UWorld* world = GetWorld();
if(world->WorldType == EWorldType::PIE)
{
m_StartGameStartTimer();
}
// Find lobby state blueprint
for(TActorIterator<ALobbyState> it(world); it; ++it)
{
m_lobbyState = *it;
}
if(m_lobbyState)
{
GPRINT("Received traveled lobby state " + m_lobbyState->GetName());
m_totalPlayerCount = m_lobbyState->players.Num();
}
else
{
m_totalPlayerCount = 1;
}
}
void ADefaultGameMode::BeginPlay()
{
Super::BeginPlay();
#if PLATFORM_SPECIFIC_WIN == 0
HeatMapMetrics::ResetMap();
#endif
}
void ADefaultGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
#if UE_INCLUDE_METRICS
if (Metrics::EndSession())
JPRINT("Metrics session ended");
else
JERROR("Failed to close metrics session");
#endif
Super::EndPlay(EndPlayReason);
#if PLATFORM_SPECIFIC_WIN == 0
HeatMapMetrics::ExportToFile();
#endif
}
void ADefaultGameMode::PostSeamlessTravel()
{
GPRINT("PostSeamlessTravel");
Super::PostSeamlessTravel();
}
void ADefaultGameMode::HandleSeamlessTravelPlayer(AController*& C)
{
Super::HandleSeamlessTravelPlayer(C);
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
// Register new player
ADefaultPlayerController* newPC = Cast<ADefaultPlayerController>(C);
if(!newPC)
{
GERROR("Invalid player genereted by HandleSeamlessTravelPlayer");
}
else
{
GPRINT("Transfering logged in player " + C->GetName());
AGameStateBase* gameState = Cast<AGameStateBase>(GameState);
gameState->RegisterPlayer(newPC);
}
TSharedPtr<const FUniqueNetId> netID = newPC->PlayerState->UniqueId.GetUniqueNetId();
if(m_lobbyState)
{
m_lobbyState->players.RemoveAll([netID](FLobbyStatePlayer& item)
{
return item.netID.IsValid() && netID.IsValid() && (*item.netID == *netID);
});
}
m_numTravelledPlayers++;
if(m_numTravelledPlayers >= m_totalPlayerCount)
{
// Set a timer to start the game
m_StartGameStartTimer();
}
else
{
int32 left = m_totalPlayerCount - m_numTravelledPlayers;
GPRINT("Still waiting for " + left + " more players");
for(auto& p : m_lobbyState->players)
{
FString name = inst->sessionManager->GetPlayerName(*p.netID);
GPRINT("--" + name + " [" + p.netID->ToDebugString() + "]");
}
}
}
void ADefaultGameMode::PreLogin(const FString& Options, const FString& Address, const TSharedPtr<const FUniqueNetId>& UniqueId, FString& ErrorMessage)
{
Super::PreLogin(Options, Address, UniqueId, ErrorMessage);
FString username = "Invalid";
if(GameState->GetMatchState() == MatchState::InProgress)
{
// Check if player is reconnecting but was previously connected
FString uniqueIDString = UniqueId->ToString();
if (m_connectedPlayersNetID.Contains(uniqueIDString))
{
GWPRINT(L"Reconnecting player " + username + " / " + uniqueIDString);
}
else
{
ErrorMessage = "Game has already started";
}
}
}
APlayerController* ADefaultGameMode::Login(class UPlayer* NewPlayer, ENetRole InRemoteRole, const FString& Portal, const FString& Options, const TSharedPtr<const FUniqueNetId>& UniqueId, FString& ErrorMessage)
{
// Register players that ever connected to the game so they can reconnect
APlayerController* controller = Super::Login(NewPlayer, InRemoteRole, Portal, Options, UniqueId, ErrorMessage);
if (controller)
{
FString uniqueIDString = UniqueId->ToString();
m_connectedPlayersNetID.Add(uniqueIDString);
GWPRINT(L"Registering connected player ID " + uniqueIDString);
}
// Register logged on player
// Note: this should only happen when running a PIE instance or standalone with a startup level
// normal login procedure happens in the menu and players are then transfered to the game using seamless travel
ADefaultPlayerController* defPC = Cast<ADefaultPlayerController>(controller);
if(!defPC)
{
GERROR("Invalid player genereted by HandleSeamlessTravelPlayer");
}
else
{
GPRINT("Logging in new player " + defPC->GetName());
AGameStateBase* gameState = Cast<AGameStateBase>(GameState);
gameState->RegisterPlayer(defPC);
}
return controller;
}
void ADefaultGameMode::PostLogin(APlayerController* NewPlayer)
{
Super::PostLogin(NewPlayer);
// Auto assign team when in PIE
UWorld* world = GetWorld();
if(world->WorldType == EWorldType::PIE)
{
AGameStateBase* gameState = Cast<AGameStateBase>(GameState);
APlayerStateBase* state = Cast<APlayerStateBase>(NewPlayer->PlayerState);
state->AutoAssignTeam();
}
}
void ADefaultGameMode::Logout(AController* Exiting)
{
GWPRINT(L"Player left the game");
// Unregister players if not playing
if (GameState->GetMatchState() != MatchState::InProgress)
{
if (Exiting->PlayerState)
{
FString uniqueIDString = Exiting->PlayerState->UniqueId->ToString();
GWPRINT(L"Removing connected player ID " + uniqueIDString);
m_connectedPlayersNetID.Remove(uniqueIDString);
}
}
Super::Logout(Exiting);
}
void ADefaultGameMode::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
m_gameTimer += DeltaTime;
}
void ADefaultGameMode::HandleMatchHasStarted()
{
GWPRINT(L"Match Started");
GetGameState<ADefaultGameState>()->gameEnded = false;
// Assign team mates
ADefaultGameState* gameState = GetWorld()->GetGameState<ADefaultGameState>();
TArray<TArray<ADefaultPlayerState*>> playersByTeam = gameState->GetPlayersByTeam<ADefaultPlayerState>();
for (int32 i = 0; i < playersByTeam.Num(); i++)
{
auto& arr = playersByTeam[i];
if (arr.Num() == 2)
{
arr[0]->teamMate = arr[1];
arr[1]->teamMate = arr[0];
}
}
Super::HandleMatchHasStarted();
}
void ADefaultGameMode::HandleMatchHasEnded()
{
GWPRINT(L"Match Ended");
GetGameState<ADefaultGameState>()->gameEnded = true;
Super::HandleMatchHasStarted();
}
void ADefaultGameMode::RegisterPlayerSpawn(APlayerSpawn& spawn)
{
if (spawn.assignedTeam > m_playerSpawns.Num() - 1)
m_playerSpawns.SetNumZeroed(spawn.assignedTeam + 1);
m_playerSpawns[spawn.assignedTeam] = &spawn;
}
void ADefaultGameMode::RegisterPlayer(class ANetworkPlayer& player)
{
if(m_players.Contains(&player))
{
GWARNING("Player already registered with game mode " + player.GetName());
return;
}
m_players.Add(&player);
}
void ADefaultGameMode::UnregisterPlayer(class ANetworkPlayer& player)
{
m_players.Remove(&player);
}
void ADefaultGameMode::StartGame()
{
#if UE_INCLUDE_METRICS
// Start a new metrics session, and output the file in the Metrics folder
FString filePath = FPaths::GameDir() + FString(L"Metrics/");
if (Metrics::StartSession(filePath.GetCharArray().GetData()))
{
// Retrieve the active map size
for (TActorIterator<AMiniMapVolume> iter(GetWorld()); iter; ++iter)
{
AMiniMapVolume* const volume = *iter;
volume->SetActorRotation(FRotator(0, 0, 0));
FVector area = volume->area->GetScaledBoxExtent();
const FVector pos = volume->GetActorLocation();
FVector2D min, max;
min.X = -area.X + pos.X;
min.Y = -area.Y + pos.Y;
max.X = area.X + pos.X;
max.Y = area.Y + pos.Y;
Metrics::SetMapData(min.X, max.X, min.Y, max.Y);
break;
}
JPRINT("New metrics session started");
}
else
JERROR("Failed to open new metrics session");
#endif
GPRINT("Starting Game");
SetMatchState(MatchState::InProgress);
m_gameTimer = 0;
}
APlayerSpawn* ADefaultGameMode::GetOptimalSpawn(int32 team)
{
check(team > 0);
if (m_playerSpawns.Num() == 0)
return nullptr;
if (m_playerSpawns.Num() == 1)
return m_playerSpawns[0];
if (team - 1 < m_playerSpawns.Num())
return m_playerSpawns[team - 1];
// Either no players yet, or something went wrong
// Pick random spawn
return m_playerSpawns[FMath::Rand() % m_playerSpawns.Num()];
}
TArray<class ANetworkPlayer*> ADefaultGameMode::GetPlayers()
{
return m_players;
}
void ADefaultGameMode::BossHasBeenKilled(int32 team)
{
TArray<class ANetworkCharacter*> players(m_players);
// Set winning team in gamestate (replicated)
GetGameState<ADefaultGameState>()->gameWinningTeam = team;
EndMatch();
// Destroy all player pawns so that they respawn as spectators
for (int32 i = 0; i < players.Num(); i++)
{
players[i]->Destroy(true);
}
TPRINT("Boss was killed. (ADefaultGameMode) the team = " + team);
}
float ADefaultGameMode::GetGameTime()
{
return m_gameTimer;
}
void ADefaultGameMode::m_StartGameStartTimer()
{
GPRINT("Starting game in 2 seconds");
GetWorld()->GetTimerManager().SetTimer(m_gameStartTimer, this, &ADefaultGameMode::m_GameStart, 2.0f, false);
}
void ADefaultGameMode::m_GameStart()
{
StartGame();
}

View File

@@ -0,0 +1,65 @@
// Project Lab - NHTV Igad
#pragma once
#include "GameFramework/GameMode.h"
#include "DefaultGameMode.generated.h"
UCLASS(minimalapi)
class ADefaultGameMode : public AGameMode
{
GENERATED_BODY()
public:
ADefaultGameMode();
virtual void PreInitializeComponents() override;
virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) override;
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void PostSeamlessTravel() override;
virtual void HandleSeamlessTravelPlayer(AController*& C) override;
virtual void PreLogin(const FString& Options, const FString& Address, const TSharedPtr<const FUniqueNetId>& UniqueId, FString& ErrorMessage) override;
virtual APlayerController* Login(class UPlayer* NewPlayer, ENetRole InRemoteRole, const FString& Portal, const FString& Options, const TSharedPtr<const FUniqueNetId>& UniqueId, FString& ErrorMessage) override;
virtual void PostLogin(APlayerController* NewPlayer) override;
virtual void Logout(AController* Exiting);
virtual void Tick(float DeltaTime) override;
virtual void HandleMatchHasStarted() override;
virtual void HandleMatchHasEnded() override;
void RegisterPlayerSpawn(class APlayerSpawn& spawn);
void RegisterPlayer(class ANetworkPlayer& player);
void UnregisterPlayer(class ANetworkPlayer& player);
void StartGame();
APlayerSpawn* GetOptimalSpawn(int32 team);
TArray<class ANetworkPlayer*> GetPlayers();
void BossHasBeenKilled(int32 team);
float GetGameTime();
protected:
void m_StartGameStartTimer();
void m_GameStart();
float m_gameTimer;
float m_gameRestartTimer;
FTimerHandle m_gameStartTimer;
UPROPERTY()
class ALobbyState* m_lobbyState;
int32 m_numTravelledPlayers;
int32 m_totalPlayerCount;
TArray<FString> m_connectedPlayersNetID;
UPROPERTY()
TArray<class APlayerSpawn*> m_playerSpawns;
UPROPERTY()
TArray<class ANetworkPlayer*> m_players;
};

View File

@@ -0,0 +1,32 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "DefaultGameState.h"
#include "MapData.h"
ADefaultGameState::ADefaultGameState()
{
PrimaryActorTick.bCanEverTick = true;
bReplicates = true;
}
void ADefaultGameState::BeginPlay()
{
Super::BeginPlay();
}
void ADefaultGameState::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ADefaultGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ADefaultGameState, gameEnded);
DOREPLIFETIME(ADefaultGameState, gameWinningTeam);
DOREPLIFETIME(ADefaultGameState, gameRestartingIn);
}

View File

@@ -0,0 +1,27 @@
// Project Lab - NHTV Igad
#pragma once
#include "GameStateBase.h"
#include "MiniMap.h"
#include "DefaultGameState.generated.h"
UCLASS()
class UNREALPROJECT_API ADefaultGameState : public AGameStateBase
{
GENERATED_BODY()
friend class ADefaultGameMode;
public:
ADefaultGameState();
virtual void BeginPlay() override;
virtual void Tick(float DeltaSeconds) override;
class ASoundEffect* CreateSoundEffect(class USoundBase* sndClass, const FVector& position);
UPROPERTY(Replicated, BlueprintReadOnly, Category = "GameState")
bool gameEnded;
UPROPERTY(Replicated, BlueprintReadOnly, Category = "GameState")
int32 gameWinningTeam;
UPROPERTY(Replicated, BlueprintReadOnly, Category = "GameState")
float gameRestartingIn;
MiniMap::QuadTree<6> minimapQuadtree;
};

View File

@@ -0,0 +1,119 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "DefaultGameInstance.h"
#include "DefaultGameMode.h"
#include "PlayerSpawn.h"
#include "DefaultGameState.h"
#include "DefaultGameInstance.h"
#include "NetworkCharacter.h"
#include "DefaultPlayer.h"
#include "KingOfTheHillGameMode.h"
#include "KOTHBossSpawner.h"
#if PLATFORM_SPECIFIC_WIN == 0
#include "InputManager.hpp"
using namespace Input;
#endif
#include "DefaultPlayerController.h"
ADefaultPlayer::ADefaultPlayer()
{
PrimaryActorTick.bCanEverTick = true;
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
// Create a camera boom
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->AttachTo(RootComponent);
CameraBoom->bAbsoluteRotation = true;
CameraBoom->TargetArmLength = 1500.f;
CameraBoom->RelativeRotation = FRotator(-60.f, 45.f, 0.f);
CameraBoom->bDoCollisionTest = false;
CameraBoom->bInheritPitch = false;
CameraBoom->bInheritYaw = false;
CameraBoom->bInheritRoll = false;
// Create a camera
TopDownCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("TopDownCamera"));
TopDownCamera->AttachTo(CameraBoom, USpringArmComponent::SocketName);
TopDownCamera->bUsePawnControlRotation = false;
// Don't rotate character to camera direction
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
respawnDelay = 0.25f;
respawnTimer = 0.0f;
isRespawning = false;
bReplicates = true;
bAlwaysRelevant = true;
bReplicateMovement = false;
}
void ADefaultPlayer::BeginPlay()
{
Super::BeginPlay();
if(Role == ROLE_Authority)
isRespawning = true;
}
void ADefaultPlayer::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
}
void ADefaultPlayer::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
// Spawn the player
if(Role == ROLE_Authority)
{
if(isRespawning)
{
respawnTimer -= DeltaSeconds;
m_OnRep_RespawnTimer();
bool respawn = true;
AKingOfTheHillGameMode* gameMode = Cast<AKingOfTheHillGameMode>(GetWorld()->GetAuthGameMode());
if (gameMode)
{
respawn = gameMode->bossSpawner->IsBossAlive();
}
if(respawnTimer <= 0.0f &&respawn)
{
ADefaultPlayerController* controller = Cast<ADefaultPlayerController>(GetController());
if(IsValid(controller))
{
controller->SpawnCharacterForClient();
isRespawning = false;
}
else
{
isRespawning = false;
}
}
}
}
}
void ADefaultPlayer::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ADefaultPlayer, respawnTimer);
DOREPLIFETIME(ADefaultPlayer, isRespawning);
}
void ADefaultPlayer::OnSpawn()
{
check(Role == ROLE_Authority);
respawnTimer = respawnDelay;
isRespawning = true;
m_OnRep_RespawnTimer();
}
void ADefaultPlayer::m_OnRep_RespawnTimer()
{
onRespawnTimerSet.Broadcast(respawnTimer);
}

View File

@@ -0,0 +1,47 @@
// Project Lab - NHTV Igad
#pragma once
#include "GameFramework/Pawn.h"
#include "DefaultPlayer.generated.h"
UCLASS(Config=Game)
class UNREALPROJECT_API ADefaultPlayer : public APawn
{
GENERATED_BODY()
public:
ADefaultPlayer();
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void Tick(float DeltaSeconds) override;
// Resets the respawn timer
void OnSpawn();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* camera;
UPROPERTY(ReplicatedUsing=m_OnRep_RespawnTimer, BlueprintReadOnly, Category = "RespawnTimer")
float respawnTimer;
UPROPERTY(Replicated, BlueprintReadOnly, Category = "RespawnTimer")
bool isRespawning;
UPROPERTY(Config, BlueprintReadOnly)
float respawnDelay;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRespawnTimerSet, float, time);
UPROPERTY(BlueprintAssignable)
FOnRespawnTimerSet onRespawnTimerSet;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent* CameraBoom;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* TopDownCamera;
private:
UFUNCTION()
void m_OnRep_RespawnTimer();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,212 @@
// Project Lab - NHTV Igad
#pragma once
#include "GameFramework/PlayerController.h"
#include "IngameSkillTree.h"
#include "PlayerControllerBase.h"
#include "PlayerSetupState.h"
#include "DefaultPlayerController.generated.h"
UCLASS()
class UNREALPROJECT_API ADefaultPlayerController : public APlayerControllerBase
{
GENERATED_BODY()
public:
ADefaultPlayerController(const FObjectInitializer& init);
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void Destroyed() override;
virtual void SetupInputComponent() override;
virtual void Tick(float DeltaSeconds) override;
virtual void Possess(APawn* aPawn) override;
virtual void SeamlessTravelFrom(class APlayerController* OldPC) override;
// This is used to handle the player being destroyed
virtual void PawnPendingDestroy(APawn* inPawn) override;
void UpdatePlayerPosition(const FVector& location, const FRotator& rotator);
// Called when the possesed pawn is replicated
virtual void OnRep_Pawn() override;
UFUNCTION(Reliable, Client)
void OnLevelUpClient(const TArray<FIngameSkillTreeSkill>& updatedSkills, const TArray<FIngameSkillTreeSkill>& oldSkills);
// Only called on server, called to learn skills on the currently owned pawn
void LearnSkillsForLevel();
bool GetAbilityButtonLocation(class UAbilityInfo* ability, int32& slot, bool& isAlt);
int32 GetCurrentAbilityLevel(class UAbilityInfo* ability);
void ToggleSkillTree();
void ToggleMenu();
UFUNCTION(BlueprintCallable, Category="UI")
void OnHideUI();
void OnCharacterCreated(class ANetworkCharacter* character);
void OnCharacterDestroyed(class ANetworkCharacter* character);
const TArray<class UAbilityInfo*>& GetAbilities(APawn* targetPawn = nullptr);
bool HasGameFocus();
// Ability cast
void CastAbility(int32 index);
// Called for hold abilities to turn them off
void UnCastAbility(int32 index);
UAbilityInfo* GetMappedAbility(int32 index);
// Ability Button-Down events
void CastAbility0();
void CastAbility1();
void CastAbility2();
void CastAbility3();
void CastAbility4();
void CastAbility5();
void CastAbility6();
void CastAbility7();
void CastAbility8();
// Ability Button-Up events
void UnCastAbility0();
void UnCastAbility1();
void UnCastAbility2();
void UnCastAbility3();
void UnCastAbility4();
void UnCastAbility5();
void UnCastAbility6();
void UnCastAbility7();
void UnCastAbility8();
void CoopModifierOn();
void CoopModifierOff();
void MoveForward(float Value);
void MoveRight(float Value);
void TurnAtRate(float Rate);
void LookUp(float Rate);
void LookUpAtRate(float Rate);
void RightMouseDown();
void RightMouseUp();
void LeftMouseDown();
void ToggleSwitch();
void LeftMouseUp();
void HoldDown();
void HoldUp();
void SpawnCharacterForClient();
void SpawnCharacterForClient(const FVector& location, const FRotator& rotation);
UFUNCTION(Reliable, Server, WithValidation)
void LearnSkillOnServer(class UBaseSkillObject* object);
UFUNCTION( Reliable, Server, WithValidation )
void UnlearnSkillOnServer(class UBaseSkillObject* object);
void OnLearnSkill(class UBaseSkillObject* object);
void OnUnlearnSkill(class UBaseSkillObject* object);
TArray<class UBaseSkillObject*>& GetSkills();
void UpdateLevel();
UFUNCTION(Exec)
void SetLevel(int32 level);
UFUNCTION(Reliable, Server, WithValidation)
void SetLevel_Server(int32 level);
UFUNCTION(Exec)
void Suicide();
UFUNCTION(Reliable, Server, WithValidation)
void Suicide_Server();
UFUNCTION(Exec)
void TestWinGame(int32 team);
UFUNCTION(Client, Reliable)
void SpawnCombatText(const FVector& position, const FString& string, const FLinearColor& color);
UFUNCTION(Client, Reliable)
void SpawnArcingCombatText(const FVector& position, const FString& string, const FLinearColor& color);
// Called when setupState is replicated
UFUNCTION()
virtual void OnRep_Setup() override;
UFUNCTION(BlueprintCallable, Category="State")
FCharacterClassProperty GetCharacterClassProperties() const;
UFUNCTION(BlueprintCallable, Category = "State")
class UIngameHUD* GetHUD();
// Player location history
struct Location
{
Location(const FVector& location, const FRotator& rotation) : location(location), rotation(rotation) {}
FVector location;
FRotator rotation;
};
TArray<Location> playerLocationHistory;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
class UCharacterClassPropertySet* characterClassProperties;
METRICS_EXPR(Metrics::PlayerHandle* metricsHandle);
protected:
// Called when the player possesed pawn has changed, on client or server(players)
virtual void m_OnPawnChanged();
private:
void m_UpdateUI();
void m_CreateIngameSkilltree();
void m_StartBasicAttack();
void m_TickBasicAttack();
void m_StopBasicAttack();
// To check editor play
bool m_seamlessTravelDone;
// Selected class properties the player chose for his character build
FCharacterClassProperty m_classProperties;
UPROPERTY(Replicated)
TArray<class UBaseSkillObject*> m_skills;
// Check for recent mouse/controller usage
FVector2D m_mouseLast;
FVector m_holdDirection;
bool m_rightMouseDown;
bool m_coopModifier;
bool m_holdPosition;
bool m_skillTreeInitialized;
bool m_usingBasicAttack;
TArray<class UAbilityInfo*> m_mainSkillMapping;
TArray<class UAbilityInfo*> m_altSkillMapping;
TSet<class UAbilityInfo*> m_learnedAbilities;
TArray<FIngameSkillTreeSkill> m_alreadyLearnedSkills;
uint32 m_framesSinceMenuFocus;
UPROPERTY()
class UMenuScreen* m_ingameMenu;
UPROPERTY()
class UMenuPanel* m_ingameMenuPanel;
UPROPERTY()
class UIngameHUD* m_hud;
UPROPERTY()
class UUserWidget* m_respawnWidget;
UPROPERTY()
class USkillTreeWidget* m_skillTreeWidget;
friend class ABuff;
UPROPERTY()
class AIngameSkillTree* m_skillTree;
UPROPERTY(Replicated)
class ADefaultPlayer* m_defaultPlayer;
};

View File

@@ -0,0 +1,121 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "DefaultPlayerState.h"
#include "DefaultGameState.h"
#include "DefaultGameInstance.h"
#include "NetworkPlayer.h"
int32 maxLevel = 16;
ADefaultPlayerState::ADefaultPlayerState()
{
// Set the default values for the constant formula variables.
// BaseXP + (VarXP * (Level ^ Exp));
m_baseExperience = 200;
m_variableExperience = 20;
m_exponentExperience = 1.2f;
// Default values for the player.
m_level = 1;
m_experience = 0;
character = nullptr;
// Calculate the experience to level up.
m_experienceToLevel = m_CalculateExperienceToLevel(m_level);
kills = 0;
deaths = 0;
}
void ADefaultPlayerState::BeginPlay()
{
Super::BeginPlay();
}
void ADefaultPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ADefaultPlayerState, m_level);
DOREPLIFETIME(ADefaultPlayerState, m_experience);
DOREPLIFETIME(ADefaultPlayerState, m_experienceToLevel);
DOREPLIFETIME(ADefaultPlayerState, character);
DOREPLIFETIME(ADefaultPlayerState, teamMate);
DOREPLIFETIME(ADefaultPlayerState, kills);
DOREPLIFETIME(ADefaultPlayerState, deaths);
}
// The player level gets converted to an int32, because this function is BlueprintCallable,
// which does not allow int8 values.
int32 ADefaultPlayerState::GetLevel() const
{
return (int32)m_level;
}
int32 ADefaultPlayerState::GetMaxLevel() const
{
return maxLevel;
}
int32 ADefaultPlayerState::GetExperience() const
{
return m_experience;
}
int32 ADefaultPlayerState::GetExperienceToLevel() const
{
return m_experienceToLevel;
}
void ADefaultPlayerState::GainExperience(int32 gainedExperience)
{
check(gainedExperience >= 0);
m_experience += gainedExperience;
if (m_level == maxLevel)
return;
// Check if a player has leveled up (multiple times).
while (m_experience >= m_experienceToLevel)
{
// Level up.
++m_level;
m_experience -= m_experienceToLevel;
// Calculate the experience to level up.
m_experienceToLevel = m_CalculateExperienceToLevel(m_level);
if (character)
character->OnLevelUp_Server(m_level);
if (m_level == maxLevel)
{
m_experience = 0;
break;
}
}
}
void ADefaultPlayerState::SetLevel(int32 level)
{
if (Role == ROLE_Authority)
{
GPRINT("Setting level to " + level);
m_level = FMath::Clamp(level, 1, maxLevel);
m_experienceToLevel = m_CalculateExperienceToLevel(m_level + 1);
m_experience = 0;
if(character)
{
character->OnLevelUp_Server(level);
}
}
}
// Refer to the google/excel sheet for the exact value this will generate.
// Google sheet: https://docs.google.com/spreadsheets/d/1iuSg5efQSg0l2iQ5RLv3wF44RX2-i-gF-1QUB896bVE/edit#gid=0
// Excel sheet: I:/Y2015A-Y3-BountyMaze/Programming/Documentation/Experience required to level Sheet.xlsx
int32 ADefaultPlayerState::m_CalculateExperienceToLevel(int32 level)
{
// BaseXP + (VarXP * (Level ^ Exp));
return m_baseExperience + m_variableExperience * (pow(level, m_exponentExperience));
}

View File

@@ -0,0 +1,68 @@
// Project Lab - NHTV Igad
#pragma once
#include "PlayerStateBase.h"
#include "DefaultPlayerState.generated.h"
/*
In-game state of a player.
*/
UCLASS()
class UNREALPROJECT_API ADefaultPlayerState : public APlayerStateBase
{
GENERATED_BODY()
public:
ADefaultPlayerState();
virtual void BeginPlay() override;
// Get Functions
// Integer level value from 1-max
UFUNCTION(BlueprintCallable, Category = "Level Functions")
int32 GetLevel() const;
UFUNCTION(BlueprintCallable, Category = "Level Functions")
int32 GetMaxLevel() const;
// Current experience in-level
UFUNCTION(BlueprintCallable, Category = "Level Functions")
int32 GetExperience() const;
// Totale experience needed for the next level
UFUNCTION(BlueprintCallable, Category = "Level Functions")
int32 GetExperienceToLevel() const;
// DEBUG functionality for setting a player level
void SetLevel(int32 level);
// Call the function to grant a player with experience, this function will also handle leveling up.
UFUNCTION(BlueprintCallable, Category = "Level Functions")
void GainExperience(int32 gainedExperience);
// The character the player currently controls, or null when dead
UPROPERTY(BlueprintReadOnly, Replicated, Category = "Character")
class ANetworkPlayer* character;
// The person that is in the same team as this player, or null when dead or there is none
UPROPERTY(BlueprintReadOnly, Replicated, Category = "Character")
class ADefaultPlayerState* teamMate;
UPROPERTY(BlueprintReadonly, Replicated, Category = "Score")
int32 kills;
UPROPERTY(BlueprintReadonly, Replicated, Category = "Score")
int32 deaths;
private:
// Calculates the experience needed to level up to a given level
int32 m_CalculateExperienceToLevel(int32 level);
// Variables to keep track of what level/experience the player has.
UPROPERTY(Replicated)
int8 m_level;
UPROPERTY(Replicated)
int32 m_experience;
// Variable to check if a player has leveled up.
UPROPERTY(Replicated)
int32 m_experienceToLevel;
// Variables for calculating the experience to level up.
int32 m_baseExperience;
int32 m_variableExperience;
float m_exponentExperience;
};

View File

@@ -0,0 +1,201 @@
#include "UnrealProject.h"
#include "GameStateBase.h"
#include "PlayerStateBase.h"
#include "DefaultPlayerController.h"
#include "DefaultGameInstance.h"
#include "MapData.h"
AGameStateBase::AGameStateBase()
{
m_mapTeamCount = 4;
m_hasWarnedFrank = false;
}
void AGameStateBase::BeginPlay()
{
Super::BeginPlay();
mapPath = GetWorld()->GetPathName();
m_LoadActiveMapInfo();
}
void AGameStateBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
OnPlayerStateChange.Clear();
}
void AGameStateBase::RegisterPlayer(APlayerControllerBase* player)
{
check(Role == ROLE_Authority);
check(player);
APlayerStateBase* state = Cast<APlayerStateBase>(player->PlayerState);
check(state);
m_playersByController.Add(player, state);
m_players.Add(state);
// More than 1 player in editor means multiplayer in PIE mode
if(m_players.Num() > 1)
{
EnterLobby();
}
OnPlayerStateChange.Broadcast(nullptr);
}
void AGameStateBase::DeregisterPlayer(APlayerControllerBase* player)
{
check(Role == ROLE_Authority);
m_playersByController.Remove(player);
m_players.SetNum(0);
for(auto it = m_playersByController.CreateIterator(); it; ++it)
{
m_players.Add(it->Value);
}
OnPlayerStateChange.Broadcast(nullptr);
}
void AGameStateBase::EnterLobby()
{
check(Role == ROLE_Authority);
for(auto it = m_playersByController.CreateIterator(); it; ++it)
{
it->Key->EnterLobby();
}
}
void AGameStateBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AGameStateBase, m_players);
DOREPLIFETIME(AGameStateBase, m_mapTeamCount);
DOREPLIFETIME(AGameStateBase, mapPath);
}
TArray<class APlayerStateBase*>& AGameStateBase::GetPlayers()
{
return m_players;
}
TArray<TArray<class APlayerStateBase*>> AGameStateBase::GetPlayersByTeam()
{
TArray<TArray<class APlayerStateBase*>> ret;
for(int32 i = 0; i < m_players.Num(); i++)
{
APlayerStateBase* player = m_players[i];
if(!player)
continue;
int32 team = player->GetTeam();
if(team >= ret.Num())
{
ret.SetNum(team + 1);
}
TArray<class APlayerStateBase*>& dstArray = ret[team];
dstArray.Add(player);
}
return ret;
}
TArray<int32> AGameStateBase::GetTeamSizes()
{
TArray<int32> teamSizes;
teamSizes.SetNumZeroed(m_mapTeamCount);
for(int32 i = 0; i < m_players.Num(); i++)
{
if(!m_players[i])
continue;
int32 team = m_players[i]->GetTeam() - 1;
if(team < 0 || team >= m_mapTeamCount)
continue;
teamSizes[team]++;
}
return TArray<int32>(teamSizes);
}
int32 AGameStateBase::GetOptimalAutoJoinTeam()
{
if(GetWorld()->IsPlayInEditor())
{
// PIE uses different assignment, default to 2 players per team and then move to the next team
TArray<int32> teamSize = GetTeamSizes();
int32 optimalTeam = -1;
for(int32 i = 0; i < m_mapTeamCount; i++)
{
if(teamSize[i] < 2)
{
optimalTeam = i;
break;
}
}
return optimalTeam + 1;
}
else
{
TArray<int32> teamSize = GetTeamSizes();
int32 smallestSize = 2;
int32 optimalTeam = -1;
for(int32 i = 0; i < m_mapTeamCount; i++)
{
if(teamSize[i] < smallestSize)
{
optimalTeam = i;
smallestSize = teamSize[i];
}
}
return optimalTeam + 1;
}
}
int32 AGameStateBase::GetMapTeamCount() const
{
return m_mapTeamCount;
}
UMapData* AGameStateBase::GetMapData() const
{
return m_mapData;
}
void AGameStateBase::m_OnRep_MapPath()
{
GPRINT("Map changed to " + mapPath);
m_LoadActiveMapInfo();
if(m_mapData)
GPRINT("Found attached map data " + m_mapData->GetName());
else
GERROR("No attached map data found.");
}
void AGameStateBase::OnPlayerStateChange_Multi_Implementation()
{
if (m_playersByController.Num() > 0)
OnPlayerStateChange.Broadcast(nullptr);
}
TMap<APlayerControllerBase*, APlayerStateBase*> AGameStateBase::GetPlayersByController()
{
return m_playersByController;
}
bool AGameStateBase::m_LoadActiveMapInfo()
{
UDefaultGameInstance* gameInstance = Cast<UDefaultGameInstance>(GetGameInstance());
if(!gameInstance)
return false;
m_mapData = gameInstance->GetMapData(mapPath);
if(!m_mapData)
{
if(!m_hasWarnedFrank)
{
GWERROR(L"Failed to load map info, Create a map info asset with the name formated as I_ + map name in the same folder and add the I_<map_name> file to the map list frank!!!");
m_hasWarnedFrank = true;
}
m_mapTeamCount = 4;
}
else
{
m_mapTeamCount = m_mapData->maxTeamCount;
}
return true;
}

View File

@@ -0,0 +1,106 @@
#pragma once
#include "GameFramework/GameState.h"
#include "GameStateBase.Generated.h"
/*
Lobby state
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlayerStateChange, class APlayerControllerBase*, playerController);
USTRUCT()
struct FLobbyStatePlayer
{
GENERATED_USTRUCT_BODY()
public:
int32 team;
TSharedPtr<const FUniqueNetId> netID;
};
UCLASS()
class ALobbyState : public AInfo
{
GENERATED_BODY()
public:
UPROPERTY()
TArray<FLobbyStatePlayer> players;
};
/*
Base game state, keeping all the players in the current game and keeping their assigned teams
*/
UCLASS()
class AGameStateBase : public AGameState
{
GENERATED_BODY()
friend class AMenuGameMode;
public:
AGameStateBase();
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason);
virtual void RegisterPlayer(class APlayerControllerBase* player);
virtual void DeregisterPlayer(class APlayerControllerBase* player);
// Tells all the players to go to the lobby screen
void EnterLobby();
TArray<class APlayerStateBase*>& GetPlayers();
template<typename T> TArray<T*> GetPlayers()
{
return reinterpret_cast<TArray<T*>&>(GetPlayers());
}
TArray<TArray<class APlayerStateBase*>> GetPlayersByTeam();
template<typename T> TArray<TArray<T*>> GetPlayersByTeam()
{
auto r = GetPlayersByTeam();
return reinterpret_cast<TArray<TArray<T*>>&>(r);
}
UFUNCTION(BlueprintCallable, Category = "Players")
TArray<class APlayerControllerBase*> GetPlayerControllers()
{
TArray<class APlayerControllerBase*> result;
m_playersByController.GenerateKeyArray(result);
return result;
}
TArray<int32> GetTeamSizes();
int32 GetOptimalAutoJoinTeam();
UFUNCTION(BlueprintCallable, Category="Team")
int32 GetMapTeamCount() const;
UFUNCTION(BlueprintCallable, Category = "Map")
UMapData* GetMapData() const;
UPROPERTY(Replicated, ReplicatedUsing = m_OnRep_MapPath, BlueprintReadOnly, Category = "Map")
FString mapPath;
// Called when a player has joined/left or switched team
UFUNCTION(NetMulticast, Reliable)
void OnPlayerStateChange_Multi();
UPROPERTY(BlueprintAssignable, Category = Events)
FOnPlayerStateChange OnPlayerStateChange;
TMap<class APlayerControllerBase*, class APlayerStateBase*> GetPlayersByController();
private:
// Tries to load the current map data
bool m_LoadActiveMapInfo();
UFUNCTION()
void m_OnRep_MapPath();
UPROPERTY(Replicated)
int32 m_mapTeamCount;
UPROPERTY()
class UMapData* m_mapData;
// For debugging
bool m_hasWarnedFrank;
UPROPERTY()
TMap<class APlayerControllerBase*, class APlayerStateBase*> m_playersByController;
UPROPERTY(Replicated)
TArray<class APlayerStateBase*> m_players;
};

View File

@@ -0,0 +1,31 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "KOTHGameState.h"
void AKOTHGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AKOTHGameState, kothTeams);
DOREPLIFETIME(AKOTHGameState, maxGameScore);
DOREPLIFETIME(AKOTHGameState, overtime);
}
FKOTHTeamState AKOTHGameState::GetTeam(int32 team) const
{
// Team to array index [1-5] -> [0-4]
team -= 1;
if(team >= 0 && team < kothTeams.Num())
return kothTeams[team];
return FKOTHTeamState();
}
void AKOTHGameState::BroadcastOnHillCaptured_Implementation(int32 team)
{
onHillCaptured.Broadcast(team);
}
void AKOTHGameState::m_OnRep_Overtime()
{
GPRINT("Overtime = " + overtime);
}

View File

@@ -0,0 +1,38 @@
// Project Lab - NHTV Igad
#pragma once
#include "DefaultGameState.h"
#include "KOTHTeamState.h"
#include "KOTHGameState.generated.h"
UCLASS()
class AKOTHGameState : public ADefaultGameState
{
GENERATED_BODY()
public:
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const;
UFUNCTION(BlueprintCallable, Category="Game")
FKOTHTeamState GetTeam(int32 team) const;
UFUNCTION(NetMulticast, Reliable)
void BroadcastOnHillCaptured(int32 team);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHillCaptured, int32, team);
UPROPERTY(BlueprintAssignable, Category="Game")
FOnHillCaptured onHillCaptured;
UPROPERTY(Replicated, ReplicatedUsing=m_OnRep_Overtime, BlueprintReadOnly)
bool overtime;
UPROPERTY(Replicated, BlueprintReadOnly)
TArray<FKOTHTeamState> kothTeams;
UPROPERTY(Replicated, BlueprintReadOnly)
float maxGameScore;
private:
UFUNCTION()
void m_OnRep_Overtime();
friend class AKingOfTheHillGameMode;
};

View File

@@ -0,0 +1,47 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "KOTHPlayerController.h"
#include "NetworkPlayer.h"
#include "KOTHHUD.h"
#include "KOTHGameState.h"
void AKOTHPlayerController::BeginPlay()
{
Super::BeginPlay();
}
void AKOTHPlayerController::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
AKOTHGameState* gs = GetGameState<AKOTHGameState>();
if(gs && m_kothhud)
{
m_kothhud->Update(gs->kothTeams);
}
}
void AKOTHPlayerController::m_OnPawnChanged()
{
Super::m_OnPawnChanged();
class ANetworkPlayer* character = Cast<ANetworkPlayer>(GetPawn());
if(character)
{
if(IsLocalController())
{
if(!m_kothhud)
{
m_kothhud = CreateWidget<UKOTHHUD>(GetWorld(), kothHUD);
if(m_kothhud)
{
m_kothhud->AddToViewport(2);
}
}
}
}
if(m_kothhud)
m_kothhud->SetVisibility(character ? ESlateVisibility::Visible : ESlateVisibility::Hidden);
}

View File

@@ -0,0 +1,23 @@
// Project Lab - NHTV Igad
#pragma once
#include "DefaultPlayerController.h"
#include "KOTHPlayerController.generated.h"
UCLASS()
class UNREALPROJECT_API AKOTHPlayerController : public ADefaultPlayerController
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
virtual void Tick(float DeltaSeconds) override;
UPROPERTY(EditDefaultsOnly)
TSubclassOf<class UKOTHHUD> kothHUD;
protected:
virtual void m_OnPawnChanged();
private:
class UKOTHHUD* m_kothhud;
};

View File

@@ -0,0 +1,18 @@
#pragma once
#include "KOTHTeamState.Generated.h"
USTRUCT(BlueprintType)
struct FKOTHTeamState
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadOnly)
float score;
UPROPERTY(BlueprintReadOnly)
float maxScore;
// If this is greater than 0, the team has recently received score
UPROPERTY(BlueprintReadOnly)
int32 active;
UPROPERTY(BlueprintReadOnly)
bool containsMembers;
};

View File

@@ -0,0 +1,131 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "KingOfTheHillGameMode.h"
#include "DefaultGameState.h"
#include "DefaultGameState.h"
#include "DefaultGameInstance.h"
#include "PlayerSpawn.h"
#include "NetworkPlayer.h"
#include "CreatureSpawn.h"
#include "DefaultPlayer.h"
#include "DefaultPlayerState.h"
#include "KOTHPlayerController.h"
#include "KOTHGameState.h"
#include "KOTHBossSpawner.h"
AKingOfTheHillGameMode::AKingOfTheHillGameMode()
{
PrimaryActorTick.bCanEverTick = true;
// Lobby system starts the match
bDelayedStart = true;
PlayerControllerClass = AKOTHPlayerController::StaticClass();
DefaultPawnClass = ConstructorHelpers::FClassFinder<APawn>(TEXT("/Game/Assets/Blueprints/BP_DefaultPlayer")).Class;
SpectatorClass = ConstructorHelpers::FClassFinder<ASpectatorPawn>(TEXT("/Game/Assets/Blueprints/BP_SpectatorPawn")).Class;
GameStateClass = AKOTHGameState::StaticClass();
PlayerStateClass = ADefaultPlayerState::StaticClass();
maxGameScore = 60.0f;
m_currentTeamWithHill = -1;
}
void AKingOfTheHillGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
Super::InitGame(MapName, Options, ErrorMessage);
auto spawnerIt = TActorIterator<AKOTHBossSpawner>(GetWorld(), AKOTHBossSpawner::StaticClass());
if(!spawnerIt)
{
GERROR("No instance of AKOTHBossSpawner found in the level, you need one when using the KOTH game mode!");
}
else
{
bossSpawner = *spawnerIt;
}
}
void AKingOfTheHillGameMode::HandleMatchHasStarted()
{
AKOTHGameState* gameState = GetGameState<AKOTHGameState>();
gameState->maxGameScore = maxGameScore;
TArray<int32> teamsize = gameState->GetTeamSizes();
for (int i = 0; i < teamsize.Num(); i++)
{
FKOTHTeamState state;
state.active = 0;
state.score = 0.0f;
state.maxScore = maxGameScore;
state.containsMembers = teamsize[i] > 0;
gameState->kothTeams.Add(state);
}
Super::HandleMatchHasStarted();
}
void AKingOfTheHillGameMode::Tick(float deltaTime)
{
Super::Tick(deltaTime);
AKOTHGameState* gameState = GetGameState<AKOTHGameState>();
for (int i = 0; i < gameState->kothTeams.Num(); i++)
{
FKOTHTeamState& state = gameState->kothTeams[i];
if(state.score >= maxGameScore)
{
// Check overtime condition
if(bossSpawner)
{
// Check overtime conditions / convert index to team index [0-4] -> [1-5]
if(!bossSpawner->IsContested(i + 1) && bossSpawner->IsBossAlive())
{
WinGame(i);
}
else
{
gameState->overtime = true;
}
}
}
if(state.active > 0)
{
state.active--;
}
}
}
void AKingOfTheHillGameMode::WinGame(int team)
{
TArray<class ANetworkCharacter*> players(m_players);
// Set winning team in gamestate (replicated)
GetGameState<ADefaultGameState>()->gameWinningTeam = team;
EndMatch();
// Destroy all player pawns so that they respawn as spectators
for (int32 i = 0; i < players.Num(); i++)
{
if(IsValid(players[i]))
{
players[i]->Destroy(true);
}
}
//TPRINT("Boss was killed. (ADefaultGameMode) the team = " + team);
}
void AKingOfTheHillGameMode::AddScore(int team, float duration)
{
// team to array index [1-5] -> [0-4]
team -= 1;
AKOTHGameState* gameState = GetGameState<AKOTHGameState>();
if(team < 0 || team >= gameState->kothTeams.Num())
return;
gameState->kothTeams[team].score += duration;
gameState->kothTeams[team].active = 2;
if(m_currentTeamWithHill != team)
{
gameState->BroadcastOnHillCaptured(team);
m_currentTeamWithHill = team;
}
}

View File

@@ -0,0 +1,35 @@
// Project Lab - NHTV Igad
#pragma once
#include "GameState/DefaultGameMode.h"
#include "KOTHTeamState.h"
#include "KingOfTheHillGameMode.generated.h"
/**
*
*/
UCLASS(Config=Game)
class UNREALPROJECT_API AKingOfTheHillGameMode : public ADefaultGameMode
{
GENERATED_BODY()
public:
AKingOfTheHillGameMode();
virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) override;
virtual void HandleMatchHasStarted() override;
virtual void Tick(float DeltaTime) override;
void AddScore(int team, float duration);
void WinGame(int team);
UPROPERTY(Config)
float maxGameScore;
UPROPERTY(BlueprintReadOnly)
class AKOTHBossSpawner* bossSpawner;
private:
int32 m_currentTeamWithHill;
friend class AKOTHGameState;
};

View File

@@ -0,0 +1,26 @@
// Project Lab - NHTV Igad
#pragma once
#include "Blueprint/UserWidget.h"
#include "MapData.generated.h"
UCLASS(BlueprintType)
class UMapData : public UDataAsset
{
GENERATED_BODY()
public:
UMapData();
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Level")
FString friendlyName;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Level")
FString description;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Level",
meta = (ClampMin = "2", ClampMax = "4", UIMin = "2", UIMax = "4"))
int32 maxTeamCount;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Level")
UTexture2D* icon;
UPROPERTY(BlueprintReadOnly, Category = "Level")
FString pathToAsset;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "MapList.generated.h"
UCLASS(BlueprintType)
class UMapList : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "MapList")
TArray<UMapData*> mapList;
};

View File

@@ -0,0 +1,78 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MatchMaking.h"
#include "Online.h"
/*
const uint8* FUniqueNetIdMatchMaking::GetBytes() const
{
return (uint8*)&UniqueNetId;
}
int32 FUniqueNetIdMatchMaking::GetSize() const
{
return sizeof(uint64);
}
#if PLATFORM_SPECIFIC_WIN == 0
#pragma warning(push)
#pragma warning(disable:4996)
#include "steam/steam_api.h"
bool FUniqueNetIdMatchMaking::IsValid() const
{
return UniqueNetId != 0 && CSteamID(UniqueNetId).IsValid();
}
#pragma warning(pop)
#else
bool FUniqueNetIdMatchMaking::IsValid() const
{
return UniqueNetId != 0;
}
#endif
FString FUniqueNetIdMatchMaking::ToString() const
{
return FString::Printf(TEXT("%llu"), UniqueNetId);
}
FString FUniqueNetIdMatchMaking::ToDebugString() const
{
return ToString();
}
inline uint64 GetNetId(FNamedOnlineSession* session)
{
FOnlineSessionInfoMatchMaking* SessionInfo = (FOnlineSessionInfoMatchMaking*)(session->SessionInfo.Get());
FInternetAddrMatchMaking* addr = (FInternetAddrMatchMaking*)SessionInfo->P2PAddr.Get();
return addr->NetId.UniqueNetId;
}
inline uint32 GetChannelId(FNamedOnlineSession* session)
{
FOnlineSessionInfoMatchMaking* SessionInfo = (FOnlineSessionInfoMatchMaking*)(session->SessionInfo.Get());
FInternetAddrMatchMaking* addr = (FInternetAddrMatchMaking*)SessionInfo->P2PAddr.Get();
return addr->ChannelId;
}
MatchMaking::MatchMaking(FNamedOnlineSession* session) : IMatchMakingInterface(GetNetId(session), GetChannelId(session))
{
}
MatchMaking::~MatchMaking()
{
}
void MatchMaking::OnConnect()
{
JPRINT("Connected to matchmaking server");
}
void MatchMaking::OnDisconnect()
{
JERROR("Disconnected from matchmaking server");
}
void MatchMaking::OnForceJoinSession(NetID netID, ChannelID channelID)
{
}
*/

View File

@@ -0,0 +1,53 @@
// Project Lab - NHTV Igad
/*
#pragma once
#include "MatchMaking.hpp"
#include "Networking.h"
class FOnlineSessionInfoMatchMaking : public FOnlineSessionInfo
{
public:
virtual ~FOnlineSessionInfoMatchMaking() {}
enum PH {};
PH SessionType;
TSharedPtr<class FInternetAddr> HostAddr;
TSharedPtr<class FInternetAddr> P2PAddr;
};
class FUniqueNetIdMatchMaking : public FUniqueNetId
{
public:
~FUniqueNetIdMatchMaking() {}
uint64 UniqueNetId;
virtual const uint8* GetBytes() const override;
virtual int32 GetSize() const override;
virtual bool IsValid() const override;
virtual FString ToString() const override;
virtual FString ToDebugString() const override;
};
class FInternetAddrMatchMaking : public FInternetAddr
{
public:
virtual ~FInternetAddrMatchMaking() {}
FUniqueNetIdMatchMaking NetId;
int32 ChannelId;
};
class UNREALPROJECT_API MatchMaking : public IMatchMakingInterface
{
public:
MatchMaking(class FNamedOnlineSession* session);
~MatchMaking();
virtual void OnConnect() override;
virtual void OnDisconnect() override;
virtual void OnForceJoinSession(NetID netID, ChannelID channelID) override;
};
*/

View File

@@ -0,0 +1,332 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "DefaultGameInstance.h"
#include "MenuController.h"
#include "SplashScreen.h"
#include "LobbyMenu.h"
#include "CharacterSettings.h"
#include "StartPromptScreen.h"
#include "PlayerStateBase.h"
#include "GameStateBase.h"
#include "SessionManager.h"
#include "TransitionScreen.h"
#include "ScreenOverlay.h"
#include "SkillTreeWidget.h"
#include "DefaultPlayerController.h"
#include "MenuScreen.h"
#include "MenuGameMode.h"
static UClass* menuWidgetClass = nullptr;
static UClass* lobbyWidgetClass = nullptr;
static UClass* splashScreenClass = nullptr;
static UClass* startScreenClass = nullptr;
static UClass* skillTreeClass = nullptr;
static TSubclassOf<UMenuPanel> mainSubMenuClass;
AMenuController::AMenuController(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
menuWidgetClass = ConstructorHelpers::FClassFinder<UMenuScreen>(TEXT("/Game/Assets/GUI/WEEGEE_Menu")).Class;
lobbyWidgetClass = ConstructorHelpers::FClassFinder<ULobbyMenu>(TEXT("/Game/Assets/GUI/WEEGEE_Lobby")).Class;
splashScreenClass = ConstructorHelpers::FClassFinder<USplashScreen>(TEXT("/Game/Assets/GUI/WEEGEE_Splash")).Class;
startScreenClass = ConstructorHelpers::FClassFinder<UStartPromptScreen>(TEXT("/Game/Assets/GUI/WEEGEE_StartPrompt")).Class;
skillTreeClass = ConstructorHelpers::FClassFinder<USkillTreeWidget>(TEXT("/Game/Assets/GUI/WEEGEE_SkillTree")).Class;
mainSubMenuClass = ConstructorHelpers::FClassFinder<UMenuPanel>(TEXT("/Game/Assets/GUI/Menus/WEEGEE_MainSubMenu")).Class;
this->bShowMouseCursor = true;
m_inLobby = false;
PrimaryActorTick.bCanEverTick = true;
}
void AMenuController::BeginPlay()
{
Super::BeginPlay();
if(IsLocalController())
{
// Create skill tree widget
skillTree = CreateWidget<USkillTreeWidget>(GetWorld(), skillTreeClass);
skillTree->AddToViewport(5);
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
UWorld* world = GetWorld();
bool showSplash = !inst->splashScreenShown;
if(world->WorldType == EWorldType::PIE)
showSplash = false;
if(inst->sessionManager->HasNetConnection())
showSplash = false;
if(showSplash)
{
m_splash = CreateWidget<USplashScreen>(this, splashScreenClass);
m_splash->onDone.AddDynamic(this, &AMenuController::m_OnSplashScreenEnded);
check(m_splash);
m_splash->AddToViewport();
}
else
{
m_OnStartPromptClosed();
}
inst->splashScreenShown = true;
inst->sessionManager->onAcceptInvite.AddUObject(this, &AMenuController::m_OnInviteAccepted);
}
}
void AMenuController::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
inst->sessionManager->onAcceptInvite.RemoveAll(this);
}
void AMenuController::NotifyLoadedWorld(FName WorldPackageName, bool bFinalDest)
{
Super::NotifyLoadedWorld(WorldPackageName, bFinalDest);
if(bFinalDest)
{
GPRINT("Final level loaded for player controller " + GetName());
if(transitionScreen)
{
// Hide transition screen
transitionScreen->OnHide();
}
}
}
void AMenuController::Destroyed()
{
GPRINT("Menu controller destroyed " + GetName());
Super::Destroyed();
}
void AMenuController::PreClientTravel(const FString& PendingURL, ETravelType TravelType, bool bIsSeamlessTravel)
{
if(bIsSeamlessTravel)
{
// Hide lobby UI
if(lobbyMenu)
lobbyMenu->OnHide();
// Close all the menu
if(m_splash)
m_splash->RemoveFromViewport();
if(m_startScreen)
m_startScreen->RemoveFromViewport();
if(lobbyMenu)
lobbyMenu->RemoveFromViewport();
if(mainMenu)
mainMenu->RemoveFromViewport();
if(skillTree)
skillTree->RemoveFromViewport();
mainMenu = nullptr;
lobbyMenu = nullptr;
skillTree = nullptr;
m_startScreen = nullptr;
m_splash = nullptr;
// Show transition screen
transitionScreen->OnShow();
GPRINT("Seamless travel on player " + GetName() + " to \"" + PendingURL + "\"");
}
else
{
GPRINT("Regular travel on player to " + GetName() + " to \"" + PendingURL + "\"");
}
Super::PreClientTravel(PendingURL, TravelType, bIsSeamlessTravel);
}
void AMenuController::SeamlessTravelFrom(class APlayerController* OldPC)
{
Super::SeamlessTravelFrom(OldPC);
GPRINT("Seamless travel to menu [" + GetName() + "]<-[" + OldPC->GetName() + "]");
}
void AMenuController::SeamlessTravelTo(class APlayerController* NewPC)
{
Super::SeamlessTravelTo(NewPC);
GPRINT("Seamless travel to game level [" + GetName() + "]->[" + NewPC->GetName() + "]");
APlayerStateBase* pcbn = Cast<APlayerStateBase>(NewPC->PlayerState);
APlayerStateBase* pcbo = GetPlayerState<APlayerStateBase>();
check(pcbn && pcbo);
pcbn->Transfer(pcbo);
ADefaultPlayerController* newDPC = Cast<ADefaultPlayerController>(NewPC);
if(newDPC)
{
newDPC->setupState = setupState;
}
}
void AMenuController::OnLearnSkill(class UBaseSkillObject* object)
{
}
void AMenuController::OnUnlearnSkill(class UBaseSkillObject* object)
{
}
void AMenuController::OnEnterLobby()
{
if(m_inLobby)
return;
if(mainMenu && lobbyMenu)
{
mainMenu->OnHide();
lobbyMenu->OnShow();
lobbyMenu->OnLobbyEnter();
m_inLobby = true;
OnRep_PlayerState();
}
else
{
GERROR("wat");
}
// Set Default character slot
/// TODO: Add selector widget
//UCharacterSettings* settings = Cast<UDefaultGameInstance>(GetGameInstance())->GetCharacterSettings();
//if(settings->characterSaves.Num() > 0)
//{
// const FCharacterSave& save = settings->characterSaves[0];
// FPlayerSetupState pss;
// pss.customizations = save.characterCustomization;
// pss.skills = save.skillTreeState;
// pss.characterClass = save.characterClass;
// SetSetupState(pss);
//}
}
/// TODO, validate skill tree / customization here
bool AMenuController::SetSetupState_Validate(const FPlayerSetupState& state)
{
return true;
}
void AMenuController::SetSetupState_Implementation(const FPlayerSetupState& state)
{
setupState = state;
// Resync the lobby
UWorld* const world = GetWorld();
if (IsValid(world))
{
AGameStateBase* const gameState = Cast<AGameStateBase>(GetWorld()->GetGameState());
if (IsValid(gameState))
gameState->OnPlayerStateChange.Broadcast(this);
}
}
void AMenuController::OnRep_PlayerState()
{
// Request a team if we're in a lobby
if(m_inLobby)
{
m_AutoRequestTeam();
}
// Send to the server that we got our player state
m_OnClientInitialized();
}
void AMenuController::SwitchToMenu()
{
mainMenu->OnShow();
lobbyMenu->OnHide();
m_inLobby = false;
}
bool AMenuController::m_OnClientInitialized_Validate()
{
return true;
}
void AMenuController::m_OnClientInitialized_Implementation()
{
AMenuGameMode* gm = Cast<AMenuGameMode>(GetWorld()->GetAuthGameMode());
if(gm)
{
gm->onPlayerJoined.Broadcast(this);
}
}
void AMenuController::m_OnInviteAccepted(bool success, const FOnlineSessionSearchResult& result)
{
if(success)
{
GPRINT("Accepted an invite");
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
inst->sessionManager->onJoinSessionComplete.AddUObject(this, &AMenuController::m_OnSessionJoined);
inst->sessionManager->JoinSession(result);
}
}
void AMenuController::m_OnSessionJoined(int32 result)
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
inst->sessionManager->onJoinSessionComplete.RemoveAll(this);
if(result == EOnJoinSessionCompleteResult::Success)
{
ClientTravel(inst->sessionManager->joinConnectionString, ETravelType::TRAVEL_Absolute);
}
else
{
TArray<FString> options;
options.Add("OK");
overlay->ShowMessageBox("Error", FString("Failed to accept invite (") + FString::FromInt(result) + ")", options);
GERROR("Failed to join session invite");
SwitchToMenu();
}
}
void AMenuController::m_AutoRequestTeam()
{
UWorld* world = GetWorld();
APlayerStateBase* state = GetPlayerState<APlayerStateBase>();
if(state)
{
state->AutoAssignTeam();
if(world->IsPlayInEditor())
state->SetReadyState(true);
//GetWorld()->GetTimerManager().ClearTimer(m_teamRequestTimer);
}
}
void AMenuController::m_OnSplashScreenEnded()
{
m_splash = nullptr;
// Create start prompt
m_startScreen = CreateWidget<UStartPromptScreen>(this, startScreenClass);
check(m_startScreen);
m_startScreen->onClosed.AddUObject(this, &AMenuController::m_OnStartPromptClosed);
m_startScreen->AddToViewport();
}
void AMenuController::m_OnStartPromptClosed()
{
m_startScreen = nullptr;
// Open main menu
lobbyMenu = CreateWidget<ULobbyMenu>(this, lobbyWidgetClass);
lobbyMenu->AddToViewport();
mainMenu = CreateWidget<UMenuScreen>(this, menuWidgetClass);
mainMenu->AddToViewport();
mainMenu->OnShow();
mainMenu->OpenScreenMenu(mainSubMenuClass);
// Enter lobby when initially connected
UWorld* world = GetWorld();
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(world->GetGameInstance());
if(inst->sessionManager->HasNetConnection())
{
OnEnterLobby();
}
}
bool AMenuController::WorldIsPIE() const
{
UWorld* const world = GetWorld();
if (!IsValid(world)) return false;
return world->WorldType == EWorldType::PIE;
}

View File

@@ -0,0 +1,71 @@
// Project Lab - NHTV Igad
#pragma once
#include "GameFramework/PlayerController.h"
#include "PlayerControllerBase.h"
#include "MenuController.generated.h"
UCLASS()
class UNREALPROJECT_API AMenuController : public APlayerControllerBase
{
GENERATED_BODY()
public:
AMenuController(const FObjectInitializer& ObjectInitializer);
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void NotifyLoadedWorld(FName WorldPackageName, bool bFinalDest) override;
virtual void Destroyed() override;
virtual void PreClientTravel(const FString& PendingURL, ETravelType TravelType, bool bIsSeamlessTravel) override;
virtual void SeamlessTravelFrom(class APlayerController* OldPC) override;
virtual void SeamlessTravelTo(class APlayerController* NewPC) override;
virtual void OnLearnSkill(class UBaseSkillObject* object);
virtual void OnUnlearnSkill(class UBaseSkillObject* object);
virtual void OnEnterLobby();
UFUNCTION(Server, WithValidation, Reliable)
virtual void SetSetupState(const FPlayerSetupState& state);
virtual void OnRep_PlayerState() override;
void SwitchToMenu();
UPROPERTY(BlueprintReadOnly)
class UMenuScreen* mainMenu;
UPROPERTY(BlueprintReadOnly)
class ULobbyMenu* lobbyMenu;
UPROPERTY(BlueprintReadOnly)
class USkillTreeWidget* skillTree;
UFUNCTION(BlueprintCallable, Category = "PIE")
bool WorldIsPIE() const;
private:
UFUNCTION(Server, WithValidation, Reliable)
void m_OnClientInitialized();
void m_OnInviteAccepted(bool success, const FOnlineSessionSearchResult& result);
UFUNCTION()
void m_OnSessionJoined(int32 result);
void m_AutoRequestTeam();
FTimerHandle m_teamRequestTimer;
// Called when the splash screens end
UFUNCTION()
void m_OnSplashScreenEnded();
// Called when the start prompt closes
UFUNCTION()
void m_OnStartPromptClosed();
UPROPERTY()
class USplashScreen* m_splash;
UPROPERTY()
class UStartPromptScreen* m_startScreen;
// True if we are currently in the lobby screen, this can be checked if it is set before the menu widgets are created
bool m_inLobby;
};

View File

@@ -0,0 +1,160 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MenuGameMode.h"
#include "MenuController.h"
#include "DefaultGameInstance.h"
#include "MapData.h"
#include "GameStateBase.h"
#include "PlayerStateBase.h"
#include "PlayerControllerBase.h"
AMenuGameMode::AMenuGameMode()
{
static ConstructorHelpers::FClassFinder<APawn> MenuPawnCF(TEXT("/Game/Assets/Blueprints/BP_MenuPawn"));
PlayerControllerClass = AMenuController::StaticClass();
DefaultPawnClass = MenuPawnCF.Class;
GameStateClass = AGameStateBase::StaticClass();
PlayerStateClass = APlayerStateBase::StaticClass();
bUseSeamlessTravel = true;
}
void AMenuGameMode::PreInitializeComponents()
{
Super::PreInitializeComponents();
GWPRINT(L"Menu arguments = " + OptionsString);
AGameStateBase* gameState = GetGameState<AGameStateBase>();
check(gameState);
// Set initial map
UWorld* world = GetWorld();
if(world)
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(world->GetGameInstance());
if(inst->maps.Num() > 0)
{
m_mapData = inst->maps[0];
m_mapPath = gameState->mapPath = m_mapData->pathToAsset;
GPRINT("Setting default map to \"" + m_mapData->friendlyName + "\" [" + m_mapPath + "]");
gameState->m_LoadActiveMapInfo();
}
}
}
void AMenuGameMode::BeginPlay()
{
Super::BeginPlay();
UWorld* world = GetWorld();
m_lobbyState = world->SpawnActor<ALobbyState>(ALobbyState::StaticClass());
}
void AMenuGameMode::GetSeamlessTravelActorList(bool bToEntry, TArray<AActor*>& ActorList)
{
Super::GetSeamlessTravelActorList(bToEntry, ActorList);
ActorList.Add(m_lobbyState);
}
APlayerController* AMenuGameMode::Login(class UPlayer* NewPlayer, ENetRole InRemoteRole, const FString& Portal, const FString& Options, const TSharedPtr<const FUniqueNetId>& UniqueId, FString& ErrorMessage)
{
// Aquire the player controller by calling the super function
APlayerController* player = Super::Login(NewPlayer, InRemoteRole, Portal, Options, UniqueId, ErrorMessage);
APlayerControllerBase* pc = Cast<APlayerControllerBase>(player);
// Update persona
APlayerStateBase* state = Cast<APlayerStateBase>(pc->PlayerState);
FString name = "<unknown>";
if(state)
{
state->UpdatePersona();
name = state->nickname;
}
GWARNING("Player " + name + " logged in [options=" + Options + "][portal=" + Portal + "]");
return player;
}
FString AMenuGameMode::InitNewPlayer(class APlayerController* NewPlayerController, const TSharedPtr<const FUniqueNetId>& UniqueId, const FString& Options, const FString& Portal /* = TEXT("") */)
{
FString r = Super::InitNewPlayer(NewPlayerController, UniqueId, Options, Portal);
return r;
}
void AMenuGameMode::Logout(AController* existing)
{
// Get name of disconnected player
APlayerStateBase* state = Cast<APlayerStateBase>(existing->PlayerState);
FString name = "<unknown>";
if(state)
name = state->nickname;
GWARNING("Player " + name + " left the game");
// Deregister the player
APlayerControllerBase* pc = Cast<APlayerControllerBase>(existing);
Cast<AGameStateBase>(GameState)->DeregisterPlayer(pc);
Super::Logout(existing);
}
void AMenuGameMode::PostLogin(APlayerController* NewPlayer)
{
Super::PostLogin(NewPlayer);
AGameStateBase* gs = GetGameState<AGameStateBase>();
APlayerControllerBase* pc = Cast<APlayerControllerBase>(NewPlayer);
// Register the player
Cast<AGameStateBase>(GameState)->RegisterPlayer(pc);
}
void AMenuGameMode::SetMap(FString mapPath)
{
m_mapPath = mapPath;
Cast<AGameStateBase>(GameState)->mapPath = m_mapPath;
}
bool AMenuGameMode::StartGame()
{
UWorld* world = GetWorld();
if(!world)
{
GERROR("No world, cant start game");
return false;
}
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(world->GetGameInstance());
m_mapData = inst->GetMapData(m_mapPath);
if(!m_mapData)
{
GERROR("Can't start game, no map data found for map [" + m_mapPath + "]");
return false;
}
// Inject all the players into the lobby state
AGameStateBase* state = Cast<AGameStateBase>(GameState);
m_lobbyState->players.SetNum(0);
for(APlayerState* ps : state->PlayerArray)
{
APlayerStateBase* psb = Cast<APlayerStateBase>(ps);
if(psb)
{
FLobbyStatePlayer lsp;
lsp.netID = psb->UniqueId.GetUniqueNetId();
lsp.team = psb->GetTeam();
m_lobbyState->players.Add(lsp);
}
}
GWARNING("Performing server travel to " + m_mapPath);
world->ServerTravel(m_mapPath, true);
return true;
}
void AMenuGameMode::AssignMapData(class UMapData* mapData)
{
if(!mapData)
{
GERROR("Invalid map passed to AssignMapData");
return;
}
AGameStateBase* gameState = GetGameState<AGameStateBase>();
gameState->mapPath = m_mapPath = mapData->pathToAsset;
gameState->m_LoadActiveMapInfo();
}

View File

@@ -0,0 +1,42 @@
// Project Lab - NHTV Igad
#pragma once
#include "GameFramework/GameMode.h"
#include "MenuGameMode.generated.h"
class AGameNameCharacter;
class AGameNamePlayerController;
class AGameNameSpectator;
UCLASS(minimalapi)
class AMenuGameMode : public AGameMode
{
GENERATED_BODY()
friend class AGameStateBase;
friend class APlayerStateBase;
public:
AMenuGameMode();
virtual void PreInitializeComponents() override;
virtual void BeginPlay() override;
virtual void GetSeamlessTravelActorList(bool bToEntry, TArray<AActor*>& ActorList) override;
virtual APlayerController* Login(class UPlayer* NewPlayer, ENetRole InRemoteRole, const FString& Portal, const FString& Options, const TSharedPtr<const FUniqueNetId>& UniqueId, FString& ErrorMessage) override;
virtual FString InitNewPlayer(class APlayerController* NewPlayerController, const TSharedPtr<const FUniqueNetId>& UniqueId, const FString& Options, const FString& Portal /* = TEXT("") */) override;
virtual void Logout(AController* existing) override;
virtual void PostLogin(APlayerController* NewPlayer) override;
// Player joined callback
DECLARE_MULTICAST_DELEGATE_OneParam(FOnPlayerJoined, APlayerController*)
FOnPlayerJoined onPlayerJoined;
void SetMap(FString mapPath);
bool StartGame();
void AssignMapData(class UMapData* mapData);
private:
UPROPERTY()
class ALobbyState* m_lobbyState;
FString m_mapPath;
class UMapData* m_mapData;
};

View File

@@ -0,0 +1,14 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "MiniMapVolume.h"
AMiniMapVolume::AMiniMapVolume()
{
area = CreateDefaultSubobject<UBoxComponent>(TEXT("Area"));
area->SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName);
area->bGenerateOverlapEvents = false;
area->Mobility = EComponentMobility::Static;
RootComponent = area;
}

View File

@@ -0,0 +1,18 @@
// Project Lab - NHTV Igad
#pragma once
#include "GameFramework/Actor.h"
#include "MiniMapVolume.generated.h"
UCLASS()
class UNREALPROJECT_API AMiniMapVolume : public AActor
{
GENERATED_BODY()
public:
AMiniMapVolume();
UPROPERTY(EditAnywhere, Category = "Area")
class UBoxComponent* area;
};

View File

@@ -0,0 +1,397 @@
// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "DefaultGameInstance.h"
#include "PlayerControllerBase.h"
#include "ScreenOverlay.h"
#include "Prefs.h"
#include "TransitionScreen.h"
#include "SessionManager.h"
#include "Engine.h"
#if PLATFORM_SPECIFIC_WIN == 0
#include "InputManager.hpp"
using namespace Input;
#endif
UClass* overlayWidgetClass;
UClass* transitionWidgetClass;
APlayerControllerBase::APlayerControllerBase(const FObjectInitializer& init)
: Super(init)
{
overlayWidgetClass = ConstructorHelpers::FClassFinder<UScreenOverlay>(TEXT("/Game/Assets/GUI/WEEGEE_Overlay")).Class;
transitionWidgetClass = ConstructorHelpers::FClassFinder<UTransitionScreen>(TEXT("/Game/Assets/GUI/WEEGEE_Transition")).Class;
}
void APlayerControllerBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(APlayerControllerBase, setupState);
}
void APlayerControllerBase::BeginPlay()
{
UDefaultGameInstance* game = Cast<UDefaultGameInstance>(GetGameInstance());
if(IsLocalController())
{
transitionScreen = CreateWidget<UTransitionScreen>(GetWorld(), transitionWidgetClass);
transitionScreen->AddToViewport(800);
overlay = CreateWidget<UScreenOverlay>(GetWorld(), overlayWidgetClass);
overlay->AddToViewport(1000);
AddMenuInputItem(overlay, 10);
check(overlay);
UWorld* world = GetWorld();
AGameMode* gameMode = nullptr;
if(world)
gameMode = GetWorld()->GetAuthGameMode();
//if(!game->errorMessage.IsEmpty())
//{
// game->DestroySession(false);
// game->ShowNetworkError(FString("Disconnected from game, ") + game->errorMessage);
// game->errorMessage.Empty();
//}
//else if(gameMode && UGameplayStatics::HasOption(gameMode->OptionsString, "closed"))
//{
// game->DestroySession(false);
// game->ShowNetworkError(FString("Disconnected from game"));
//}
}
Super::BeginPlay();
}
void APlayerControllerBase::SetupInputComponent()
{
if (GetWorld()->WorldType == EWorldType::Preview)
return;
Super::SetupInputComponent();
#if PLATFORM_SPECIFIC_WIN == 0
GPRINT("Binding Menu Input manager bindings on " + GetName());
InputManager* inputManager = InputManager::GetInstance();
UPrefs* prefs = Cast<UDefaultGameInstance>(GetGameInstance())->GetPrefs();
// Connect initial controller
inputManager->SearchForJoystick();
ScanForJoysticks();
int32 connect = 0;
for(int32 i = 0; i < m_recentJoystickScan.Num(); i++)
{
if(m_recentJoystickScan[i] == prefs->useControllerName)
{
connect = i;
break;
}
}
SelectJoystick(connect);
inputManager->RegisterCallback(IJP_FACEDOWN, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuConfirm);
inputManager->RegisterCallback(IJP_FACERIGHT,IME_PRESSED, this, &APlayerControllerBase::m_OnMenuBack);
inputManager->RegisterCallback(IJP_START, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuStart);
inputManager->RegisterCallback(IJP_DPADUP, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuUp);
inputManager->RegisterCallback(IJP_DPADDOWN, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuDown);
inputManager->RegisterCallback(IJP_DPADLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuLeft);
inputManager->RegisterCallback(IJP_DPADRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuRight);
inputManager->RegisterCallback(IJP_TRIGGERLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuLeftPressed);
inputManager->RegisterCallback(IJP_TRIGGERRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuRightPressed);
inputManager->RegisterCallback(IJP_TRIGGERLEFT, IME_RELEASED, this, &APlayerControllerBase::m_OnMenuLeftReleased);
inputManager->RegisterCallback(IJP_TRIGGERRIGHT, IME_RELEASED, this, &APlayerControllerBase::m_OnMenuRightReleased);
inputManager->RegisterCallback(IJP_SHOULDERLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuShoulderLeft);
inputManager->RegisterCallback(IJP_SHOULDERRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuShoulderRight);
inputManager->RegisterCallback(IJP_START, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOptionsButton);
inputManager->RegisterCallback(IJP_FACELEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOpt1);
inputManager->RegisterCallback(IJP_FACEUP, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOpt2);
#endif
InputComponent->BindAction("MenuSelect", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuConfirm);
InputComponent->BindAction("MenuBack", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuBack);
InputComponent->BindAction("MenuStart", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuStart);
InputComponent->BindAction("MenuUp", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuUp);
InputComponent->BindAction("MenuDown", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuDown);
InputComponent->BindAction("MenuLeft", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuLeftPressed);
InputComponent->BindAction("MenuRight", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuRightPressed);
InputComponent->BindAction("MenuLeftUp", EInputEvent::IE_Released, this, &APlayerControllerBase::m_OnMenuLeftReleased);
InputComponent->BindAction("MenuRightUp", EInputEvent::IE_Released, this, &APlayerControllerBase::m_OnMenuRightReleased);
InputComponent->BindAction("MenuOpt1", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuOpt1);
InputComponent->BindAction("MenuOpt2", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuOpt2);
InputComponent->BindAction("MenuShoulderLeft", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuShoulderLeft);
InputComponent->BindAction("MenuShoulderRight", EInputEvent::IE_Pressed, this, &APlayerControllerBase::m_OnMenuShoulderRight);
}
void APlayerControllerBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if(IsLocalController())
{
if(transitionScreen)
{
transitionScreen->OnHide();
transitionScreen->RemoveFromViewport();
}
if(overlay)
{
overlay->RemoveFromViewport();
}
}
Super::EndPlay(EndPlayReason);
}
void APlayerControllerBase::PlayerTick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);
if(m_repeatLeft)
OnMenuAction(EMenuActionBinding::Repeat_Left);
if(m_repeatRight)
OnMenuAction(EMenuActionBinding::Repeat_Right);
}
void APlayerControllerBase::Destroyed()
{
Super::Destroyed();
#if PLATFORM_SPECIFIC_WIN == 0
GPRINT("Unbinding Menu Input manager bindings on " + GetName());
InputManager* inputManager = InputManager::GetInstance();
inputManager->DeregisterCallback(IJP_FACEDOWN, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuConfirm);
inputManager->DeregisterCallback(IJP_FACERIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuBack);
inputManager->DeregisterCallback(IJP_START, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuStart);
inputManager->DeregisterCallback(IJP_DPADUP, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuUp);
inputManager->DeregisterCallback(IJP_DPADDOWN, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuDown);
inputManager->DeregisterCallback(IJP_DPADLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuLeft);
inputManager->DeregisterCallback(IJP_DPADRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuRight);
inputManager->DeregisterCallback(IJP_TRIGGERLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuLeftPressed);
inputManager->DeregisterCallback(IJP_TRIGGERRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuRightPressed);
inputManager->DeregisterCallback(IJP_TRIGGERLEFT, IME_RELEASED, this, &APlayerControllerBase::m_OnMenuLeftReleased);
inputManager->DeregisterCallback(IJP_TRIGGERRIGHT, IME_RELEASED, this, &APlayerControllerBase::m_OnMenuRightReleased);
inputManager->DeregisterCallback(IJP_SHOULDERLEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuShoulderLeft);
inputManager->DeregisterCallback(IJP_SHOULDERRIGHT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuShoulderRight);
inputManager->DeregisterCallback(IJP_START, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOptionsButton);
inputManager->DeregisterCallback(IJP_FACELEFT, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOpt1);
inputManager->DeregisterCallback(IJP_FACEUP, IME_PRESSED, this, &APlayerControllerBase::m_OnMenuOpt2);
#endif
}
void APlayerControllerBase::OnLearnSkill(class UBaseSkillObject* object)
{
}
void APlayerControllerBase::OnUnlearnSkill(class UBaseSkillObject* object)
{
}
void APlayerControllerBase::AddMenuInputItem(class UMenuItemBase* item, int32 priority)
{
if(!item)
return;
if(m_menuItemStack.Contains(item))
return;
item->priority = priority;
int32 at = m_menuItemStack.Num();
if(at == -1)
{
m_menuItemStack.Add(item);
return;
}
while(at > 0 && m_menuItemStack[at-1]->priority > priority)
{
at--;
}
m_menuItemStack.Insert(item, at);
}
void APlayerControllerBase::RemoveMenuInputItem(class UMenuItemBase* item)
{
m_menuItemStack.Remove(item);
}
EInputMethod APlayerControllerBase::GetInputMethod() const
{
#if PLATFORM_SPECIFIC_WIN == 0
InputManager* inputManager = InputManager::GetInstance();
switch(inputManager->GetConnectedType())
{
default:
case InputDeviceType::IDT_NONE:
case InputDeviceType::IDT_NOTDEFINED:
return EInputMethod::PC;
break;
case InputDeviceType::IDT_XBOX:
return EInputMethod::X360;
break;
case InputDeviceType::IDT_DS4:
return EInputMethod::DS4;
break;
}
#else
return EInputMethod::DS4;
#endif
}
TArray<FString> APlayerControllerBase::ScanForJoysticks()
{
#if PLATFORM_SPECIFIC_WIN == 0
m_recentJoystickScan.SetNum(0);
InputManager* inputManager = InputManager::GetInstance();
inputManager->SearchForJoystick();
for(std::wstring s : inputManager->GetJoystickNames())
{
m_recentJoystickScan.Add(FString(s.c_str()));
}
#endif
return m_recentJoystickScan;
}
void APlayerControllerBase::SelectJoystick(int32 joystick)
{
#if PLATFORM_SPECIFIC_WIN == 0
if(joystick >= m_recentJoystickScan.Num())
return;
InputManager* inputManager = InputManager::GetInstance();
GWPRINT(L"Connecting joystick [" + joystick + L"] " + m_recentJoystickScan[joystick]);
inputManager->ConnectJoystickByIndex(joystick);
#endif
}
void APlayerControllerBase::PreClientTravel(const FString& PendingURL, ETravelType TravelType, bool bIsSeamlessTravel)
{
GPRINT("PRE TRAVEL: " + PendingURL);
Super::PreClientTravel(PendingURL, TravelType, bIsSeamlessTravel);
}
void APlayerControllerBase::OnEnterLobby()
{
GWARNING("PC: Entering lobby " + GetName());
}
void APlayerControllerBase::ReturnToMenu()
{
GWARNING("PC: Leaving game " + GetName());
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
inst->sessionManager->DestroySession();
inst->sessionManager->CloseNetConnections();
// Load menu level
//FString error;
//FWorldContext* worldContext = GEngine->GetWorldContextFromWorld(GetWorld());
//FURL target = FURL(*inst->menuLevelPath);
//int32 ret = GEngine->Browse(*worldContext, target, error);
//if(ret == EBrowseReturnVal::Failure)
//{
// GERROR("Failed to load menu level: " + error);
//}
GetWorld()->ServerTravel(inst->menuLevelPath, true);
}
void APlayerControllerBase::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
GPRINT("[" + GetName() + "]Got player state");
}
void APlayerControllerBase::OnRep_Pawn()
{
Super::OnRep_Pawn();
if(GetPawn())
{
GPRINT("[" + GetName() + "]Got pawn: " + GetPawn()->GetName());
}
else
{
GPRINT("[" + GetName() + "]Unpossessed pawn");
}
}
void APlayerControllerBase::EnterLobby_Implementation()
{
OnEnterLobby();
}
void APlayerControllerBase::OnRep_Setup()
{
GPRINT("GOT SETUP STATE ON " + GetName());
m_onSetupStateSet = true;
}
void APlayerControllerBase::m_OnMenuConfirm()
{
OnMenuAction(EMenuActionBinding::Confirm);
}
void APlayerControllerBase::m_OnMenuBack()
{
OnMenuAction(EMenuActionBinding::Back);
}
void APlayerControllerBase::m_OnMenuStart()
{
OnMenuAction(EMenuActionBinding::Start);
}
void APlayerControllerBase::m_OnMenuUp()
{
OnMenuAction(EMenuActionBinding::Up);
}
void APlayerControllerBase::m_OnMenuDown()
{
OnMenuAction(EMenuActionBinding::Down);
}
void APlayerControllerBase::m_OnMenuLeft()
{
OnMenuAction(EMenuActionBinding::Left);
}
void APlayerControllerBase::m_OnMenuRight()
{
OnMenuAction(EMenuActionBinding::Right);
}
void APlayerControllerBase::m_OnMenuOpt1()
{
OnMenuAction(EMenuActionBinding::Opt1);
}
void APlayerControllerBase::m_OnMenuOpt2()
{
OnMenuAction(EMenuActionBinding::Opt2);
}
void APlayerControllerBase::m_OnMenuLeftPressed()
{
m_repeatLeft = true;
m_OnMenuLeft();
OnMenuAction(EMenuActionBinding::Repeat_Left);
OnMenuAction(EMenuActionBinding::Trigger_Left);
}
void APlayerControllerBase::m_OnMenuRightPressed()
{
m_repeatRight = true;
m_OnMenuRight();
OnMenuAction(EMenuActionBinding::Repeat_Right);
OnMenuAction(EMenuActionBinding::Trigger_Right);
}
void APlayerControllerBase::m_OnMenuLeftReleased()
{
m_repeatLeft = false;
}
void APlayerControllerBase::m_OnMenuRightReleased()
{
m_repeatRight = false;
}
void APlayerControllerBase::m_OnMenuShoulderLeft()
{
OnMenuAction(EMenuActionBinding::Shoulder_Left);
}
void APlayerControllerBase::m_OnMenuShoulderRight()
{
OnMenuAction(EMenuActionBinding::Shoulder_Right);
}
void APlayerControllerBase::m_OnMenuOptionsButton()
{
OnMenuAction(EMenuActionBinding::Options);
}
void APlayerControllerBase::OnMenuAction(EMenuActionBinding action)
{
m_menuItemStack.Remove(nullptr);
for(int32 i = m_menuItemStack.Num(); i > 0;)
{
i--;
m_menuItemStack[i]->NativeOnMenuAction(action);
if(m_menuItemStack[i]->blockInput)
break;
}
}

View File

@@ -0,0 +1,122 @@
// Project Lab - NHTV Igad
#pragma once
#include "MenuAction.h"
#include "GameFramework/PlayerController.h"
#include "PlayerSetupState.h"
#include "PlayerControllerBase.generated.h"
// Input method for local players
UENUM(BlueprintType)
enum class EInputMethod : uint8
{
PC,
X360,
DS4
};
/**
*
*/
UCLASS()
class UNREALPROJECT_API APlayerControllerBase : public APlayerController
{
GENERATED_BODY()
public:
APlayerControllerBase(const FObjectInitializer& init);
virtual void BeginPlay() override;
virtual void SetupInputComponent() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
virtual void PlayerTick(float DeltaTime) override;
virtual void Destroyed() override;
virtual void OnLearnSkill(class UBaseSkillObject* object);
virtual void OnUnlearnSkill(class UBaseSkillObject* object);
UFUNCTION(BlueprintCallable, Category = "MenuStack")
void AddMenuInputItem(class UMenuItemBase* item, int32 priority = 0);
UFUNCTION(BlueprintCallable, Category = "MenuStack")
void RemoveMenuInputItem(class UMenuItemBase* item);
UFUNCTION(BlueprintCallable, Category = "Input")
EInputMethod GetInputMethod() const;
UFUNCTION(BlueprintCallable, Category = "Input")
TArray<FString> ScanForJoysticks();
UFUNCTION(BlueprintCallable, Category = "Input")
void SelectJoystick(int32 joystick);
virtual void PreClientTravel(const FString& PendingURL, ETravelType TravelType, bool bIsSeamlessTravel);
// Called from the game when we should move to the lobby
UFUNCTION(Client, Reliable)
void EnterLobby();
virtual void OnEnterLobby();
// Call to return to the main menu / leave current game
UFUNCTION(BlueprintCallable, Category = "Game")
void ReturnToMenu();
// Replication callbacks
virtual void OnRep_PlayerState() override;
virtual void OnRep_Pawn() override;
// Template state acquisition functions
template<typename T> T* GetPlayerState()
{
return Cast<T>(PlayerState);
}
template<typename T> T* GetGameState()
{
if(!GetWorld())
return nullptr;
return Cast<T>(GetWorld()->GameState);
}
// Called when the setupState is replicated
UFUNCTION()
virtual void OnRep_Setup();
// Lobby Character selection
UPROPERTY(replicatedUsing=OnRep_Setup)
FPlayerSetupState setupState;
UPROPERTY(BlueprintReadOnly, Category = "Overlay")
class UScreenOverlay* overlay;
UPROPERTY(BlueprintReadOnly, Category = "Overlay")
class UTransitionScreen* transitionScreen;
protected:
virtual void OnMenuAction(EMenuActionBinding action);
private:
void m_OnMenuConfirm();
void m_OnMenuBack();
void m_OnMenuStart();
void m_OnMenuUp();
void m_OnMenuDown();
void m_OnMenuLeft();
void m_OnMenuRight();
void m_OnMenuOpt1();
void m_OnMenuOpt2();
void m_OnMenuLeftPressed();
void m_OnMenuRightPressed();
void m_OnMenuLeftReleased();
void m_OnMenuRightReleased();
void m_OnMenuShoulderLeft();
void m_OnMenuShoulderRight();
void m_OnMenuOptionsButton();
bool m_onSetupStateSet;
bool m_repeatLeft;
bool m_repeatRight;
TArray<FString> m_recentJoystickScan;
UPROPERTY()
TArray<class UMenuItemBase*> m_menuItemStack;
};

View File

@@ -0,0 +1,79 @@
#pragma once
#include "SkillTreeState.h"
#include "PlayerSetupState.generated.h"
// Properties belonging to a character class
USTRUCT()
struct FCharacterClassProperty
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, EditAnywhere)
FString name;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
class UAbilityInfo* basicAttack;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
TArray<TSubclassOf<class AItemBase>> classItems;
// Class Specific Curves, if these are not set the character defaults are used
UPROPERTY(BlueprintReadOnly, EditAnywhere)
UCurveFloat* healthCurve;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
UCurveFloat* manaCurve;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
UCurveFloat* armorCurve;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
UCurveFloat* attackSpeedCurve;
};
// A data asset that provides a list of character classes
UCLASS()
class UCharacterClassPropertySet : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, EditAnywhere)
TArray<FCharacterClassProperty> classes;
UFUNCTION(BlueprintCallable, Category = "Character Class")
FCharacterClassProperty GetCharacterClass(int32 classID) const;
};
// Setings for character bone scales
USTRUCT(BlueprintType)
struct FCharacterCustomization
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, Category = "Save")
float overallScale = 1.0f;
UPROPERTY(BlueprintReadWrite, Category = "Save")
float headScale = 1.0f;
UPROPERTY(BlueprintReadWrite, Category = "Save")
float torsoScale = 1.0f;
UPROPERTY(BlueprintReadWrite, Category = "Save")
float leftArmScale = 1.0f;
UPROPERTY(BlueprintReadWrite, Category = "Save")
float rightArmScale = 1.0f;
UPROPERTY(BlueprintReadWrite, Category = "Save")
float legThicknessYScale = 1.0f;
UPROPERTY(BlueprintReadWrite, Category = "Save")
float legThicknessZScale = 1.0f;
};
// All player settings, from the lobby
// this includes selected skill set and character customizations
USTRUCT()
struct FPlayerSetupState
{
GENERATED_BODY()
public:
UPROPERTY()
FSkillTreeState skills;
UPROPERTY()
FCharacterCustomization customizations;
UPROPERTY()
int32 characterClass;
};

View File

@@ -0,0 +1,110 @@
#include "UnrealProject.h"
#include "PlayerStateBase.h"
#include "GameStateBase.h"
#include "DefaultGameInstance.h"
#include "SessionManager.h"
APlayerStateBase::APlayerStateBase()
{
bReplicates = true;
bAlwaysRelevant = true;
}
void APlayerStateBase::BeginPlay()
{
Super::BeginPlay();
}
void APlayerStateBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(APlayerStateBase, m_team);
DOREPLIFETIME(APlayerStateBase, m_readyToStart);
}
bool APlayerStateBase::SetReadyState_Validate(bool state)
{
return true;
}
void APlayerStateBase::SetReadyState_Implementation(bool state)
{
m_readyToStart = state;
}
bool APlayerStateBase::GetReadyState() const
{
return m_readyToStart;
}
void APlayerStateBase::Transfer(APlayerStateBase* oldState)
{
m_team = oldState->m_team;
UpdatePersona();
}
int32 APlayerStateBase::GetTeam() const
{
return m_team;
}
bool APlayerStateBase::AutoAssignTeam_Validate()
{
return true;
}
void APlayerStateBase::AutoAssignTeam_Implementation()
{
if(m_team > 0)
return; // Already has a team
AGameStateBase* const state = Cast<AGameStateBase>(GetWorld()->GetGameState());
check(state);
RequestTeamEntry(state->GetOptimalAutoJoinTeam());
}
void APlayerStateBase::UpdatePersona()
{
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
if (inst && UniqueId.IsValid())
{
avatar = inst->sessionManager->GetPlayerAvatar(*UniqueId);
nickname = inst->sessionManager->GetPlayerName(*UniqueId);
}
}
bool APlayerStateBase::RequestTeamEntry_Validate(uint8 team)
{
UWorld* const world = GetWorld();
check(world);
AGameStateBase* const state = Cast<AGameStateBase>(world->GetGameState());
check(state);
return (team > 0 && team <= state->GetMapTeamCount());
}
void APlayerStateBase::RequestTeamEntry_Implementation(uint8 team)
{
if(team == m_team)
return;
if(m_readyToStart && !(m_team == 0))
return;
UWorld* const world = GetWorld();
if(world)
{
AGameStateBase* const state = Cast<AGameStateBase>(world->GetGameState());
if(state)
{
TArray<APlayerStateBase*> players = state->GetPlayers();
int32 teamSize = 0;
for(int32 i = 0; i < players.Num(); i++)
{
if(players[i] == nullptr)
continue;
if(players[i]->GetTeam() == team)
teamSize++;
}
if (teamSize < 2)
{
m_team = team;
state->OnPlayerStateChange_Multi();
}
}
}
}

View File

@@ -0,0 +1,52 @@
#pragma once
#include "PlayerStateBase.Generated.h"
/*
Base player state, keeping the team the player is in
this class also keeps the ready state of players in the lobby and contains some functionality for joining/switching teams
*/
UCLASS()
class APlayerStateBase : public APlayerState
{
GENERATED_BODY()
friend class AGameStateBase;
friend class AMenuGameMode;
public:
APlayerStateBase();
virtual void BeginPlay() override;
UFUNCTION(Server, Reliable, WithValidation, Category = "Lobby")
void SetReadyState(bool state);
UFUNCTION(BlueprintCallable, Category = "Lobby")
bool GetReadyState() const;
// Used for seamless travel, this copies the previous player state into the current one
void Transfer(APlayerStateBase* oldState);
// Requests entry to a team
UFUNCTION(Server, Reliable, WithValidation, Category = "Lobby")
void RequestTeamEntry(uint8 team);
// Automatically join the prefered team by the server
UFUNCTION(Server, Reliable, WithValidation, BlueprintCallable, Category = "Lobby")
void AutoAssignTeam();
UFUNCTION(BlueprintCallable, Category = "Lobby")
int32 GetTeam() const;
// Updates the nickname and the avatar of the player using the online subsystem
UFUNCTION(BlueprintCallable, Category = "Character")
void UpdatePersona();
UPROPERTY(BlueprintReadOnly, Category = "Character")
UTexture2D* avatar;
UPROPERTY(BlueprintReadOnly, Category = "Character")
FString nickname;
private:
UPROPERTY(Replicated)
bool m_readyToStart;
UPROPERTY(Replicated)
int32 m_team;
};

View File

@@ -0,0 +1,488 @@
#include "UnrealProject.h"
#include "SessionManager.h"
#include "DefaultGameInstance.h"
#include "MatchMaking.h"
#if PLATFORM_SPECIFIC_WIN == 0
// Steam includes + disable warnings for unsafe functions
#pragma warning(push)
#pragma warning(disable:4996)
#include "steam/steam_api.h"
#include "steam/isteamutils.h"
#include "steam/isteamfriends.h"
#pragma warning(pop)
#endif
// For avatar to UTexture2D
#include "ImageUtils.h"
#include <string>
#define POST_DESTROY_CREATE 1
#define POST_DESTROY_JOIN 2
#define POST_DESTROY_FIND 3
namespace
{
uint64 NetIDToInt(const FUniqueNetId& id)
{
std::wstring str = id.ToString().GetCharArray().GetData();
uint64 asInt = 0;
str >> asInt;
return asInt;
}
}
static UTexture2D* defaultAvatar;
USessionManager::USessionManager()
{
onCreateSessionCompleteDelegate = FOnCreateSessionCompleteDelegate::CreateUObject(this, &USessionManager::m_OnCreateSessionComplete);
onDestroySessionCompleteDelegate = FOnDestroySessionCompleteDelegate::CreateUObject(this, &USessionManager::m_OnDestroySessionComplete);
onStartSessionCompleteDelegate = FOnStartSessionCompleteDelegate::CreateUObject(this, &USessionManager::m_OnStartOnlineGameComplete);
onFindSessionsCompleteDelegate = FOnFindSessionsCompleteDelegate::CreateUObject(this, &USessionManager::m_OnFindSessionsComplete);
onJoinSessionCompleteDelegate = FOnJoinSessionCompleteDelegate::CreateUObject(this, &USessionManager::m_OnJoinSessionComplete);
onPingSearchResultsCompleteDelegate = FOnPingSearchResultsCompleteDelegate::CreateUObject(this, &USessionManager::m_OnPingSearchResultsComplete);
// Default avatar in case some doesn't have an avatar or the online subsystem does not provide one
defaultAvatar = ConstructorHelpers::FObjectFinder<UTexture2D>(TEXT("/Game/Assets/GUI/564e176e1a279230")).Object;
m_postDestroyAction = 0;
sessionSearch = MakeShareable(new FOnlineSessionSearch());
sessionSearch->bIsLanQuery = false;
sessionSearch->MaxSearchResults = 100;
sessionSearch->PingBucketSize = 50;
sessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals);
// Cached online settings
m_onlineSystem = IOnlineSubsystem::Get();
check(m_onlineSystem);
m_session = m_onlineSystem->GetSessionInterface();
check(m_session.IsValid());
GPRINT("Session manager created with subsystem: " + m_onlineSystem->GetInstanceName());
uint32 sessionCount = m_session->GetNumSessions();
GPRINT(sessionCount + " initial sessions found.");
// Set default session settings
sessionSettings.bIsLANMatch = false;
sessionSettings.bUsesPresence = true;
sessionSettings.NumPublicConnections = 4;
sessionSettings.NumPrivateConnections = 0;
sessionSettings.bAllowInvites = true;
sessionSettings.bAllowJoinInProgress = true;
sessionSettings.bShouldAdvertise = true;
sessionSettings.bAllowJoinViaPresence = true;
sessionSettings.bAllowJoinViaPresenceFriendsOnly = false;
sessionSettings.Set(SETTING_MAPNAME, FString("No Map"), EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);
// Register ping callback
onPingSearchResultsCompleteDelegateHandle = m_session->AddOnPingSearchResultsCompleteDelegate_Handle(onPingSearchResultsCompleteDelegate);
}
void USessionManager::Shutdown()
{
m_session->ClearOnPingSearchResultsCompleteDelegate_Handle(onPingSearchResultsCompleteDelegateHandle);
// Clean up sessions
uint32 sessionCount = m_session->GetNumSessions();
if(sessionCount > 0)
{
DestroySession();
}
// Close net connections
CloseNetConnections();
}
void USessionManager::CreateSession(bool destroyOld)
{
if(destroyOld && SessionExists())
{
m_postDestroyAction = POST_DESTROY_CREATE;
DestroySession();
return;
}
APlayerController* pc = m_instance->GetFirstLocalPlayerController();
if(!pc)
{
GERROR("There is no player controller!!");
onCreateSessionComplete.Broadcast(false);
return;
}
// Register delegate
onCreateSessionCompleteDelegateHandle = m_session->AddOnCreateSessionCompleteDelegate_Handle(onCreateSessionCompleteDelegate);
const FUniqueNetId& netID = *pc->PlayerState->UniqueId;
if(!m_session->CreateSession(netID, GameSessionName, sessionSettings))
{
GERROR("m_session->CreateSession call failed!");
onCreateSessionComplete.Broadcast(false);
m_session->ClearOnCreateSessionCompleteDelegate_Handle(onCreateSessionCompleteDelegateHandle);
}
}
void USessionManager::FindSessions(bool destroyOld)
{
APlayerController* pc = m_instance->GetFirstLocalPlayerController();
if(!pc)
{
GERROR("There is no player controller!!");
onFindSessionsComplete.Broadcast(false);
return;
}
if(destroyOld && SessionExists())
{
m_postDestroyAction = POST_DESTROY_FIND;
DestroySession();
return;
}
// Register delegate
onFindSessionsCompleteDelegateHandle = m_session->AddOnFindSessionsCompleteDelegate_Handle(onFindSessionsCompleteDelegate);
const FUniqueNetId& netID = *pc->PlayerState->UniqueId;
if(!m_session->FindSessions(netID, sessionSearch.ToSharedRef()))
{
GERROR("m_session->CreateSession call failed!");
onCreateSessionComplete.Broadcast(false);
m_session->ClearOnCreateSessionCompleteDelegate_Handle(onCreateSessionCompleteDelegateHandle);
}
}
void USessionManager::JoinSession(const FOnlineSessionSearchResult& searchResult, bool destroyOld)
{
joinSessionResult = searchResult;
if(destroyOld && SessionExists())
{
m_postDestroyAction = POST_DESTROY_JOIN;
DestroySession();
return;
}
APlayerController* pc = m_instance->GetFirstLocalPlayerController();
if(!pc)
{
GERROR("There is no player controller!!");
onFindSessionsComplete.Broadcast(false);
return;
}
// Register delegate
onJoinSessionCompleteDelegateHandle = m_session->AddOnJoinSessionCompleteDelegate_Handle(onJoinSessionCompleteDelegate);
const FUniqueNetId& netID = *pc->PlayerState->UniqueId;
if(!m_session->JoinSession(netID, GameSessionName, joinSessionResult))
{
GERROR("m_session->CreateSession call failed!");
onCreateSessionComplete.Broadcast(false);
m_session->ClearOnJoinSessionCompleteDelegate_Handle(onJoinSessionCompleteDelegateHandle);
}
}
void USessionManager::DestroySession()
{
onDestroySessionCompleteDelegateHandle = m_session->AddOnDestroySessionCompleteDelegate_Handle(onDestroySessionCompleteDelegate);
if(!m_session->DestroySession(GameSessionName))
{
GERROR("m_session->DestroySession call failed!");
onDestroySessionComplete.Broadcast(false);
m_session->ClearOnDestroySessionCompleteDelegate_Handle(onDestroySessionCompleteDelegateHandle);
}
}
void USessionManager::PingResult(const FOnlineSessionSearchResult& result)
{
if(!m_session->PingSearchResults(result))
{
onPingComplete.Broadcast(false);
}
}
bool USessionManager::SessionExists() const
{
return m_session->GetNumSessions() > 0;
}
void USessionManager::StartSession()
{
m_session->StartSession(GameSessionName);
}
void USessionManager::EndSession()
{
m_session->EndSession(GameSessionName);
}
bool USessionManager::HasNetConnection()
{
// Server?
UWorld* world = m_instance->GetWorld();
if(world->GetNetDriver())
return true;
// Client?
APlayerController* pc = m_instance->GetFirstLocalPlayerController();
if(pc->GetNetConnection())
return true;
return false;
}
void USessionManager::CloseNetConnections()
{
if(!m_instance)
{
GERROR("INSTANCE NOT SET/INVALID??");
return;
}
// Close the network connection
UWorld* world = m_instance->GetWorld();
if(!world)
{
GERROR("Can't close net connections, No world set");
return;
}
ENetMode netMode = world->GetNetMode();
GPRINT("Leaving client's net mode = " + netMode);
UNetDriver* netDriver = world->GetNetDriver();
APlayerController* pc = m_instance->GetFirstLocalPlayerController();
UNetConnection* netConnection = pc ? pc->GetNetConnection() : nullptr;
if(netConnection)
{
check(netMode == NM_Client);
// Close the client connection
netConnection->Close();
}
else if(netDriver)
{
check(netMode == NM_ListenServer);
// Shutdown the net driver
world->SetNetDriver(nullptr);
GEngine->DestroyNamedNetDriver(world, NAME_GameNetDriver);
}
}
void USessionManager::AcceptUserInvite(const bool bWasSuccess, const int32 ControllerId, TSharedPtr<const FUniqueNetId> Us, const FOnlineSessionSearchResult& res)
{
onAcceptInvite.Broadcast(bWasSuccess, res);
}
FString USessionManager::GetPlayerName(const FUniqueNetId& netID)
{
FString ret;
#if PLATFORM_SPECIFIC_WIN == 0
ISteamFriends* friends = SteamFriends();
if(friends)
{
FString steamIDString = netID.ToString();
CSteamID steamID((uint64)_wtoi64(steamIDString.GetCharArray().GetData()));
const char* name = SteamFriends()->GetFriendPersonaName(steamID);
if(!name)
ret = FString(TEXT("<unknown>"));
else
ret = FString(name);
}
else
#endif
{
IOnlineIdentityPtr identity = m_onlineSystem->GetIdentityInterface();
ret = identity->GetPlayerNickname(netID);
}
return ret;
}
FString USessionManager::GetSteamID(const FUniqueNetId& netID)
{
FString ret;
#if PLATFORM_SPECIFIC_WIN == 0
ISteamFriends* friends = SteamFriends();
if(friends)
{
FString steamIDString = netID.ToString();
CSteamID steamID((uint64)_wtoi64(steamIDString.GetCharArray().GetData()));
return steamIDString;
}
#endif
return ret;
}
UTexture2D* USessionManager::GetPlayerAvatar(const FUniqueNetId& netID)
{
#if PLATFORM_SPECIFIC_WIN == 0
FString idString = netID.GetHexEncodedString();
if(m_profilePictures.Contains(idString))
{
UTexture2D* ret = m_profilePictures[idString];
if(ret)
{
return ret;
}
else
{
m_profilePictures.Remove(idString);
}
}
FString steamIDString = netID.ToString();
CSteamID steamID((uint64)_wtoi64(steamIDString.GetCharArray().GetData()));
ISteamFriends* steamFriends = SteamFriends();
if(!steamFriends)
return defaultAvatar;
int imgId = steamFriends->GetMediumFriendAvatar(steamID);
if(imgId == -1)
return defaultAvatar;
uint32 w, h;
if(!SteamUtils()->GetImageSize(imgId, &w, &h))
return defaultAvatar;
TArray<FColor> imagePixels;
imagePixels.SetNumUninitialized(w * h);
if(!SteamUtils()->GetImageRGBA(imgId, (uint8*)imagePixels.GetData(), w * h * 4))
return defaultAvatar;
for(uint32 i = 0; i < (w*h); i++)
{
imagePixels[i] = FColor(imagePixels[i].B, imagePixels[i].G, imagePixels[i].R, imagePixels[i].A);
}
FString imageName = FString("T_Avatar_") + netID.ToString();
FCreateTexture2DParameters params;
params.bDeferCompression = false;
params.bSRGB = false;
params.bUseAlpha = true;
UTexture2D* tex = m_CreateTexture2D(w, h, imagePixels, true);
//FImageUtils::CreateTexture2D(w, h, imagePixels, GetWorld(), imageName, EObjectFlags)0, params);
check(tex);
m_profilePictures.Add(idString, tex);
return tex;
#else
return defaultAvatar;
#endif
}
// Solution to ConstructTexture2D not being supported on consoles.
// From: https://answers.unrealengine.com/questions/33571/fimageutilscreatetexture2d-causes-brief-hang-is-th.html
UTexture2D* USessionManager::m_CreateTexture2D(const int32 srcWidth, const int32 srcHeight, const TArray<FColor>& srcColor, const bool useAlpha)
{
UTexture2D* texture = UTexture2D::CreateTransient(srcWidth, srcHeight, PF_B8G8R8A8);
uint8* MipData = static_cast<uint8*>(texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE));
// Create base mip.
uint8* DestPtr = NULL;
const FColor* SrcPtr = NULL;
for(int32 y = 0; y < srcHeight; y++)
{
DestPtr = &MipData[(srcHeight - 1 - y) * srcWidth * sizeof(FColor)];
SrcPtr = const_cast<FColor*>(&srcColor[(srcHeight - 1 - y) * srcWidth]);
for(int32 x = 0; x < srcWidth; x++)
{
*DestPtr++ = SrcPtr->B;
*DestPtr++ = SrcPtr->G;
*DestPtr++ = SrcPtr->R;
if(useAlpha)
{
*DestPtr++ = SrcPtr->A;
}
else
{
*DestPtr++ = 0xFF;
}
SrcPtr++;
}
}
// Unlock the texture
texture->PlatformData->Mips[0].BulkData.Unlock();
texture->UpdateResource();
return texture;
}
void USessionManager::m_OnCreateSessionComplete(FName sessionName, bool successful)
{
GWARNING("OnCreateSessionComplete " + sessionName.ToString().GetCharArray().GetData() + ", " + successful);
m_session->ClearOnCreateSessionCompleteDelegate_Handle(onCreateSessionCompleteDelegateHandle);
// Start listen server
UWorld* world = m_instance->GetWorld();
if(world->Listen(m_listenURL))
{
GPRINT("Started listen server on " + m_listenURL.ToString());
}
else
{
successful = false;
GERROR("Failed to start listen server!");
}
onCreateSessionComplete.Broadcast(successful);
}
void USessionManager::m_OnStartOnlineGameComplete(FName sessionName, bool successful)
{
GWARNING("OnStartOnlineGameComplete " + sessionName.ToString().GetCharArray().GetData() + ", " + successful);
m_session->ClearOnStartSessionCompleteDelegate_Handle(onStartSessionCompleteDelegateHandle);
}
void USessionManager::m_OnFindSessionsComplete(bool successful)
{
GWARNING("OnFindSessionsComplete " + successful +
" with " + sessionSearch->SearchResults.Num() + " sessions");
m_session->ClearOnFindSessionsCompleteDelegate_Handle(onFindSessionsCompleteDelegateHandle);
onFindSessionsComplete.Broadcast(true);
}
void USessionManager::m_OnJoinSessionComplete(FName sessionName, EOnJoinSessionCompleteResult::Type result)
{
GWARNING("OnJoinSessionComplete " + sessionName.ToString().GetCharArray().GetData() + ", " + int32(result));
m_session->ClearOnJoinSessionCompleteDelegate_Handle(onJoinSessionCompleteDelegateHandle);
if(result == EOnJoinSessionCompleteResult::Success)
{
if(!m_session->GetResolvedConnectString(joinSessionResult, GamePort, joinConnectionString))
result = EOnJoinSessionCompleteResult::CouldNotRetrieveAddress;
else if(joinConnectionString.IsEmpty())
result = EOnJoinSessionCompleteResult::CouldNotRetrieveAddress;
else
GPRINT("Resolved joined session address to: " + joinConnectionString);
}
onJoinSessionComplete.Broadcast(result);
}
void USessionManager::m_OnDestroySessionComplete(FName sessionName, bool successful)
{
GWARNING("OnDestroySessionComplete " + sessionName.ToString().GetCharArray().GetData() + ", " + successful);
m_session->ClearOnDestroySessionCompleteDelegate_Handle(onDestroySessionCompleteDelegateHandle);
if(m_postDestroyAction != 0)
{
if(m_postDestroyAction == POST_DESTROY_CREATE)
{
CreateSession(false);
}
else if(m_postDestroyAction == POST_DESTROY_JOIN)
{
JoinSession(joinSessionResult, false);
}
else if(m_postDestroyAction == POST_DESTROY_FIND)
{
FindSessions(false);
}
m_postDestroyAction = 0;
return;
}
onDestroySessionComplete.Broadcast(successful);
}
void USessionManager::m_OnPingSearchResultsComplete(bool successful)
{
onPingComplete.Broadcast(successful);
}

View File

@@ -0,0 +1,115 @@
#pragma once
#include "Online.h"
#include "SessionManager.Generated.h"
/*
A Class for managing the online subsystem and sessions
*/
UCLASS()
class USessionManager : public UObject
{
GENERATED_BODY()
friend class UDefaultGameInstance;
UPROPERTY()
class UDefaultGameInstance* m_instance;
public:
USessionManager();
void Shutdown();
void CreateSession(bool destroyOld = true);
void FindSessions(bool destroyOld = true);
void JoinSession(const class FOnlineSessionSearchResult& searchResult, bool destroyOld = true);
void DestroySession();
void PingResult(const class FOnlineSessionSearchResult& result);
// Checks if a session is active
bool SessionExists() const;
// Mark session as in progress
void StartSession();
// Mark session as ended
void EndSession();
// Check if we currently are connected to a other clients, client or server
bool HasNetConnection();
// Closes all network connections, client or server
void CloseNetConnections();
// Routed from the game instance to signal the user has accepted an invite through the online subsystem
void AcceptUserInvite(const bool bWasSuccess, const int32 ControllerId,
TSharedPtr<const FUniqueNetId> Us, const FOnlineSessionSearchResult& res);
// Get a player's nickname for a NetId in the current session
//UFUNCTION(BlueprintCallable, Category = "Friends")
FString GetPlayerName(const FUniqueNetId& netID);
// Get the SteamID of a player or an empty string if not connected to steam
FString GetSteamID(const FUniqueNetId& netID);
// Get a player's avatar for a NetId in the current session
//UFUNCTION(BlueprintCallable, Category="Friends")
UTexture2D* GetPlayerAvatar(const FUniqueNetId& netID);
// Public delegates
DECLARE_MULTICAST_DELEGATE_OneParam(FSessionCallback, bool);
FSessionCallback onDestroySessionComplete;
FSessionCallback onCreateSessionComplete;
FSessionCallback onFindSessionsComplete;
DECLARE_MULTICAST_DELEGATE_OneParam(FOnJoinSessionComplete, int32);
FOnJoinSessionComplete onJoinSessionComplete;
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnAcceptInvite, bool, const FOnlineSessionSearchResult&);
FOnAcceptInvite onAcceptInvite;
DECLARE_MULTICAST_DELEGATE_OneParam(FOnPingComplete, bool)
FOnPingComplete onPingComplete;
// The settings used when creating new sessions
FOnlineSessionSettings sessionSettings;
FOnlineSessionSearchResult joinSessionResult;
FString joinConnectionString;
// An ongoing session search
TSharedPtr<class FOnlineSessionSearch> sessionSearch;
private:
// Session action callbacks
void m_OnCreateSessionComplete(FName sessionName, bool successful);
void m_OnStartOnlineGameComplete(FName sessionName, bool successful);
void m_OnFindSessionsComplete(bool successful);
void m_OnJoinSessionComplete(FName sessionName, EOnJoinSessionCompleteResult::Type result);
void m_OnDestroySessionComplete(FName sessionName, bool successful);
void m_OnPingSearchResultsComplete(bool successful);
// Used to create a texture for the player avatars
class UTexture2D* m_CreateTexture2D(const int32 srcWidth, const int32 srcHeight, const TArray<FColor>& srcColor, const bool useAlpha);
// Session action callback delegate handles
FOnCreateSessionCompleteDelegate onCreateSessionCompleteDelegate;
FOnStartSessionCompleteDelegate onStartSessionCompleteDelegate;
FOnFindSessionsCompleteDelegate onFindSessionsCompleteDelegate;
FOnJoinSessionCompleteDelegate onJoinSessionCompleteDelegate;
FOnDestroySessionCompleteDelegate onDestroySessionCompleteDelegate;
FOnPingSearchResultsCompleteDelegate onPingSearchResultsCompleteDelegate;
FDelegateHandle onCreateSessionCompleteDelegateHandle;
FDelegateHandle onStartSessionCompleteDelegateHandle;
FDelegateHandle onFindSessionsCompleteDelegateHandle;
FDelegateHandle onJoinSessionCompleteDelegateHandle;
FDelegateHandle onDestroySessionCompleteDelegateHandle;
FDelegateHandle onPingSearchResultsCompleteDelegateHandle;
// Action to perform after destroy event finished
int32 m_postDestroyAction;
// Online subsystem handles
IOnlineSubsystem* m_onlineSystem;
IOnlineSessionPtr m_session;
// The local player's NetId
TSharedPtr<const FUniqueNetId> m_localNetID;
// A list of cached profile pictures
UPROPERTY()
TMap<FString, UTexture2D*> m_profilePictures;
// URL for the listen server
FURL m_listenURL;
};