HAxis sos
This commit is contained in:
24
Source/UnrealProject/GameState/AbilityBindings.h
Normal file
24
Source/UnrealProject/GameState/AbilityBindings.h
Normal 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
|
||||
557
Source/UnrealProject/GameState/DefaultGameInstance.cpp
Normal file
557
Source/UnrealProject/GameState/DefaultGameInstance.cpp
Normal 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");
|
||||
}
|
||||
*/
|
||||
107
Source/UnrealProject/GameState/DefaultGameInstance.h
Normal file
107
Source/UnrealProject/GameState/DefaultGameInstance.h
Normal 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;
|
||||
};
|
||||
381
Source/UnrealProject/GameState/DefaultGameMode.cpp
Normal file
381
Source/UnrealProject/GameState/DefaultGameMode.cpp
Normal 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();
|
||||
}
|
||||
65
Source/UnrealProject/GameState/DefaultGameMode.h
Normal file
65
Source/UnrealProject/GameState/DefaultGameMode.h
Normal 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;
|
||||
};
|
||||
32
Source/UnrealProject/GameState/DefaultGameState.cpp
Normal file
32
Source/UnrealProject/GameState/DefaultGameState.cpp
Normal 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);
|
||||
}
|
||||
27
Source/UnrealProject/GameState/DefaultGameState.h
Normal file
27
Source/UnrealProject/GameState/DefaultGameState.h
Normal 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;
|
||||
};
|
||||
119
Source/UnrealProject/GameState/DefaultPlayer.cpp
Normal file
119
Source/UnrealProject/GameState/DefaultPlayer.cpp
Normal 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);
|
||||
}
|
||||
47
Source/UnrealProject/GameState/DefaultPlayer.h
Normal file
47
Source/UnrealProject/GameState/DefaultPlayer.h
Normal 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();
|
||||
};
|
||||
1335
Source/UnrealProject/GameState/DefaultPlayerController.cpp
Normal file
1335
Source/UnrealProject/GameState/DefaultPlayerController.cpp
Normal file
File diff suppressed because it is too large
Load Diff
212
Source/UnrealProject/GameState/DefaultPlayerController.h
Normal file
212
Source/UnrealProject/GameState/DefaultPlayerController.h
Normal 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;
|
||||
|
||||
};
|
||||
121
Source/UnrealProject/GameState/DefaultPlayerState.cpp
Normal file
121
Source/UnrealProject/GameState/DefaultPlayerState.cpp
Normal 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));
|
||||
}
|
||||
|
||||
68
Source/UnrealProject/GameState/DefaultPlayerState.h
Normal file
68
Source/UnrealProject/GameState/DefaultPlayerState.h
Normal 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;
|
||||
};
|
||||
201
Source/UnrealProject/GameState/GameStateBase.cpp
Normal file
201
Source/UnrealProject/GameState/GameStateBase.cpp
Normal 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;
|
||||
}
|
||||
106
Source/UnrealProject/GameState/GameStateBase.h
Normal file
106
Source/UnrealProject/GameState/GameStateBase.h
Normal 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;
|
||||
};
|
||||
31
Source/UnrealProject/GameState/KOTHGameState.cpp
Normal file
31
Source/UnrealProject/GameState/KOTHGameState.cpp
Normal 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);
|
||||
}
|
||||
38
Source/UnrealProject/GameState/KOTHGameState.h
Normal file
38
Source/UnrealProject/GameState/KOTHGameState.h
Normal 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;
|
||||
};
|
||||
47
Source/UnrealProject/GameState/KOTHPlayerController.cpp
Normal file
47
Source/UnrealProject/GameState/KOTHPlayerController.cpp
Normal 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);
|
||||
}
|
||||
23
Source/UnrealProject/GameState/KOTHPlayerController.h
Normal file
23
Source/UnrealProject/GameState/KOTHPlayerController.h
Normal 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;
|
||||
};
|
||||
18
Source/UnrealProject/GameState/KOTHTeamState.h
Normal file
18
Source/UnrealProject/GameState/KOTHTeamState.h
Normal 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;
|
||||
};
|
||||
131
Source/UnrealProject/GameState/KingOfTheHillGameMode.cpp
Normal file
131
Source/UnrealProject/GameState/KingOfTheHillGameMode.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
35
Source/UnrealProject/GameState/KingOfTheHillGameMode.h
Normal file
35
Source/UnrealProject/GameState/KingOfTheHillGameMode.h
Normal 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;
|
||||
};
|
||||
26
Source/UnrealProject/GameState/MapData.h
Normal file
26
Source/UnrealProject/GameState/MapData.h
Normal 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;
|
||||
};
|
||||
11
Source/UnrealProject/GameState/MapList.h
Normal file
11
Source/UnrealProject/GameState/MapList.h
Normal 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;
|
||||
};
|
||||
78
Source/UnrealProject/GameState/MatchMaking.cpp
Normal file
78
Source/UnrealProject/GameState/MatchMaking.cpp
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
53
Source/UnrealProject/GameState/MatchMaking.h
Normal file
53
Source/UnrealProject/GameState/MatchMaking.h
Normal 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;
|
||||
};
|
||||
*/
|
||||
332
Source/UnrealProject/GameState/MenuController.cpp
Normal file
332
Source/UnrealProject/GameState/MenuController.cpp
Normal 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;
|
||||
}
|
||||
71
Source/UnrealProject/GameState/MenuController.h
Normal file
71
Source/UnrealProject/GameState/MenuController.h
Normal 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;
|
||||
};
|
||||
160
Source/UnrealProject/GameState/MenuGameMode.cpp
Normal file
160
Source/UnrealProject/GameState/MenuGameMode.cpp
Normal 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();
|
||||
}
|
||||
42
Source/UnrealProject/GameState/MenuGameMode.h
Normal file
42
Source/UnrealProject/GameState/MenuGameMode.h
Normal 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;
|
||||
};
|
||||
14
Source/UnrealProject/GameState/MiniMapVolume.cpp
Normal file
14
Source/UnrealProject/GameState/MiniMapVolume.cpp
Normal 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;
|
||||
}
|
||||
18
Source/UnrealProject/GameState/MiniMapVolume.h
Normal file
18
Source/UnrealProject/GameState/MiniMapVolume.h
Normal 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;
|
||||
};
|
||||
397
Source/UnrealProject/GameState/PlayerControllerBase.cpp
Normal file
397
Source/UnrealProject/GameState/PlayerControllerBase.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
122
Source/UnrealProject/GameState/PlayerControllerBase.h
Normal file
122
Source/UnrealProject/GameState/PlayerControllerBase.h
Normal 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;
|
||||
};
|
||||
79
Source/UnrealProject/GameState/PlayerSetupState.h
Normal file
79
Source/UnrealProject/GameState/PlayerSetupState.h
Normal 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;
|
||||
};
|
||||
110
Source/UnrealProject/GameState/PlayerStateBase.cpp
Normal file
110
Source/UnrealProject/GameState/PlayerStateBase.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Source/UnrealProject/GameState/PlayerStateBase.h
Normal file
52
Source/UnrealProject/GameState/PlayerStateBase.h
Normal 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;
|
||||
};
|
||||
488
Source/UnrealProject/GameState/SessionManager.cpp
Normal file
488
Source/UnrealProject/GameState/SessionManager.cpp
Normal 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);
|
||||
}
|
||||
115
Source/UnrealProject/GameState/SessionManager.h
Normal file
115
Source/UnrealProject/GameState/SessionManager.h
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user