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_TopMenubar.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 2016-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_TopMenubar.h"
27
28#include "Application.h"
29#include "Actor.h"
30#include "ActorManager.h"
31#include "CameraManager.h"
32#include "DashBoardManager.h"
33#include "FlexBody.h"
34#include "GameContext.h"
35#include "GfxScene.h"
36#include "GUIManager.h"
37#include "GUIUtils.h"
38#include "GUI_MainSelector.h"
39#include "InputEngine.h"
40#include "Language.h"
41#include "Network.h"
42#include "PlatformUtils.h"
43#include "Replay.h"
44#include "SkyManager.h"
45#include "Terrain.h"
46#include "Terrn2FileFormat.h"
47#include "TuneupFileFormat.h"
48#include "GfxWater.h"
49#include "ScriptEngine.h"
50#include "Console.h"
51#include "ContentManager.h"
52
53#include <algorithm>
54#include <fmt/format.h>
55
56
57#ifdef USE_CURL
58# include <curl/curl.h>
59# include <curl/easy.h>
60#endif //USE_CURL
61
62#if defined(_MSC_VER) && defined(GetObject) // This MS Windows macro from <wingdi.h> (Windows Kit 8.1) clashes with RapidJSON
63# undef GetObject
64#endif
65
66using namespace RoR;
67using namespace GUI;
68
69#if defined(USE_CURL)
70
71static size_t CurlWriteFunc(void *ptr, size_t size, size_t nmemb, std::string* data)
72{
73 data->append((char*)ptr, size * nmemb);
74 return size * nmemb;
75}
76
78{
79 // If local file 'savegames/waypoints.json' exists, load it; otherwise download from GitHub.
80 // -----------------------------------------------------------------------------------------
81
82 if (FileExists(PathCombine(App::sys_savegames_dir->getStr(), "waypoints.json")))
83 {
84 try
85 {
86 Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource("waypoints.json", RGN_SAVEGAMES);
88 m.description = stream->getAsString();
90 }
91 catch (...)
92 {
93 RoR::HandleGenericException("Top menubar / AI presets");
95 m.description = "Failed to load local AI presets.";
97 }
98
99 return; // DONE
100 }
101
102 std::string url = "https://raw.githubusercontent.com/RigsOfRods-Community/ai-waypoints/main/waypoints.json";
103 std::string response_payload;
104 long response_code = 0;
105
106 CURL *curl = curl_easy_init();
107 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
108 curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
109#ifdef _WIN32
110 curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
111#endif // _WIN32
112 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteFunc);
113 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_payload);
114
115 CURLcode curl_result = curl_easy_perform(curl);
116 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
117
118 curl_easy_cleanup(curl);
119 curl = nullptr;
120
121 if (curl_result != CURLE_OK || response_code != 200)
122 {
123 Ogre::LogManager::getSingleton().stream()
124 << "[RoR|Repository] Failed to download AI presets;"
125 << " Error: '" << curl_easy_strerror(curl_result) << "'; HTTP status code: " << response_code;
127 m.description = "Failed to download AI presets.";
129 }
130 else
131 {
133 m.description = response_payload;
135 }
136}
137
138#endif // defined(USE_CURL)
139
141{
142 // Constructs `ActorPtr` - doesn't compile without `#include Actor.h` - not pretty if in header (even if auto-generated by C++).
143
144 ai_presets_all.SetArray();
145 ai_presets_bundled.SetArray();
146 ai_presets_extern.SetArray();
147}
148
150{
151 // Destructs `ActorPtr` - doesn't compile without `#include Actor.h` - not pretty if in header (even if auto-generated by C++).
152}
153
154void TopMenubar::Draw(float dt)
155{
156 // ## ImGui's 'menubar' and 'menuitem' features won't quite cut it...
157 // ## Let's do our own menus and menuitems using buttons and coloring tricks.
158
160
161 int num_playable_actors = 0;
163 {
164 if (!actor->ar_hide_in_actor_list)
165 {
166 num_playable_actors++;
167 }
168 }
169
170 std::string sim_title = _LC("TopMenubar", "Simulation");
171 std::string actors_title = fmt::format("{} ({})", _LC("TopMenubar", "Vehicles"), num_playable_actors);
172 std::string savegames_title = _LC("TopMenubar", "Saves");
173 std::string settings_title = _LC("TopMenubar", "Settings");
174 std::string tools_title = _LC("TopMenubar", "Tools");
175 std::string ai_title = _LC("TopMenubar", "Vehicle AI");
176 std::string tuning_title = _LC("TopMenubar", "Tuning");
177
178 int menubar_num_buttons = 5;
179 float menubar_content_width =
180 ImGui::CalcTextSize(sim_title.c_str()).x +
181 ImGui::CalcTextSize(actors_title.c_str()).x +
182 ImGui::CalcTextSize(savegames_title.c_str()).x +
183 ImGui::CalcTextSize(settings_title.c_str()).x +
184 ImGui::CalcTextSize(tools_title.c_str()).x;
185
187 {
188 menubar_num_buttons += 1;
189 menubar_content_width += ImGui::CalcTextSize(ai_title.c_str()).x;
190 }
191
193 {
194 menubar_num_buttons += 1;
195 menubar_content_width += ImGui::CalcTextSize(tuning_title.c_str()).x;
196 }
197
198 menubar_content_width +=
199 (ImGui::GetStyle().ItemSpacing.x * (menubar_num_buttons - 1)) +
200 (ImGui::GetStyle().FramePadding.x * (menubar_num_buttons * 2));
201
202 ImVec2 window_target_pos = ImVec2((ImGui::GetIO().DisplaySize.x/2.f) - (menubar_content_width / 2.f), theme.screen_edge_padding.y);
203 if (!this->ShouldDisplay(window_target_pos))
204 {
206 m_confirm_remove_all = false;
207 this->DrawSpecialStateBox(10.f);
208 return;
209 }
210
211 ImGui::PushStyleColor(ImGuiCol_WindowBg, theme.semitransparent_window_bg);
212 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0,0,0,0)); // Fully transparent
213
214 // The panel
215 int flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove
216 | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize;
217 ImGui::SetNextWindowContentSize(ImVec2(menubar_content_width, 0.f));
218 ImGui::SetNextWindowPos(window_target_pos);
219 ImGui::Begin("Top menubar", nullptr, flags);
220
221 if (ImGui::IsWindowHovered())
222 {
223 ai_menu = false;
224 }
225
226 // The 'simulation' button
227 ImVec2 window_pos = ImGui::GetWindowPos();
228 ImVec2 sim_cursor = ImGui::GetCursorPos();
229 ImGui::Button(sim_title.c_str());
230 if ((m_open_menu != TopMenu::TOPMENU_SIM) && ImGui::IsItemHovered())
231 {
233 }
234
235 // The 'Tuning' button - only shown if enabled
236 ImVec2 tuning_cursor = ImVec2(0, 0);
238 {
239 ImGui::SameLine();
240 tuning_cursor = ImGui::GetCursorPos();
241 ImGui::Button(tuning_title.c_str());
242 if ((m_open_menu != TopMenu::TOPMENU_TUNING) && ImGui::IsItemHovered())
243 {
245 }
246 }
247
248 // The 'AI' button - only shown in singleplayer
249 ImVec2 ai_cursor = ImVec2(0, 0);
251 {
252 ImGui::SameLine();
253 ai_cursor = ImGui::GetCursorPos();
254 ImGui::Button(ai_title.c_str());
255 if ((m_open_menu != TopMenu::TOPMENU_AI) && ImGui::IsItemHovered())
256 {
258 }
259 }
260
261 ImGui::SameLine();
262
263 // The 'vehicles' button
264 ImVec2 actors_cursor = ImGui::GetCursorPos();
265 ImGui::Button(actors_title.c_str());
266 if ((m_open_menu != TopMenu::TOPMENU_ACTORS) && ImGui::IsItemHovered())
267 {
269 }
270
271 ImGui::SameLine();
272
273 // The 'savegames' button
274 ImVec2 savegames_cursor = ImGui::GetCursorPos();
275 ImGui::Button(savegames_title.c_str());
276 if ((m_open_menu != TopMenu::TOPMENU_SAVEGAMES) && ImGui::IsItemHovered())
277 {
281 m_savegame_names.clear();
282 for (int i = 0; i <= 9; i++)
283 {
284 Ogre::String filename = Ogre::StringUtil::format("quicksave-%d.sav", i);
285 m_savegame_names.push_back(App::GetGameContext()->ExtractSceneName(filename));
286 }
287 }
288
289 ImGui::SameLine();
290
291 // The 'settings' button
292 ImVec2 settings_cursor = ImGui::GetCursorPos();
293 ImGui::Button(settings_title.c_str());
294 if ((m_open_menu != TopMenu::TOPMENU_SETTINGS) && ImGui::IsItemHovered())
295 {
297#ifdef USE_CAELUM
298 if (App::gfx_sky_mode->getEnum<GfxSkyMode>() == GfxSkyMode::CAELUM)
300#endif // USE_CAELUM
301 }
302
303 ImGui::SameLine();
304
305 // The 'tools' button
306 ImVec2 tools_cursor = ImGui::GetCursorPos();
307 ImGui::Button(tools_title.c_str());
308 if ((m_open_menu != TopMenu::TOPMENU_TOOLS) && ImGui::IsItemHovered())
309 {
311 }
312
313 ImVec2 topmenu_final_size = ImGui::GetWindowSize();
314 App::GetGuiManager()->RequestGuiCaptureKeyboard(ImGui::IsWindowHovered());
315 ImGui::End();
316
317 this->DrawSpecialStateBox(window_target_pos.y + topmenu_final_size.y + 10.f);
318
319 ImVec2 menu_pos;
320 ActorPtr current_actor = App::GetGameContext()->GetPlayerActor();
321 switch (m_open_menu)
322 {
324 menu_pos.y = window_pos.y + sim_cursor.y + MENU_Y_OFFSET;
325 menu_pos.x = sim_cursor.x + window_pos.x - ImGui::GetStyle().WindowPadding.x;
326 ImGui::SetNextWindowPos(menu_pos);
327 if (ImGui::Begin(_LC("TopMenubar", "Sim menu"), nullptr, static_cast<ImGuiWindowFlags_>(flags)))
328 {
329 // TODO: Display hotkeys on the right side of the menu (with different text color)
330
331 if (ImGui::Button(_LC("TopMenubar", "Get new vehicle")))
332 {
334
336 m.payload = reinterpret_cast<void*>(new LoaderType(LT_AllBeam));
338 }
339
340 if (current_actor != nullptr)
341 {
342 if (ImGui::Button(_LC("TopMenubar", "Show vehicle description")))
343 {
345 }
346
347 if (current_actor->ar_state != ActorState::NETWORKED_OK)
348 {
349 if (ImGui::Button(_LC("TopMenubar", "Reload current vehicle")))
350 {
353 rq->amr_actor = current_actor->ar_instance_id;
355 }
356
357 if (ImGui::Button(_LC("TopMenubar", "Remove current vehicle")))
358 {
359 App::GetGameContext()->PushMessage(Message(MSG_SIM_DELETE_ACTOR_REQUESTED, static_cast<void*>(new ActorPtr(current_actor))));
360 }
361 }
362 }
363 else if (App::GetGameContext()->GetLastSpawnedActor())
364 {
365 if (ImGui::Button(_LC("TopMenubar", "Activate last spawned vehicle")))
366 {
371 }
372
373 if (ImGui::Button(_LC("TopMenubar", "Reload last spawned vehicle")))
374 {
379 }
380
381 if (ImGui::Button(_LC("TopMenubar", "Remove last spawned vehicle")))
382 {
385 }
386 }
387
388 if (!App::GetGameContext()->GetActorManager()->GetLocalActors().empty())
389 {
390 if (ImGui::Button(_LC("TopMenubar", "Remove all vehicles")))
391 {
393 }
395 {
396 ImGui::PushStyleColor(ImGuiCol_Text, ORANGE_TEXT);
397 if (ImGui::Button(_LC("TopMenubar", " [!] Confirm removal")))
398 {
400 {
401 if (!actor->ar_hide_in_actor_list && !actor->isPreloadedWithTerrain() &&
402 actor->ar_state != ActorState::NETWORKED_OK)
403 {
405 }
406 }
407 m_confirm_remove_all = false;
408 }
409 ImGui::PopStyleColor();
410 }
411
412 if (ImGui::Button(_LC("TopMenubar", "Activate all vehicles")))
413 {
415 }
416
417 bool force_trucks_active = App::GetGameContext()->GetActorManager()->AreTrucksForcedAwake();
418 if (ImGui::Checkbox(_LC("TopMenubar", "Activated vehicles never sleep"), &force_trucks_active))
419 {
421 }
422
423 if (ImGui::Button(_LC("TopMenubar", "Send all vehicles to sleep")))
424 {
426 }
427 }
428
429 if (App::mp_state->getEnum<MpState>() != MpState::CONNECTED)
430 {
431 if (ImGui::Button(_LC("TopMenubar", "Reload current terrain")))
432 {
434 // Order is required - create chain.
436 new CacheEntryPtr(App::GetGameContext()->GetTerrain()->getCacheEntry())));
438 App::GetGameContext()->GetTerrain()->getCacheEntry()->fname));
439 }
440
441 if (App::sim_state->getEnum<SimState>() == SimState::EDITOR_MODE
442 && App::GetGameContext()->GetTerrain()->getCacheEntry()->resource_bundle_type == "FileSystem"
443 && ImGui::Button(_LC("TopMenubar", "Save changes to terrain")))
444 {
445 // This is a project (unzipped mod) - update TOBJ files in place
447 }
448 }
449
450 ImGui::Separator();
451
452 if (ImGui::Button(_LC("TopMenubar", "Back to menu")))
453 {
454 if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
455 {
457 }
460 }
461
462 if (ImGui::Button(_LC("TopMenubar", "Exit")))
463 {
465 }
466
468 m_open_menu_hoverbox_max.x = menu_pos.x + ImGui::GetWindowWidth() + MENU_HOVERBOX_PADDING.x;
469 m_open_menu_hoverbox_max.y = menu_pos.y + ImGui::GetWindowHeight() + MENU_HOVERBOX_PADDING.y;
470 App::GetGuiManager()->RequestGuiCaptureKeyboard(ImGui::IsWindowHovered());
471 ImGui::End();
472 }
473 break;
474
476 menu_pos.y = window_pos.y + actors_cursor.y + MENU_Y_OFFSET;
477 menu_pos.x = actors_cursor.x + window_pos.x - ImGui::GetStyle().WindowPadding.x;
478 ImGui::SetNextWindowPos(menu_pos);
479 if (ImGui::Begin(_LC("TopMenubar", "Actors menu"), nullptr, static_cast<ImGuiWindowFlags_>(flags)))
480 {
481 if (App::mp_state->getEnum<MpState>() != MpState::CONNECTED)
482 {
484 }
485 else
486 {
487#ifdef USE_SOCKETW
489 this->DrawMpUserToActorList(net_user_info);
490
491 std::vector<RoRnet::UserInfo> remote_users = App::GetNetwork()->GetUserInfos();
492 for (auto& user: remote_users)
493 {
494 this->DrawMpUserToActorList(user);
495 }
496#endif // USE_SOCKETW
497 }
499 m_open_menu_hoverbox_max.x = menu_pos.x + ImGui::GetWindowWidth() + MENU_HOVERBOX_PADDING.x;
500 m_open_menu_hoverbox_max.y = menu_pos.y + ImGui::GetWindowHeight() + MENU_HOVERBOX_PADDING.y;
501 App::GetGuiManager()->RequestGuiCaptureKeyboard(ImGui::IsWindowHovered());
502 ImGui::End();
503 }
504 break;
505
507 menu_pos.y = window_pos.y + savegames_cursor.y + MENU_Y_OFFSET;
508 menu_pos.x = savegames_cursor.x + window_pos.x - ImGui::GetStyle().WindowPadding.x;
509 ImGui::SetNextWindowPos(menu_pos);
510 if (ImGui::Begin(_LC("TopMenubar", "Savegames"), nullptr, static_cast<ImGuiWindowFlags_>(flags)))
511 {
512 if (ImGui::Button(_LC("TopMenubar", "Quicksave")))
513 {
516 }
517 ImGui::SameLine();
518 ImGui::TextColored(GRAY_HINT_TEXT, "(NUMPAD: /)");
519
520 if (m_quickload)
521 {
522 if (ImGui::Button(_LC("TopMenubar", "Quickload")))
523 {
526 }
527 ImGui::SameLine();
528 ImGui::TextColored(GRAY_HINT_TEXT, "(NUMPAD: *)");
529 }
530
531 ImGui::Separator();
532
533 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "(Save with CTRL+ALT+1..5)"));
534 for (int i = 1; i <= 5; i++)
535 {
536 Ogre::String name = _LC("TopMenubar", "Empty Slot");
537 if (!m_savegame_names[i].empty())
538 {
539 name = m_savegame_names[i];
540 }
541 Ogre::String caption = Ogre::StringUtil::format("%d. %s##Save", i, name.c_str());
542 if (ImGui::Button(caption.c_str()))
543 {
544 Ogre::String filename = Ogre::StringUtil::format("quicksave-%d.sav", i);
547 }
548 }
549
550 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "(Load with ALT+1..5)"));
551 for (int i = 1; i <= 5; i++)
552 {
553 if (!m_savegame_names[i].empty())
554 {
555 Ogre::String name = m_savegame_names[i];
556 Ogre::String caption = Ogre::StringUtil::format("%d. %s##Load", i, name.c_str());
557 if (ImGui::Button(caption.c_str()))
558 {
559 Ogre::String filename = Ogre::StringUtil::format("quicksave-%d.sav", i);
562 }
563 }
564 }
565
567 m_open_menu_hoverbox_max.x = menu_pos.x + ImGui::GetWindowWidth() + MENU_HOVERBOX_PADDING.x;
568 m_open_menu_hoverbox_max.y = menu_pos.y + ImGui::GetWindowHeight() + MENU_HOVERBOX_PADDING.y;
569 App::GetGuiManager()->RequestGuiCaptureKeyboard(ImGui::IsWindowHovered());
570 ImGui::End();
571 }
572 break;
573
575 menu_pos.y = window_pos.y + settings_cursor.y + MENU_Y_OFFSET;
576 menu_pos.x = settings_cursor.x + window_pos.x - ImGui::GetStyle().WindowPadding.x;
577 ImGui::SetNextWindowPos(menu_pos);
578 if (ImGui::Begin(_LC("TopMenubar", "Settings menu"), nullptr, static_cast<ImGuiWindowFlags_>(flags)))
579 {
580 // AUDIO SETTINGS
581 ImGui::Separator();
582 ImGui::PushItemWidth(125.f); // Width includes [+/-] buttons
583 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Audio:"));
584 DrawGFloatSlider(App::audio_master_volume, _LC("TopMenubar", "Volume"), 0, 1);
585
586 // RENDER SETTINGS
587 ImGui::Separator();
588 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Frames per second:"));
589 if (App::gfx_envmap_enabled->getBool())
590 {
591 DrawGIntSlider(App::gfx_envmap_rate, _LC("TopMenubar", "Reflections"), 0, 2);
592 }
593 DrawGIntSlider(App::gfx_fps_limit, _LC("TopMenubar", "Game"), 0, 240);
594
595 // SIM SETTINGS
596 ImGui::Separator();
597 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Simulation:"));
598 float slowmotion = std::min(App::GetGameContext()->GetActorManager()->GetSimulationSpeed(), 1.0f);
599 if (ImGui::SliderFloat(_LC("TopMenubar", "Slow motion"), &slowmotion, 0.01f, 1.0f))
600 {
602 }
603 float timelapse = std::max(App::GetGameContext()->GetActorManager()->GetSimulationSpeed(), 1.0f);
604 if (ImGui::SliderFloat(_LC("TopMenubar", "Time lapse"), &timelapse, 1.0f, 10.0f))
605 {
607 }
608
609 // CAMERA SETTINGS
611 {
612 ImGui::Separator();
613 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Camera:"));
614 DrawGFloatSlider(App::gfx_static_cam_fov_exp, _LC("TopMenubar", "FOV"), 0.8f, 1.5f);
615 DrawGIntSlider(App::gfx_camera_height, _LC("TopMenubar", "Height"), 1, 50);
616 }
617 else
618 {
619 ImGui::Separator();
620 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Camera:"));
622 {
623 int fov = App::gfx_fov_internal->getInt();
624 if (ImGui::SliderInt(_LC("TopMenubar", "FOV"), &fov, 10, 120))
625 {
627 }
628 }
629 else
630 {
631 int fov = App::gfx_fov_external->getInt();
632 if (ImGui::SliderInt(_LC("TopMenubar", "FOV"), &fov, 10, 120))
633 {
635 }
636 }
637 if (App::GetCameraManager()->GetCurrentBehavior() == CameraManager::CAMERA_BEHAVIOR_FIXED)
638 {
639 DrawGCheckbox(App::gfx_fixed_cam_tracking, _LC("TopMenubar", "Tracking"));
640 }
641 }
642
643 // SKY SETTINGS
644#ifdef USE_CAELUM
645 if (App::gfx_sky_mode->getEnum<GfxSkyMode>() == GfxSkyMode::CAELUM)
646 {
647 ImGui::Separator();
648 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Time of day:"));
649 float time = App::GetGameContext()->GetTerrain()->getSkyManager()->GetTime();
650 if (ImGui::SliderFloat("", &time, m_daytime - 0.5f, m_daytime + 0.5f, ""))
651 {
652 App::GetGameContext()->GetTerrain()->getSkyManager()->SetTime(time);
653 }
654 ImGui::SameLine();
655 DrawGCheckbox(App::gfx_sky_time_cycle, _LC("TopMenubar", "Cycle"));
656 if (App::gfx_sky_time_cycle->getBool())
657 {
658 DrawGIntSlider(App::gfx_sky_time_speed, _LC("TopMenubar", "Speed"), 10, 2000);
659 }
660 }
661#endif // USE_CAELUM
662
663 // WATER SETTINGS
664 if (RoR::App::gfx_water_waves->getBool() && App::mp_state->getEnum<MpState>() != MpState::CONNECTED && App::GetGameContext()->GetTerrain()->getWater())
665 {
666 if (App::gfx_water_mode->getEnum<GfxWaterMode>() != GfxWaterMode::HYDRAX && App::gfx_water_mode->getEnum<GfxWaterMode>() != GfxWaterMode::NONE)
667 {
668 ImGui::PushID("waves");
669 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Waves Height:"));
670 if(ImGui::SliderFloat("", &m_waves_height, 0.f, 4.f, ""))
671 {
673 }
674 ImGui::PopID();
675 }
676 }
677
678 // VEHICLE CONTROL SETTINGS
679 if (current_actor != nullptr)
680 {
681 ImGui::Separator();
682 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Vehicle control options:"));
683 DrawGCheckbox(App::io_hydro_coupling, _LC("TopMenubar", "Keyboard steering speed coupling"));
684 }
685
686 // MULTIPLAYER SETTINGS
687 if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
688 {
689 ImGui::Separator();
690 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Multiplayer:"));
691 DrawGCheckbox(App::mp_pseudo_collisions, _LC("TopMenubar", "Collisions"));
692 DrawGCheckbox(App::mp_hide_net_labels, _LC("TopMenubar", "Hide labels"));
693 }
694 ImGui::PopItemWidth();
696 m_open_menu_hoverbox_max.x = menu_pos.x + ImGui::GetWindowWidth() + MENU_HOVERBOX_PADDING.x;
697 m_open_menu_hoverbox_max.y = menu_pos.y + ImGui::GetWindowHeight() + MENU_HOVERBOX_PADDING.y;
698 App::GetGuiManager()->RequestGuiCaptureKeyboard(ImGui::IsWindowHovered());
699 ImGui::End();
700 }
701 break;
702
704 menu_pos.y = window_pos.y + tools_cursor.y + MENU_Y_OFFSET;
705 menu_pos.x = tools_cursor.x + window_pos.x - ImGui::GetStyle().WindowPadding.x;
706 ImGui::SetNextWindowPos(menu_pos);
707 if (ImGui::Begin(_LC("TopMenubar", "Tools menu"), nullptr, static_cast<ImGuiWindowFlags_>(flags)))
708 {
709 if (ImGui::Button(_LC("TopMenubar", "Friction settings")))
710 {
713 }
714
715 if (ImGui::Button(_LC("TopMenubar", "Show console")))
716 {
719 }
720
721 if (ImGui::Button(_LC("TopMenubar", "Texture tool")))
722 {
725 }
726
727 if (ImGui::Button(_LC("TopMenubar", "Collisions debug")))
728 {
731 }
732
733 if (current_actor != nullptr)
734 {
735 if (ImGui::Button(_LC("TopMenubar", "Node / Beam utility")))
736 {
739 }
740
741 if (ImGui::Button(_LC("TopMenubar", "FlexBody debug")))
742 {
745 }
746 }
747
748 if (ImGui::Button(_LC("TopMenubar", "Browse gadgets ...")))
749 {
752 }
753
754 if (ImGui::Button(_LC("TopMenubar", "Browse repository ...")))
755 {
758 }
759
760 ImGui::Separator();
761 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Pre-spawn diag. options:"));
762
763 bool diag_mass = App::diag_truck_mass->getBool();
764 if (ImGui::Checkbox(_LC("TopMenubar", "Node mass recalc. logging"), &diag_mass))
765 {
766 App::diag_truck_mass->setVal(diag_mass);
767 }
768 if (ImGui::IsItemHovered())
769 {
770 ImGui::BeginTooltip();
771 ImGui::Text("%s", _LC("TopMenubar", "Extra logging on runtime - mass recalculation"));
772 ImGui::EndTooltip();
773 }
774
775 bool diag_break = App::diag_log_beam_break->getBool();
776 if (ImGui::Checkbox(_LC("TopMenubar", "Beam break logging"), &diag_break))
777 {
778 App::diag_log_beam_break->setVal(diag_break);
779 }
780 if (ImGui::IsItemHovered())
781 {
782 ImGui::BeginTooltip();
783 ImGui::Text("%s", _LC("TopMenubar", "Extra logging on runtime"));
784 ImGui::EndTooltip();
785 }
786
787 bool diag_deform = App::diag_log_beam_deform->getBool();
788 if (ImGui::Checkbox(_LC("TopMenubar", "Beam deform. logging"), &diag_deform))
789 {
790 App::diag_log_beam_deform->setVal(diag_deform);
791 }
792 if (ImGui::IsItemHovered())
793 {
794 ImGui::BeginTooltip();
795 ImGui::Text("%s", _LC("TopMenubar", "Extra logging on runtime"));
796 ImGui::EndTooltip();
797 }
798
799 bool diag_trig = App::diag_log_beam_trigger->getBool();
800 if (ImGui::Checkbox(_LC("TopMenubar", "Trigger logging"), &diag_trig))
801 {
803 }
804 if (ImGui::IsItemHovered())
805 {
806 ImGui::BeginTooltip();
807 ImGui::Text("%s", _LC("TopMenubar", "Extra logging on runtime - trigger beams activity"));
808 ImGui::EndTooltip();
809 }
810
811 bool diag_vcam = App::diag_videocameras->getBool();
812 if (ImGui::Checkbox(_LC("TopMenubar", "VideoCamera direction marker"), &diag_vcam))
813 {
814 App::diag_videocameras->setVal(diag_vcam);
815 }
816 if (ImGui::IsItemHovered())
817 {
818 ImGui::BeginTooltip();
819 ImGui::Text("%s", _LC("TopMenubar", "Visual marker of VideoCameras direction"));
820 ImGui::EndTooltip();
821 }
822
823 ImGui::PushItemWidth(125.f); // Width includes [+/-] buttons
824 ImGui::Separator();
825 ImGui::TextColored(GRAY_HINT_TEXT, _LC("TopMenubar", "Visual options:"));
826 DrawGIntSlider(App::gfx_polygon_mode, _LC("TopMenubar", "Polygon mode"), 1, 3);
827 if (ImGui::IsItemHovered())
828 {
829 ImGui::BeginTooltip();
830 ImGui::Text("%s", _LC("TopMenubar", "1 = Solid"));
831 ImGui::Text("%s", _LC("TopMenubar", "2 = Wireframe"));
832 ImGui::Text("%s", _LC("TopMenubar", "3 = Points"));
833 ImGui::EndTooltip();
834 }
835
837 m_open_menu_hoverbox_max.x = menu_pos.x + ImGui::GetWindowWidth() + MENU_HOVERBOX_PADDING.x;
838 m_open_menu_hoverbox_max.y = menu_pos.y + ImGui::GetWindowHeight() + MENU_HOVERBOX_PADDING.y;
839 App::GetGuiManager()->RequestGuiCaptureKeyboard(ImGui::IsWindowHovered());
840 ImGui::End();
841 }
842 break;
843
845 menu_pos.y = window_pos.y + ai_cursor.y + MENU_Y_OFFSET;
846 menu_pos.x = ai_cursor.x + window_pos.x - ImGui::GetStyle().WindowPadding.x;
847 ImGui::SetNextWindowPos(menu_pos);
848 if (ImGui::Begin(_LC("TopMenubar", "AI menu"), nullptr, static_cast<ImGuiWindowFlags_>(flags)))
849 {
850 if (ImGui::IsWindowHovered())
851 {
852 ai_menu = false;
853 }
854
855 ImGui::PushItemWidth(125.f); // Width includes [+/-] buttons
856 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "General options:"));
857
858 if (ai_num < 1)
859 ai_num = 1;
860
861
862 if (ai_mode == 2 || ai_mode == 3) // Drag Race or Crash driving mode
863 {
864 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
865 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
866 }
867
868 ImGui::InputInt(_LC("TopMenubar", "Vehicle count"), &ai_num, 1, 100);
869 if (ImGui::IsItemHovered())
870 {
871 ImGui::BeginTooltip();
872 ImGui::Text("%s", _LC("TopMenubar", "Number of vehicles"));
873 ImGui::EndTooltip();
874 }
875
876 if (ai_mode == 2 || ai_mode == 3) // Drag Race or Crash driving mode
877 {
878 ImGui::PopItemFlag();
879 ImGui::PopStyleVar();
880 }
881
882 if (ai_num < 2)
883 {
884 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
885 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
886 }
887
888 if (ai_mode == 3) // Crash driving mode
889 {
890 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
891 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
892 }
893
894 ImGui::InputInt(_LC("TopMenubar", "Distance"), &ai_distance, 1, 100);
895 if (ImGui::IsItemHovered())
896 {
897 ImGui::BeginTooltip();
898 ImGui::Text("%s", _LC("TopMenubar", "Following distance in meters"));
899 ImGui::EndTooltip();
900 }
901
902 if (ai_mode == 3) // Crash driving mode
903 {
904 ImGui::PopItemFlag();
905 ImGui::PopStyleVar();
906 }
907
908 std::string label1 = "Behind";
909 if (ai_position_scheme == 1)
910 {
911 label1 = "Parallel";
912 }
913 else if (ai_position_scheme == 2)
914 {
915 label1 = "Opposite";
916 }
917
918 if (ai_mode == 2 || ai_mode == 3) // Drag Race or Crash driving mode
919 {
920 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
921 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
922 }
923
924 if (ImGui::BeginCombo("Position", label1.c_str()))
925 {
926 if (ImGui::Selectable("Behind"))
927 {
929 }
930 if (ImGui::Selectable("Parallel"))
931 {
933 }
934 ImGui::EndCombo();
935 }
936 if (ImGui::IsItemHovered())
937 {
938 ImGui::BeginTooltip();
939 ImGui::Text("%s", _LC("TopMenubar", "Positioning scheme"));
940 ImGui::Separator();
941 ImGui::Text("%s", _LC("TopMenubar", "Behind: Set vehicle behind vehicle, in line"));
942 ImGui::Text("%s", _LC("TopMenubar", "Parallel: Set vehicles in parallel, useful for certain scenarios like drag races"));
943 ImGui::EndTooltip();
944 }
945
946 if (ai_num < 2)
947 {
948 ImGui::PopItemFlag();
949 ImGui::PopStyleVar();
950 }
951
952 if (ai_times < 1)
953 ai_times = 1;
954
955 if (ai_mode == 4) // Chase driving mode
956 {
957 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
958 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
959 }
960
961 ImGui::InputInt(_LC("TopMenubar", "Repeat times"), &ai_times, 1, 100);
962 if (ImGui::IsItemHovered())
963 {
964 ImGui::BeginTooltip();
965 ImGui::Text("%s", _LC("TopMenubar", "How many times to loop the path"));
966 ImGui::EndTooltip();
967 }
968
969 if (ai_mode == 4) // Chase driving mode
970 {
971 ImGui::PopItemFlag();
972 ImGui::PopStyleVar();
973 }
974
975 if (ai_mode == 2 || ai_mode == 3) // Drag Race or Crash driving mode
976 {
977 ImGui::PopItemFlag();
978 ImGui::PopStyleVar();
979 }
980
981 ImGui::Separator();
982 ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Vehicle options:"));
983
984 std::string label2 = "Normal";
985 if (ai_mode == 1)
986 {
987 label2 = "Race";
988 }
989 else if (ai_mode == 2)
990 {
991 label2 = "Drag Race";
992 }
993 else if (ai_mode == 3)
994 {
995 label2 = "Crash";
996 }
997 else if (ai_mode == 4)
998 {
999 label2 = "Chase";
1000 }
1001
1002 for (auto actor : App::GetGameContext()->GetActorManager()->GetLocalActors())
1003 {
1004 if (actor->ar_driveable == AI)
1005 {
1006 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
1007 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
1008 break;
1009 }
1010 }
1011
1012 if (ImGui::BeginCombo("Mode", label2.c_str()))
1013 {
1014 if (ImGui::Selectable("Normal"))
1015 {
1016 ai_mode = 0;
1017
1018 if (ai_mode_prev == 2 || ai_mode_prev == 3)
1019 {
1024 }
1026 }
1027 if (ImGui::Selectable("Race"))
1028 {
1029 ai_mode = 1;
1030
1031 if (ai_mode_prev == 2 || ai_mode_prev == 3)
1032 {
1037 }
1039 }
1040 if (ImGui::Selectable("Drag Race"))
1041 {
1042 ai_mode = 2;
1043
1044 if (ai_mode_prev != 3)
1045 {
1050 }
1052 ai_num = 2;
1053 ai_speed = 1000;
1055 ai_times = 1;
1056 }
1057 if (ImGui::Selectable("Crash"))
1058 {
1059 ai_mode = 3;
1060 if (ai_mode_prev != 2)
1061 {
1066 }
1068 ai_num = 2;
1069 ai_speed = 100;
1071 ai_times = 1;
1072 }
1073 if (ImGui::Selectable("Chase"))
1074 {
1075 ai_mode = 4;
1076
1077 if (ai_mode_prev == 2 || ai_mode_prev == 3)
1078 {
1083 }
1085 }
1086 ImGui::EndCombo();
1087 }
1088 if (ImGui::IsItemHovered())
1089 {
1090 ImGui::BeginTooltip();
1091 ImGui::Text("%s", _LC("TopMenubar", "Land vehicle driving mode"));
1092 ImGui::Separator();
1093 ImGui::Text("%s", _LC("TopMenubar", "Normal: Modify speed according to turns, other vehicles and character"));
1094 ImGui::Text("%s", _LC("TopMenubar", "Race: Always keep defined speed"));
1095 ImGui::Text("%s", _LC("TopMenubar", "Drag Race: Two vehicles performing a drag race"));
1096 ImGui::Text("%s", _LC("TopMenubar", "Crash: Two vehicles driving in opposite direction"));
1097 ImGui::Text("%s", _LC("TopMenubar", "Chase: Follow character and player vehicle"));
1098 ImGui::EndTooltip();
1099 }
1100
1101 for (auto actor : App::GetGameContext()->GetActorManager()->GetLocalActors())
1102 {
1103 if (actor->ar_driveable == AI)
1104 {
1105 ImGui::PopItemFlag();
1106 ImGui::PopStyleVar();
1107 break;
1108 }
1109 }
1110
1111 if (ai_speed < 1)
1112 ai_speed = 1;
1113
1114 ImGui::InputInt(_LC("TopMenubar", "Speed"), &ai_speed, 1, 100);
1115 if (ImGui::IsItemHovered())
1116 {
1117 ImGui::BeginTooltip();
1118 ImGui::Text("%s", _LC("TopMenubar", "Speed in km/h for land vehicles or knots/s for boats"));
1119 ImGui::EndTooltip();
1120 }
1121
1122 if (ai_altitude < 1)
1123 ai_altitude = 1;
1124
1125 ImGui::InputInt(_LC("TopMenubar", "Altitude"), &ai_altitude, 1, 100);
1126 if (ImGui::IsItemHovered())
1127 {
1128 ImGui::BeginTooltip();
1129 ImGui::Text("%s", _LC("TopMenubar", "Airplane maximum altitude in feet"));
1130 ImGui::EndTooltip();
1131 }
1132
1133 ImGui::Separator();
1134
1135 if (ImGui::Button(StripColorMarksFromText(ai_dname).c_str(), ImVec2(250, 0)))
1136 {
1137 ai_select = true;
1138
1140 m.payload = reinterpret_cast<void*>(new LoaderType(LT_AllBeam));
1142 }
1143 if (ImGui::IsItemHovered())
1144 {
1145 ImGui::BeginTooltip();
1146 ImGui::Text("%s", _LC("TopMenubar", "Land vehicles, boats and airplanes"));
1147 ImGui::EndTooltip();
1148 }
1149
1150 if (ai_mode == 2 || ai_mode == 3) // Drag Race or Crash driving mode
1151 {
1152 ImGui::PushID("vehicle2");
1153 if (ImGui::Button(StripColorMarksFromText(ai_dname2).c_str(), ImVec2(250, 0)))
1154 {
1155 ai_select2 = true;
1156
1158 m.payload = reinterpret_cast<void*>(new LoaderType(LT_AllBeam));
1160 }
1161 if (ImGui::IsItemHovered())
1162 {
1163 ImGui::BeginTooltip();
1164 ImGui::Text("%s", _LC("TopMenubar", "Land vehicles, boats and airplanes"));
1165 ImGui::EndTooltip();
1166 }
1167 ImGui::PopID();
1168 }
1169
1170 ImGui::Separator();
1171
1172 if (ai_rec)
1173 {
1174 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
1175 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
1176 }
1177
1178 if (!ai_waypoints.empty() || ai_mode == 4) // Waypoints provided or Chase driving mode
1179 {
1180 ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[ImGuiCol_ButtonActive]);
1181 }
1182
1183 if (ImGui::Button(_LC("TopMenubar", "Start"), ImVec2(80, 0)))
1184 {
1185 if (ai_mode == 4) // Chase driving mode
1186 {
1187 ai_waypoints.clear();
1188 if (App::GetGameContext()->GetPlayerActor()) // We are in vehicle
1189 {
1190 ai_events waypoint;
1191 waypoint.position = App::GetGameContext()->GetPlayerActor()->getPosition() + Ogre::Vector3(20, 0, 0);
1192 ai_waypoints.push_back(waypoint);
1193 }
1194 else // We are in feet
1195 {
1196 ai_events waypoint;
1197 waypoint.position = App::GetGameContext()->GetPlayerCharacter()->getPosition() + Ogre::Vector3(20, 0, 0);
1198 ai_waypoints.push_back(waypoint);
1199 }
1201 }
1202 else
1203 {
1204 if (ai_waypoints.empty())
1205 {
1207 fmt::format(_LC("TopMenubar", "Select a preset, record or open survey map ({}) to set waypoints."),
1208 App::GetInputEngine()->getEventCommandTrimmed(EV_SURVEY_MAP_CYCLE)), "lightbulb.png");
1209 }
1210 else
1211 {
1213 }
1214 }
1215 }
1216
1217 if (!ai_waypoints.empty() || ai_mode == 4) // Waypoints provided or Chase driving mode
1218 {
1219 ImGui::PopStyleColor();
1220 }
1221
1222 ImGui::SameLine();
1223
1224 if (ImGui::Button(_LC("TopMenubar", "Stop"), ImVec2(80, 0)))
1225 {
1226 if (ai_mode == 4) // Chase driving mode
1227 {
1228 ai_waypoints.clear();
1229 }
1230
1232 {
1233 if (actor->ar_driveable == AI)
1234 {
1236 }
1237 }
1238 }
1239
1240 if (ai_rec)
1241 {
1242 ImGui::PopItemFlag();
1243 ImGui::PopStyleVar();
1244 }
1245
1246 ImGui::SameLine();
1247
1248 ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[ImGuiCol_Button]);
1249 std::string label = "Record";
1250 if (ai_rec)
1251 {
1252 label = "Recording";
1253 ImGui::PushStyleColor(ImGuiCol_Button, RED_TEXT);
1254 }
1255
1256 if (ImGui::Button(label.c_str(), ImVec2(80, 0)))
1257 {
1258 if (!ai_rec)
1259 {
1260 ai_waypoints.clear();
1261 ai_rec = true;
1262 }
1263 else
1264 {
1265 ai_rec = false;
1266 }
1267 }
1268
1269 ImGui::PopStyleColor();
1270 ImGui::Separator();
1271
1272 if (ImGui::CollapsingHeader(_LC("TopMenubar", "Presets")))
1273 {
1274 // Draw whatever we already have (i.e. presets bundled with terrain, see '[AI Presets]' in terrn2 format).
1275 size_t num_rows = ai_presets_all.GetArray().Size();
1276 int display_count = 0;
1277 for (size_t i = 0; i < num_rows; i++)
1278 {
1279 rapidjson::Value& j_row = ai_presets_all[static_cast<rapidjson::SizeType>(i)];
1280
1281 if (j_row.HasMember("terrain") && App::sim_terrain_name->getStr() == j_row["terrain"].GetString())
1282 {
1283 display_count++;
1284 if (ImGui::Button(j_row["preset"].GetString(), ImVec2(250, 0)))
1285 {
1286 ai_waypoints.clear();
1287
1288 for (size_t i = 0; i < j_row["waypoints"].Size(); i++)
1289 {
1290 float x = j_row["waypoints"][i][0].GetFloat();
1291 float y = j_row["waypoints"][i][1].GetFloat();
1292 float z = j_row["waypoints"][i][2].GetFloat();
1293
1294 ai_events waypoint;
1295 waypoint.position = Ogre::Vector3(x, y, z);
1296
1297 int speed = -1;
1298 if (j_row["waypoints"][i].Size() == 4) // Custom speed defined
1299 {
1300 speed = j_row["waypoints"][i][3].GetInt();
1301 if (speed < 5)
1302 {
1303 speed = -1;
1304 }
1305 }
1306 waypoint.speed = speed;
1307 ai_waypoints.push_back(waypoint);
1308 }
1309 }
1310 }
1311 }
1312
1313 // Fetch additional presets, or display error if failed
1314 if (ai_presets_extern.Empty())
1315 {
1317 {
1318 float spinner_size = 8.f;
1319 ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.f) - spinner_size);
1320 LoadingIndicatorCircle("spinner", spinner_size, theme.value_blue_text_color, theme.value_blue_text_color, 10, 10);
1321 }
1322 else if (ai_presets_extern_error != "")
1323 {
1324 ImGui::TextColored(RED_TEXT, "%s", _LC("TopMenubar", "Failed to fetch external presets."));
1325 if (ImGui::Button(_LC("TopMenubar", "Retry")))
1326 {
1327 this->FetchExternAiPresetsOnBackground(); // Will post `MSG_NET_REFRESH_AI_PRESETS` when done.
1328 }
1329 }
1330 else
1331 {
1332 this->FetchExternAiPresetsOnBackground(); // Will post `MSG_NET_REFRESH_AI_PRESETS` when done.
1333 }
1334 }
1335
1336 // If no presets found, display message
1337 if (display_count == 0 && !ai_presets_extern_fetching && ai_presets_extern_error == "")
1338 {
1339 ImGui::Text("%s", _LC("TopMenubar", "No presets found for this terrain :("));
1340 ImGui::Text("%s", _LC("TopMenubar", "Supported terrains:"));
1341 ImGui::Separator();
1342
1343 ImGui::BeginChild("terrains-scrolling", ImVec2(0.f, 200), false);
1344
1345 for (size_t i = 0; i < num_rows; i++)
1346 {
1347 rapidjson::Value& j_row_terrains = ai_presets_all[static_cast<rapidjson::SizeType>(i)];
1348 if (j_row_terrains.HasMember("terrains"))
1349 {
1350 for (size_t i = 0; i < j_row_terrains["terrains"].Size(); i++)
1351 {
1352 ImGui::Text("%s", j_row_terrains["terrains"][i].GetString());
1353 }
1354 }
1355 }
1356
1357 ImGui::EndChild();
1358 }
1359 }
1360
1361 if (ImGui::CollapsingHeader(_LC("TopMenubar", "Waypoints")))
1362 {
1363 if (ai_waypoints.empty())
1364 {
1365 ImGui::Text("%s", _LC("TopMenubar", "No waypoints defined."));
1366 }
1367 else
1368 {
1369 if (ImGui::Button(_LC("TopMenubar", "Export"), ImVec2(250, 0)))
1370 {
1371 std::string s;
1372
1373 for (int i = 0; i < ai_waypoints.size(); i++)
1374 {
1375 // Write position
1376 s += "\n [" + std::to_string(ai_waypoints[i].position.x) + ", " + std::to_string(ai_waypoints[i].position.y) + ", " + std::to_string(ai_waypoints[i].position.z);
1377
1378 // Write custom speed
1379 if (ai_waypoints[i].speed >= 5)
1380 {
1381 s += ", " + std::to_string(ai_waypoints[i].speed);
1382 }
1383
1384 // Close
1385 s += "]";
1386 if (i != ai_waypoints.size() - 1)
1387 {
1388 s += ",";
1389 }
1390 }
1391
1392 std::string json = fmt::format("\n {{\n \"terrain\":\"{}\",\n \"preset\":\"Preset name\",\n \"waypoints\":\n [{}\n ]\n }}", App::sim_terrain_name->getStr(), s);
1393 RoR::Log(json.c_str());
1394
1396 fmt::format(_LC("TopMenubar", "{} waypoints exported to RoR.log"),
1397 ai_waypoints.size()), "lightbulb.png");
1398 }
1399
1400 ImGui::BeginChild("waypoints-scrolling", ImVec2(0.f, 200), false);
1401
1402 for (int i = 0; i < ai_waypoints.size(); i++)
1403 {
1404 ImGui::PushID(i);
1405 ImGui::AlignTextToFramePadding();
1406 ImGui::Text("%d", i);
1407 ImGui::SameLine();
1408 if (ImGui::Button("teleport", ImVec2(60, 0)))
1409 {
1410 Ogre::Vector3* payload = new Ogre::Vector3(ai_waypoints[i].position);
1412 }
1413 if (ImGui::IsItemHovered())
1414 {
1415 ImGui::BeginTooltip();
1416 std::string w = "x:" + std::to_string(ai_waypoints[i].position.x) + " y:" + std::to_string(ai_waypoints[i].position.y) + " z:" + std::to_string(ai_waypoints[i].position.z);
1417 ImGui::Text(w.c_str());
1418 ImGui::EndTooltip();
1419 }
1420 ImGui::SameLine();
1421 ImGui::SetNextItemWidth(90);
1422
1423 if (ai_waypoints[i].speed < -1)
1424 {
1425 ai_waypoints[i].speed = -1;
1426 }
1427 ImGui::InputInt(_LC("TopMenubar", "speed"), &ai_waypoints[i].speed, 1, 100);
1428 if (ImGui::IsItemHovered())
1429 {
1430 ImGui::BeginTooltip();
1431 ImGui::Text(_LC("TopMenubar", "Set waypoint speed in km/h for land vehicles"));
1432 ImGui::Separator();
1433 ImGui::Text(_LC("TopMenubar", "Value -1: Ignore, vehicle will use default speed"));
1434 ImGui::Text(_LC("TopMenubar", "Value >= 5: Override default speed"));
1435 ImGui::EndTooltip();
1436 }
1437 ImGui::PopID();
1438 }
1439
1440 ImGui::EndChild();
1441 }
1442 }
1443
1444 ImGui::PopItemWidth();
1446 m_open_menu_hoverbox_max.x = menu_pos.x + ImGui::GetWindowWidth() + MENU_HOVERBOX_PADDING.x;
1447 m_open_menu_hoverbox_max.y = menu_pos.y + ImGui::GetWindowHeight() + MENU_HOVERBOX_PADDING.y;
1448 App::GetGuiManager()->RequestGuiCaptureKeyboard(ImGui::IsWindowHovered());
1449 ImGui::End();
1450 }
1451 break;
1452
1454 menu_pos.y = window_pos.y + tuning_cursor.y + MENU_Y_OFFSET;
1455 menu_pos.x = tuning_cursor.x + window_pos.x - ImGui::GetStyle().WindowPadding.x;
1456 ImGui::SetNextWindowPos(menu_pos);
1457 if (ImGui::Begin(_LC("TopMenubar", "Tuning menu"), nullptr, static_cast<ImGuiWindowFlags_>(flags)))
1458 {
1459 this->RefreshTuningMenu(); // make sure our local context is valid
1460 if (!tuning_actor)
1461 {
1462 ImGui::PushStyleColor(ImGuiCol_Text, GRAY_HINT_TEXT);
1463 ImGui::Text("%s", _LC("Tuning", "You are on foot."));
1464 ImGui::Text("%s", _LC("Tuning", "Enter a vehicle to tune it."));
1465 ImGui::PopStyleColor();
1466 }
1467 else
1468 {
1471
1472 // SAVED TUNEUPS
1473 ImGui::TextDisabled(fmt::format(_LC("Tuning", "Saved tuneups ({})"), tuning_saves.cqy_results.size()).c_str());
1474 for (CacheQueryResult& tuneup_result: tuning_saves.cqy_results)
1475 {
1476 ImGui::PushID(tuneup_result.cqr_entry->fname.c_str());
1477
1478 ImGui::AlignTextToFramePadding();
1479 ImGui::Bullet();
1480
1481 // Load button (with tuneup name)
1482 ImGui::SameLine();
1483 if (ImGui::Button(tuneup_result.cqr_entry->dname.c_str()))
1484 {
1487 req->mpr_subject = tuneup_result.cqr_entry->fname;
1490 // Why 'MODIFY_PROJECT_REQUESTED' for loading?
1491 // Instead of loading with the saved tuneup directly, we keep the autogenerated and sync it with the save.
1492 // That way, subsequent editing doesn't modify the save until user saves again.
1493 }
1494
1495 // Delete button (right-aligned)
1496 ImGui::SameLine();
1497 if (tuning_rwidget_cursorx_min < ImGui::GetCursorPosX()) // Make sure button won't draw over item name
1498 tuning_rwidget_cursorx_min = ImGui::GetCursorPosX();
1499 std::string delbtn_text = _LC("Tuning", "Delete");
1500 float delbtn_w = ImGui::CalcTextSize(delbtn_text.c_str()).x + ImGui::GetStyle().FramePadding.x * 2;
1501 float delbtn_cursorx = ImGui::GetWindowContentRegionWidth() - delbtn_w;
1502 if (delbtn_cursorx < tuning_rwidget_cursorx_min)
1503 delbtn_cursorx = tuning_rwidget_cursorx_min;
1504 ImGui::SetCursorPosX(delbtn_cursorx);
1505 ImGui::PushStyleColor(ImGuiCol_Button, TUNING_HOLDTOCONFIRM_COLOR);
1506 bool delbtn_pressed = RoR::ImButtonHoldToConfirm(delbtn_text, /*small:*/true, TUNING_HOLDTOCONFIRM_TIMELIMIT);
1507 ImGui::PopStyleColor(); //ImGuiCol_Button
1508 if (delbtn_pressed)
1509 {
1511 }
1512
1513 ImGui::PopID(); // tuneup_result.cqr_entry->fname.c_str()
1514 }
1515
1516 // WORKING TUNEUP
1517 ImGui::Separator();
1518 ImGui::AlignTextToFramePadding();
1519 ImGui::TextDisabled(_LC("Tuning", "Working tuneup"));
1521 {
1522 ImGui::InputText(_LC("Tuning", "Name"), tuning_savebox_buf.GetBuffer(), tuning_savebox_buf.GetCapacity());
1523
1524 if (ImGui::Button(_LC("Tuning","Save")))
1525 {
1533 }
1534 ImGui::SameLine();
1535 ImGui::Checkbox(_LC("Tuning", "Overwrite"), &tuning_savebox_overwrite);
1536
1537 // Cancel button (right-aligned)
1538 if (tuning_rwidget_cursorx_min < ImGui::GetCursorPosX()) // Make sure button won't draw over save button
1539 tuning_rwidget_cursorx_min = ImGui::GetCursorPosX();
1540 std::string cancelbtn_text = _LC("Tuning", "Cancel");
1541 float cancelbtn_w = ImGui::CalcTextSize(cancelbtn_text.c_str()).x + ImGui::GetStyle().FramePadding.x * 2;
1542 float cancelbtn_cursorx = ImGui::GetWindowContentRegionWidth() - cancelbtn_w;
1543 if (cancelbtn_cursorx < tuning_rwidget_cursorx_min)
1544 cancelbtn_cursorx = tuning_rwidget_cursorx_min;
1545 ImGui::SetCursorPosX(cancelbtn_cursorx);
1546 if (ImGui::SmallButton(_LC("Tuning", "Cancel")))
1547 {
1548 tuning_savebox_visible = false;
1549 }
1550 ImGui::Separator();
1551 }
1552 else if (tuneup_def)
1553 {
1554 ImGui::SameLine();
1555 if (ImGui::Button(_LC("Tuning", "Save as...")))
1556 {
1558 }
1559
1560 // Reset button (right-aligned)
1561 ImGui::SameLine();
1562 if (tuning_rwidget_cursorx_min < ImGui::GetCursorPosX()) // Make sure button won't draw over save button
1563 tuning_rwidget_cursorx_min = ImGui::GetCursorPosX();
1564 ImGui::AlignTextToFramePadding();
1565 std::string resetbtn_text = _LC("Tuning", "Reset");
1566 float delbtn_w = ImGui::CalcTextSize(resetbtn_text.c_str()).x + ImGui::GetStyle().FramePadding.x * 2;
1567 float delbtn_cursorx = ImGui::GetWindowContentRegionWidth() - delbtn_w;
1568 if (delbtn_cursorx < tuning_rwidget_cursorx_min)
1569 delbtn_cursorx = tuning_rwidget_cursorx_min;
1570 ImGui::SetCursorPosX(delbtn_cursorx);
1571 ImGui::PushStyleColor(ImGuiCol_Button, TUNING_HOLDTOCONFIRM_COLOR);
1572 bool resetbtn_pressed = ImButtonHoldToConfirm(resetbtn_text, /*small:*/false, TUNING_HOLDTOCONFIRM_TIMELIMIT);
1573 ImGui::PopStyleColor(); //ImGuiCol_Button
1574 if (resetbtn_pressed)
1575 {
1577 request->mpr_target_actor = tuning_actor;
1580 }
1581 }
1582
1583 // ADDONPARTS
1584
1585 ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver);
1586 std::string addonparts_title = fmt::format(_LC("TopMenubar", "Addon parts ({})"), tuning_addonparts.size());
1587 if (ImGui::CollapsingHeader(addonparts_title.c_str()))
1588 {
1589 for (size_t i = 0; i < tuning_addonparts.size(); i++)
1590 {
1591 const CacheEntryPtr& addonpart_entry = tuning_addonparts[i];
1592
1593 ImGui::PushID(addonpart_entry->fname.c_str());
1594 const bool conflict_w_hovered = tuning_hovered_addonpart
1595 && (addonpart_entry != tuning_hovered_addonpart)
1597 bool used = TuneupUtil::isAddonPartUsed(tuneup_def, addonpart_entry->fname);
1598 const ImVec2 checkbox_cursor = ImGui::GetCursorScreenPos();
1599 if (ImGui::Checkbox(addonpart_entry->dname.c_str(), &used)
1600 && !conflict_w_hovered
1602 {
1604 req->mpr_type = (used)
1607 req->mpr_subject = addonpart_entry->fname;
1610 }
1611 // Draw conflict markers
1613 {
1614 // Gray-ish X inside the checkbox
1615 const float square_sz = ImGui::GetFrameHeight();
1616 const ImVec2 min = checkbox_cursor + ImGui::GetStyle().FramePadding*1.4f;
1617 const ImVec2 max = checkbox_cursor + (ImVec2(square_sz, square_sz) - ImGui::GetStyle().FramePadding*1.5f);
1618 const ImColor X_COLOR(0.5f, 0.48f, 0.45f);
1619 ImGui::GetWindowDrawList()->AddLine(min, max, X_COLOR, 4.f);
1620 ImGui::GetWindowDrawList()->AddLine(ImVec2(min.x, max.y), ImVec2(max.x, min.y), X_COLOR, 4.f);
1621 }
1622 if (conflict_w_hovered)
1623 {
1624 // Red unrounded square around the checkbox
1625 const float square_sz = ImGui::GetFrameHeight();
1626 const ImVec2 min = checkbox_cursor;
1627 const ImVec2 max = checkbox_cursor + ImVec2(square_sz + 0.5f, square_sz);
1628 const ImColor SQ_COLOR(0.7f, 0.1f, 0.f);
1629 ImGui::GetWindowDrawList()->AddRect(min, max, SQ_COLOR, 0.f, ImDrawCornerFlags_None, 3.f);
1630 }
1631 // Record when checkbox is hovered - for drawing conflict markers
1632 if (ImGui::IsItemHovered())
1633 {
1634 tuning_hovered_addonpart = addonpart_entry;
1635 }
1636 else if (tuning_hovered_addonpart == addonpart_entry)
1637 {
1638 tuning_hovered_addonpart = nullptr;
1639 }
1640 // Reload button (right-aligned)
1641 ImGui::SameLine();
1642 if (tuning_rwidget_cursorx_min < ImGui::GetCursorPosX()) // Make sure button won't draw over save button
1643 tuning_rwidget_cursorx_min = ImGui::GetCursorPosX();
1644 ImGui::AlignTextToFramePadding();
1645 std::string reloadbtn_text = _LC("Tuning", "Reload");
1646 const float reloadbtn_w = ImGui::CalcTextSize(reloadbtn_text.c_str()).x + ImGui::GetStyle().FramePadding.x * 2;
1647 const float reloadbtn_cursorx = std::max(ImGui::GetWindowContentRegionWidth() - reloadbtn_w, tuning_rwidget_cursorx_min);
1648 ImGui::SetCursorPosX(reloadbtn_cursorx);
1649 const bool reloadbtn_pressed = ImGui::SmallButton(reloadbtn_text.c_str());
1650 if (reloadbtn_pressed)
1651 {
1652 // Create spawn request while actor still exists
1653 // Note we don't use `ActorModifyRequest::Type::RELOAD` because we don't need the bundle reloaded.
1656 srq->asr_rotation = Ogre::Quaternion(Ogre::Degree(270) - Ogre::Radian(tuning_actor->getRotation()), Ogre::Vector3::UNIT_Y);
1663
1664 // Request bundle reloading and chain the actor delete/spawn messages to it.
1668 }
1669
1670 ImGui::PopID(); //(addonpart_entry->fname.c_str());
1671 }
1672
1673 if (ImGui::Button(_LC("Tuning", "Browse all parts")))
1674 {
1676 }
1677
1678 ImGui::Separator();
1679 }
1680
1681 // Draw props
1682 size_t total_props = tuning_actor->GetGfxActor()->getProps().size();
1683 std::string props_title = fmt::format(_LC("Tuning", "Props ({})"), total_props);
1684 if (ImGui::CollapsingHeader(props_title.c_str()))
1685 {
1686 // Draw all props (those removed by addonparts are also present as placeholders)
1687 for (Prop const& p: tuning_actor->GetGfxActor()->getProps())
1688 {
1689 ImGui::PushID(p.pp_id);
1690 ImGui::AlignTextToFramePadding();
1691
1692 this->DrawTuningBoxedSubjectIdInline(p.pp_id);
1693
1695 p.pp_id,
1696 p.pp_media[0],
1697 tuneup_def && tuneup_def->isPropUnwanted(p.pp_id),
1698 tuneup_def && tuneup_def->isPropForceRemoved(p.pp_id),
1701
1702 // Draw special prop tooltip
1703 if (p.pp_beacon_type == 'L' || p.pp_beacon_type == 'R' || p.pp_beacon_type == 'w')
1704 {
1705 ImGui::SameLine();
1706 ImGui::TextDisabled("(special!)");
1707 if (ImGui::IsItemHovered())
1708 {
1709 ImGui::BeginTooltip();
1710 ImGui::Text("special prop - aerial nav light");
1711 ImGui::EndTooltip();
1712 }
1713 }
1714 else if (p.pp_wheel_mesh_obj)
1715 {
1716 ImGui::SameLine();
1717 ImGui::TextDisabled("(special!)");
1718 if (ImGui::IsItemHovered())
1719 {
1720 ImGui::BeginTooltip();
1721 ImGui::Text("special prop - dashboard + dirwheel");
1722 ImGui::EndTooltip();
1723 }
1724
1725 }
1726
1728 p.pp_id,
1729 tuneup_def && tuneup_def->isPropProtected(p.pp_id),
1732
1733 ImGui::PopID(); // p.pp_id
1734 }
1735
1736 ImGui::Separator();
1737 }
1738
1739 // Ditto for flexbodies
1740 size_t total_flexbodies = tuning_actor->GetGfxActor()->GetFlexbodies().size();
1741 std::string flexbodies_title = fmt::format(_LC("Tuning", "Flexbodies ({})"), total_flexbodies);
1742 if (ImGui::CollapsingHeader(flexbodies_title.c_str()))
1743 {
1744 // Draw all flexbodies (those removed by addonparts are also present as placeholders)
1745 for (FlexBody* flexbody: tuning_actor->GetGfxActor()->GetFlexbodies())
1746 {
1747 ImGui::PushID(flexbody->getID());
1748 ImGui::AlignTextToFramePadding();
1749
1750 this->DrawTuningBoxedSubjectIdInline(flexbody->getID());
1751
1753 flexbody->getID(),
1754 flexbody->getOrigMeshName(),
1755 tuneup_def && tuneup_def->isFlexbodyUnwanted(flexbody->getID()),
1756 tuneup_def && tuneup_def->isFlexbodyForceRemoved(flexbody->getID()),
1759
1761 flexbody->getID(),
1762 tuneup_def && tuneup_def->isFlexbodyProtected(flexbody->getID()),
1765
1766 ImGui::PopID(); // flexbody->getID()
1767 }
1768 }
1769
1770 // Draw wheels
1771 const int total_wheels = tuning_actor->ar_num_wheels;
1772 std::string wheels_title = fmt::format(_LC("TopMenubar", "Wheels ({})"), total_wheels);
1773 if (ImGui::CollapsingHeader(wheels_title.c_str()))
1774 {
1775 for (WheelID_t i = 0; i < total_wheels; i++)
1776 {
1777 ImGui::PushID(i);
1778 ImGui::AlignTextToFramePadding();
1779
1781
1782 // Draw R/L radio buttons
1783 WheelSide forced_side = WheelSide::INVALID;
1784 if (tuneup_def && tuneup_def->isWheelSideForced(i, /*[out]*/forced_side))
1785 {
1786 ImGui::PushStyleColor(ImGuiCol_Border, ORANGE_TEXT);
1787 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.f);
1788 }
1789 const RoR::WheelSide active_side = TuneupUtil::getTweakedWheelSide(tuneup_def, i, tuning_actor->GetGfxActor()->getWheelSide(i));
1790 RoR::WheelSide selected_side = active_side;
1791 if (ImGui::RadioButton("##L", active_side == WheelSide::LEFT))
1792 selected_side = WheelSide::LEFT;
1793 ImGui::SameLine();
1794 ImGui::TextDisabled("|");
1795 ImGui::SameLine();
1796 if (ImGui::RadioButton("##R", active_side == WheelSide::RIGHT))
1797 selected_side = WheelSide::RIGHT;
1798
1799 // Draw rim mesh name
1800 ImGui::SameLine();
1801 ImGui::Text("%s", tuning_actor->GetGfxActor()->getWheelRimMeshName(i).c_str());
1802
1803 // Draw reset button
1804
1805 bool resetPressed = false;
1806 if (tuneup_def && tuneup_def->isWheelSideForced(i, forced_side))
1807 {
1808 ImGui::SameLine();
1809 ImGui::SameLine();
1810 ImGui::PushStyleColor(ImGuiCol_Text, GRAY_HINT_TEXT);
1811 resetPressed = ImGui::SmallButton(_LC("Tuning", "Reset"));
1812 ImGui::PopStyleColor(); //ImGuiCol_Text, GRAY_HINT_TEXT
1813 ImGui::PopStyleVar(); //ImGuiStyleVar_FrameBorderSize, 1.f
1814 ImGui::PopStyleColor(); //ImGuiCol_Border, ORANGE_TEXT
1815 }
1816
1817 // modify project if needed
1818 if (selected_side != active_side)
1819 {
1822 req->mpr_subject_id = i;
1823 req->mpr_value_int = (int)selected_side;
1826 }
1827 else if (resetPressed)
1828 {
1831 req->mpr_subject_id = i;
1834 }
1835
1837 i,
1838 tuneup_def && tuneup_def->isWheelProtected(i),
1841
1842 ImGui::PopID(); // i
1843 }
1844 }
1845
1846 // Draw flares
1847 size_t total_flares = tuning_actor->ar_flares.size();
1848 std::string flares_title = fmt::format(_LC("Tuning", "Flares ({})"), total_flares);
1849 if (ImGui::CollapsingHeader(flares_title.c_str()))
1850 {
1851 // Draw all flares (those removed by addonparts are also present as placeholders)
1852 for (FlareID_t flareid = 0; flareid < (int)tuning_actor->ar_flares.size(); flareid++)
1853 {
1854 ImGui::PushID(flareid);
1855 ImGui::AlignTextToFramePadding();
1856
1857 this->DrawTuningBoxedSubjectIdInline(flareid);
1858
1859 // Compose flare description string
1860 const FlareType flaretype = tuning_actor->ar_flares[flareid].fl_type;
1861 std::string flarename;
1862 if (flaretype == FlareType::USER)
1863 {
1864 int controlnumber = tuning_actor->ar_flares[flareid].controlnumber + 1; // Convert range 0-9 to 1-10
1865 flarename = fmt::format("{} {}", (char)flaretype, controlnumber);
1866 }
1867 else if (flaretype == FlareType::DASHBOARD)
1868 {
1869 std::string linkname = tuning_actor->ar_dashboard->getLinkNameForID((DashData)tuning_actor->ar_flares[flareid].dashboard_link);
1870 flarename = fmt::format("{} {}", (char)flaretype, linkname);
1871 }
1872 else
1873 {
1874 flarename = fmt::format("{}", (char)flaretype);
1875 }
1876
1878 flareid,
1879 flarename,
1880 tuneup_def && tuneup_def->isFlareUnwanted(flareid),
1881 tuneup_def && tuneup_def->isFlareForceRemoved(flareid),
1884
1886 flareid,
1887 tuneup_def && tuneup_def->isFlareProtected(flareid),
1890
1891 ImGui::PopID(); // flareid
1892 }
1893 }
1894
1895 // Draw exhausts
1896 size_t total_exhausts = tuning_actor->GetGfxActor()->getExhausts().size();
1897 std::string exhausts_title = fmt::format(_LC("Tuning", "Exhausts ({})"), total_exhausts);
1898 if (ImGui::CollapsingHeader(exhausts_title.c_str()))
1899 {
1900 // Draw all exhausts (those removed by addonparts are also present as placeholders)
1901 for (ExhaustID_t exhaustid = 0; exhaustid < (int)total_exhausts; exhaustid++)
1902 {
1903 ImGui::PushID(exhaustid);
1904 ImGui::AlignTextToFramePadding();
1905
1906 this->DrawTuningBoxedSubjectIdInline(exhaustid);
1907
1909 exhaustid,
1910 tuning_actor->GetGfxActor()->getExhausts()[exhaustid].particleSystemName,
1911 tuneup_def && tuneup_def->isExhaustUnwanted(exhaustid),
1912 tuneup_def && tuneup_def->isExhaustForceRemoved(exhaustid),
1915
1917 exhaustid,
1918 tuneup_def && tuneup_def->isExhaustProtected(exhaustid),
1921
1922 ImGui::PopID(); // exhaustid
1923 }
1924 }
1925
1926 // Draw managed materials
1927 size_t total_materials = tuning_actor->ar_managed_materials.size();
1928 std::string materials_title = fmt::format(_LC("Tuning", "Managed Materials ({})"), total_materials);
1929 if (ImGui::CollapsingHeader(materials_title.c_str()))
1930 {
1931 // Draw all materials (those removed by addonparts are also present as placeholders)
1932 for (auto mm_pair: tuning_actor->ar_managed_materials)
1933 {
1934 const std::string& material_name = mm_pair.first;
1935 ImGui::PushID(material_name.c_str());
1936 ImGui::AlignTextToFramePadding();
1937
1940 material_name,
1941 tuneup_def && tuneup_def->isManagedMatUnwanted(material_name),
1942 tuneup_def && tuneup_def->isManagedMatForceRemoved(material_name),
1945
1948 tuneup_def && tuneup_def->isManagedMatProtected(material_name),
1951 material_name);
1952
1953 ImGui::PopID(); // material_name.c_str()
1954 }
1955 }
1956
1957 // Draw videocameras
1958 size_t total_videocameras = tuning_actor->GetGfxActor()->getVideoCameras().size();
1959 std::string videocameras_title = fmt::format(_LC("Tuning", "Videocameras ({})"), total_videocameras);
1960 if (ImGui::CollapsingHeader(videocameras_title.c_str()))
1961 {
1962 // Draw all videocameras (those removed by addonparts are also present as placeholders)
1963 for (VideoCameraID_t videocameraid = 0; videocameraid < (int)total_videocameras; videocameraid++)
1964 {
1965 ImGui::PushID(videocameraid);
1966 ImGui::AlignTextToFramePadding();
1967
1968 this->DrawTuningBoxedSubjectIdInline(videocameraid);
1969
1970 const VideoCamera& vcam = tuning_actor->GetGfxActor()->getVideoCameras()[videocameraid];
1971
1972 // Draw RTT material name
1973 ImGui::SameLine();
1974 ImGui::Dummy(ImVec2(3, 3));
1975 ImGui::SameLine();
1976 ImGui::Text("%s", tuning_actor->GetGfxActor()->getVideoCameras()[videocameraid].vcam_mat_name_orig.c_str());
1977
1978 // Setup the 'forced' state orange styling
1979
1980 VideoCamRole forced_role_def = VCAM_ROLE_INVALID;
1981 if (tuneup_def && tuneup_def->isVideoCameraRoleForced(videocameraid, /*[out]*/forced_role_def))
1982 {
1983 ImGui::PushStyleColor(ImGuiCol_Border, ORANGE_TEXT);
1984 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.f);
1985 }
1986
1987 // Draw the 'flipped' checkbox where applicable
1988 const bool is_mirror_flip = vcam.vcam_role == VCAM_ROLE_MIRROR || vcam.vcam_role == VCAM_ROLE_TRACKING_MIRROR;
1989 const bool is_mirror_noflip = vcam.vcam_role == VCAM_ROLE_MIRROR_NOFLIP || vcam.vcam_role == VCAM_ROLE_TRACKING_MIRROR_NOFLIP;
1990 bool checkbox_pressed = false;
1991 VideoCamRole desired_role = VCAM_ROLE_INVALID;
1992 if ((is_mirror_flip || is_mirror_noflip))
1993 {
1994 ImGui::SameLine();
1995 bool checkbox_checked = is_mirror_flip;
1996 checkbox_pressed = ImGui::Checkbox(_LC("Tuning", "Flipped"), &checkbox_checked);
1997 switch (vcam.vcam_role)
1998 {
1999 case VCAM_ROLE_MIRROR: desired_role = VCAM_ROLE_MIRROR_NOFLIP; break;
2000 case VCAM_ROLE_MIRROR_NOFLIP: desired_role = VCAM_ROLE_MIRROR; break;
2003 default: break;
2004 }
2005 }
2006 else
2007 {
2008 // Inform user why this videocamera isn't flippable
2009 ImGui::SameLine();
2010 switch (vcam.vcam_role)
2011 {
2012 case VCAM_ROLE_MIRROR_PROP_LEFT: ImGui::TextDisabled(_LC("Tuning", "(Mirror prop - Left)")); break;
2013 case VCAM_ROLE_MIRROR_PROP_RIGHT: ImGui::TextDisabled(_LC("Tuning", "(Mirror prop - Right)")); break;
2014 default: ImGui::TextDisabled(_LC("Tuning", "(Videocamera)")); break;
2015 }
2016 }
2017
2018 // Draw reset button and reset the orange styling
2019
2020 bool reset_pressed = false;
2021 if (tuneup_def && tuneup_def->isVideoCameraRoleForced(videocameraid, /*[out]*/forced_role_def))
2022 {
2023 ImGui::SameLine();
2024 ImGui::SameLine();
2025 ImGui::PushStyleColor(ImGuiCol_Text, GRAY_HINT_TEXT);
2026 reset_pressed = ImGui::SmallButton(_LC("Tuning", "Reset"));
2027 ImGui::PopStyleColor(); //ImGuiCol_Text, GRAY_HINT_TEXT
2028 ImGui::PopStyleVar(); //ImGuiStyleVar_FrameBorderSize, 1.f
2029 ImGui::PopStyleColor(); //ImGuiCol_Border, ORANGE_TEXT
2030 }
2031
2032 // modify project if needed
2033 if (checkbox_pressed)
2034 {
2037 req->mpr_subject_id = videocameraid;
2038 req->mpr_value_int = (int)desired_role;
2041 }
2042 else if (reset_pressed)
2043 {
2046 req->mpr_subject_id = videocameraid;
2049 }
2050
2051
2052 ImGui::PopID(); // videocameraid
2053 }
2054 }
2055 }
2056
2058 m_open_menu_hoverbox_max.x = menu_pos.x + ImGui::GetWindowWidth() + MENU_HOVERBOX_PADDING.x;
2059 m_open_menu_hoverbox_max.y = menu_pos.y + ImGui::GetWindowHeight() + MENU_HOVERBOX_PADDING.y;
2060 App::GetGuiManager()->RequestGuiCaptureKeyboard(ImGui::IsWindowHovered());
2061 ImGui::End();
2062 }
2063 break;
2064
2065 default:
2066 m_open_menu_hoverbox_min = ImVec2(0,0);
2067 m_open_menu_hoverbox_max = ImVec2(0,0);
2068 }
2069
2070 ImGui::PopStyleColor(2); // WindowBg, Button
2071}
2072
2073bool TopMenubar::ShouldDisplay(ImVec2 window_pos)
2074{
2075 if (!App::GetGuiManager()->AreStaticMenusAllowed())
2076 {
2077 return false;
2078 }
2079
2080 if (ImGui::IsMouseDown(1))
2081 {
2082 return false;
2083 }
2084
2085 if (ai_menu)
2086 {
2088 return true;
2089 }
2090
2091 ImVec2 box_min(0,0);
2092 ImVec2 box_max(ImGui::GetIO().DisplaySize.x, ImGui::GetStyle().WindowPadding.y + PANEL_HOVERBOX_HEIGHT);
2093 ImVec2 mouse_pos = ImGui::GetIO().MousePos;
2094 const bool window_hovered ((mouse_pos.x >= box_min.x) && (mouse_pos.x <= box_max.x) &&
2095 (mouse_pos.y >= box_min.y) && (mouse_pos.y <= box_max.y));
2096 bool result = window_hovered;
2097
2098 bool menu_hovered = false;
2100 {
2101 menu_hovered = ((mouse_pos.x >= m_open_menu_hoverbox_min.x) && (mouse_pos.x <= m_open_menu_hoverbox_max.x) &&
2102 (mouse_pos.y >= m_open_menu_hoverbox_min.y) && (mouse_pos.y <= m_open_menu_hoverbox_max.y));
2103 }
2104 result |= menu_hovered;
2105
2106 bool box_hovered = false;
2108 {
2109 box_hovered = ((mouse_pos.x >= m_state_box_hoverbox_min.x) && (mouse_pos.x <= m_state_box_hoverbox_max.x) &&
2110 (mouse_pos.y >= m_state_box_hoverbox_min.y) && (mouse_pos.y <= m_state_box_hoverbox_max.y));
2111 result |= box_hovered;
2112 }
2113
2114 if (box_hovered && !menu_hovered)
2115 {
2117 }
2118
2119 return result;
2120}
2121
2123{
2124 // Count actors owned by the player
2125 unsigned int num_actors_player = 0;
2127 {
2128 if (actor->ar_net_source_id == user.uniqueid)
2129 {
2130 ++num_actors_player;
2131 }
2132 }
2133
2134 // Display user in list
2135#ifdef USE_SOCKETW
2136 const Ogre::ColourValue player_color = App::GetNetwork()->GetPlayerColor(user.colournum);
2137 ImVec4 player_gui_color(player_color.r, player_color.g, player_color.b, 1.f);
2138 ImGui::PushStyleColor(ImGuiCol_Text, player_gui_color);
2139 ImGui::Text("%s: %u (%s, Ver: %s, Lang: %s)",
2140 user.username, num_actors_player,
2141 App::GetNetwork()->UserAuthToStringShort(user).c_str(),
2142 user.clientversion, user.language);
2143 ImGui::PopStyleColor();
2144#endif // USE_SOCKETW
2145
2146 // Display actor list
2147 Ogre::TexturePtr tex1 = FetchIcon("control_pause.png");
2148 Ogre::TexturePtr tex2 = FetchIcon("control_play.png");
2149 int i = 0;
2151 {
2152 if ((!actor->ar_hide_in_actor_list) && (actor->ar_net_source_id == user.uniqueid))
2153 {
2154 std::string id = fmt::format("{}:{}", i++, user.uniqueid);
2155 ImGui::PushID(id.c_str());
2156 if (actor->ar_state == ActorState::NETWORKED_OK)
2157 {
2158 if (ImGui::ImageButton(reinterpret_cast<ImTextureID>(tex1->getHandle()), ImVec2(16, 16)))
2159 {
2161 }
2162 }
2163 else if (actor->ar_state == ActorState::NETWORKED_HIDDEN)
2164 {
2165 if (ImGui::ImageButton(reinterpret_cast<ImTextureID>(tex2->getHandle()), ImVec2(16, 16)))
2166 {
2168 }
2169 }
2170 else // Our actor(s)
2171 {
2172 std::string text_buf_rem = fmt::format(" X ##[{}]", i);
2173 ImGui::PushStyleColor(ImGuiCol_Text, RED_TEXT);
2174 if (ImGui::Button(text_buf_rem.c_str()))
2175 {
2177 }
2178 ImGui::PopStyleColor();
2179 }
2180 ImGui::PopID();
2181 ImGui::SameLine();
2182
2183 std::string actortext_buf = fmt::format("{} ({}) ##[{}:{}]", StripColorMarksFromText(actor->ar_design_name).c_str(), actor->ar_filename.c_str(), i++, user.uniqueid);
2184 if (ImGui::Button(actortext_buf.c_str())) // Button clicked?
2185 {
2187 }
2188 }
2189 }
2190}
2191
2193{
2194 std::vector<ActorPtr> actor_list;
2196 {
2197 if (!actor->ar_hide_in_actor_list)
2198 {
2199 actor_list.emplace_back(actor);
2200 }
2201 }
2202 if (actor_list.empty())
2203 {
2204 ImGui::PushStyleColor(ImGuiCol_Text, GRAY_HINT_TEXT);
2205 ImGui::Text("%s", _LC("TopMenubar", "None spawned yet"));
2206 ImGui::Text("%s", _LC("TopMenubar", "Use [Simulation] menu"));
2207 ImGui::PopStyleColor();
2208 }
2209 else
2210 {
2211 ActorPtr player_actor = App::GetGameContext()->GetPlayerActor();
2212 int i = 0;
2213 for (ActorPtr& actor : actor_list)
2214 {
2215 std::string text_buf_rem = fmt::format("X ##[{}]", i);
2216 ImGui::PushStyleColor(ImGuiCol_Text, RED_TEXT);
2217 if (ImGui::Button(text_buf_rem.c_str()))
2218 {
2220 }
2221 ImGui::PopStyleColor();
2222 ImGui::SameLine();
2223
2224 std::string text_buf = fmt::format( "[{}] {}", i++, StripColorMarksFromText(actor->ar_design_name).c_str());
2225 if (actor == player_actor)
2226 {
2227 ImGui::PushStyleColor(ImGuiCol_Text, GREEN_TEXT);
2228 }
2229 else if (std::find(actor->ar_linked_actors.begin(), actor->ar_linked_actors.end(), player_actor) != actor->ar_linked_actors.end())
2230 {
2231 ImGui::PushStyleColor(ImGuiCol_Text, ORANGE_TEXT);
2232 }
2233 else if (actor->ar_state == ActorState::LOCAL_SIMULATED)
2234 {
2235 ImGui::PushStyleColor(ImGuiCol_Text, WHITE_TEXT);
2236 }
2237 else
2238 {
2239 ImGui::PushStyleColor(ImGuiCol_Text, GRAY_HINT_TEXT);
2240 }
2241 if (ImGui::Button(text_buf.c_str())) // Button clicked?
2242 {
2244 }
2245 ImGui::PopStyleColor();
2246 }
2247 }
2248}
2249
2250void DrawRepairBoxEvent(events ev, std::string const& desc)
2251{
2252 ImDrawEventHighlighted(ev); ImGui::SameLine(); ImGui::TextDisabled(desc.c_str()); ImGui::NextColumn();
2253}
2254
2255void DrawRepairBoxModkey(OIS::KeyCode modkey, std::string const& desc)
2256{
2257 ImDrawModifierKeyHighlighted(modkey); ImGui::SameLine(); ImGui::TextDisabled(desc.c_str()); ImGui::NextColumn();
2258}
2259
2261{
2262 float content_width = 0.f;
2263 // Always drawn on top:
2264 std::string special_text;
2265 ImVec4 special_color = ImGui::GetStyle().Colors[ImGuiCol_Text]; // Regular color
2266 float special_text_centering_weight = 1.f; // 0 = no centering
2267 // Only for race_box:
2268 std::string special_text_b;
2269 std::string special_text_c;
2270 std::string special_text_d;
2271 ImVec4 special_color_c = ImVec4(0,0,0,0);
2273
2274 // Gather state info
2275 if (App::GetGameContext()->GetActorManager()->IsSimulationPaused() && !App::GetGuiManager()->IsGuiHidden())
2276 {
2277 special_color = ORANGE_TEXT;
2278 special_text = fmt::format(_LC("TopMenubar", "All physics paused, press {} to resume"),
2279 App::GetInputEngine()->getEventCommandTrimmed(EV_COMMON_TOGGLE_PHYSICS));
2280 content_width = ImGui::CalcTextSize(special_text.c_str()).x;
2281 }
2282 else if (App::GetGameContext()->GetPlayerActor() &&
2283 App::GetGameContext()->GetPlayerActor()->ar_physics_paused &&
2284 !App::GetGuiManager()->IsGuiHidden())
2285 {
2286 special_color = GREEN_TEXT;
2287 special_text = fmt::format(_LC("TopMenubar", "Vehicle physics paused, press {} to resume"),
2288 App::GetInputEngine()->getEventCommandTrimmed(EV_TRUCK_TOGGLE_PHYSICS));
2289 content_width = ImGui::CalcTextSize(special_text.c_str()).x;
2290 }
2291 else if (App::GetGameContext()->GetPlayerActor() &&
2292 App::GetGameContext()->GetPlayerActor()->ar_state == ActorState::LOCAL_REPLAY)
2293 {
2294 content_width = 300;
2296 special_text = _LC("TopMenubar", "Replay");
2297 }
2298 else if (App::GetGameContext()->GetRepairMode().IsLiveRepairActive())
2299 {
2300 special_text = fmt::format(_LC("TopMenubar", "Live repair mode, hit '{}' to stop"),
2301 App::GetInputEngine()->getEventCommandTrimmed(EV_COMMON_REPAIR_TRUCK));
2302 content_width = 450;
2304 special_color = GREEN_TEXT;
2305 special_text_centering_weight = 0.7f;
2306 }
2307 else if (App::GetGameContext()->GetRepairMode().IsQuickRepairActive())
2308 {
2309 special_text = fmt::format(_LC("TopMenubar", "Quick repair ('{}' for Live repair)"),
2310 App::GetInputEngine()->getEventCommandTrimmed(EV_COMMON_LIVE_REPAIR_MODE));
2311 content_width = 450;
2313 special_color = ORANGE_TEXT;
2314 special_text_centering_weight = 0.7f;
2315 }
2316 else if (App::GetGfxScene()->GetSimDataBuffer().simbuf_dir_arrow_visible)
2317 {
2319
2320 // Calculate distance
2323 float distance = 0.0f;
2325 if (player_actor != nullptr && App::GetGameContext()->GetPlayerActor() &&
2327 {
2328 distance = player_actor->GetGfxActor()->GetSimDataBuffer().simbuf_pos.distance(data.simbuf_dir_arrow_target);
2329 }
2330 else
2331 {
2332 distance = data.simbuf_character_pos.distance(data.simbuf_dir_arrow_target);
2333 }
2334
2335 // format text
2337 special_text_b = fmt::format("{:.1f} {}", distance, _LC("DirectionArrow", "meter"));
2338 content_width = ImGui::CalcTextSize(special_text.c_str()).x + ImGui::CalcTextSize(special_text_b.c_str()).x;
2339
2341 special_text_c = fmt::format("{:02d}.{:02d}.{:02d}", (int)(time) / 60, (int)(time) % 60, (int)(time * 100.0) % 100);
2343 special_color_c = (time_diff > 0.0f)
2344 ? theme.value_red_text_color
2345 : ((time_diff < 0.0f) ? theme.success_text_color : theme.value_blue_text_color);
2346
2348 {
2350 special_text_d = fmt::format("{:02d}.{:02d}.{:02d}", (int)(best_time) / 60, (int)(best_time) % 60, (int)(best_time * 100.0) % 100);
2351 }
2352 }
2353 else if (App::sim_state->getEnum<SimState>() == SimState::EDITOR_MODE)
2354 {
2355 special_color = GREEN_TEXT;
2356 special_text = fmt::format(_LC("TopMenubar", "Terrain editing mode, press {} to exit"),
2357 App::GetInputEngine()->getEventCommandTrimmed(EV_COMMON_TOGGLE_TERRAIN_EDITOR));
2358
2359 if (App::GetGameContext()->GetTerrain()->getCacheEntry()->resource_bundle_type == "Zip")
2360 {
2361 // This is a read-only (ZIPped) terrain; offer the importer script.
2362 content_width = ImGui::CalcTextSize(special_text.c_str()).x + 25.f;
2364 }
2365 else
2366 {
2367 special_text = fmt::format(_LC("TopMenubar", "Terrain editing mode, press {} to exit"),
2368 App::GetInputEngine()->getEventCommandTrimmed(EV_COMMON_TOGGLE_TERRAIN_EDITOR));
2369 content_width = ImGui::CalcTextSize(special_text.c_str()).x;
2371 }
2372 }
2373
2374 // Draw box if needed
2375 if (!special_text.empty())
2376 {
2377 ImVec2 box_pos;
2378 box_pos.y = top_offset;
2379 box_pos.x = (ImGui::GetIO().DisplaySize.x / 2) - ((content_width / 2) + ImGui::GetStyle().FramePadding.x);
2380 ImGui::SetNextWindowPos(box_pos);
2381 ImGui::SetNextWindowSize(ImVec2(0.f, 0.f));
2382 ImGui::SetNextWindowContentWidth(content_width);
2383 ImGuiWindowFlags flags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
2384 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse;
2385 ImGui::PushStyleColor(ImGuiCol_WindowBg, App::GetGuiManager()->GetTheme().semitransparent_window_bg);
2386 if (ImGui::Begin(special_text.c_str(), nullptr, flags))
2387 {
2389 {
2390 // Center the text, the box may be wider
2391 float text_w = ImGui::CalcTextSize(special_text.c_str()).x;
2392 ImGui::SetCursorPosX(((content_width / 2) - (text_w / 2)) * special_text_centering_weight);
2393 }
2394 ImGui::TextColored(special_color, "%s", special_text.c_str());
2395
2397 {
2398 ImGui::SameLine();
2399
2400 // Progress bar with frame index/count
2402 float fraction = (float)std::abs(replay->getCurrentFrame())/(float)replay->getNumFrames();
2403 Str<100> pbar_text; pbar_text << replay->getCurrentFrame() << "/" << replay->getNumFrames();
2404 float pbar_width = content_width - (ImGui::GetStyle().ItemSpacing.x + ImGui::CalcTextSize(special_text.c_str()).x);
2405 ImGui::ProgressBar(fraction, ImVec2(pbar_width, ImGui::GetTextLineHeight()), pbar_text.ToCStr());
2406
2407 // Game time text
2408 float time_sec = replay->getLastReadTime() / 1000000.0;
2409 char str[200];
2410 int str_pos = 0;
2411 if (time_sec > 60)
2412 {
2413 int min = (int)time_sec / 60;
2414 str_pos = snprintf(str, 200, "%dmin ", min);
2415 time_sec -= (float)min * 60.f;
2416 }
2417 snprintf(str+str_pos, 200-str_pos, "%.2fsec", time_sec);
2418 ImGui::TextDisabled("%s: %s", _LC("TopMenubar", "Time"), str);
2419
2420 }
2422 {
2423 ImGui::SameLine();
2424 ImGui::Text(special_text_b.c_str());
2425 ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2) - (ImGui::CalcTextSize(special_text_c.c_str()).x / 2));
2426 ImGui::TextColored(special_color_c,"%s", special_text_c.c_str());
2427
2428 Str<300> text;
2429 text << "Best Time: " << special_text_d.c_str();
2430 ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2) - (ImGui::CalcTextSize(text).x / 2));
2431
2432 if (!special_text_d.empty())
2433 {
2434 ImGui::TextDisabled(text);
2435 }
2436 }
2438 {
2439 // Draw special element on the right
2440 ImGui::SameLine();
2441 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.f, 0.f));
2443 {
2445 ImGui::ProgressBar(fraction, ImVec2(15.f, ImGui::GetTextLineHeight() / 2.f), "");
2446 ImGui::SameLine();
2447 }
2448 DrawGCheckbox(App::ui_show_live_repair_controls, _LC("LiveRepair", "Show controls"));
2449 ImGui::PopStyleVar(); // FramePadding
2450
2451 const ImVec2 MINI_SPACING = ImVec2(2.f,0.f);
2452 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, MINI_SPACING);
2453
2454 if (App::ui_show_live_repair_controls->getBool())
2455 {
2456 const float INDENT = 15.f;
2457 ImGui::Separator();
2458 ImGui::TextDisabled("%s:", _LC("LiveRepair", "Movement"));
2459 ImGui::Columns(3);
2460 ImGui::SetColumnWidth(0, INDENT);
2461 ImGui::NextColumn();
2462 DrawRepairBoxEvent(EV_CHARACTER_FORWARD,_LC("LiveRepair", "Forward"));
2463 DrawRepairBoxEvent(EV_CHARACTER_BACKWARDS,_LC("LiveRepair", "Backward"));
2464 ImGui::NextColumn();
2465 DrawRepairBoxEvent(EV_CHARACTER_SIDESTEP_LEFT,_LC("LiveRepair", "Left"));
2466 DrawRepairBoxEvent(EV_CHARACTER_SIDESTEP_RIGHT,_LC("LiveRepair", "Right"));
2467 ImGui::NextColumn();
2468 DrawRepairBoxEvent(EV_TRUCK_ACCELERATE,_LC("LiveRepair", "Up"));
2469 DrawRepairBoxEvent(EV_TRUCK_BRAKE,_LC("LiveRepair", "Down"));
2470 ImGui::Columns(1);
2471
2472 ImGui::TextDisabled("%s:", _LC("LiveRepair", "Rotation"));
2473 ImGui::Columns(3);
2474 ImGui::SetColumnWidth(0, INDENT);
2475 ImGui::NextColumn();
2476 DrawRepairBoxEvent(EV_TRUCK_STEER_LEFT,_LC("LiveRepair", "Rot. left"));
2477 DrawRepairBoxEvent(EV_TRUCK_STEER_RIGHT,_LC("LiveRepair", "Rot. right"));
2478 ImGui::Columns(1);
2479
2480 ImGui::TextDisabled("%s:", _LC("LiveRepair", "Modifiers"));
2481 ImGui::Columns(4);
2482 ImGui::SetColumnWidth(0, INDENT);
2483 ImGui::SetColumnWidth(1, 125);
2484 ImGui::SetColumnWidth(2, 125);
2485 ImGui::NextColumn();
2486 DrawRepairBoxModkey(OIS::KC_LMENU,_LC("LiveRepair", "Slow step")); // Left alt
2487 DrawRepairBoxModkey(OIS::KC_LSHIFT,_LC("LiveRepair", "Fast step"));
2488 DrawRepairBoxModkey(OIS::KC_LCONTROL,_LC("LiveRepair", "10x step")); // Left ctrl
2489 ImGui::Columns(1);
2490
2492 ImGui::TextDisabled("%s (%s):", _LC("LiveRepair", "Reset mode"), ToLocalizedString(resetmode).c_str());
2493 ImGui::Dummy(ImVec2(INDENT, 1.f));
2494 ImGui::SameLine();
2495 DrawRepairBoxEvent(EV_COMMON_TOGGLE_RESET_MODE,_LC("LiveRepair", "Switch reset mode"));
2496 }
2497 ImGui::PopStyleVar(); // ItemSpacing
2498 }
2500 {
2501 ImGui::Separator();
2502 // notice text
2503 std::string lbl_readonly = _LC("TopMenubar", "This terrain is read only.");
2504 ImGui::SetCursorPosX(ImGui::GetCursorPosX()
2505 + (ImGui::GetWindowContentRegionWidth() / 2 - ImGui::CalcTextSize(lbl_readonly.c_str()).x/2));
2506 ImGui::TextDisabled("%s", lbl_readonly.c_str());
2507 // import button
2508 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2, 0.2, 0.2, 1.0));
2509 std::string btn_import = _LC("TopMenubar", "Import as editable project.");
2510 ImGui::SetCursorPosX(ImGui::GetCursorPosX()
2511 + ((ImGui::GetWindowContentRegionWidth() / 2 - ImGui::CalcTextSize(btn_import.c_str()).x / 2) - ImGui::GetStyle().FramePadding.x));
2512 if (!m_terrn_import_started && ImGui::Button(btn_import.c_str()))
2513 {
2515 rq->lsr_filename = "terrain_project_importer.as";
2519 }
2520 ImGui::PopStyleColor(); // ImGuiCol_Button
2521 }
2523 {
2524 // Info text
2525 std::string lbl_usemenu = _LC("TopMenubar", "Use 'Simulation' menu to save changes.");
2526 ImGui::SetCursorPosX(ImGui::GetCursorPosX()
2527 + (ImGui::GetWindowContentRegionWidth() / 2 - ImGui::CalcTextSize(lbl_usemenu.c_str()).x / 2));
2528 ImGui::TextDisabled("%s", lbl_usemenu.c_str());
2529 }
2530 const ImVec2 PAD = ImVec2(5, 5); // To bridge top menubar hoverbox and statebox hoverbox
2531 m_state_box_hoverbox_min = box_pos - PAD;
2532 m_state_box_hoverbox_max.x = box_pos.x + ImGui::GetWindowWidth();
2533 m_state_box_hoverbox_max.y = box_pos.y + ImGui::GetWindowHeight();
2535 // DO NOT `RequestGuiCaptureKeyboard()` - we want to use the hotkeys through it.
2536 ImGui::End();
2537 }
2538 ImGui::PopStyleColor(1); // WindowBg
2539 }
2540
2541 if (App::sim_state->getEnum<SimState>() != SimState::EDITOR_MODE)
2542 {
2543 m_terrn_import_started = false;
2544 }
2545}
2546
2548{
2549 // Load 'bundled' AI presets - see section `[AI Presets]` in terrn2 file format
2550 // ----------------------------------------------------------------------------
2551
2553
2554 for (const std::string& filename: terrain->GetDef()->ai_presets_files)
2555 {
2556 rapidjson::Document j_doc;
2557 if (Ogre::ResourceGroupManager::getSingleton().resourceExists(terrain->getTerrainFileResourceGroup(), filename))
2558 {
2560 }
2561 else
2562 {
2563 LOG(fmt::format("[RoR|Terrain] AI presets file '{}' declared in '{}' not found!", filename, terrain->getTerrainFileName()));
2564 }
2565
2566 // Ensure the format is about right
2567 if (!j_doc.IsArray())
2568 {
2569 LOG(fmt::format("[RoR|Terrain] AI presets file '{}' declared in '{}' has wrong format - the root element is not an array!",
2570 filename, terrain->getTerrainFileName()));
2571 }
2572 else
2573 {
2574 // Finally add the presets to the list
2575 for (const rapidjson::Value& j_bundled_preset: j_doc.GetArray())
2576 {
2577 rapidjson::Value preset_copy(j_bundled_preset, App::GetGuiManager()->TopMenubar.ai_presets_bundled.GetAllocator());
2579 }
2580 }
2581 }
2582
2584}
2585
2587{
2588#if defined(USE_CURL)
2589 std::packaged_task<void()> task(FetchAiPresetsThreadFunc);
2590 std::thread(std::move(task)).detach();
2592#endif // defined(USE_CURL)
2593}
2594
2596{
2597 // Combine external and bundled presets into one JSON doc
2598 // -------------------------------------------------------
2599
2600 ai_presets_all.Clear();
2601 ai_presets_all.SetArray();
2602
2603 for (rapidjson::Value& bundled_preset: ai_presets_bundled.GetArray())
2604 {
2605 rapidjson::Value preset_copy(bundled_preset, ai_presets_all.GetAllocator());
2606 ai_presets_all.PushBack(preset_copy, ai_presets_all.GetAllocator());
2607 }
2608
2609 for (const rapidjson::Value& extern_preset: ai_presets_extern.GetArray())
2610 {
2611 rapidjson::Value preset_copy(extern_preset, ai_presets_all.GetAllocator());
2612 ai_presets_all.PushBack(preset_copy, ai_presets_all.GetAllocator());
2613 }
2614}
2615
2617{
2618 // Updates/resets the tuning menu for the current vehicle driven by player (if any).
2619 // -------------------------------------------------------------------------------
2620
2621 if (App::sim_tuning_enabled->getBool()
2622 && (App::mp_state->getEnum<MpState>() != MpState::CONNECTED)
2623 && App::GetGameContext()->GetPlayerActor()
2624 && (tuning_actor != App::GetGameContext()->GetPlayerActor()))
2625 {
2628
2629 tuning_addonparts.clear();
2631
2632 // Addonparts matched by GUID
2633 if (tuning_actor->getUsedActorEntry()->guid != "")
2634 {
2635 CacheQuery query_addonparts;
2636 query_addonparts.cqy_filter_type = LT_AddonPart;
2637 query_addonparts.cqy_filter_guid = tuning_actor->getUsedActorEntry()->guid;
2638 query_addonparts.cqy_filter_target_filename = tuning_actor->getTruckFileName(); // Addonparts without any filenames listed will just pass.
2639 App::GetCacheSystem()->Query(query_addonparts);
2640 for (CacheQueryResult& res: query_addonparts.cqy_results)
2641 {
2642 tuning_addonparts.push_back(res.cqr_entry);
2643 }
2644 }
2645
2646 // Addonparts force-installed via [browse all] button; watch for duplicates.
2648 {
2649 for (std::string const& use_addonpart_fname: tuning_actor->getWorkingTuneupDef()->use_addonparts)
2650 {
2651 CacheEntryPtr entry = App::GetCacheSystem()->FindEntryByFilename(LT_AddonPart, /*partial:*/false, use_addonpart_fname);
2652 if (entry)
2653 {
2654 if (std::find(tuning_addonparts.begin(), tuning_addonparts.end(), entry) == tuning_addonparts.end())
2655 {
2656 tuning_addonparts.push_back(entry);
2657 }
2658 }
2659 }
2660 }
2661
2665 tuning_saves.cqy_filter_category_id = CID_Tuneups; // Exclude auto-generated entries
2668
2669 // Refresh `tuning_conflicts` database ~ test eligible addonparts each with each once.
2670 tuning_conflicts.clear();
2671 for (size_t i1 = 0; i1 < tuning_addonparts.size(); i1++)
2672 {
2673 for (size_t i2 = i1; i2 < tuning_addonparts.size(); i2++)
2674 {
2675 if (i1 != i2)
2676 {
2678 }
2679 }
2680 }
2681
2682 // Refresh `tuning_addonparts_conflicting` listing ~ test used addonparts against unused.
2686 {
2687 for (const std::string& use_addonpart_fname: tuning_actor->getWorkingTuneupDef()->use_addonparts)
2688 {
2689 CacheEntryPtr use_addonpart_entry = App::GetCacheSystem()->FindEntryByFilename(LT_AddonPart, /*partial:*/false, use_addonpart_fname);
2690 for (size_t i = 0; i < tuning_addonparts.size(); i++)
2691 {
2692 if (tuning_addonparts[i] != use_addonpart_entry)
2693 {
2696 }
2697 }
2698 }
2699 }
2700
2702 }
2703 else if (!App::sim_tuning_enabled->getBool() || !App::GetGameContext()->GetPlayerActor())
2704 {
2705 tuning_addonparts.clear();
2707 tuning_actor = nullptr;
2708 }
2709}
2710
2711void TopMenubar::DrawTuningProtectedChkRightAligned(const int subject_id, bool protectchk_value, ModifyProjectRequestType request_type_set, ModifyProjectRequestType request_type_reset, const std::string& subject /* ="" */)
2712{
2713 // > resolve the alignment
2714 ImGui::SameLine();
2715 if (tuning_rwidget_cursorx_min < ImGui::GetCursorPosX()) // Make sure button won't draw over item name
2716 tuning_rwidget_cursorx_min = ImGui::GetCursorPosX();
2717 std::string protectchk_text = _LC("Tuning", "Protected");
2718 float protectchk_w = ImGui::CalcTextSize(protectchk_text.c_str()).x + ImGui::GetStyle().FramePadding.x * 2;
2719 float protectchk_cursorx = (ImGui::GetWindowContentRegionWidth() - protectchk_w) - 20.f;
2720 if (protectchk_cursorx < tuning_rwidget_cursorx_min)
2721 protectchk_cursorx = tuning_rwidget_cursorx_min;
2722 ImGui::SetCursorPosX(protectchk_cursorx);
2723
2724 // > set styling and draw
2725 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
2726 bool chk_pressed = ImGui::Checkbox(protectchk_text.c_str(), &protectchk_value);
2727 ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
2728
2729 // > handle user action
2730 if (chk_pressed)
2731 {
2733 request->mpr_target_actor = tuning_actor;
2734 if (subject_id == TUNING_SUBJECTID_USE_NAME)
2735 {
2736 request->mpr_subject = subject;
2737 }
2738 else
2739 {
2740 request->mpr_subject_id = subject_id;
2741 }
2742 request->mpr_type = (protectchk_value) ? request_type_set : request_type_reset;
2744 }
2745}
2746
2748{
2749 // Draw subject ID in outlined box
2750 // -------------------------------
2751 ImGui::GetWindowDrawList()->AddRect(
2752 ImGui::GetCursorScreenPos(),
2753 ImGui::GetCursorScreenPos() + ImGui::CalcTextSize("00") + ImGui::GetStyle().FramePadding*2,
2754 ImColor(ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]),
2755 ImGui::GetStyle().FrameRounding);
2756 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().FramePadding.x);
2757 ImGui::Text("%02d", subject_id);
2758 ImGui::SameLine();
2759 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().FramePadding.x);
2760}
2761
2762void TopMenubar::DrawTuningForceRemoveControls(const int subject_id, const std::string& name, const bool is_unwanted, const bool is_force_removed, ModifyProjectRequestType request_type_set, ModifyProjectRequestType request_type_reset)
2763{
2764 // Common for props and flexbodies: draws the force-remove checkbox and the reset button
2765 // ------------------------------------------------------------------------------------
2766
2767 // Draw the checkbox for force-removing.
2768 bool isEnabled = !is_unwanted && !is_force_removed;
2769 if (is_force_removed)
2770 {
2771 ImGui::PushStyleColor(ImGuiCol_Border, ORANGE_TEXT);
2772 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.f);
2773 }
2774 bool chkPressed = ImGui::Checkbox(name.c_str(), &isEnabled);
2775 bool resetPressed = false;
2776 if (is_force_removed)
2777 {
2778 ImGui::SameLine();
2779 ImGui::PushStyleColor(ImGuiCol_Text, GRAY_HINT_TEXT);
2780 resetPressed = ImGui::SmallButton(_LC("Tuning", "Reset"));
2781 ImGui::PopStyleColor(); //ImGuiCol_Text, GRAY_HINT_TEXT
2782 ImGui::PopStyleVar(); //ImGuiStyleVar_FrameBorderSize, 1.f
2783 ImGui::PopStyleColor(); //ImGuiCol_Border, ORANGE_TEXT
2784 }
2785
2786 // perform project modification if needed
2787 if (chkPressed && !isEnabled)
2788 {
2790 req->mpr_type = request_type_set;
2791 if (subject_id == TUNING_SUBJECTID_USE_NAME)
2792 {
2793 req->mpr_subject = name;
2794 }
2795 else
2796 {
2797 req->mpr_subject_id = subject_id;
2798 }
2801 }
2802 else if ((chkPressed && isEnabled) || resetPressed)
2803 {
2805 req->mpr_type = request_type_reset;
2806 if (subject_id == TUNING_SUBJECTID_USE_NAME)
2807 {
2808 req->mpr_subject = name;
2809 }
2810 else
2811 {
2812 req->mpr_subject_id = subject_id;
2813 }
2816 }
2817
2818}
2819
2821{
2822 switch (which)
2823 {
2828 default:
2829 return true;
2830 }
2831}
Central state/object manager and communications hub.
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
void LOG(const char *msg)
Legacy alias - formerly a macro.
#define RGN_SAVEGAMES
Definition Application.h:51
static size_t CurlWriteFunc(void *ptr, size_t size, size_t nmemb, std::string *data)
void DrawRepairBoxModkey(OIS::KeyCode modkey, std::string const &desc)
void FetchAiPresetsThreadFunc()
void DrawRepairBoxEvent(events ev, std::string const &desc)
Game state manager and message-queue provider.
Handles controller inputs from player.
#define _LC(ctx, str)
Definition Language.h:38
Platform-specific utilities. We use narrow UTF-8 encoded strings as paths. Inspired by http://utf8eve...
The vehicle tuning system; applies addonparts and user overrides to vehicles.
CacheEntryPtr & getUsedSkinEntry()
Definition Actor.cpp:4878
DashBoardManagerPtr ar_dashboard
Definition Actor.h:484
GfxActor * GetGfxActor()
Definition Actor.h:309
float getRotation()
Definition Actor.cpp:355
Replay * getReplay()
Definition Actor.cpp:4663
float getMinHeight(bool skip_virtual_nodes=true)
Definition Actor.cpp:1568
Ogre::Vector3 getPosition()
Definition Actor.cpp:370
std::vector< flare_t > ar_flares
Definition Actor.h:367
ActorState ar_state
Definition Actor.h:518
std::string getTruckFileName()
Definition Actor.h:267
std::map< std::string, Ogre::MaterialPtr > ar_managed_materials
Definition Actor.h:379
ActorInstanceID_t ar_instance_id
Static attr; session-unique ID.
Definition Actor.h:429
Ogre::String getSectionConfig()
Definition Actor.h:270
int ar_num_wheels
Definition Actor.h:385
CacheEntryPtr & getUsedActorEntry()
The actor entry itself.
Definition Actor.cpp:4873
TuneupDefPtr & getWorkingTuneupDef()
Definition Actor.cpp:4893
ActorPtrVec & GetActors()
bool LoadScene(Ogre::String filename)
Definition Savegame.cpp:240
void SetSimulationSpeed(float speed)
bool SaveScene(Ogre::String filename)
Definition Savegame.cpp:418
void SetTrucksForcedAwake(bool forced)
bool AreTrucksForcedAwake() const
std::vector< ActorPtr > GetLocalActors()
static bool CheckForAddonpartConflict(CacheEntryPtr addonpart1, CacheEntryPtr addonpart2, AddonPartConflictVec &conflicts)
static void RecordAddonpartConflicts(CacheEntryPtr addonpart1, CacheEntryPtr addonpart2, AddonPartConflictVec &conflicts)
T getEnum() const
Definition CVar.h:99
float getFloat() const
Definition CVar.h:96
std::string const & getStr() const
Definition CVar.h:95
bool getBool() const
Definition CVar.h:98
void setVal(T val)
Definition CVar.h:72
int getInt() const
Definition CVar.h:97
Ogre::String fname
filename
Definition CacheSystem.h:67
Ogre::String dname
name parsed from the file
Definition CacheSystem.h:70
Ogre::String guid
global unique id; Type "addonpart" leaves this empty and uses addonpart_guids; Always lowercase.
Definition CacheSystem.h:77
CacheEntryPtr FindEntryByFilename(RoR::LoaderType type, bool partial, const std::string &_filename_maybe_bundlequalified)
Returns NULL if none found; "Bundle-qualified" format also specifies the ZIP/directory in modcache,...
size_t Query(CacheQuery &query)
Ogre::Vector3 getPosition()
Definition Character.cpp:92
@ CONSOLE_MSGTYPE_INFO
Generic message.
Definition Console.h:60
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition Console.cpp:103
@ CONSOLE_SYSTEM_NOTICE
Definition Console.h:51
bool LoadAndParseJson(std::string const &filename, std::string const &rg_name, rapidjson::Document &j_doc)
std::string getLinkNameForID(DashData id)
Flexbody = A deformable mesh; updated on CPU every frame, then uploaded to video memory.
Definition FlexBody.h:44
void SetVisible(bool visible)
void SetVisible(bool value)
void SetVisible(bool visible)
const ImVec4 WHITE_TEXT
std::vector< std::string > m_savegame_names
CacheQuery tuning_saves
Tuneups saved by user, with category ID RoR::CID_AddonpartUser
std::vector< bool > tuning_addonparts_conflict_w_used
1:1 with tuning_addonparts; True means the eligible (not used) addonpart conflicts with an used addon...
void DrawTuningProtectedChkRightAligned(const int subject_id, bool is_protected, ModifyProjectRequestType request_type_set, ModifyProjectRequestType request_type_reset, const std::string &subject="")
const ImVec4 ORANGE_TEXT
bool ShouldDisplay(ImVec2 window_pos)
const ImVec4 TUNING_HOLDTOCONFIRM_COLOR
Str< 200 > tuning_savebox_buf
Buffer for tuneup name to be saved.
const ImVec4 GRAY_HINT_TEXT
rapidjson::Document ai_presets_bundled
Presets bundled with the terrain, see [AI Presets] section in .terrn2 file format.
const float PANEL_HOVERBOX_HEIGHT
bool tuning_savebox_overwrite
Status of "Overwrite?" checkbox.
bool ai_presets_extern_fetching
True if the (down)load of 'extern' waypoints is in progress.
const int TUNING_SUBJECTID_USE_NAME
void DrawMpUserToActorList(RoRnet::UserInfo &user)
AddonPartConflictVec tuning_conflicts
Conflicts between eligible addonparts tweaking the same element.
const float TUNING_HOLDTOCONFIRM_TIMELIMIT
Delete button must be held for several sec to confirm.
rapidjson::Document ai_presets_extern
Externally provided presets (GitHub repo or local 'savegames/waypoints.json' file).
const ImVec4 GREEN_TEXT
std::string m_quicksave_name
rapidjson::Document ai_presets_all
The full list of presets, used for display. Needs to be refreshed when terrain is loaded.
std::vector< CacheEntryPtr > tuning_addonparts
Addonparts eligible for current actor, both matched by GUID and force-installed by user via [browse a...
const float MENU_Y_OFFSET
std::vector< ai_events > ai_waypoints
void DrawTuningBoxedSubjectIdInline(int subject_id)
void RefreshAiPresets()
Refresh the list of presets, used for display. Needs to be called when terrain is loaded.
float tuning_rwidget_cursorx_min
Avoid drawing right-side widgets ('Delete' button or 'Protected' chk) over saved tuneup names.
std::string ai_presets_extern_error
Error message from the (down)load of 'extern' waypoints.
ActorPtr tuning_actor
Detecting actor change to update cached values.
void DrawSpecialStateBox(float top_offset)
bool IsMenuEnabled(TopMenu which)
bool tuning_savebox_visible
User pressed 'save active' to open savebox.
void LoadBundledAiPresets(TerrainPtr terrain)
Loads JSON files from [AI Presets] section in .terrn2 file format.
void FetchExternAiPresetsOnBackground()
Initiate threaded (down)load of 'extern' waypoints from GitHub repo.
void DrawTuningForceRemoveControls(const int subject_id, const std::string &name, const bool is_unwanted, const bool is_force_removed, ModifyProjectRequestType request_type_set, ModifyProjectRequestType request_type_reset)
const ImVec2 MENU_HOVERBOX_PADDING
CacheEntryPtr tuning_hovered_addonpart
void SetVisible(TPanelMode mode, TPanelFocus focus=TPANELFOCUS_NONE)
GUI::RepositorySelector RepositorySelector
Definition GUIManager.h:130
GUI::TextureToolWindow TextureToolWindow
Definition GUIManager.h:128
GUI::VehicleInfoTPanel VehicleInfoTPanel
Definition GUIManager.h:120
GUI::TopMenubar TopMenubar
Definition GUIManager.h:133
GUI::FrictionSettings FrictionSettings
Definition GUIManager.h:127
GUI::CollisionsDebug CollisionsDebug
Definition GUIManager.h:116
void RequestGuiCaptureKeyboard(bool val)
Pass true during frame to prevent input passing to application.
GuiTheme & GetTheme()
Definition GUIManager.h:168
GUI::NodeBeamUtils NodeBeamUtils
Definition GUIManager.h:131
GUI::ConsoleWindow ConsoleWindow
Definition GUIManager.h:134
GUI::FlexbodyDebug FlexbodyDebug
Definition GUIManager.h:137
Character * GetPlayerCharacter()
const ActorPtr & GetPlayerActor()
const TerrainPtr & GetTerrain()
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
const ActorPtr & GetLastSpawnedActor()
Last actor spawned by user and still alive.
RepairMode & GetRepairMode()
void ChainMessage(Message m)
Add to last pushed message's chain.
std::string GetQuicksaveFilename()
For currently loaded terrain (cvar 'sim_terrain_name')
Definition Savegame.cpp:56
ActorManager * GetActorManager()
std::vector< FlexBody * > & GetFlexbodies()
Definition GfxActor.h:71
ActorSB & GetSimDataBuffer()
Definition GfxActor.h:128
std::string getWheelRimMeshName(WheelID_t wheel_id)
Definition GfxActor.h:155
std::vector< VideoCamera > & getVideoCameras()
Definition GfxActor.h:75
std::vector< Prop > & getProps()
Definition GfxActor.h:72
WheelSide getWheelSide(WheelID_t wheel_id)
Definition GfxActor.h:154
std::vector< Exhaust > & getExhausts()
Definition GfxActor.h:74
DebugViewType GetDebugView() const
Definition GfxActor.h:145
GameContextSB & GetSimDataBuffer()
Definition GfxScene.h:80
Ogre::ColourValue GetPlayerColor(int color_num)
Definition Network.cpp:94
std::string UserAuthToStringShort(RoRnet::UserInfo const &user)
Definition Network.cpp:861
RoRnet::UserInfo GetLocalUserData()
Definition Network.cpp:707
std::vector< RoRnet::UserInfo > GetUserInfos()
Definition Network.cpp:713
float GetLiveRepairTimer() const
Definition RepairMode.h:44
int getCurrentFrame() const
Definition Replay.h:54
int getNumFrames() const
Definition Replay.h:53
unsigned long getLastReadTime()
Definition Replay.cpp:178
ScriptUnitID_t loadScript(Ogre::String filename, ScriptCategory category=ScriptCategory::TERRAIN, ActorPtr associatedActor=nullptr, std::string buffer="")
Loads a script.
Wrapper for classic c-string (local buffer) Refresher: strlen() excludes '\0' terminator; strncat() A...
Definition Str.h:36
const char * ToCStr() const
Definition Str.h:46
char * GetBuffer()
Definition Str.h:48
size_t GetCapacity() const
Definition Str.h:49
std::string getTerrainFileResourceGroup()
Definition Terrain.cpp:564
SkyManager * getSkyManager()
Definition Terrain.cpp:522
std::string getTerrainFileName()
Definition Terrain.cpp:559
Wavefield * getWater()
Definition Terrain.h:87
Terrn2DocumentPtr GetDef()
Definition Terrain.cpp:584
static bool isAddonPartUsed(TuneupDefPtr &tuneup_entry, const std::string &filename)
static WheelSide getTweakedWheelSide(TuneupDefPtr &tuneup_entry, WheelID_t wheel_id, WheelSide orig_val)
void SetWavesHeight(float)
Definition Wavefield.cpp:85
std::string PathCombine(std::string a, std::string b)
bool FileExists(const char *path)
Path must be UTF-8 encoded.
@ EDITOR_MODE
Hacky, but whatever... added by Ulteq, 2016.
@ AI
machine controlled by an Artificial Intelligence
Definition SimData.h:89
@ EV_TRUCK_ACCELERATE
accelerate the truck
@ EV_COMMON_TOGGLE_RESET_MODE
toggle truck reset truck mode (soft vs. hard)
@ EV_TRUCK_TOGGLE_PHYSICS
toggle physics simulation
@ EV_SURVEY_MAP_CYCLE
cycle overview-map mode
@ EV_CHARACTER_SIDESTEP_LEFT
sidestep to the left
@ EV_COMMON_TOGGLE_PHYSICS
toggle physics on/off
@ EV_CHARACTER_FORWARD
step forward with the character
@ EV_TRUCK_STEER_LEFT
steer left
@ EV_COMMON_REPAIR_TRUCK
repair truck to original condition
@ EV_TRUCK_BRAKE
brake
@ EV_TRUCK_STEER_RIGHT
steer right
@ EV_CHARACTER_SIDESTEP_RIGHT
sidestep to the right
@ EV_COMMON_LIVE_REPAIR_MODE
toggles live repair and recovery mode, controlled by keyboard
@ EV_CHARACTER_BACKWARDS
step backwards with the character
@ EV_COMMON_TOGGLE_TERRAIN_EDITOR
toggle terrain editor
@ MSG_GUI_OPEN_MENU_REQUESTED
@ MSG_SIM_TELEPORT_PLAYER_REQUESTED
Payload = Ogre::Vector3* (owner)
@ MSG_NET_FETCH_AI_PRESETS_SUCCESS
Description = JSON string.
@ MSG_SIM_UNHIDE_NET_ACTOR_REQUESTED
Payload = ActorPtr* (owner)
@ MSG_SIM_HIDE_NET_ACTOR_REQUESTED
Payload = ActorPtr* (owner)
@ MSG_SIM_UNLOAD_TERRN_REQUESTED
@ MSG_NET_DISCONNECT_REQUESTED
@ MSG_NET_FETCH_AI_PRESETS_FAILURE
Description = message.
@ MSG_SIM_SPAWN_ACTOR_REQUESTED
Payload = RoR::ActorSpawnRequest* (owner)
@ MSG_GUI_OPEN_SELECTOR_REQUESTED
Payload = LoaderType* (owner), Description = GUID | empty.
@ MSG_SIM_SEAT_PLAYER_REQUESTED
Payload = RoR::ActorPtr (owner) | nullptr.
@ MSG_EDI_SAVE_TERRN_CHANGES_REQUESTED
@ MSG_APP_LOAD_SCRIPT_REQUESTED
Payload = RoR::LoadScriptRequest* (owner)
Definition Application.h:93
@ MSG_SIM_LOAD_SAVEGAME_REQUESTED
@ MSG_APP_SHUTDOWN_REQUESTED
Definition Application.h:86
@ MSG_EDI_CREATE_PROJECT_REQUESTED
Payload = RoR::CreateProjectRequest* (owner)
@ MSG_EDI_DELETE_PROJECT_REQUESTED
Payload = RoR::CacheEntryPtr* (owner)
@ MSG_EDI_MODIFY_PROJECT_REQUESTED
Payload = RoR::UpdateProjectRequest* (owner)
@ MSG_SIM_LOAD_TERRN_REQUESTED
@ MSG_SIM_DELETE_ACTOR_REQUESTED
Payload = RoR::ActorPtr* (owner)
@ MSG_SIM_MODIFY_ACTOR_REQUESTED
Payload = RoR::ActorModifyRequest* (owner)
@ MSG_EDI_RELOAD_BUNDLE_REQUESTED
Payload = RoR::CacheEntryPtr* (owner)
@ LOCAL_SIMULATED
simulated (local) actor
@ NETWORKED_OK
not simulated (remote) actor
@ NETWORKED_HIDDEN
not simulated, not updated (remote)
@ CUSTOM
Loaded by user via either: A) ingame console 'loadscript'; B) RoR.cfg 'app_custom_scripts'; C) comman...
std::string ToLocalizedString(SimGearboxMode e)
CVar * gfx_sky_mode
CVar * gfx_polygon_mode
ContentManager * GetContentManager()
CVar * gfx_static_cam_fov_exp
CVar * ui_show_live_repair_controls
bool
CVar * gfx_sky_time_cycle
CVar * gfx_water_mode
CVar * sim_state
CVar * mp_hide_net_labels
InputEngine * GetInputEngine()
CVar * diag_log_beam_trigger
CVar * gfx_envmap_rate
CVar * sim_terrain_name
CVar * gfx_sky_time_speed
CameraManager * GetCameraManager()
CVar * diag_truck_mass
CVar * audio_master_volume
GUIManager * GetGuiManager()
GameContext * GetGameContext()
CVar * gfx_camera_height
CVar * sim_tuning_enabled
CVar * sys_savegames_dir
CVar * sim_soft_reset_mode
GfxScene * GetGfxScene()
CVar * gfx_water_waves
CVar * diag_log_beam_deform
CVar * gfx_fov_external
CVar * mp_state
CVar * gfx_fps_limit
CVar * sim_live_repair_interval
Hold EV_COMMON_REPAIR_TRUCK to enter LiveRepair mode. 0 or negative interval disables.
Console * GetConsole()
CVar * gfx_fov_internal
CVar * mp_pseudo_collisions
CVar * io_hydro_coupling
ScriptEngine * GetScriptEngine()
CacheSystem * GetCacheSystem()
CVar * gfx_envmap_enabled
CVar * diag_videocameras
CVar * diag_log_beam_break
Network * GetNetwork()
CVar * gfx_fixed_cam_tracking
void ImDrawEventHighlighted(events input_event)
Definition GUIUtils.cpp:446
void Log(const char *msg)
The ultimate, application-wide logging function. Adds a line (any length) in 'RoR....
LoaderType
< Search mode for ModCache::Query() & Operation mode for GUI::MainSelector
@ LT_Tuneup
@ LT_AddonPart
@ LT_Gadget
@ LT_AllBeam
@ SAVE_TUNEUP
Dumps .tuneup file with CID_Tuneup from source actor, will not overwrite existing unless explicitly i...
ModifyProjectRequestType
@ TUNEUP_PROTECTED_WHEEL_RESET
'subject_id' is wheel ID.
@ TUNEUP_FORCED_VCAM_ROLE_RESET
'subject_id' is video camera ID.
@ TUNEUP_FORCEREMOVE_MANAGEDMAT_RESET
'subject' is managed material name.
@ TUNEUP_FORCEREMOVE_FLEXBODY_SET
'subject_id' is flexbody ID.
@ TUNEUP_PROTECTED_WHEEL_SET
'subject_id' is wheel ID.
@ TUNEUP_PROTECTED_MANAGEDMAT_SET
'subject' is managed material name.
@ TUNEUP_FORCEREMOVE_EXHAUST_SET
'subject_id' is exhaust ID.
@ TUNEUP_USE_ADDONPART_RESET
'subject' is addonpart filename.
@ TUNEUP_PROTECTED_PROP_SET
'subject_id' is prop ID.
@ TUNEUP_USE_ADDONPART_SET
'subject' is addonpart filename.
@ TUNEUP_FORCEREMOVE_PROP_SET
'subject_id' is prop ID.
@ TUNEUP_PROTECTED_EXHAUST_SET
'subject_id' is exhaust ID.
@ TUNEUP_PROTECTED_FLARE_SET
'subject_id' is flare ID.
@ TUNEUP_FORCED_WHEEL_SIDE_RESET
'subject_id' is wheel ID.
@ TUNEUP_FORCEREMOVE_MANAGEDMAT_SET
'subject' is managed material name.
@ TUNEUP_PROTECTED_FLEXBODY_RESET
'subject_id' is flexbody ID.
@ TUNEUP_PROTECTED_FLEXBODY_SET
'subject_id' is flexbody ID.
@ TUNEUP_FORCED_WHEEL_SIDE_SET
'subject_id' is wheel ID, 'value_int' is RoR::WheelSide
@ TUNEUP_PROTECTED_EXHAUST_RESET
'subject_id' is exhaust ID.
@ TUNEUP_PROTECTED_MANAGEDMAT_RESET
'subject' is managed material name.
@ TUNEUP_FORCED_VCAM_ROLE_SET
'subject_id' is video camera ID, 'value_int' is RoR::VideoCamRole
@ PROJECT_RESET_TUNEUP
'subject' is empty. This resets the auto-generated tuneup to orig. values.
@ TUNEUP_FORCEREMOVE_FLARE_RESET
'subject_id' is flare ID.
@ TUNEUP_PROTECTED_PROP_RESET
'subject_id' is prop ID.
@ TUNEUP_FORCEREMOVE_FLEXBODY_RESET
'subject_id' is flexbody ID.
@ TUNEUP_FORCEREMOVE_PROP_RESET
'subject_id' is prop ID.
@ TUNEUP_PROTECTED_FLARE_RESET
'subject_id' is flare ID.
@ TUNEUP_FORCEREMOVE_EXHAUST_RESET
'subject_id' is exhaust ID.
@ PROJECT_LOAD_TUNEUP
'subject' is tuneup filename. This overwrites the auto-generated tuneup with the save.
@ TUNEUP_FORCEREMOVE_FLARE_SET
'subject_id' is flare ID.
void HandleGenericException(const std::string &from, BitMask_t flags)
WheelSide
Used by rig-def/addonpart/tuneup formats to specify wheel rim mesh orientation.
VideoCamRole
@ VCAM_ROLE_MIRROR
Flips the video output and when not in driver cam, acts like a natural mirror, not a screen.
@ VCAM_ROLE_TRACKING_MIRROR_NOFLIP
A MIRROR_NOFLIP(2) with tracking node set.
@ VCAM_ROLE_TRACKING_MIRROR
A MIRROR(1) with tracking node set.
@ VCAM_ROLE_INVALID
@ VCAM_ROLE_MIRROR_PROP_LEFT
The classic 'special prop/rear view mirror'.
@ VCAM_ROLE_MIRROR_PROP_RIGHT
The classic 'special prop/rear view mirror'.
@ VCAM_ROLE_MIRROR_NOFLIP
Same as VCAM_ROLE_MIRROR, but without flipping the texture horizontally (expects texcoords to be alre...
void DrawGIntSlider(CVar *cvar, const char *label, int v_min, int v_max)
Definition GUIUtils.cpp:316
int WheelID_t
Index to Actor::ar_wheels, use RoR::WHEELID_INVALID as empty value.
RefCountingObjectPtr< Actor > ActorPtr
int VideoCameraID_t
Index into GfxActor::m_videocameras, use RoR::VIDEOCAMERAID_INVALID as empty value.
@ CAELUM
Caelum (best looking, slower)
int FlareID_t
Index into Actor::ar_flares, use RoR::FLAREID_INVALID as empty value.
int ExhaustID_t
Index into GfxActor::m_exhausts, use RoR::EXHAUSTID_INVALID as empty value.
void ImDrawModifierKeyHighlighted(OIS::KeyCode key)
Definition GUIUtils.cpp:489
void DrawGFloatSlider(CVar *cvar, const char *label, float v_min, float v_max)
Definition GUIUtils.cpp:326
SimResetMode
bool DrawGCheckbox(CVar *cvar, const char *label)
Definition GUIUtils.cpp:287
@ CID_Tuneups
For unsorted tuneup files.
std::string StripColorMarksFromText(std::string const &text)
Definition GUIUtils.cpp:225
RefCountingObjectPtr< CacheEntry > CacheEntryPtr
Ogre::TexturePtr FetchIcon(const char *name)
Definition GUIUtils.cpp:372
void LoadingIndicatorCircle(const char *label, const float indicator_radius, const ImVec4 &main_color, const ImVec4 &backdrop_color, const int circle_count, const float speed)
Draws animated loading spinner.
Definition GUIUtils.cpp:158
bool ImButtonHoldToConfirm(const std::string &btn_idstr, const bool smallbutton, const float time_limit)
Definition GUIUtils.cpp:512
ActorInstanceID_t amr_actor
Definition SimData.h:875
@ RELOAD
Full reload from filesystem, requested by user.
ActorState simbuf_actor_state
Definition SimBuffers.h:115
Ogre::Vector3 simbuf_pos
Definition SimBuffers.h:123
Ogre::String asr_config
Definition SimData.h:836
CacheEntryPtr asr_cache_entry
Optional, overrides 'asr_filename' and 'asr_cache_entry_num'.
Definition SimData.h:834
TuneupDefPtr asr_working_tuneup
Only filled when editing tuneup via Tuning menu.
Definition SimData.h:842
Ogre::Vector3 asr_position
Definition SimData.h:837
CacheEntryPtr asr_skin_entry
Definition SimData.h:840
Ogre::Quaternion asr_rotation
Definition SimData.h:838
@ USER
Direct selection by user via GUI.
std::string cqy_filter_target_filename
Exact match (case-insensitive); leave empty to disable (currently only used with addonparts)
std::string cqy_filter_guid
Exact match (case-insensitive); leave empty to disable.
std::vector< CacheQueryResult > cqy_results
RoR::LoaderType cqy_filter_type
Creates subdirectory in 'My Games\Rigs of Rods\projects', pre-populates it with files and adds modcac...
CreateProjectRequestType cpr_type
CacheEntryPtr cpr_source_entry
The original mod to copy files from.
std::string cpr_name
Directory and also the mod file (without extension).
ActorPtr cpr_source_actor
Only for type SAVE_TUNEUP
Ogre::Vector3 simbuf_character_pos
Definition SimBuffers.h:204
float simbuf_race_time_diff
Definition SimBuffers.h:213
ActorPtr simbuf_player_actor
Definition SimBuffers.h:203
Ogre::Vector3 simbuf_dir_arrow_target
Definition SimBuffers.h:216
float simbuf_race_best_time
Definition SimBuffers.h:212
std::string simbuf_dir_arrow_text
Definition SimBuffers.h:217
std::string lsr_filename
Load from resource ('.as' file or '.gadget' file); If buffer is supplied, use this as display name on...
ScriptCategory lsr_category
Unified game event system - all requests and state changes are reported using a message.
Definition GameContext.h:52
std::string description
Definition GameContext.h:58
void * payload
Definition GameContext.h:59
ModifyProjectRequestType mpr_type
A mesh attached to vehicle frame via 3 nodes.
Definition GfxData.h:156
bool isFlexbodyUnwanted(FlexbodyID_t flexbodyid)
bool isManagedMatUnwanted(const std::string &matname)
bool isVideoCameraRoleForced(VideoCameraID_t camera_id, VideoCamRole &out_val) const
bool isFlexbodyProtected(FlexbodyID_t flexbodyid) const
bool isPropProtected(PropID_t propid) const
bool isFlareUnwanted(FlareID_t flareid)
bool isPropForceRemoved(PropID_t propid)
bool isPropUnwanted(PropID_t propid)
bool isWheelSideForced(WheelID_t wheelid, WheelSide &out_val) const
std::set< std::string > use_addonparts
Addonpart filenames.
bool isFlexbodyForceRemoved(FlexbodyID_t flexbodyid)
bool isFlareForceRemoved(FlareID_t flareid)
bool isManagedMatForceRemoved(const std::string &matname)
bool isWheelProtected(WheelID_t wheelid) const
bool isManagedMatProtected(const std::string &matname) const
bool isExhaustUnwanted(ExhaustID_t exhaustid)
bool isExhaustProtected(ExhaustID_t exhaustid) const
bool isExhaustForceRemoved(ExhaustID_t exhaustid)
bool isFlareProtected(FlareID_t flareid) const
An Ogre::Camera mounted on the actor and rendering into either in-scene texture or external window.
Definition GfxData.h:215
VideoCamRole vcam_role
Definition GfxData.h:216
char language[10]
user's language. For example "de-DE" or "en-US"
Definition RoRnet.h:199
char username[RORNET_MAX_USERNAME_LEN]
the nickname of the user (UTF-8)
Definition RoRnet.h:196
int32_t colournum
colour set by server
Definition RoRnet.h:194
uint32_t uniqueid
user unique id
Definition RoRnet.h:191
char clientversion[25]
a version number of the client. For example 1 for RoR 0.35
Definition RoRnet.h:201
Ogre::Vector3 position