Pause menu
This commit is contained in:
@@ -35,6 +35,7 @@ set(EDITSCENE_SOURCES
|
||||
systems/RoomLayoutSystem.cpp
|
||||
systems/FurnitureLibrary.cpp
|
||||
systems/StartupMenuSystem.cpp
|
||||
systems/PauseMenuSystem.cpp
|
||||
systems/PlayerControllerSystem.cpp
|
||||
systems/CharacterSlotSystem.cpp
|
||||
systems/CharacterRegistry.cpp
|
||||
@@ -205,6 +206,7 @@ set(EDITSCENE_HEADERS
|
||||
ui/CharacterClassDatabaseEditor.hpp
|
||||
systems/CharacterClassSystem.hpp
|
||||
systems/StartupMenuSystem.hpp
|
||||
systems/PauseMenuSystem.hpp
|
||||
systems/PlayerControllerSystem.hpp
|
||||
systems/EditorUISystem.hpp
|
||||
systems/CellGridSystem.hpp
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "systems/NormalDebugSystem.hpp"
|
||||
#include "systems/RoomLayoutSystem.hpp"
|
||||
#include "systems/StartupMenuSystem.hpp"
|
||||
#include "systems/PauseMenuSystem.hpp"
|
||||
#include "systems/DialogueSystem.hpp"
|
||||
#include "systems/CharacterClassSystem.hpp"
|
||||
#include "systems/PregnancySystem.hpp"
|
||||
@@ -151,6 +152,13 @@ void ImGuiRenderListener::preViewportUpdate(
|
||||
sms->update(m_deltaTime);
|
||||
}
|
||||
|
||||
// Render pause menu in game mode (inside ImGui frame scope)
|
||||
if (m_editorApp &&
|
||||
m_editorApp->getGameMode() == EditorApp::GameMode::Game &&
|
||||
m_editorApp->getGamePlayState() == EditorApp::GamePlayState::Paused) {
|
||||
PauseMenuSystem::getInstance().update(m_deltaTime);
|
||||
}
|
||||
|
||||
// Render dialogue box (game mode or editor preview)
|
||||
DialogueSystem::getInstance().update(m_deltaTime);
|
||||
|
||||
@@ -455,6 +463,7 @@ void EditorApp::setup()
|
||||
m_world, m_sceneMgr, this);
|
||||
DialogueSystem::getInstance().init(this);
|
||||
DialogueSystem::getInstance().loadSettings("dialogue.json");
|
||||
PauseMenuSystem::getInstance().init(this);
|
||||
|
||||
m_characterClassSystem =
|
||||
std::make_unique<CharacterClassSystem>(
|
||||
@@ -494,6 +503,7 @@ void EditorApp::setup()
|
||||
if (m_startupMenuSystem)
|
||||
m_startupMenuSystem->prepareFont();
|
||||
DialogueSystem::getInstance().prepareFont();
|
||||
PauseMenuSystem::getInstance().prepareFont();
|
||||
}
|
||||
|
||||
// Now show the overlay — font atlas will be built with our font
|
||||
@@ -635,6 +645,8 @@ void EditorApp::setGamePlayState(GamePlayState state)
|
||||
setWindowGrab(true);
|
||||
} else if (state == GamePlayState::Menu) {
|
||||
setWindowGrab(false);
|
||||
} else if (state == GamePlayState::Paused) {
|
||||
setWindowGrab(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -900,6 +912,8 @@ void EditorApp::createAxes()
|
||||
|
||||
bool EditorApp::frameRenderingQueued(const Ogre::FrameEvent &evt)
|
||||
{
|
||||
bool paused = (m_gamePlayState == GamePlayState::Paused);
|
||||
|
||||
if (m_gameMode == GameMode::Editor) {
|
||||
// Update camera
|
||||
if (m_camera) {
|
||||
@@ -919,81 +933,83 @@ bool EditorApp::frameRenderingQueued(const Ogre::FrameEvent &evt)
|
||||
m_characterSlotSystem->update();
|
||||
}
|
||||
|
||||
/* --- Animation / procedural generation --- */
|
||||
if (m_animationTreeSystem) {
|
||||
m_animationTreeSystem->update(evt.timeSinceLastFrame);
|
||||
if (m_behaviorTreeSystem)
|
||||
m_behaviorTreeSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
if (m_pathFollowingSystem) {
|
||||
m_pathFollowingSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
if (m_proceduralMeshSystem) {
|
||||
m_proceduralMeshSystem->update();
|
||||
}
|
||||
|
||||
/* --- Static world generation (meshes + physics) --- */
|
||||
if (m_roomLayoutSystem) {
|
||||
m_roomLayoutSystem->update();
|
||||
}
|
||||
if (m_cellGridSystem) {
|
||||
m_cellGridSystem->update();
|
||||
}
|
||||
|
||||
/* --- Normal debug visualization (after geometry is built) --- */
|
||||
if (m_normalDebugSystem) {
|
||||
m_normalDebugSystem->update();
|
||||
}
|
||||
|
||||
/* --- NavMesh builds after static geometry is ready --- */
|
||||
if (m_navMeshSystem) {
|
||||
m_navMeshSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Smart Object system (AI navigation to smart objects) --- */
|
||||
if (m_smartObjectSystem) {
|
||||
m_smartObjectSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- GOAP Planner system (plan generation) --- */
|
||||
if (m_goapPlannerSystem) {
|
||||
m_goapPlannerSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- GOAP Runner system (plan execution) --- */
|
||||
if (m_goapRunnerSystem) {
|
||||
m_goapRunnerSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Actuator system (player interaction prompts) --- */
|
||||
if (m_actuatorSystem) {
|
||||
m_actuatorSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Event Handler system (event-driven BTs) --- */
|
||||
if (m_eventHandlerSystem) {
|
||||
m_eventHandlerSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Dynamic physics (characters after static world) --- */
|
||||
|
||||
if (m_characterSystem) {
|
||||
m_characterSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Buoyancy system (before physics so impulse is integrated) --- */
|
||||
if (m_buoyancySystem) {
|
||||
// Update camera position for water detection area
|
||||
if (m_camera) {
|
||||
Ogre::Vector3 cameraPos = m_camera->getPosition();
|
||||
m_buoyancySystem->setCameraPosition(cameraPos);
|
||||
/* --- Gameplay systems (paused when in Paused state) --- */
|
||||
if (!paused) {
|
||||
/* --- Animation / procedural generation --- */
|
||||
if (m_animationTreeSystem) {
|
||||
m_animationTreeSystem->update(evt.timeSinceLastFrame);
|
||||
if (m_behaviorTreeSystem)
|
||||
m_behaviorTreeSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
if (m_pathFollowingSystem) {
|
||||
m_pathFollowingSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
if (m_proceduralMeshSystem) {
|
||||
m_proceduralMeshSystem->update();
|
||||
}
|
||||
m_buoyancySystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Main physics step --- */
|
||||
if (m_physicsSystem) {
|
||||
m_physicsSystem->update(evt.timeSinceLastFrame);
|
||||
/* --- Static world generation (meshes + physics) --- */
|
||||
if (m_roomLayoutSystem) {
|
||||
m_roomLayoutSystem->update();
|
||||
}
|
||||
if (m_cellGridSystem) {
|
||||
m_cellGridSystem->update();
|
||||
}
|
||||
|
||||
/* --- Normal debug visualization (after geometry is built) --- */
|
||||
if (m_normalDebugSystem) {
|
||||
m_normalDebugSystem->update();
|
||||
}
|
||||
|
||||
/* --- NavMesh builds after static geometry is ready --- */
|
||||
if (m_navMeshSystem) {
|
||||
m_navMeshSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Smart Object system (AI navigation to smart objects) --- */
|
||||
if (m_smartObjectSystem) {
|
||||
m_smartObjectSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- GOAP Planner system (plan generation) --- */
|
||||
if (m_goapPlannerSystem) {
|
||||
m_goapPlannerSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- GOAP Runner system (plan execution) --- */
|
||||
if (m_goapRunnerSystem) {
|
||||
m_goapRunnerSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Actuator system (player interaction prompts) --- */
|
||||
if (m_actuatorSystem) {
|
||||
m_actuatorSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Event Handler system (event-driven BTs) --- */
|
||||
if (m_eventHandlerSystem) {
|
||||
m_eventHandlerSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Dynamic physics (characters after static world) --- */
|
||||
if (m_characterSystem) {
|
||||
m_characterSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Buoyancy system (before physics so impulse is integrated) --- */
|
||||
if (m_buoyancySystem) {
|
||||
// Update camera position for water detection area
|
||||
if (m_camera) {
|
||||
Ogre::Vector3 cameraPos = m_camera->getPosition();
|
||||
m_buoyancySystem->setCameraPosition(cameraPos);
|
||||
}
|
||||
m_buoyancySystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
/* --- Main physics step --- */
|
||||
if (m_physicsSystem) {
|
||||
m_physicsSystem->update(evt.timeSinceLastFrame);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Rendering support systems --- */
|
||||
@@ -1127,6 +1143,13 @@ bool EditorApp::keyPressed(const OgreBites::KeyboardEvent &evt)
|
||||
m_currentModifiers = evt.keysym.mod;
|
||||
|
||||
if (m_gameMode == GameMode::Game) {
|
||||
if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) {
|
||||
if (m_gamePlayState == GamePlayState::Playing)
|
||||
setGamePlayState(GamePlayState::Paused);
|
||||
else if (m_gamePlayState == GamePlayState::Paused)
|
||||
setGamePlayState(GamePlayState::Playing);
|
||||
return true;
|
||||
}
|
||||
bool pressed = true;
|
||||
switch (evt.keysym.sym) {
|
||||
case 'w':
|
||||
@@ -1242,6 +1265,11 @@ flecs::entity EditorApp::getSelectedEntity() const
|
||||
return flecs::entity::null();
|
||||
}
|
||||
|
||||
PauseMenuSystem *EditorApp::getPauseMenuSystem() const
|
||||
{
|
||||
return &PauseMenuSystem::getInstance();
|
||||
}
|
||||
|
||||
DialogueSystem *EditorApp::getDialogueSystem() const
|
||||
{
|
||||
return &DialogueSystem::getInstance();
|
||||
|
||||
@@ -30,6 +30,7 @@ class CharacterSystem;
|
||||
class CellGridSystem;
|
||||
class RoomLayoutSystem;
|
||||
class StartupMenuSystem;
|
||||
class PauseMenuSystem;
|
||||
class DialogueSystem;
|
||||
class PlayerControllerSystem;
|
||||
class BuoyancySystem;
|
||||
@@ -192,6 +193,7 @@ public:
|
||||
{
|
||||
return m_startupMenuSystem.get();
|
||||
}
|
||||
PauseMenuSystem *getPauseMenuSystem() const;
|
||||
DialogueSystem *getDialogueSystem() const;
|
||||
ActuatorSystem *getActuatorSystem() const
|
||||
{
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
#include "PauseMenuSystem.hpp"
|
||||
#include "../EditorApp.hpp"
|
||||
#include "../components/StartupMenu.hpp"
|
||||
#include <flecs.h>
|
||||
#include <functional>
|
||||
#include <imgui.h>
|
||||
#include <OgreFontManager.h>
|
||||
#include <OgreImGuiOverlay.h>
|
||||
#include <OgreLogManager.h>
|
||||
#include <OgreOverlayManager.h>
|
||||
#include <OgreRoot.h>
|
||||
#include <vector>
|
||||
|
||||
PauseMenuSystem::PauseMenuSystem()
|
||||
: m_editorApp(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
PauseMenuSystem::~PauseMenuSystem() = default;
|
||||
|
||||
PauseMenuSystem &PauseMenuSystem::getInstance()
|
||||
{
|
||||
static PauseMenuSystem instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void PauseMenuSystem::init(EditorApp *editorApp)
|
||||
{
|
||||
m_editorApp = editorApp;
|
||||
}
|
||||
|
||||
void PauseMenuSystem::ensureFontLoaded(const Ogre::String &fontName,
|
||||
float fontSize)
|
||||
{
|
||||
if (m_fontLoaded && m_currentFontName == fontName &&
|
||||
m_currentFontSize == fontSize)
|
||||
return;
|
||||
|
||||
Ogre::ImGuiOverlay *overlay = m_editorApp->getImGuiOverlay();
|
||||
if (!overlay)
|
||||
return;
|
||||
|
||||
Ogre::FontPtr font;
|
||||
try {
|
||||
if (Ogre::FontManager::getSingleton().resourceExists(
|
||||
"PauseMenuFont", "General")) {
|
||||
Ogre::FontManager::getSingleton().remove("PauseMenuFont",
|
||||
"General");
|
||||
}
|
||||
font = Ogre::FontManager::getSingleton().create("PauseMenuFont",
|
||||
"General");
|
||||
font->setType(Ogre::FontType::FT_TRUETYPE);
|
||||
font->setSource(fontName);
|
||||
font->setTrueTypeSize(fontSize);
|
||||
font->setTrueTypeResolution(75);
|
||||
font->addCodePointRange(Ogre::Font::CodePointRange(32, 255));
|
||||
font->load();
|
||||
} catch (...) {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"PauseMenuSystem: Failed to load font " + fontName);
|
||||
m_menuFont = nullptr;
|
||||
m_fontLoaded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_menuFont = overlay->addFont("PauseMenuFont", "General");
|
||||
m_currentFontName = fontName;
|
||||
m_currentFontSize = fontSize;
|
||||
m_fontLoaded = true;
|
||||
}
|
||||
|
||||
void PauseMenuSystem::prepareFont()
|
||||
{
|
||||
if (!m_editorApp)
|
||||
return;
|
||||
|
||||
flecs::world *world = m_editorApp->getWorld();
|
||||
if (!world)
|
||||
return;
|
||||
|
||||
flecs::entity menuEntity = flecs::entity::null();
|
||||
world->query<StartupMenuComponent>().each(
|
||||
[&](flecs::entity e, StartupMenuComponent &) {
|
||||
if (!menuEntity.is_alive())
|
||||
menuEntity = e;
|
||||
});
|
||||
|
||||
if (menuEntity.is_alive()) {
|
||||
auto &sm = menuEntity.get<StartupMenuComponent>();
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"PauseMenuSystem: Using font=" + sm.fontName +
|
||||
", size=" + Ogre::StringConverter::toString(sm.fontSize));
|
||||
ensureFontLoaded(sm.fontName, sm.fontSize);
|
||||
} else {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"PauseMenuSystem: No StartupMenuComponent found, using defaults");
|
||||
ensureFontLoaded("Kenney Bold.ttf", 36.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void PauseMenuSystem::update(float deltaTime)
|
||||
{
|
||||
(void)deltaTime;
|
||||
|
||||
if (!m_editorApp || m_editorApp->getGamePlayState() !=
|
||||
EditorApp::GamePlayState::Paused)
|
||||
return;
|
||||
|
||||
renderMenu();
|
||||
}
|
||||
|
||||
void PauseMenuSystem::renderMenu()
|
||||
{
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x, size.y), ImGuiCond_Always);
|
||||
|
||||
ImVec4 bgColor = ImVec4(0.0f, 0.0f, 0.0f, 0.85f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, bgColor);
|
||||
|
||||
ImGui::Begin("PauseMenu", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoFocusOnAppearing);
|
||||
|
||||
if (m_menuFont)
|
||||
ImGui::PushFont(m_menuFont);
|
||||
|
||||
struct ButtonData {
|
||||
const char *label;
|
||||
std::function<void()> action;
|
||||
};
|
||||
|
||||
std::vector<ButtonData> buttons;
|
||||
buttons.push_back({ "RETURN TO GAME", [&]() {
|
||||
m_editorApp->setGamePlayState(
|
||||
EditorApp::GamePlayState::Playing);
|
||||
} });
|
||||
buttons.push_back({ "SAVE GAME", [&]() {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"Save game not implemented");
|
||||
} });
|
||||
buttons.push_back({ "OPTIONS", [&]() {
|
||||
Ogre::LogManager::getSingleton().logMessage(
|
||||
"Options not implemented");
|
||||
} });
|
||||
buttons.push_back({ "QUIT", [&]() {
|
||||
Ogre::Root::getSingleton().queueEndRendering();
|
||||
} });
|
||||
|
||||
float buttonWidth = 0.0f;
|
||||
float buttonsHeight = 0.0f;
|
||||
float extraPixels = 20.0f;
|
||||
for (const auto &b : buttons) {
|
||||
ImVec2 textSize = ImGui::CalcTextSize(b.label);
|
||||
float bwidth = textSize.x +
|
||||
(ImGui::GetStyle().FramePadding.x * 2.0f) +
|
||||
extraPixels;
|
||||
float bheight =
|
||||
textSize.y + (ImGui::GetStyle().FramePadding.y * 2.0f);
|
||||
if (buttonWidth < bwidth)
|
||||
buttonWidth = bwidth;
|
||||
buttonsHeight += bheight + ImGui::GetStyle().ItemSpacing.y;
|
||||
}
|
||||
if (!buttons.empty())
|
||||
buttonsHeight -= ImGui::GetStyle().ItemSpacing.y;
|
||||
|
||||
ImGui::SetCursorPosY((size.y - buttonsHeight) * 0.5f);
|
||||
for (const auto &b : buttons) {
|
||||
ImGui::SetCursorPosX((size.x - buttonWidth) * 0.5f);
|
||||
if (ImGui::Button(b.label, ImVec2(buttonWidth, 0)))
|
||||
b.action();
|
||||
}
|
||||
|
||||
if (m_menuFont)
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef EDITSCENE_PAUSEMENUSYSTEM_HPP
|
||||
#define EDITSCENE_PAUSEMENUSYSTEM_HPP
|
||||
#pragma once
|
||||
|
||||
#include <Ogre.h>
|
||||
#include <imgui.h>
|
||||
|
||||
class EditorApp;
|
||||
|
||||
/**
|
||||
* Singleton pause menu system.
|
||||
*
|
||||
* Renders a full-screen pause menu in game mode when EditorApp
|
||||
* is in GamePlayState::Paused. Font settings are copied from
|
||||
* the StartupMenuComponent configured in the scene.
|
||||
*/
|
||||
class PauseMenuSystem {
|
||||
public:
|
||||
static PauseMenuSystem &getInstance();
|
||||
|
||||
void init(EditorApp *editorApp);
|
||||
void prepareFont();
|
||||
void update(float deltaTime);
|
||||
|
||||
private:
|
||||
PauseMenuSystem();
|
||||
~PauseMenuSystem();
|
||||
|
||||
PauseMenuSystem(const PauseMenuSystem &) = delete;
|
||||
PauseMenuSystem &operator=(const PauseMenuSystem &) = delete;
|
||||
|
||||
void renderMenu();
|
||||
void ensureFontLoaded(const Ogre::String &fontName, float fontSize);
|
||||
|
||||
EditorApp *m_editorApp = nullptr;
|
||||
|
||||
bool m_fontLoaded = false;
|
||||
Ogre::String m_currentFontName;
|
||||
float m_currentFontSize = 0.0f;
|
||||
ImFont *m_menuFont = nullptr;
|
||||
};
|
||||
|
||||
#endif // EDITSCENE_PAUSEMENUSYSTEM_HPP
|
||||
Reference in New Issue
Block a user