Rigs of Rods 2023.09
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
GUI_GameMainMenu.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2005-2012 Pierre-Michel Ricordel
4 Copyright 2007-2012 Thomas Fischer
5 Copyright 2013-2020 Petr Ohlidal
6
7 For more information, see http://www.rigsofrods.org/
8
9 Rigs of Rods is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License version 3, as
11 published by the Free Software Foundation.
12
13 Rigs of Rods is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20*/
21
25
26#include "GUI_GameMainMenu.h"
27#include <fmt/format.h>
28
29#include "Application.h"
30#include "GameContext.h"
31#include "GUIManager.h"
32#include "GUIUtils.h"
33#include "GUI_MainSelector.h"
34#include "Language.h"
35
36#include "PlatformUtils.h"
37#include "RoRVersion.h"
38#include "RoRnet.h" // for version string
39
40using namespace RoR;
41using namespace GUI;
42
44{
45 this->DrawMenuPanel();
46 if (App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
47 {
48 this->DrawVersionBox();
49 if (cache_updated)
50 {
51 this->DrawNoticeBox();
52 }
53 }
54}
55
60
62{
63 if (App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
64 {
65 title = "Main menu";
66 m_num_buttons = 7;
67 if (FileExists(PathCombine(App::sys_savegames_dir->getStr(), "autosave.sav")))
68 {
70 }
71 }
72 else
73 {
74 m_num_buttons = 5;
75 if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
76 {
77 title = "Menu";
78 }
79 else
80 {
81 title = "Pause";
82 }
83 }
84
85 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, BUTTON_PADDING);
86 ImGui::PushStyleColor(ImGuiCol_TitleBg, ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive]);
87 ImGui::PushStyleColor(ImGuiCol_WindowBg, WINDOW_BG_COLOR);
88 ImGui::PushStyleColor(ImGuiCol_Button, BUTTON_BG_COLOR);
89
90 // Display in bottom left corner for single-screen setups and centered for multi-screen (typically 3-screen) setups.
91 ImVec2 display_size = ImGui::GetIO().DisplaySize;
92 if ((display_size.x > 2200.f) && (display_size.y < 1100.f)) // Silly approximate values
93 {
94 ImGui::SetNextWindowPosCenter();
95 }
96 else
97 {
98 const float btn_height = ImGui::GetTextLineHeight() + (BUTTON_PADDING.y * 2);
99 const float window_height = (m_num_buttons*btn_height) + (m_num_buttons*ImGui::GetStyle().ItemSpacing.y) + (2*ImGui::GetStyle().WindowPadding.y); // buttons + titlebar; 2x spacing around separator
100 const float margin = display_size.y / 15.f;
101 const float top = display_size.y - window_height - margin;
102 ImGui::SetNextWindowPos(ImVec2(margin, top));
103 }
104 ImGui::SetNextWindowContentWidth(WINDOW_WIDTH);
105 int flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize;
106 if (ImGui::Begin(_LC("MainMenu", title), nullptr, static_cast<ImGuiWindowFlags_>(flags)))
107 {
108 this->HandleInputEvents();
109
110 int button_index = 0;
111 ImVec2 btn_size(WINDOW_WIDTH, 0.f);
112
113 if (App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
114 {
115 if (HighlightButton(_LC("MainMenu", "Single player"), btn_size, button_index++))
116 {
117 this->SetVisible(false);
119 m.payload = reinterpret_cast<void*>(new LoaderType(LT_Terrain));
121 }
122
123 if (FileExists(PathCombine(App::sys_savegames_dir->getStr(), "autosave.sav")))
124 {
125 if ( HighlightButton(_LC("MainMenu", "Resume game"), btn_size, button_index++))
126 {
128 this->SetVisible(false);
129 }
130 }
131 }
132 else if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
133 {
134 if (HighlightButton(_LC("MainMenu", "Resume game"), btn_size, button_index++))
135 {
137 this->SetVisible(false);
138 }
139 }
140
141 if (App::app_state->getEnum<AppState>() == AppState::MAIN_MENU)
142 {
143 if (HighlightButton(_LC("MainMenu", "Multiplayer"), btn_size, button_index++))
144 {
146 this->SetVisible(false);
147 }
148
149 if (HighlightButton(_LC("MainMenu", "Repository"), btn_size, button_index++))
150 {
152 this->SetVisible(false);
153 }
154
155 if (HighlightButton(_LC("MainMenu", "Settings"), btn_size, button_index++))
156 {
158 this->SetVisible(false);
159 }
160
161 if (HighlightButton(_LC("MainMenu", "Controls"), btn_size, button_index++))
162 {
164 this->SetVisible(false);
165 }
166
167 if (HighlightButton(_LC("MainMenu", "About"), btn_size, button_index++))
168 {
170 this->SetVisible(false);
171 }
172 }
173 else if (App::app_state->getEnum<AppState>() == AppState::SIMULATION)
174 {
175 if (HighlightButton(_LC("MainMenu", "Repository"), btn_size, button_index++))
176 {
178 this->SetVisible(false);
179 }
180
181 if (HighlightButton(_LC("MainMenu", "Controls"), btn_size, button_index++))
182 {
184 this->SetVisible(false);
185 }
186
187 if (HighlightButton(_L("Return to menu"), btn_size, button_index++))
188 {
190 if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
191 {
193 }
195 }
196 }
197
198 if (HighlightButton(_LC("MainMenu", "Exit game"), btn_size, button_index))
199 {
201 this->SetVisible(false);
202 }
203 }
204
205 if (App::app_state->getEnum<AppState>() == AppState::SIMULATION && App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
206 {
207 // Prevent key presses from propagating to simulation.
208 // Otherwise navigating menu with keys also moves/steers in game.
209 // CAUTION: This queues the request! It becomes effective next frame.
211
212 // Before reading keys via IMGUI, make sure keyboard capturing is requested (not just queued).
213 // Otherwise game event might already have been invoked and duplicate actions may happen.
214 if (App::GetGuiManager()->IsGuiCaptureKeyboardRequested() &&
215 ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
216 {
217 this->SetVisible(false);
218 }
219 }
220
221 if (!this->IsVisible())
222 {
223 cache_updated = false;
224 }
225
226 App::GetGuiManager()->RequestGuiCaptureKeyboard(ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows));
227 ImGui::End();
228 ImGui::PopStyleVar();
229 ImGui::PopStyleColor(3);
230 m_kb_enter_index = -1;
231}
232
234{
235 const float margin = ImGui::GetIO().DisplaySize.y / 30.f;
236 std::string game_ver = fmt::format("{}: {}", _LC("MainMenu", "Game version"), ROR_VERSION_STRING);
237 std::string rornet_ver = fmt::format("{}: {}", _LC("MainMenu", "Net. protocol"), RORNET_VERSION);
238 float text_w = std::max(
239 ImGui::CalcTextSize(game_ver.c_str()).x, ImGui::CalcTextSize(rornet_ver.c_str()).x);
240 ImVec2 box_size(
241 (2 * ImGui::GetStyle().WindowPadding.y) + text_w,
242 (2 * ImGui::GetStyle().WindowPadding.y) + (2 * ImGui::GetTextLineHeight()));
243 ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize - (box_size + ImVec2(margin, margin)));
244
245 ImGui::PushStyleColor(ImGuiCol_WindowBg, WINDOW_BG_COLOR);
246 ImGuiWindowFlags flags =
247 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
248 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar |
249 ImGuiWindowFlags_NoInputs;
250 if (ImGui::Begin(_LC("MainMenu", "Version box"), nullptr, flags))
251 {
252 ImGui::Text("%s", game_ver.c_str());
253 ImGui::Text("%s", rornet_ver.c_str());
254 ImGui::End();
255 }
256 ImGui::PopStyleColor(1);
257}
258
260{
261 Ogre::TexturePtr tex = FetchIcon("accept.png");
262
263 const float margin = ImGui::GetIO().DisplaySize.y / 30.f;
264 std::string game_ver = fmt::format("{}: {}", _LC("MainMenu", "Game version"), ROR_VERSION_STRING); // needed to align with VersionBox
265 std::string rornet_ver = fmt::format("{}: {}", _LC("MainMenu", "Net. protocol"), RORNET_VERSION); // needed to align with VersionBox
266 std::string cache_ntc = fmt::format("{}", _LC("MainMenu", "Cache updated"));
267 float text_w = std::max(
268 ImGui::CalcTextSize(game_ver.c_str()).x, ImGui::CalcTextSize(rornet_ver.c_str()).x);
269 ImVec2 box_size(
270 (2 * ImGui::GetStyle().WindowPadding.y) + text_w,
271 (2 * ImGui::GetStyle().WindowPadding.y) + (4.5 * ImGui::GetTextLineHeight()));
272 ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize - (box_size + ImVec2(margin, margin)));
273
274 ImGui::PushStyleColor(ImGuiCol_WindowBg, WINDOW_BG_COLOR);
275 ImGuiWindowFlags flags =
276 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
277 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar |
278 ImGuiWindowFlags_NoInputs;
279 if (ImGui::Begin(_LC("MainMenu", "Notice box"), nullptr, flags))
280 {
281 ImGui::Image(reinterpret_cast<ImTextureID>(tex->getHandle()), ImVec2(16, 16));
282 ImGui::SameLine();
283 ImGui::Text("%s", cache_ntc.c_str());
284 ImGui::End();
285 }
286 ImGui::PopStyleColor(1);
287}
288
289bool GameMainMenu::HighlightButton(const std::string& txt,ImVec2 btn_size, int index) const{
290 std::string button_txt = (m_kb_focus_index == index) ? fmt::format("--> {} <--", txt) : txt;
291 return ImGui::Button(button_txt.c_str(), btn_size) || (m_kb_enter_index == index);
292}
293
295{
296 // Only handle keystrokes if keyboard capture is not requested or requested by us.
297 bool kb_capture_req = App::GetGuiManager()->IsGuiCaptureKeyboardRequested();
298 bool window_hovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
299 if (!kb_capture_req || window_hovered)
300 {
301 // Keyboard updates - move up/down and wrap on top/bottom. Initial index is '-1' which means "no focus"
302 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
303 {
305 }
306 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
307 {
309 }
310 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter)))
311 {
313 }
314 }
315}
Central state/object manager and communications hub.
#define _L
Game state manager and message-queue provider.
#define _LC(ctx, str)
Definition Language.h:38
Platform-specific utilities. We use narrow UTF-8 encoded strings as paths. Inspired by http://utf8eve...
const char *const ROR_VERSION_STRING
void SetVisible(bool visible)
bool HighlightButton(const std::string &text, ImVec2 btn_size, int index) const
GUI::RepositorySelector RepositorySelector
Definition GUIManager.h:130
bool IsGuiCaptureKeyboardRequested() const
Definition GUIManager.h:144
GUI::MultiplayerSelector MultiplayerSelector
Definition GUIManager.h:123
GUI::GameAbout GameAbout
Definition GUIManager.h:118
GUI::GameSettings GameSettings
Definition GUIManager.h:119
void RequestGuiCaptureKeyboard(bool val)
Pass true during frame to prevent input passing to application.
GUI::GameControls GameControls
Definition GUIManager.h:129
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
std::string PathCombine(std::string a, std::string b)
bool FileExists(const char *path)
Path must be UTF-8 encoded.
@ MSG_GUI_OPEN_MENU_REQUESTED
@ MSG_SIM_UNPAUSE_REQUESTED
@ MSG_SIM_UNLOAD_TERRN_REQUESTED
@ MSG_NET_DISCONNECT_REQUESTED
@ MSG_GUI_OPEN_SELECTOR_REQUESTED
Payload = LoaderType* (owner), Description = GUID | empty.
@ MSG_SIM_LOAD_SAVEGAME_REQUESTED
@ MSG_APP_SHUTDOWN_REQUESTED
Definition Application.h:86
GUIManager * GetGuiManager()
GameContext * GetGameContext()
CVar * sys_savegames_dir
CVar * app_state
CVar * mp_state
LoaderType
< Search mode for ModCache::Query() & Operation mode for GUI::MainSelector
@ LT_Terrain
Ogre::TexturePtr FetchIcon(const char *name)
Definition GUIUtils.cpp:372
#define RORNET_VERSION
Definition RoRnet.h:35
Unified game event system - all requests and state changes are reported using a message.
Definition GameContext.h:52
void * payload
Definition GameContext.h:59