26
26
#include " rfg/xtbl/XtblManager.h"
27
27
#include " util/TaskScheduler.h"
28
28
#include " common/filesystem/Path.h"
29
+ #include " rfg/PackfileVFS.h"
30
+ #include < tinyxml2/tinyxml2.h>
31
+ #include < RfgTools++/formats/packfiles/Packfile3.h>
32
+ #include " common/Defer.h"
29
33
30
34
CVar CVar_ShowFPS (" Show FPS" , ConfigType::Bool,
31
35
" If enabled an FPS meter is shown on the main menu bar." ,
@@ -39,63 +43,6 @@ CVar CVar_UI_Theme("UI Theme", ConfigType::String,
39
43
false // Show in settings
40
44
);
41
45
42
- // Used in MainGui::DrawMainMenuBar()
43
- std::vector<const char *> TerritoryList =
44
- {
45
- " terr01" ,
46
- " dlc01" ,
47
- " mp_cornered" ,
48
- " mp_crashsite" ,
49
- " mp_crescent" ,
50
- " mp_crevice" ,
51
- " mp_deadzone" ,
52
- " mp_downfall" ,
53
- " mp_excavation" ,
54
- " mp_fallfactor" ,
55
- " mp_framework" ,
56
- " mp_garrison" ,
57
- " mp_gauntlet" ,
58
- " mp_overpass" ,
59
- " mp_pcx_assembly" ,
60
- " mp_pcx_crossover" ,
61
- " mp_pinnacle" ,
62
- " mp_quarantine" ,
63
- " mp_radial" ,
64
- " mp_rift" ,
65
- " mp_sandpit" ,
66
- " mp_settlement" ,
67
- " mp_warlords" ,
68
- " mp_wasteland" ,
69
- " mp_wreckage" ,
70
- " mpdlc_broadside" ,
71
- " mpdlc_division" ,
72
- " mpdlc_islands" ,
73
- " mpdlc_landbridge" ,
74
- " mpdlc_minibase" ,
75
- " mpdlc_overhang" ,
76
- " mpdlc_puncture" ,
77
- " mpdlc_ruins" ,
78
- " wc1" ,
79
- " wc2" ,
80
- " wc3" ,
81
- " wc4" ,
82
- " wc5" ,
83
- " wc6" ,
84
- " wc7" ,
85
- " wc8" ,
86
- " wc9" ,
87
- " wc10" ,
88
- " wcdlc1" ,
89
- " wcdlc2" ,
90
- " wcdlc3" ,
91
- " wcdlc4" ,
92
- " wcdlc5" ,
93
- " wcdlc6" ,
94
- " wcdlc7" ,
95
- " wcdlc8" ,
96
- " wcdlc9"
97
- };
98
-
99
46
// Titles for outliner and inspector windows. Used when drawing the windows, but also needed when setting their default docking positions
100
47
const char * OutlinerIdentifier = ICON_FA_LIST " Outliner" ;
101
48
const char * InspectorIdentifier = ICON_FA_WRENCH " Inspector" ;
@@ -148,6 +95,12 @@ void MainGui::Update()
148
95
ImGui::GetIO ().FontGlobalScale = lastFrameScale;
149
96
}
150
97
98
+ // Load level definitions from xtbls when possible
99
+ if (!levelDefinitionsLoaded_ && State.PackfileVFS ->Ready ())
100
+ {
101
+ LoadLevelDefinitions ();
102
+ }
103
+
151
104
// Draw always visible UI elements
152
105
DrawMainMenuBar ();
153
106
DrawDockspace ();
@@ -500,6 +453,30 @@ void MainGui::DrawMainMenuBar()
500
453
}
501
454
ImGui::EndMenu ();
502
455
}
456
+ if (ImGui::BeginMenu (" Theme" ))
457
+ {
458
+
459
+ if (ImGui::MenuItem (" Dark" ))
460
+ {
461
+ gui::SetThemePreset (Dark);
462
+ CVar_UI_Theme.Get <string>() = " dark" ;
463
+ Config::Get ()->Save ();
464
+ }
465
+ if (ImGui::MenuItem (" Orange" ))
466
+ {
467
+ gui::SetThemePreset (Orange);
468
+ CVar_UI_Theme.Get <string>() = " orange" ;
469
+ Config::Get ()->Save ();
470
+ }
471
+ if (ImGui::MenuItem (" Blue" ))
472
+ {
473
+ gui::SetThemePreset (Blue);
474
+ CVar_UI_Theme.Get <string>() = " blue" ;
475
+ Config::Get ()->Save ();
476
+ }
477
+ ImGui::EndMenu ();
478
+ }
479
+ ImGui::Separator ();
503
480
#ifdef DEVELOPMENT_BUILD
504
481
if (ImGui::MenuItem (" ImGui Demo" , nullptr , &imguiDemoWindowOpen_))
505
482
{
@@ -524,32 +501,6 @@ void MainGui::DrawMainMenuBar()
524
501
// Draw tools menu
525
502
if (ImGui::BeginMenu (" Tools" ))
526
503
{
527
- bool canOpenTerritory = State.CurrentProject && State.CurrentProject ->Loaded ();
528
- if (ImGui::BeginMenu (" Open territory" , canOpenTerritory))
529
- {
530
- for (const char * territory : TerritoryList)
531
- {
532
- string territoryShortname = string (territory);
533
- string territoryFilename = territoryShortname;
534
- if (territoryFilename == " terr01" ) territoryFilename = " zonescript_terr01" ;
535
- else if (territoryFilename == " dlc01" ) territoryFilename = " zonescript_dlc01" ;
536
- territoryFilename += " .vpp_pc" ;
537
-
538
- bool overrideOpenCheck = (ImGui::IsKeyDown (ImGuiKey_RightCtrl) || ImGui::IsKeyDown (ImGuiKey_LeftCtrl)) && (ImGui::IsKeyDown (ImGuiKey_RightShift) || ImGui::IsKeyDown (ImGuiKey_LeftShift));
539
- bool alreadyOpen = false ;
540
- for (Handle <IDocument> doc : State.Documents )
541
- {
542
- if (doc->Title == territoryShortname)
543
- alreadyOpen = true ;
544
- }
545
-
546
- if (ImGui::MenuItem (territory, " " , nullptr , overrideOpenCheck || !alreadyOpen))
547
- {
548
- State.CreateDocument (territoryShortname, CreateHandle<TerritoryDocument>(&State, territoryFilename, territoryShortname));
549
- }
550
- }
551
- ImGui::EndMenu ();
552
- }
553
504
if (ImGui::BeginMenu (" Localization" ))
554
505
{
555
506
if (ImGui::MenuItem (" View localized strings" ))
@@ -582,29 +533,35 @@ void MainGui::DrawMainMenuBar()
582
533
ImGui::EndMenu ();
583
534
}
584
535
585
- // Draw themes menu
586
- if (ImGui::BeginMenu ( " Theme " ) )
536
+ bool canOpenTerritory = State. CurrentProject && State. CurrentProject -> Loaded ();
537
+ if (canOpenTerritory )
587
538
{
588
-
589
- if (ImGui::MenuItem (" Dark" ))
590
- {
591
- gui::SetThemePreset (Dark);
592
- CVar_UI_Theme.Get <string>() = " dark" ;
593
- Config::Get ()->Save ();
594
- }
595
- if (ImGui::MenuItem (" Orange" ))
539
+ if (ImGui::BeginMenu (" Maps" , canOpenTerritory))
596
540
{
597
- gui::SetThemePreset (Orange);
598
- CVar_UI_Theme.Get <string>() = " orange" ;
599
- Config::Get ()->Save ();
541
+ for (LevelDefinition& definition : levelDefinitions_)
542
+ {
543
+ bool overrideOpenCheck = (ImGui::IsKeyDown (ImGuiKey_RightCtrl) || ImGui::IsKeyDown (ImGuiKey_LeftCtrl)) && (ImGui::IsKeyDown (ImGuiKey_RightShift) || ImGui::IsKeyDown (ImGuiKey_LeftShift));
544
+ bool alreadyOpen = false ;
545
+ for (Handle <IDocument> doc : State.Documents )
546
+ {
547
+ if (doc->Title == definition.Name )
548
+ alreadyOpen = true ;
549
+ }
550
+
551
+ if (ImGui::MenuItem (definition.Name .c_str (), (definition.Filename + " .vpp_pc" ).c_str (), nullptr , overrideOpenCheck || !alreadyOpen))
552
+ {
553
+ State.CreateDocument (definition.Name , CreateHandle<TerritoryDocument>(&State, definition.Filename + " .vpp_pc" , definition.Name ));
554
+ }
555
+ }
556
+ ImGui::EndMenu ();
600
557
}
601
- if (ImGui::MenuItem (" Blue" ))
558
+ }
559
+ else
560
+ {
561
+ if (ImGui::BeginMenu (" Open a project to edit maps" , false ))
602
562
{
603
- gui::SetThemePreset (Blue);
604
- CVar_UI_Theme.Get <string>() = " blue" ;
605
- Config::Get ()->Save ();
563
+ ImGui::EndMenu ();
606
564
}
607
- ImGui::EndMenu ();
608
565
}
609
566
610
567
bool showFPS = CVar_ShowFPS.Get <bool >();
@@ -963,6 +920,109 @@ void MainGui::DrawProjectSaveLoadDialogs()
963
920
State.FontManager ->FontMedium .Pop ();
964
921
}
965
922
923
+ bool LoadLevelDefinitionsFromXml (tinyxml2::XMLDocument* doc, std::vector<LevelDefinition>& definitions)
924
+ {
925
+ tinyxml2::XMLElement* root = doc->FirstChildElement ();
926
+ tinyxml2::XMLElement* table = root->FirstChildElement (" Table" );
927
+ tinyxml2::XMLElement* current = table->FirstChildElement (" mp_level_list" );
928
+ while (current)
929
+ {
930
+ LevelDefinition definition;
931
+ tinyxml2::XMLElement* nameXml = current->FirstChildElement (" Name" );
932
+ tinyxml2::XMLElement* filenameXml = current->FirstChildElement (" Filename" );
933
+ if (!nameXml)
934
+ {
935
+ LOG_ERROR (" Level definition is missing <Name>. Skipping definition..." );
936
+ return false ;
937
+ }
938
+ if (!filenameXml)
939
+ {
940
+ LOG_ERROR (" Level definition is missing <Filename>. Skipping definition..." );
941
+ return false ;
942
+ }
943
+
944
+ definition.Name = nameXml->GetText ();
945
+ definition.Filename = filenameXml->GetText ();
946
+ definitions.push_back (definition);
947
+ current = current->NextSiblingElement (" mp_level_list" );
948
+ }
949
+
950
+ return true ;
951
+ }
952
+
953
+ void MainGui::LoadLevelDefinitions ()
954
+ {
955
+ // Wait for packfiles to be parsed
956
+ if (!State.PackfileVFS ->Ready () || levelDefinitionsLoaded_)
957
+ return ;
958
+
959
+ // Add hardcoded definitions
960
+ levelDefinitions_.push_back ({ " Terr01" , " zonescript_terr01" });
961
+ levelDefinitions_.push_back ({ " Dlc01" , " zonescript_dlc01" });
962
+
963
+ // Get misc.vpp_pc
964
+ Handle <Packfile3> misc = State.PackfileVFS ->GetPackfile (" misc.vpp_pc" );
965
+ if (!misc)
966
+ {
967
+ LOG_ERROR (" Failed to get misc.vpp_pc for level definition loading! Cannot load level list." );
968
+ levelDefinitionsLoaded_ = true ; // Set true on failure so it doesn't spam the error log
969
+ return ;
970
+ }
971
+
972
+ // Extract xtbls from misc
973
+ auto mpLevels = misc->ExtractSingleFile (" mp_levels.xtbl" );
974
+ auto dlcMpLevels = misc->ExtractSingleFile (" dlc02_mp_levels.xtbl" );
975
+ if (!mpLevels)
976
+ {
977
+ LOG_ERROR (" Failed to extract mp_levels.xtbl from misc.vpp_pc. Can't load level list." );
978
+ levelDefinitionsLoaded_ = true ;
979
+ return ;
980
+ }
981
+ if (!dlcMpLevels)
982
+ {
983
+ LOG_ERROR (" Failed to extract dlc02_mp_levels.xtbl from misc.vpp_pc. Can't load level list." );
984
+ levelDefinitionsLoaded_ = true ;
985
+ return ;
986
+ }
987
+
988
+ // Parse xtbls
989
+ tinyxml2::XMLDocument* mpLevelsXml = new tinyxml2::XMLDocument ();
990
+ tinyxml2::XMLDocument* dlcMpLevelsXml = new tinyxml2::XMLDocument ();
991
+ defer (delete mpLevelsXml);
992
+ defer (delete dlcMpLevelsXml);
993
+ if (mpLevelsXml->Parse ((const char *)mpLevels.value ().data (), mpLevels.value ().size ()) != tinyxml2::XML_SUCCESS)
994
+ {
995
+ LOG_ERROR (" Failed to parse mp_levels.xtbl. Can't load level list." );
996
+ levelDefinitionsLoaded_ = true ;
997
+ return ;
998
+ }
999
+ if (dlcMpLevelsXml->Parse ((const char *)dlcMpLevels.value ().data (), dlcMpLevels.value ().size ()) != tinyxml2::XML_SUCCESS)
1000
+ {
1001
+ LOG_ERROR (" Failed to parse dlc02_mp_levels.xtbl. Can't load level list." );
1002
+ levelDefinitionsLoaded_ = true ;
1003
+ return ;
1004
+ }
1005
+
1006
+ // Read definitions from xtbls
1007
+ if (!LoadLevelDefinitionsFromXml (mpLevelsXml, levelDefinitions_))
1008
+ {
1009
+ LOG_ERROR (" Failed to load level definitions from mp_levels.xtbl. Can't load level list." );
1010
+ levelDefinitionsLoaded_ = true ;
1011
+ return ;
1012
+ }
1013
+ if (!LoadLevelDefinitionsFromXml (dlcMpLevelsXml, levelDefinitions_))
1014
+ {
1015
+ LOG_ERROR (" Failed to load level definitions from dlc02_mp_levels.xtbl. Can't load level list." );
1016
+ levelDefinitionsLoaded_ = true ;
1017
+ return ;
1018
+ }
1019
+
1020
+ // Sort alphabetically
1021
+ std::ranges::sort (levelDefinitions_, [](LevelDefinition& a, LevelDefinition& b) -> bool { return a.Name < b.Name ; });
1022
+
1023
+ levelDefinitionsLoaded_ = true ;
1024
+ }
1025
+
966
1026
void MainGui::SaveAll ()
967
1027
{
968
1028
if (!State.CurrentProject || !State.CurrentProject ->Loaded ())
0 commit comments