HAxis sos
This commit is contained in:
285
Source/UnrealProject/GUI/Lobby/LobbyCharacterSelect.cpp
Normal file
285
Source/UnrealProject/GUI/Lobby/LobbyCharacterSelect.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "DefaultGameInstance.h"
|
||||
#include "MenuController.h"
|
||||
#include "LobbySpawn.h"
|
||||
#include "GameStateBase.h"
|
||||
#include "PlayerStateBase.h"
|
||||
#include "PlayerControllerBase.h"
|
||||
#include "CharacterBase.h"
|
||||
#include "LobbyCharacterSelect.h"
|
||||
#include "MenuGameMode.h"
|
||||
|
||||
void ULobbyCharacterSelect::m_OnPlayerJoined(APlayerController* pc)
|
||||
{
|
||||
for(auto it = m_visualCharacters.CreateIterator(); it; ++it)
|
||||
{
|
||||
(it.Value()).character->SendInitialAppearance(pc);
|
||||
}
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
// Add GameMode callback
|
||||
AMenuGameMode* gm = Cast<AMenuGameMode>(GetWorld()->GetAuthGameMode());
|
||||
if(gm)
|
||||
{
|
||||
m_onPlayerJoinedDH = gm->onPlayerJoined.AddUObject(this, &ULobbyCharacterSelect::m_OnPlayerJoined);
|
||||
}
|
||||
|
||||
UWorld* const world = GetWorld();
|
||||
for (TActorIterator<ALobbySpawn> iter(world); iter; ++iter)
|
||||
{
|
||||
TArray<ALobbySpawn*>* find = m_spawnPoints.Find(iter->assignedTeam);
|
||||
if (!find)
|
||||
{
|
||||
TArray<ALobbySpawn*> arr;
|
||||
arr.Add(*iter);
|
||||
m_spawnPoints.Add(iter->assignedTeam, arr);
|
||||
}
|
||||
else
|
||||
find->Add(*iter);
|
||||
}
|
||||
|
||||
m_requireUpdate = false;
|
||||
m_updateInterval = 0;
|
||||
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
gameState->OnPlayerStateChange.AddDynamic(this, &ULobbyCharacterSelect::UpdateVisualCharacters);
|
||||
}
|
||||
void ULobbyCharacterSelect::NativeDestruct()
|
||||
{
|
||||
Super::NativeDestruct();
|
||||
|
||||
// Remove GameMode Callback
|
||||
AMenuGameMode* gm = Cast<AMenuGameMode>(GetWorld()->GetAuthGameMode());
|
||||
if(gm)
|
||||
{
|
||||
gm->onPlayerJoined.Remove(m_onPlayerJoinedDH);
|
||||
}
|
||||
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
gameState->OnPlayerStateChange.RemoveAll(this);
|
||||
}
|
||||
void ULobbyCharacterSelect::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
|
||||
{
|
||||
// The updating is called once per frame
|
||||
// This is to prevent double calls when two players leave in one cycle
|
||||
|
||||
// Ensure that only the server runs this
|
||||
if (!GetOwningPlayer()->HasAuthority())
|
||||
return;
|
||||
|
||||
// No need to update this frame
|
||||
if (!m_requireUpdate)
|
||||
return;
|
||||
|
||||
// Await the interval
|
||||
m_updateInterval -= InDeltaTime;
|
||||
if (m_updateInterval > 0)
|
||||
return;
|
||||
|
||||
m_requireUpdate = false;
|
||||
m_updateInterval = 0.3f;
|
||||
|
||||
UWorld* world = GetWorld();
|
||||
check(world);
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
TArray<int32> teamSizes = gameState->GetTeamSizes();
|
||||
TArray<TArray<APlayerStateBase*>> playersPerTeam = gameState->GetPlayersByTeam();
|
||||
|
||||
// Check if players have left
|
||||
TMap<APlayerStateBase*, VisualSlot> outdatedCharacters = m_visualCharacters;
|
||||
TArray<APlayerStateBase*> players = gameState->GetPlayers();
|
||||
for (int32 i = 0; i < players.Num(); i++)
|
||||
{
|
||||
VisualSlot* find = outdatedCharacters.Find(players[i]);
|
||||
if (find)
|
||||
outdatedCharacters.Remove(players[i]);
|
||||
}
|
||||
for (auto iter = outdatedCharacters.CreateIterator(); iter; ++iter)
|
||||
{
|
||||
if (IsValid(iter->Value.character))
|
||||
iter->Value.character->Destroy();
|
||||
m_visualCharacters.Remove(iter->Key);
|
||||
}
|
||||
|
||||
// Check if players switched team
|
||||
TMap<APlayerStateBase*, ACharacterBase*> newCharacters;
|
||||
TMap<APlayerStateBase*, VisualSlot> newMap;
|
||||
for (int32 i = 1; i < playersPerTeam.Num(); i++)
|
||||
{
|
||||
auto& set = playersPerTeam[i];
|
||||
for (int32 j = 0; j < set.Num(); j++)
|
||||
{
|
||||
APlayerStateBase* playerState = set[j];
|
||||
|
||||
VisualSlot* find = m_visualCharacters.Find(playerState);
|
||||
const int32 team = i - 1;
|
||||
if (!find || find->team != team || find->slot != j)
|
||||
{
|
||||
// Fetch the spawn coordinates if the spawnpoint exists
|
||||
FVector position;
|
||||
FRotator rotation;
|
||||
if (team < m_spawnPoints.Num() && j < m_spawnPoints[team].Num())
|
||||
{
|
||||
position = m_spawnPoints[team][j]->GetActorLocation();
|
||||
rotation = m_spawnPoints[team][j]->GetActorRotation();
|
||||
}
|
||||
|
||||
// Check if we need to create a new character, else we just repurpose the previous character (like when the player switched slot)
|
||||
ACharacterBase* character;
|
||||
if (!find || !IsValid(find->character))
|
||||
{
|
||||
FActorSpawnParameters params;
|
||||
params.bNoFail = true;
|
||||
params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
character = GetWorld()->SpawnActor<ACharacterBase>(characterBlueprint, FTransform::Identity, params);
|
||||
if (!IsValid(character))
|
||||
{
|
||||
JERROR("Failed to spawn Actor");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
find->character->ClearEquipment();
|
||||
character = find->character;
|
||||
}
|
||||
|
||||
// Set and add
|
||||
character->SetActorLocation(position);
|
||||
character->SetActorRotation(rotation.Quaternion());
|
||||
newMap.Add(playerState, VisualSlot(team, j, character));
|
||||
newCharacters.Add(playerState, character);
|
||||
}
|
||||
else
|
||||
newMap.Add(playerState, *find);
|
||||
}
|
||||
}
|
||||
m_visualCharacters = newMap;
|
||||
|
||||
// Apply the customizations
|
||||
TMap<APlayerControllerBase*, APlayerStateBase*> controllers = gameState->GetPlayersByController();
|
||||
for (auto iter = controllers.CreateIterator(); iter; ++iter)
|
||||
{
|
||||
ACharacterBase** character = newCharacters.Find(iter->Value);
|
||||
if (!character || !IsValid(*character)) continue;
|
||||
(*character)->SetCustomizations(iter->Key->setupState.customizations);
|
||||
(*character)->EquipSkillsFromSkillTreeState(iter->Key->setupState.skills);
|
||||
|
||||
// Set class specific equipment
|
||||
if(characterClassProperties)
|
||||
{
|
||||
FCharacterClassProperty properties = characterClassProperties->GetCharacterClass(iter->Key->setupState.characterClass);
|
||||
(*character)->EquipItems(properties.classItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::Init()
|
||||
{
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
UDefaultGameInstance* const instance = Cast<UDefaultGameInstance>(world->GetGameInstance());
|
||||
check(instance);
|
||||
UCharacterSettings* const settings = instance->GetCharacterSettings();
|
||||
check(settings);
|
||||
|
||||
// Fetch the characters from the save
|
||||
check(settings->characterSaves.Num() > 0);
|
||||
m_playerCharacterSaves = settings->GetValidCharacters();
|
||||
|
||||
m_selection = 0;
|
||||
|
||||
m_ApplyCharacter();
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::OnNextCharacter()
|
||||
{
|
||||
if (m_selection == m_playerCharacterSaves.Num() - 1)
|
||||
return;
|
||||
m_selection++;
|
||||
|
||||
m_ApplyCharacter();
|
||||
}
|
||||
void ULobbyCharacterSelect::OnPreviousCharacter()
|
||||
{
|
||||
if (m_selection == 0)
|
||||
return;
|
||||
m_selection--;
|
||||
|
||||
m_ApplyCharacter();
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::UpdateCharacter_Implementation(FCharacterSave save)
|
||||
{
|
||||
// Blueprint implementation
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::m_ApplyCharacter()
|
||||
{
|
||||
UpdateCharacter(m_playerCharacterSaves[m_selection]);
|
||||
|
||||
// Apply the selected character to the player controller
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
AMenuController* const controller = Cast<AMenuController>(world->GetFirstPlayerController());
|
||||
check(controller);
|
||||
|
||||
FPlayerSetupState state;
|
||||
state.skills = m_playerCharacterSaves[m_selection].skillTreeState;
|
||||
state.customizations = m_playerCharacterSaves[m_selection].characterCustomization;
|
||||
state.characterClass = m_playerCharacterSaves[m_selection].characterClass;
|
||||
controller->SetSetupState(state);
|
||||
}
|
||||
|
||||
void ULobbyCharacterSelect::UpdateVisualCharacters(APlayerControllerBase* playerController)
|
||||
{
|
||||
if (!IsValid(characterBlueprint))
|
||||
{
|
||||
JERROR("Character blueprint not assigned");
|
||||
return;
|
||||
}
|
||||
if (!IsValid(playerController) && playerController != nullptr)
|
||||
return;
|
||||
|
||||
// Ensure that only the server runs this
|
||||
if (!GetOwningPlayer()->HasAuthority())
|
||||
return;
|
||||
|
||||
if (playerController != nullptr)
|
||||
{
|
||||
// Just update the visuals on the specific player
|
||||
APlayerStateBase* playerState = Cast<APlayerStateBase>(playerController->PlayerState);
|
||||
if (IsValid(playerState))
|
||||
{
|
||||
VisualSlot* slot = m_visualCharacters.Find(playerState);
|
||||
if (slot && IsValid(slot->character))
|
||||
{
|
||||
slot->character->SetCustomizations(playerController->setupState.customizations);
|
||||
slot->character->ClearEquipment();
|
||||
slot->character->EquipSkillsFromSkillTreeState(playerController->setupState.skills);
|
||||
|
||||
// Set class specific equipment
|
||||
if(characterClassProperties)
|
||||
{
|
||||
FCharacterClassProperty properties = characterClassProperties->GetCharacterClass(playerController->setupState.characterClass);
|
||||
slot->character->EquipItems(properties.classItems);
|
||||
}
|
||||
}
|
||||
else // Failed to find the character, refresh all
|
||||
m_requireUpdate = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_requireUpdate = true;
|
||||
}
|
||||
65
Source/UnrealProject/GUI/Lobby/LobbyCharacterSelect.h
Normal file
65
Source/UnrealProject/GUI/Lobby/LobbyCharacterSelect.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GUI/Menu/SubMenu.h"
|
||||
#include "CharacterSettings.h"
|
||||
#include "LobbyCharacterSelect.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ULobbyCharacterSelect : public USubMenu
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
void Init();
|
||||
|
||||
// On next/previous player character
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
void OnNextCharacter();
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
void OnPreviousCharacter();
|
||||
|
||||
// Called to set the name of the selected character
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Lobby")
|
||||
void UpdateCharacter(FCharacterSave save);
|
||||
|
||||
// Called to update all visual characters in the lobby (specify a controller if one player only requires a visual update, or nullptr if all characters need to be updated)
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
void UpdateVisualCharacters(class APlayerControllerBase* playerController);
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Lobby")
|
||||
TSubclassOf<class ACharacterBase> characterBlueprint;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Character Sub Menu")
|
||||
class UCharacterClassPropertySet* characterClassProperties;
|
||||
|
||||
private:
|
||||
void m_OnPlayerJoined(APlayerController* pc);
|
||||
FDelegateHandle m_onPlayerJoinedDH;
|
||||
|
||||
TArray<FCharacterSave> m_playerCharacterSaves;
|
||||
int32 m_selection;
|
||||
|
||||
TMap<int32, TArray<ALobbySpawn*>> m_spawnPoints;
|
||||
struct VisualSlot
|
||||
{
|
||||
VisualSlot(int32 team, int32 slot, ACharacterBase* character) : team(team), slot(slot), character(character) {}
|
||||
int32 team;
|
||||
int32 slot;
|
||||
ACharacterBase* character;
|
||||
};
|
||||
TMap<APlayerStateBase*, VisualSlot> m_visualCharacters;
|
||||
|
||||
void m_ApplyCharacter();
|
||||
bool m_requireUpdate;
|
||||
float m_updateInterval;
|
||||
};
|
||||
204
Source/UnrealProject/GUI/Lobby/LobbyMenu.cpp
Normal file
204
Source/UnrealProject/GUI/Lobby/LobbyMenu.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "LobbyMenu.h"
|
||||
#include "LobbySlot.h"
|
||||
#include "DefaultPlayer.h"
|
||||
#include "PlayerStateBase.h"
|
||||
#include "MenuController.h"
|
||||
#include "DefaultGameInstance.h"
|
||||
#include "MenuGameMode.h"
|
||||
#include "GameStateBase.h"
|
||||
#include "SubMenu.h"
|
||||
#include "SessionManager.h"
|
||||
#include "Engine.h"
|
||||
#include "ScreenOverlay.h"
|
||||
#include "CharacterSettings.h"
|
||||
|
||||
ULobbyMenu::ULobbyMenu(const FObjectInitializer& init)
|
||||
: Super(init)
|
||||
{
|
||||
}
|
||||
|
||||
void ULobbyMenu::NativeConstruct()
|
||||
{
|
||||
m_teamContainer = Cast<USubMenu>(WidgetTree->FindWidget("Container"));
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
void ULobbyMenu::NativeDestruct()
|
||||
{
|
||||
Super::NativeDestruct();
|
||||
}
|
||||
void ULobbyMenu::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
|
||||
{
|
||||
Super::NativeTick(MyGeometry, InDeltaTime);
|
||||
|
||||
if(!m_myState)
|
||||
{
|
||||
// Try to aquire my local player state, this is in Tick because player states are not guaranteed during BeginPlay
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
|
||||
check(player);
|
||||
m_myState = Cast<APlayerStateBase>(player->PlayerState);
|
||||
}
|
||||
|
||||
UWorld* world = GetWorld();
|
||||
check(world);
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
TArray<int32> teamSizes = gameState->GetTeamSizes();
|
||||
TArray<TArray<APlayerStateBase*>> playersPerTeam = gameState->GetPlayersByTeam();
|
||||
|
||||
// Update team slots with their respective player arrays
|
||||
for (int32 i = 0; i < (int32)m_numTeams; i++)
|
||||
{
|
||||
if((i + 1) < playersPerTeam.Num())
|
||||
{
|
||||
m_lobbySlots[i]->UpdatePlayers(playersPerTeam[i+1]);
|
||||
}
|
||||
else
|
||||
m_lobbySlots[i]->UpdatePlayers(TArray<APlayerStateBase*>());
|
||||
|
||||
if (i > teamSizes.Num())
|
||||
{
|
||||
m_lobbySlots[i]->SetState(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lobbySlots[i]->SetState(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
void ULobbyMenu::Init()
|
||||
{
|
||||
// Generate the selectable builds
|
||||
UWorld* world = GetWorld();
|
||||
check(world);
|
||||
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(world->GetGameInstance());
|
||||
check(instance);
|
||||
UCharacterSettings* settings = instance->GetCharacterSettings();
|
||||
check(settings);
|
||||
m_characters = settings->characterSaves;
|
||||
|
||||
|
||||
// Remove existing lobby slots
|
||||
for(ULobbySlot* s : m_lobbySlots)
|
||||
{
|
||||
s->RemoveFromParent();
|
||||
}
|
||||
m_lobbySlots.SetNum(0);
|
||||
|
||||
if(!m_teamContainer)
|
||||
{
|
||||
GWERROR("Container not set, can't initialize lobby menu");
|
||||
return;
|
||||
}
|
||||
|
||||
UPanelWidget* panel = Cast<UPanelWidget>(m_teamContainer->WidgetTree->RootWidget);
|
||||
if (!panel)
|
||||
{
|
||||
GWERROR(L"No UPanelWidget found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create team slots in this menu for the amount of selected teams in the game setup screen
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
m_numTeams = gameState->GetMapTeamCount();
|
||||
for (uint32 i = 0; i < m_numTeams; i++)
|
||||
{
|
||||
ULobbySlot* slot = CreateWidget<ULobbySlot>(GetWorld(), lobbySlotClass);
|
||||
check(slot);
|
||||
|
||||
panel->AddChild(slot);
|
||||
slot->SetTeamIndex(i+1);
|
||||
m_lobbySlots.Add(slot);
|
||||
}
|
||||
|
||||
m_teamContainer->RescanItems();
|
||||
}
|
||||
|
||||
void ULobbyMenu::ToggleReadyState()
|
||||
{
|
||||
// Toggle my player state's ready state
|
||||
if(!m_myState)
|
||||
{
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
|
||||
check(player);
|
||||
m_myState = Cast<APlayerStateBase>(player->PlayerState);
|
||||
}
|
||||
|
||||
if(m_myState)
|
||||
m_myState->SetReadyState(!m_myState->GetReadyState());
|
||||
}
|
||||
void ULobbyMenu::StartGame()
|
||||
{
|
||||
UWorld* world = GetWorld();
|
||||
check(world);
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
check(gameState);
|
||||
TArray<APlayerStateBase*> players = gameState->GetPlayers<APlayerStateBase>();
|
||||
|
||||
// Check if we're the host
|
||||
AMenuGameMode* gameMode = world->GetAuthGameMode<AMenuGameMode>();
|
||||
if(gameMode)
|
||||
{
|
||||
// Check if everyone's ready
|
||||
for (size_t i = 0; i < players.Num(); i++)
|
||||
{
|
||||
if (!players[i]->GetReadyState())
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell the game to start
|
||||
GPRINT("Starting game");
|
||||
if(!gameMode->StartGame())
|
||||
{
|
||||
GERROR("Failed to start game");
|
||||
TArray<FString> options;
|
||||
options.Add("OK");
|
||||
Cast<APlayerControllerBase>(GetOwningPlayer())->overlay->ShowMessageBox("Error", "Failed to start game", options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ULobbyMenu::GotoTeamSelector()
|
||||
{
|
||||
if (m_teamContainer)
|
||||
{
|
||||
OpenSubMenu(m_teamContainer);
|
||||
}
|
||||
}
|
||||
void ULobbyMenu::BackToMenu()
|
||||
{
|
||||
// Close active session
|
||||
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetOwningPlayer()->GetGameInstance());
|
||||
inst->sessionManager->DestroySession();
|
||||
inst->sessionManager->CloseNetConnections();
|
||||
|
||||
// Goto menu
|
||||
AMenuController* mc = Cast<AMenuController>(GetOwningPlayer());
|
||||
mc->SwitchToMenu();
|
||||
}
|
||||
bool ULobbyMenu::IsHost() const
|
||||
{
|
||||
if (!GetOwningPlayer())
|
||||
return false;
|
||||
return GetOwningPlayer()->Role == ROLE_Authority;
|
||||
}
|
||||
|
||||
void ULobbyMenu::OnLobbyEnter()
|
||||
{
|
||||
// Register session callbacks
|
||||
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetOwningPlayer()->GetGameInstance());
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
TArray<FCharacterSave> ULobbyMenu::GetCharacterSaves()
|
||||
{
|
||||
return m_characters;
|
||||
}
|
||||
63
Source/UnrealProject/GUI/Lobby/LobbyMenu.h
Normal file
63
Source/UnrealProject/GUI/Lobby/LobbyMenu.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
#include "MenuScreenBase.h"
|
||||
#include "CharacterSettings.h"
|
||||
#include "LobbyMenu.generated.h"
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ULobbyMenu : public UMenuScreenBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
// Must be called from blueprint to setup this element
|
||||
void Init();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Ready")
|
||||
void ToggleReadyState();
|
||||
UFUNCTION(BlueprintCallable, Category = "Ready")
|
||||
void StartGame();
|
||||
UFUNCTION(BlueprintCallable, Category = "Ready")
|
||||
void GotoTeamSelector();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Game")
|
||||
void BackToMenu();
|
||||
UFUNCTION(BlueprintCallable, Category = "Game")
|
||||
bool IsHost() const;
|
||||
|
||||
// Called when the local player enters the lobby menu & state
|
||||
void OnLobbyEnter();
|
||||
|
||||
// When this menu needs to be hidden
|
||||
UFUNCTION(BlueprintImplementableEvent)
|
||||
void OnShow();
|
||||
// When this menu needs to be shown
|
||||
UFUNCTION(BlueprintImplementableEvent)
|
||||
void OnHide();
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Lobby")
|
||||
TSubclassOf<class ULobbySlot> lobbySlotClass;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
TArray<FCharacterSave> GetCharacterSaves();
|
||||
|
||||
private:
|
||||
ULobbyMenu(const FObjectInitializer& init);
|
||||
|
||||
UPROPERTY()
|
||||
class APlayerStateBase* m_myState;
|
||||
UPROPERTY()
|
||||
TArray<class ULobbySlot*> m_lobbySlots;
|
||||
UPROPERTY()
|
||||
class USubMenu* m_teamContainer;
|
||||
|
||||
uint32 m_numTeams;
|
||||
|
||||
TArray<FCharacterSave> m_characters;
|
||||
};
|
||||
4
Source/UnrealProject/GUI/Lobby/LobbyPlayerSlot.cpp
Normal file
4
Source/UnrealProject/GUI/Lobby/LobbyPlayerSlot.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "LobbyPlayerSlot.h"
|
||||
16
Source/UnrealProject/GUI/Lobby/LobbyPlayerSlot.h
Normal file
16
Source/UnrealProject/GUI/Lobby/LobbyPlayerSlot.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GUI/PlayerSlot.h"
|
||||
#include "PlayerSlot.h"
|
||||
#include "LobbyPlayerSlot.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ULobbyPlayerSlot : public UPlayerSlot
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
125
Source/UnrealProject/GUI/Lobby/LobbySlot.cpp
Normal file
125
Source/UnrealProject/GUI/Lobby/LobbySlot.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "LobbySlot.h"
|
||||
#include "LobbyPlayerSlot.h"
|
||||
#include "PlayerStateBase.h"
|
||||
#include "PlayerControllerBase.h"
|
||||
|
||||
#include "SubMenu.h"
|
||||
#include "MenuScreenBase.h"
|
||||
#include "GameStateBase.h"
|
||||
|
||||
ULobbySlot::ULobbySlot(const FObjectInitializer& init)
|
||||
: Super(init)
|
||||
{
|
||||
}
|
||||
|
||||
void ULobbySlot::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
m_open = false;
|
||||
}
|
||||
void ULobbySlot::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
|
||||
{
|
||||
Super::NativeTick(MyGeometry, InDeltaTime);
|
||||
this->bIsEnabled = m_open;
|
||||
}
|
||||
|
||||
void ULobbySlot::SetTeamIndex(uint32 idx)
|
||||
{
|
||||
m_teamIndex = idx;
|
||||
m_UpdateTeamTitle();
|
||||
}
|
||||
void ULobbySlot::m_UpdateTeamTitle()
|
||||
{
|
||||
// Set the title of this team slot
|
||||
if (m_teamName)
|
||||
{
|
||||
std::wstring teamName = std::wstring() + L"Team " + m_teamIndex + L" (" + m_players.Num() + L"/2)";
|
||||
SetButtonText(FString(teamName.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
void ULobbySlot::UpdatePlayers(TArray<APlayerStateBase*> players)
|
||||
{
|
||||
check(m_playerList);
|
||||
check(m_playerSlots.Num() == 2);
|
||||
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
|
||||
check(player);
|
||||
APlayerStateBase* myState = Cast<APlayerStateBase>(player->PlayerState);
|
||||
|
||||
// Set all the player slots in this team to show their respective player / no player placeholder
|
||||
m_players = players;
|
||||
for (int32 i = 0; i < 2; i++)
|
||||
{
|
||||
if (i < m_players.Num())
|
||||
{
|
||||
APlayerStateBase* player = players[i];
|
||||
m_playerSlots[i]->Init(player, player == myState);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_playerSlots[i]->Init(nullptr);
|
||||
}
|
||||
}
|
||||
m_UpdateTeamTitle();
|
||||
}
|
||||
bool ULobbySlot::OnSlotSelected()
|
||||
{
|
||||
UWorld* const world = GetWorld();
|
||||
check(world);
|
||||
APlayerControllerBase* player = Cast<APlayerControllerBase>(world->GetGameInstance()->GetFirstLocalPlayerController());
|
||||
check(player);
|
||||
APlayerStateBase* myState = Cast<APlayerStateBase>(player->PlayerState);
|
||||
|
||||
AGameStateBase* gameState = Cast<AGameStateBase>(world->GetGameState());
|
||||
|
||||
if (myState->GetReadyState())
|
||||
return false;
|
||||
|
||||
if (gameState)
|
||||
{
|
||||
auto pbt = gameState->GetPlayersByTeam();
|
||||
if (m_teamIndex < (uint32)pbt.Num() && pbt[m_teamIndex].Num() == 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Try for the local player to join this team
|
||||
if (myState) // Check the state cause it can be null
|
||||
myState->RequestTeamEntry(m_teamIndex);
|
||||
else
|
||||
GWERROR(L"Player not set in " + GetName());
|
||||
|
||||
// Make sure this item is now selected
|
||||
GetSubMenu()->SelectNewItem(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ULobbySlot::Init(UTextBlock* teamName, UVerticalBox* playerList)
|
||||
{
|
||||
m_teamName = teamName;
|
||||
m_playerList = playerList;
|
||||
|
||||
// Add fixed 2 player slots to this team
|
||||
m_playerList->ClearChildren();
|
||||
for (int32 i = 0; i < 2; i++)
|
||||
{
|
||||
ULobbyPlayerSlot* playerSlot = CreateWidget<ULobbyPlayerSlot>(GetWorld(), lobbyPlayerSlotClass);
|
||||
check(playerSlot);
|
||||
playerSlot->Init(nullptr);
|
||||
m_playerSlots.Add(playerSlot);
|
||||
m_playerList->AddChild(playerSlot);
|
||||
}
|
||||
}
|
||||
|
||||
void ULobbySlot::SetState(bool open)
|
||||
{
|
||||
m_open = open;
|
||||
}
|
||||
55
Source/UnrealProject/GUI/Lobby/LobbySlot.h
Normal file
55
Source/UnrealProject/GUI/Lobby/LobbySlot.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "MenuButton.h"
|
||||
#include <set>
|
||||
#include "LobbySlot.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ULobbySlot : public UMenuButton
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
|
||||
|
||||
void SetTeamIndex(uint32 idx);
|
||||
|
||||
// Must be called from blueprint to setup this element
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
void Init(UTextBlock* teamName, UVerticalBox* playerList);
|
||||
|
||||
void UpdatePlayers(TArray<class APlayerStateBase*> players);
|
||||
void SetState(bool open);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Lobby")
|
||||
bool OnSlotSelected();
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Lobby")
|
||||
UFont* font;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category="Lobby")
|
||||
TSubclassOf<class ULobbyPlayerSlot> lobbyPlayerSlotClass;
|
||||
|
||||
private:
|
||||
ULobbySlot(const FObjectInitializer& init);
|
||||
|
||||
void m_UpdateTeamTitle();
|
||||
|
||||
UPROPERTY()
|
||||
UTextBlock* m_teamName;
|
||||
UPROPERTY()
|
||||
UVerticalBox* m_playerList;
|
||||
UPROPERTY()
|
||||
TArray<class ULobbyPlayerSlot*> m_playerSlots;
|
||||
|
||||
UPROPERTY()
|
||||
UButton* m_joinButton;
|
||||
uint32 m_teamIndex;
|
||||
UPROPERTY()
|
||||
TArray<class APlayerStateBase*> m_players;
|
||||
bool m_open;
|
||||
};
|
||||
Reference in New Issue
Block a user