RigsofRods
Soft-body Physics Simulation
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 
40 using namespace RoR;
41 using 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 
57 {
58  cache_updated = true;
59 }
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  {
69  m_num_buttons++;
70  }
71  }
72  else
73  {
74  m_num_buttons = 4;
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", "Controls"), btn_size, button_index++))
176  {
178  this->SetVisible(false);
179  }
180 
181  if (HighlightButton(_L("Return to menu"), btn_size, button_index++))
182  {
184  if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
185  {
187  }
189  }
190  }
191 
192  if (HighlightButton(_LC("MainMenu", "Exit game"), btn_size, button_index))
193  {
195  this->SetVisible(false);
196  }
197  }
198 
199  if (App::app_state->getEnum<AppState>() == AppState::SIMULATION && App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
200  {
201  // Prevent key presses from propagating to simulation.
202  // Otherwise navigating menu with keys also moves/steers in game.
203  // CAUTION: This queues the request! It becomes effective next frame.
205 
206  // Before reading keys via IMGUI, make sure keyboard capturing is requested (not just queued).
207  // Otherwise game event might already have been invoked and duplicate actions may happen.
208  if (App::GetGuiManager()->IsGuiCaptureKeyboardRequested() &&
209  ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
210  {
211  this->SetVisible(false);
212  }
213  }
214 
215  if (!this->IsVisible())
216  {
217  cache_updated = false;
218  }
219 
220  App::GetGuiManager()->RequestGuiCaptureKeyboard(ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows));
221  ImGui::End();
222  ImGui::PopStyleVar();
223  ImGui::PopStyleColor(3);
224  m_kb_enter_index = -1;
225 }
226 
228 {
229  const float margin = ImGui::GetIO().DisplaySize.y / 30.f;
230  std::string game_ver = fmt::format("{}: {}", _LC("MainMenu", "Game version"), ROR_VERSION_STRING);
231  std::string rornet_ver = fmt::format("{}: {}", _LC("MainMenu", "Net. protocol"), RORNET_VERSION);
232  float text_w = std::max(
233  ImGui::CalcTextSize(game_ver.c_str()).x, ImGui::CalcTextSize(rornet_ver.c_str()).x);
234  ImVec2 box_size(
235  (2 * ImGui::GetStyle().WindowPadding.y) + text_w,
236  (2 * ImGui::GetStyle().WindowPadding.y) + (2 * ImGui::GetTextLineHeight()));
237  ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize - (box_size + ImVec2(margin, margin)));
238 
239  ImGui::PushStyleColor(ImGuiCol_WindowBg, WINDOW_BG_COLOR);
240  ImGuiWindowFlags flags =
241  ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
242  ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar |
243  ImGuiWindowFlags_NoInputs;
244  if (ImGui::Begin(_LC("MainMenu", "Version box"), nullptr, flags))
245  {
246  ImGui::Text("%s", game_ver.c_str());
247  ImGui::Text("%s", rornet_ver.c_str());
248  ImGui::End();
249  }
250  ImGui::PopStyleColor(1);
251 }
252 
254 {
255  Ogre::TexturePtr tex = FetchIcon("accept.png");
256 
257  const float margin = ImGui::GetIO().DisplaySize.y / 30.f;
258  std::string game_ver = fmt::format("{}: {}", _LC("MainMenu", "Game version"), ROR_VERSION_STRING); // needed to align with VersionBox
259  std::string rornet_ver = fmt::format("{}: {}", _LC("MainMenu", "Net. protocol"), RORNET_VERSION); // needed to align with VersionBox
260  std::string cache_ntc = fmt::format("{}", _LC("MainMenu", "Cache updated"));
261  float text_w = std::max(
262  ImGui::CalcTextSize(game_ver.c_str()).x, ImGui::CalcTextSize(rornet_ver.c_str()).x);
263  ImVec2 box_size(
264  (2 * ImGui::GetStyle().WindowPadding.y) + text_w,
265  (2 * ImGui::GetStyle().WindowPadding.y) + (4.5 * ImGui::GetTextLineHeight()));
266  ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize - (box_size + ImVec2(margin, margin)));
267 
268  ImGui::PushStyleColor(ImGuiCol_WindowBg, WINDOW_BG_COLOR);
269  ImGuiWindowFlags flags =
270  ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize |
271  ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar |
272  ImGuiWindowFlags_NoInputs;
273  if (ImGui::Begin(_LC("MainMenu", "Notice box"), nullptr, flags))
274  {
275  ImGui::Image(reinterpret_cast<ImTextureID>(tex->getHandle()), ImVec2(16, 16));
276  ImGui::SameLine();
277  ImGui::Text("%s", cache_ntc.c_str());
278  ImGui::End();
279  }
280  ImGui::PopStyleColor(1);
281 }
282 
283 bool GameMainMenu::HighlightButton(const std::string& txt,ImVec2 btn_size, int index) const{
284  std::string button_txt = (m_kb_focus_index == index) ? fmt::format("--> {} <--", txt) : txt;
285  return ImGui::Button(button_txt.c_str(), btn_size) || (m_kb_enter_index == index);
286 }
287 
289 {
290  // Only handle keystrokes if keyboard capture is not requested or requested by us.
291  bool kb_capture_req = App::GetGuiManager()->IsGuiCaptureKeyboardRequested();
292  bool window_hovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
293  if (!kb_capture_req || window_hovered)
294  {
295  // Keyboard updates - move up/down and wrap on top/bottom. Initial index is '-1' which means "no focus"
296  if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
297  {
299  }
300  if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
301  {
303  }
304  if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Enter)))
305  {
307  }
308  }
309 }
GameContext.h
Game state manager and message-queue provider.
RoR::GUI::MultiplayerSelector::SetVisible
void SetVisible(bool v)
Definition: GUI_MultiplayerSelector.cpp:392
RoR::GUI::GameSettings::SetVisible
void SetVisible(bool v)
Definition: GUI_GameSettings.cpp:493
RoR::FetchIcon
Ogre::TexturePtr FetchIcon(const char *name)
Definition: GUIUtils.cpp:343
RoR::MpState::CONNECTED
@ CONNECTED
RoR::GUI::GameMainMenu::DrawMenuPanel
void DrawMenuPanel()
Definition: GUI_GameMainMenu.cpp:61
RoR::App::GetGuiManager
GUIManager * GetGuiManager()
Definition: Application.cpp:269
GUI_GameMainMenu.h
RoR::GUI::GameMainMenu::m_num_buttons
int m_num_buttons
Definition: GUI_GameMainMenu.h:55
RoR::AppState::MAIN_MENU
@ MAIN_MENU
RORNET_VERSION
#define RORNET_VERSION
Definition: RoRnet.h:35
RoR::MSG_SIM_UNLOAD_TERRN_REQUESTED
@ MSG_SIM_UNLOAD_TERRN_REQUESTED
Definition: Application.h:118
format
Truck file format(technical spec)
RoR::MSG_SIM_UNPAUSE_REQUESTED
@ MSG_SIM_UNPAUSE_REQUESTED
Definition: Application.h:115
GUIUtils.h
RoR::GUIManager::GameControls
GUI::GameControls GameControls
Definition: GUIManager.h:119
RoR::GUIManager::GameSettings
GUI::GameSettings GameSettings
Definition: GUIManager.h:108
Language.h
RoR::App::sys_savegames_dir
CVar * sys_savegames_dir
Definition: Application.cpp:170
RoR::GUI::GameMainMenu::DrawNoticeBox
void DrawNoticeBox()
Definition: GUI_GameMainMenu.cpp:253
GUIManager.h
RoR::GUIManager::RepositorySelector
GUI::RepositorySelector RepositorySelector
Definition: GUIManager.h:120
RoR::GUI::GameMainMenu::m_kb_focus_index
int m_kb_focus_index
Definition: GUI_GameMainMenu.h:56
RoR::App::mp_state
CVar * mp_state
Definition: Application.cpp:115
RoR::GUI::GameAbout::SetVisible
void SetVisible(bool v)
Definition: GUI_GameAbout.cpp:156
RoR::GUI::GameMainMenu::BUTTON_PADDING
const ImVec2 BUTTON_PADDING
Definition: GUI_GameMainMenu.h:40
RoR::GUIManager::MultiplayerSelector
GUI::MultiplayerSelector MultiplayerSelector
Definition: GUIManager.h:112
RoR::GUI::GameMainMenu::DrawVersionBox
void DrawVersionBox()
Definition: GUI_GameMainMenu.cpp:227
RoR::GUI::RepositorySelector::SetVisible
void SetVisible(bool visible)
Definition: GUI_RepositorySelector.cpp:1238
RoR::PathCombine
std::string PathCombine(std::string a, std::string b)
Definition: PlatformUtils.h:48
RoR::GUI::GameMainMenu::HandleInputEvents
void HandleInputEvents()
Definition: GUI_GameMainMenu.cpp:288
GUI_MainSelector.h
RoR::GameContext::PushMessage
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
Definition: GameContext.cpp:66
RoR::App::app_state
CVar * app_state
Definition: Application.cpp:79
RoR::GUI::GameMainMenu::WINDOW_BG_COLOR
const ImVec4 WINDOW_BG_COLOR
Definition: GUI_GameMainMenu.h:38
PlatformUtils.h
Platform-specific utilities. We use narrow UTF-8 encoded strings as paths. Inspired by http://utf8eve...
RoR::GUI::GameMainMenu::HighlightButton
bool HighlightButton(const std::string &text, ImVec2 btn_size, int index) const
Definition: GUI_GameMainMenu.cpp:283
RoR::MSG_GUI_OPEN_SELECTOR_REQUESTED
@ MSG_GUI_OPEN_SELECTOR_REQUESTED
Payload = LoaderType* (owner), Description = GUID | empty.
Definition: Application.h:135
RoR::LoaderType
LoaderType
< Search mode for ModCache::Query() & Operation mode for GUI::MainSelector
Definition: Application.h:289
RoR::GUIManager::IsGuiCaptureKeyboardRequested
bool IsGuiCaptureKeyboardRequested() const
Definition: GUIManager.h:135
Application.h
Central state/object manager and communications hub.
RoR::Message::payload
void * payload
Definition: GameContext.h:59
RoR::App::GetGameContext
GameContext * GetGameContext()
Definition: Application.cpp:280
RoR::GUI::GameMainMenu::cache_updated
bool cache_updated
Definition: GUI_GameMainMenu.h:59
RoR::AppState::SIMULATION
@ SIMULATION
RoRVersion.h
RoR::GUI::GameMainMenu::SetVisible
void SetVisible(bool v)
Definition: GUI_GameMainMenu.h:43
ROR_VERSION_STRING
const char *const ROR_VERSION_STRING
_LC
#define _LC(ctx, str)
Definition: Language.h:42
RoR::LT_Terrain
@ LT_Terrain
Definition: Application.h:292
RoR::Message
Unified game event system - all requests and state changes are reported using a message.
Definition: GameContext.h:51
RoR::MSG_GUI_OPEN_MENU_REQUESTED
@ MSG_GUI_OPEN_MENU_REQUESTED
Definition: Application.h:133
_L
#define _L
Definition: ErrorUtils.cpp:34
RoR::GUIManager::GameAbout
GUI::GameAbout GameAbout
Definition: GUIManager.h:107
RoR::GUI::GameMainMenu::IsVisible
bool IsVisible() const
Definition: GUI_GameMainMenu.h:42
RoR::GUI::GameControls::SetVisible
void SetVisible(bool visible)
Definition: GUI_GameControls.cpp:416
RoR::GUI::GameMainMenu::CacheUpdatedNotice
void CacheUpdatedNotice()
Definition: GUI_GameMainMenu.cpp:56
RoR::GUI::GameMainMenu::Draw
void Draw()
Definition: GUI_GameMainMenu.cpp:43
RoR::MSG_SIM_LOAD_SAVEGAME_REQUESTED
@ MSG_SIM_LOAD_SAVEGAME_REQUESTED
Definition: Application.h:117
RoR::GUI::GameMainMenu::BUTTON_BG_COLOR
const ImVec4 BUTTON_BG_COLOR
Definition: GUI_GameMainMenu.h:39
RoR::GUIManager::RequestGuiCaptureKeyboard
void RequestGuiCaptureKeyboard(bool val)
Pass true during frame to prevent input passing to application.
Definition: GUIManager.cpp:450
RoR
Definition: AppContext.h:36
RoR::GUI::GameMainMenu::title
const char * title
Definition: GUI_GameMainMenu.h:58
RoR::GUI::GameMainMenu::m_kb_enter_index
int m_kb_enter_index
Definition: GUI_GameMainMenu.h:57
RoR::MSG_APP_SHUTDOWN_REQUESTED
@ MSG_APP_SHUTDOWN_REQUESTED
Definition: Application.h:85
RoR::FileExists
bool FileExists(const char *path)
Path must be UTF-8 encoded.
Definition: PlatformUtils.cpp:163
RoR::MSG_NET_DISCONNECT_REQUESTED
@ MSG_NET_DISCONNECT_REQUESTED
Definition: Application.h:103
RoR::GUI::GameMainMenu::WINDOW_WIDTH
const float WINDOW_WIDTH
Definition: GUI_GameMainMenu.h:37