Files
Unreal_EmberWild/Source/Survival_projet/Private/MainMenuPlayerController.cpp
T
2026-07-28 14:10:17 +02:00

316 lines
7.9 KiB
C++

// Fill out your copyright notice in the Description page of Project Settings.
#include "MainMenuPlayerController.h"
#include "ConfirmDialogWidget.h"
#include "EngineUtils.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetSystemLibrary.h"
#include "MainMenuWidget.h"
#include "MenuCameraSpot.h"
#include "ScreenFadeComponent.h"
#include "SettingsMenuWidget.h"
#include "SurvivalGameInstance.h"
#define LOCTEXT_NAMESPACE "MainMenu"
namespace
{
// Le menu reste sous le voile de fondu, qui est en 1000.
constexpr int32 ZOrderMainMenu = 0;
// L'ecran de reglages recouvre le menu, la boite de confirmation passe
// devant les deux, et le fondu reste au-dessus de tout a 1000.
constexpr int32 ZOrderSettingsMenu = 50;
constexpr int32 ZOrderQuitDialog = 100;
}
AMainMenuPlayerController::AMainMenuPlayerController()
{
// Meme composant que dans le jeu : un seul fondu a maintenir.
ScreenFade = CreateDefaultSubobject<UScreenFadeComponent>(TEXT("ScreenFade"));
bShowMouseCursor = true;
QuitDialogMessage = LOCTEXT("QuitConfirm", "Quitter le jeu ?");
}
void AMainMenuPlayerController::BeginPlay()
{
Super::BeginPlay();
// L'ecran part du noir : sans ca on verrait une frame de la map, cadree
// n'importe comment, avant que le fondu ne commence.
ScreenFade->SnapToBlack();
if (AMenuCameraSpot* Spot = PickCameraSpot())
{
// Sans blend : on arrive directement sur l'angle tire au sort.
SetViewTarget(Spot);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("AMainMenuPlayerController : aucune AMenuCameraSpot active dans la map, le menu s'affichera sur la vue par defaut."));
}
if (MainMenuClass)
{
MainMenu = CreateWidget<UMainMenuWidget>(this, MainMenuClass);
if (MainMenu)
{
MainMenu->AddToViewport(ZOrderMainMenu);
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("AMainMenuPlayerController : MainMenuClass n'est pas assignee, aucun menu ne s'affichera."));
}
// GameAndUI et non UIOnly : UIOnly avale aussi la console et les raccourcis
// du moteur, ce qui rend le debug penible. Il n'y a de toute facon aucun
// input de jeu dans cette map, rien ne peut passer a travers.
FInputModeGameAndUI Mode;
Mode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
Mode.SetHideCursorDuringCapture(false);
SetInputMode(Mode);
SetShowMouseCursor(true);
ScreenFade->OnFadeFinished.AddDynamic(this, &AMainMenuPlayerController::HandleFadeFinished);
ScreenFade->FadeIn(OpeningFadeDuration);
}
AMenuCameraSpot* AMainMenuPlayerController::PickCameraSpot()
{
UWorld* World = GetWorld();
if (!World)
{
return nullptr;
}
USurvivalGameInstance* SurvivalGameInstance = GetGameInstance<USurvivalGameInstance>();
const FName LastUsed = SurvivalGameInstance ? SurvivalGameInstance->GetLastMenuCameraName() : NAME_None;
// TActorIterator plutot que GetAllActorsOfClass : pas de tableau d'AActor*
// intermediaire, pas de cast par element.
TArray<AMenuCameraSpot*> AllSpots;
TArray<AMenuCameraSpot*> Candidates;
for (TActorIterator<AMenuCameraSpot> It(World); It; ++It)
{
AMenuCameraSpot* Spot = *It;
if (!Spot->bEnabled)
{
continue;
}
AllSpots.Add(Spot);
// On ecarte l'angle du dernier passage pour ne pas revoir deux fois de
// suite le meme decor en revenant au menu.
if (Spot->GetFName() != LastUsed)
{
Candidates.Add(Spot);
}
}
// Une seule camera dans la map : on la reprend plutot que de n'afficher
// aucune vue.
if (Candidates.Num() == 0)
{
Candidates = MoveTemp(AllSpots);
}
if (Candidates.Num() == 0)
{
return nullptr;
}
AMenuCameraSpot* Chosen = Candidates[FMath::RandRange(0, Candidates.Num() - 1)];
if (SurvivalGameInstance)
{
SurvivalGameInstance->SetLastMenuCameraName(Chosen->GetFName());
}
return Chosen;
}
void AMainMenuPlayerController::StartSoloGame()
{
// Le fondu est deja lance : on ignore le second clic plutot que d'empiler
// deux chargements.
if (PendingAction != EMenuPendingAction::None)
{
return;
}
if (GameLevel.IsNull())
{
UE_LOG(LogTemp, Error, TEXT("AMainMenuPlayerController : GameLevel n'est pas assignee, impossible de lancer la partie."));
return;
}
PendingAction = EMenuPendingAction::StartGame;
// Le menu reste visible pendant le fondu mais cesse de repondre : cliquer
// Quitter pendant le fondu vers la partie serait un piege.
if (MainMenu)
{
MainMenu->SetVisibility(ESlateVisibility::HitTestInvisible);
}
ScreenFade->FadeOut(LeaveFadeDuration);
}
void AMainMenuPlayerController::QuitGame()
{
// Une partie est deja en cours de chargement : la question n'a plus de sens.
if (PendingAction != EMenuPendingAction::None)
{
return;
}
// Sans boite assignee on ne bloque pas le joueur dans son menu : on ferme.
if (!QuitDialogClass)
{
UE_LOG(LogTemp, Warning, TEXT("AMainMenuPlayerController : QuitDialogClass n'est pas assignee, fermeture sans confirmation."));
CloseGame();
return;
}
// Creee au premier clic seulement, puis reutilisee : une boite qu'on ouvre
// et ferme plusieurs fois n'a aucune raison d'etre reconstruite.
if (!QuitDialog)
{
QuitDialog = CreateWidget<UConfirmDialogWidget>(this, QuitDialogClass);
if (!QuitDialog)
{
CloseGame();
return;
}
QuitDialog->OnConfirmed.AddDynamic(this, &AMainMenuPlayerController::HandleQuitConfirmed);
QuitDialog->OnCancelled.AddDynamic(this, &AMainMenuPlayerController::HandleQuitCancelled);
QuitDialog->AddToViewport(ZOrderQuitDialog);
}
QuitDialog->Open(QuitDialogMessage);
// Le menu reste lisible derriere mais cesse de repondre : sans ca, on
// pourrait relancer une partie alors que la question est encore posee.
if (MainMenu)
{
MainMenu->SetVisibility(ESlateVisibility::HitTestInvisible);
}
}
void AMainMenuPlayerController::OpenSettings()
{
// Une partie se charge deja : ouvrir un ecran maintenant serait un piege.
if (PendingAction != EMenuPendingAction::None)
{
return;
}
if (!SettingsMenuClass)
{
UE_LOG(LogTemp, Warning, TEXT("AMainMenuPlayerController : SettingsMenuClass n'est pas assignee, les reglages ne s'ouvriront pas."));
return;
}
// Creee a la premiere ouverture seulement, puis reutilisee.
if (!SettingsMenu)
{
SettingsMenu = CreateWidget<USettingsMenuWidget>(this, SettingsMenuClass);
if (!SettingsMenu)
{
return;
}
SettingsMenu->OnCloseRequested.AddDynamic(this, &AMainMenuPlayerController::HandleSettingsCloseRequested);
SettingsMenu->AddToViewport(ZOrderSettingsMenu);
}
SettingsMenu->Open();
// Le menu reste lisible derriere mais cesse de repondre : sans ca on
// pourrait lancer une partie a travers l'ecran de reglages.
if (MainMenu)
{
MainMenu->SetVisibility(ESlateVisibility::HitTestInvisible);
}
}
void AMainMenuPlayerController::HandleSettingsCloseRequested()
{
if (SettingsMenu)
{
SettingsMenu->Close();
}
if (MainMenu)
{
MainMenu->SetVisibility(ESlateVisibility::Visible);
}
}
void AMainMenuPlayerController::HandleQuitConfirmed()
{
// Pas de fondu : le joueur vient de confirmer, le faire patienter une
// seconde de plus ne serait qu'agacant.
CloseGame();
}
void AMainMenuPlayerController::HandleQuitCancelled()
{
if (QuitDialog)
{
QuitDialog->Close();
}
if (MainMenu)
{
MainMenu->SetVisibility(ESlateVisibility::Visible);
}
}
void AMainMenuPlayerController::CloseGame()
{
UKismetSystemLibrary::QuitGame(this, this, EQuitPreference::Quit, /*bIgnorePlatformRestrictions=*/false);
}
void AMainMenuPlayerController::HandleFadeFinished()
{
// Le delegue se declenche aussi a la fin du fondu d'ouverture, ou il n'y a
// rien a faire : c'est PendingAction qui distingue les deux cas.
if (PendingAction == EMenuPendingAction::StartGame)
{
UGameplayStatics::OpenLevelBySoftObjectPtr(this, GameLevel);
}
}
void AMainMenuPlayerController::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if (SettingsMenu)
{
SettingsMenu->RemoveFromParent();
SettingsMenu = nullptr;
}
if (QuitDialog)
{
QuitDialog->RemoveFromParent();
QuitDialog = nullptr;
}
if (MainMenu)
{
MainMenu->RemoveFromParent();
MainMenu = nullptr;
}
Super::EndPlay(EndPlayReason);
}
#undef LOCTEXT_NAMESPACE