Reorganize mission menu and separate design from logic

This commit is contained in:
2026-10-17 18:27:31 +02:00
parent 0e71ef1fa7
commit d5464f7438
52 changed files with 2596 additions and 716 deletions
@@ -3,93 +3,6 @@ modded enum ChimeraMenuPreset
ERR_AdminPanel
}
// Presentation rules only: all mutations still use the authenticated command service.
class ERR_AdminMenuFields
{
static array<string> Keys()
{
return {"aiDoctrine", "aiShadow", "automaticEquipmentChancePercent", "sarkaCarsPerAnger", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "emptyRestartHours", "startMode", "radioDefenceSeconds", "aiIntelChancePercent", "intelRevealChancePercent", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "convoyCount", "antiAirChancePercent", "antiPersonnelMineChancePercent", "heliTimerMinutes"};
}
static bool Available(string key, bool hq, bool intel)
{
if (key == "aiDoctrine" || key == "aiShadow") return hq;
if (key == "sarkaCarsPerAnger") return hq;
if (key == "aiIntelChancePercent") return intel;
if (key == "automaticEquipmentChancePercent" || key == "motorbikeCount" || key == "planeAltitudeMetres" || key == "planeReplacementSeconds" || key == "planeSectorSeconds" || key == "planeReconEnabled" || key == "objectGuardCount" || key == "objectGuardEvery" || key == "objectGuardHQMode" || key == "hunterSpawnDistanceMetres" || key == "heliTimerMinutes" || key == "antiAirChancePercent" || key == "antiPersonnelMineChancePercent" || key == "convoyCount" || key == "reportIntervalSeconds" || key == "supportSquadLimit" || key == "responseRadiusMetres") return hq;
return Keys().Contains(key);
}
// A short journal has no older off-screen lines, even if its slider is zero.
static bool FollowJournal(bool empty, float slider, float height, float viewHeight, bool latest)
{
return latest || empty || slider >= 0.98 || height <= viewHeight;
}
// TextWidget interprets hash-prefixed tokens as localization IDs. Keep the
// server audit and retained entries intact; render their numeric IDs literally.
static string JournalText(string line, bool details = false)
{
return ERR_JournalText.Menu(line, details);
}
static string Display(string key, string value)
{
if (key == "aiDoctrine") { if (value == "1") return "Coordinated"; return "Current"; }
if (key == "aiShadow") { if (value == "1") return "On"; return "Off"; }
if (value == "-1" || value == "-2") return ERR_Settings.SelectionText(value.ToInt());
if (key == "startMode") { if (value == "1") return "Crash"; return "Prison"; }
if (key == "startTimeMinutes") return ERR_TimeService.Clock(value.ToInt());
if (key == "startDateCode") return ERR_EnvironmentService.Date(value.ToInt());
if (key == "weatherProfile") return ERR_EnvironmentService.ProfileName(value.ToInt());
if (key == "viewDistanceMetres" && value == "0") return "default";
return value;
}
static string StartSummary(int saved, int current)
{
return "Next start: " + Display("startMode", saved.ToString()) + " | Current round: " + Display("startMode", current.ToString());
}
static string Command(string key, string value)
{
string prefix;
if (key == "aiDoctrine") prefix = "mission doctrine";
else if (key == "aiShadow") prefix = "mission aishadow";
else if (key == "automaticEquipmentChancePercent") prefix = "hq equipment";
else if (key == "sarkaCarsPerAnger") prefix = "mission sarkacars";
else if (key == "motorbikeCount") prefix = "mission motorbikes";
else if (key == "planeReconEnabled") prefix = "mission plane";
else if (key == "planeAltitudeMetres") prefix = "mission planealtitude";
else if (key == "planeReplacementSeconds") prefix = "mission planereplacement";
else if (key == "planeSectorSeconds") prefix = "mission planesector";
else if (key == "startWeaponCount") prefix = "mission startweapons";
else if (key == "startMagazineCount") prefix = "mission startammo";
else if (key == "objectGuardEvery") prefix = "mission objectevery";
else if (key == "objectGuardHQMode") prefix = "mission objecthq";
else if (key == "objectGuardCount") prefix = "mission objectguards";
else if (key == "hunterSpawnDistanceMetres") prefix = "mission hunterdistance";
else if (key == "fieldCampCount") prefix = "mission camps";
else if (key == "fieldHospitalCount") prefix = "mission hospitals";
else if (key == "roadblockCount") prefix = "mission roadblocks";
else if (key == "emptyRestartHours") prefix = "mission emptyhours";
else if (key == "civilianTrafficCount") prefix = "mission traffic";
else if (key == "startMode") prefix = "mission start";
else if (key == "radioDefenceSeconds") prefix = "mission radio";
else if (key == "aiIntelChancePercent") prefix = "intel carrying";
else if (key == "intelRevealChancePercent") prefix = "intel reveal";
else if (key == "startTimeMinutes") prefix = "mission time";
else if (key == "timeMultiplier") prefix = "mission speed";
else if (key == "startDateCode") prefix = "mission date";
else if (key == "weatherProfile") prefix = "mission weather";
else if (key == "viewDistanceMetres") prefix = "mission viewdistance";
else if (key == "reportIntervalSeconds") prefix = "hq interval";
else if (key == "supportSquadLimit") prefix = "hq squads";
else if (key == "responseRadiusMetres") prefix = "hq radius";
else if (key == "antiAirChancePercent") prefix = "hq aa";
else if (key == "antiPersonnelMineChancePercent") prefix = "hq mines";
else if (key == "heliTimerMinutes") prefix = "hq heli timer";
else if (key == "convoyCount") prefix = "hq convoy count";
value = value.Trim();
if (prefix.IsEmpty() || value.IsEmpty() || value.Length() > 32) return "";
return prefix + " set " + value;
}
}
// Own mission dialog. ChimeraMenuBase owns focus, cursor and input-context lifetime.
class ERR_AdminMenu : ChimeraMenuBase
{
@@ -133,17 +46,9 @@ class ERR_AdminMenu : ChimeraMenuBase
m_Controller = SCR_PlayerController.Cast(GetGame().GetPlayerController());
m_Mode = ERR_GameMode.Cast(GetGame().GetGameMode());
if (!m_Controller) { Close(); return; }
array<string> names = {"PlaneOn", "PlaneOff", "Close", "OpenSettings", "OpenWorld", "OpenFactions", "OpenHQ", "OpenJournal", "PauseJournal", "JournalDetails", "LatestJournal", "KeepWeaponOn", "KeepWeaponOff", "SelfEpiOn", "SelfEpiOff", "IntelSummary", "RerollIntel", "OpenMods", "OpenIntel", "OpenStatus", "OpenTerrains", "OpenItems", "OpenTTS"};
names.Insert("RandomDay_startTimeMinutes");
names.Insert("Default_viewDistanceMetres");
names.Insert("OpenForces");
names.Insert("OpenSarka"); names.Insert("SarkaBack");
names.Insert("RestartEmptyOn"); names.Insert("RestartEmptyOff");
names.Insert("OpenGuards"); names.Insert("GuardsBack");
names.Insert("OpenSites"); names.Insert("SitesBack");
names.Insert("OpenPlane"); names.Insert("PlaneBack");
names.Insert("OpenAI"); names.Insert("AIBack");
names.Insert("OpenStarter"); names.Insert("EquipmentOverview"); names.Insert("OpenAllowedItems"); names.Insert("OpenItemPresets"); names.Insert("OpenWeights"); names.Insert("OpenRecommendations"); names.Insert("RecommendationsBack"); names.Insert("RecommendedPlane"); names.Insert("RecommendedWeapons");
array<string> names = {"PlaneOn", "PlaneOff", "Close", "PauseJournal", "JournalDetails", "LatestJournal", "KeepWeaponOn", "KeepWeaponOff", "SelfEpiOn", "SelfEpiOff", "IntelSummary", "RerollIntel", "RandomDay_startTimeMinutes", "Default_viewDistanceMetres", "RestartEmptyOn", "RestartEmptyOff", "RecommendedPlane", "RecommendedWeapons"};
foreach (string mainButton : ERR_AdminMenuPages.MainButtons()) names.Insert(mainButton);
foreach (string pageButton : Navigation()) names.Insert(pageButton);
for (int density = 1; density <= 4; density++) names.Insert("GuardEvery_" + density.ToString());
for (int policy = 0; policy <= 2; policy++) names.Insert("GuardHQ_" + policy.ToString());
foreach (string key : ERR_AdminMenuFields.Keys())
@@ -167,7 +72,7 @@ class ERR_AdminMenu : ChimeraMenuBase
m_Ready = false; m_Age = 0; m_Poll = 0;
GetGame().GetInputManager().AddActionListener(UIConstants.MENU_ACTION_BACK, EActionTrigger.PRESSED, OnBack);
m_Bound = true;
ShowPage(0);
ShowPage(ERR_AdminPage.MISSION);
UpdateActions();
RequestSnapshot();
}
@@ -194,7 +99,7 @@ class ERR_AdminMenu : ChimeraMenuBase
UpdateActions();
}
if (IsFocused() && m_Poll >= 2) RequestSnapshot();
if (m_Page != 2) return;
if (m_Page != ERR_AdminPage.JOURNAL) return;
m_LogPoll += tDelta;
if (m_LogPending && m_LogPoll > 5) m_LogPending = false;
if (IsFocused() && m_Ready && m_HQ && !m_LogPaused && !m_LogPending && m_LogPoll >= 1)
@@ -230,7 +135,7 @@ class ERR_AdminMenu : ChimeraMenuBase
SCR_EditBoxComponent edit = SCR_EditBoxComponent.GetEditBoxComponent(key, GetRootWidget());
if (edit && edit.IsInWriteMode()) return;
}
if (m_Page != 0) ShowPage(0);
if (m_Page != ERR_AdminPage.MISSION) ShowPage(ERR_AdminPage.MISSION);
else Close();
}
protected void SetText(string name, string value)
@@ -238,26 +143,14 @@ class ERR_AdminMenu : ChimeraMenuBase
TextWidget widget = TextWidget.Cast(GetRootWidget().FindAnyWidget(name));
if (widget) widget.SetText(value);
}
// Stable page order is also used by the shared navigation bar and native probe.
static array<string> Pages() { return {"SettingsPage", "HQPage", "JournalPage", "ModsPage", "FactionsPage", "WorldPage", "IntelPage", "StatusPage", "TerrainsPage", "ItemsPage", "TTSPage", "ForcesPage", "GuardsPage", "SitesPage", "PlanePage", "RecommendationsPage", "WeightsPage", "AllowedItemsPage", "ItemPresetsPage", "AIPage", "StarterPage", "SarkaPage"}; }
static array<string> Navigation() { return {"OpenSettings", "OpenHQ", "OpenJournal", "OpenMods", "OpenFactions", "OpenWorld", "OpenIntel", "OpenStatus", "OpenTerrains", "OpenItems", "OpenTTS", "OpenForces", "OpenGuards", "OpenSites", "OpenPlane", "OpenRecommendations", "OpenWeights", "OpenAllowedItems", "OpenItemPresets", "OpenAI", "OpenStarter", "OpenSarka"}; }
// Stable IDs are generated from the navigation manifest; extensions use named pages.
static array<string> Pages() { return ERR_AdminMenuPages.Pages(); }
static array<string> Navigation() { return ERR_AdminMenuPages.Navigation(); }
protected void ShowPage(int page)
{
array<string> pages = Pages();
array<string> navigation = Navigation();
if (page < 0 || page >= pages.Count()) return;
if (!ERR_AdminMenuView.ShowPage(GetRootWidget(), page)) return;
m_Page = page;
for (int i = 0; i < pages.Count(); i++)
{
GetRootWidget().FindAnyWidget(pages[i]).SetVisible(i == page);
bool selected = i == page || ((page == 12 || page == 13) && i == 11) || ((page == 16 || page == 17 || page == 18 || page == 20) && i == 9) || ((page == 14 || page == 19) && i == 1);
GetRootWidget().FindAnyWidget("Selected_" + navigation[i]).SetVisible(selected);
ERR_SelectionStyle.Choice(SCR_ButtonTextComponent.GetButtonText(navigation[i], GetRootWidget()), selected);
}
GetRootWidget().FindAnyWidget("EquipmentNavigation").SetVisible(page == 9 || page == 16 || page == 17 || page == 18 || page == 20);
GetRootWidget().FindAnyWidget("Selected_EquipmentOverview").SetVisible(page == 9);
ERR_SelectionStyle.Choice(SCR_ButtonTextComponent.GetButtonText("EquipmentOverview", GetRootWidget()), page == 9);
if (page == 2) m_LogPoll = 1;
if (page == ERR_AdminPage.JOURNAL) m_LogPoll = 1;
}
protected int m_KeepWeaponState = -1;
protected int m_SelfEpiState = -1;
@@ -312,8 +205,8 @@ class ERR_AdminMenu : ChimeraMenuBase
Widget state = GetRootWidget().FindAnyWidget("recommended_" + addonKey);
if (state)
{
if (entry.Substring(divider + 1, entry.Length() - divider - 1) == "1") state.SetColor(Color.FromRGBA(105, 190, 140, 255));
else state.SetColor(Color.FromRGBA(220, 115, 115, 255));
if (entry.Substring(divider + 1, entry.Length() - divider - 1) == "1") state.SetColor(Color.FromInt(ERR_MenuTheme.READY_TEXT));
else state.SetColor(Color.FromInt(ERR_MenuTheme.UNAVAILABLE_TEXT));
}
continue;
}
@@ -435,13 +328,10 @@ class ERR_AdminMenu : ChimeraMenuBase
if (!button || !IsFocused()) return;
string name = button.GetRootWidget().GetName();
if (name == "Close") { Close(); return; }
if (name == "RecommendationsBack") { ShowPage(3); return; }
if (name == "RecommendedPlane") { ShowPage(14); return; }
if (name == "EquipmentOverview") { ShowPage(9); return; }
if (name == "RecommendedWeapons") { ShowPage(9); return; }
if (name == "PlaneBack") { ShowPage(1); return; }
if (name == "AIBack") { ShowPage(1); return; }
if (name == "GuardsBack" || name == "SitesBack" || name == "SarkaBack") { ShowPage(11); return; }
int section = ERR_AdminMenuPages.MainButtons().Find(name);
if (section >= 0) { ShowPage(ERR_AdminMenuPages.MainPage(section)); return; }
if (name == "RecommendedPlane") { ShowPage(ERR_AdminPage.PLANE); return; }
if (name == "RecommendedWeapons") { ShowPage(ERR_AdminPage.ITEMS); return; }
int page = Navigation().Find(name);
if (page >= 0) { ShowPage(page); return; }
if (name == "JournalDetails")
@@ -0,0 +1,86 @@
// Presentation rules only: all mutations still use the authenticated command service.
class ERR_AdminMenuFields
{
static array<string> Keys()
{
return {"aiDoctrine", "aiShadow", "automaticEquipmentChancePercent", "sarkaCarsPerAnger", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "emptyRestartHours", "startMode", "radioDefenceSeconds", "aiIntelChancePercent", "intelRevealChancePercent", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "convoyCount", "antiAirChancePercent", "antiPersonnelMineChancePercent", "heliTimerMinutes"};
}
static bool Available(string key, bool hq, bool intel)
{
if (key == "aiDoctrine" || key == "aiShadow") return hq;
if (key == "sarkaCarsPerAnger") return hq;
if (key == "aiIntelChancePercent") return intel;
if (key == "automaticEquipmentChancePercent" || key == "motorbikeCount" || key == "planeAltitudeMetres" || key == "planeReplacementSeconds" || key == "planeSectorSeconds" || key == "planeReconEnabled" || key == "objectGuardCount" || key == "objectGuardEvery" || key == "objectGuardHQMode" || key == "hunterSpawnDistanceMetres" || key == "heliTimerMinutes" || key == "antiAirChancePercent" || key == "antiPersonnelMineChancePercent" || key == "convoyCount" || key == "reportIntervalSeconds" || key == "supportSquadLimit" || key == "responseRadiusMetres") return hq;
return Keys().Contains(key);
}
// A short journal has no older off-screen lines, even if its slider is zero.
static bool FollowJournal(bool empty, float slider, float height, float viewHeight, bool latest)
{
return latest || empty || slider >= 0.98 || height <= viewHeight;
}
// TextWidget interprets hash-prefixed tokens as localization IDs. Keep the
// server audit and retained entries intact; render their numeric IDs literally.
static string JournalText(string line, bool details = false)
{
return ERR_JournalText.Menu(line, details);
}
static string Display(string key, string value)
{
if (key == "aiDoctrine") { if (value == "1") return "Coordinated"; return "Current"; }
if (key == "aiShadow") { if (value == "1") return "On"; return "Off"; }
if (value == "-1" || value == "-2") return ERR_Settings.SelectionText(value.ToInt());
if (key == "startMode") { if (value == "1") return "Crash"; return "Prison"; }
if (key == "startTimeMinutes") return ERR_TimeService.Clock(value.ToInt());
if (key == "startDateCode") return ERR_EnvironmentService.Date(value.ToInt());
if (key == "weatherProfile") return ERR_EnvironmentService.ProfileName(value.ToInt());
if (key == "viewDistanceMetres" && value == "0") return "default";
return value;
}
static string StartSummary(int saved, int current)
{
return "Next start: " + Display("startMode", saved.ToString()) + " | Current round: " + Display("startMode", current.ToString());
}
static string Command(string key, string value)
{
string prefix;
if (key == "aiDoctrine") prefix = "mission doctrine";
else if (key == "aiShadow") prefix = "mission aishadow";
else if (key == "automaticEquipmentChancePercent") prefix = "hq equipment";
else if (key == "sarkaCarsPerAnger") prefix = "mission sarkacars";
else if (key == "motorbikeCount") prefix = "mission motorbikes";
else if (key == "planeReconEnabled") prefix = "mission plane";
else if (key == "planeAltitudeMetres") prefix = "mission planealtitude";
else if (key == "planeReplacementSeconds") prefix = "mission planereplacement";
else if (key == "planeSectorSeconds") prefix = "mission planesector";
else if (key == "startWeaponCount") prefix = "mission startweapons";
else if (key == "startMagazineCount") prefix = "mission startammo";
else if (key == "objectGuardEvery") prefix = "mission objectevery";
else if (key == "objectGuardHQMode") prefix = "mission objecthq";
else if (key == "objectGuardCount") prefix = "mission objectguards";
else if (key == "hunterSpawnDistanceMetres") prefix = "mission hunterdistance";
else if (key == "fieldCampCount") prefix = "mission camps";
else if (key == "fieldHospitalCount") prefix = "mission hospitals";
else if (key == "roadblockCount") prefix = "mission roadblocks";
else if (key == "emptyRestartHours") prefix = "mission emptyhours";
else if (key == "civilianTrafficCount") prefix = "mission traffic";
else if (key == "startMode") prefix = "mission start";
else if (key == "radioDefenceSeconds") prefix = "mission radio";
else if (key == "aiIntelChancePercent") prefix = "intel carrying";
else if (key == "intelRevealChancePercent") prefix = "intel reveal";
else if (key == "startTimeMinutes") prefix = "mission time";
else if (key == "timeMultiplier") prefix = "mission speed";
else if (key == "startDateCode") prefix = "mission date";
else if (key == "weatherProfile") prefix = "mission weather";
else if (key == "viewDistanceMetres") prefix = "mission viewdistance";
else if (key == "reportIntervalSeconds") prefix = "hq interval";
else if (key == "supportSquadLimit") prefix = "hq squads";
else if (key == "responseRadiusMetres") prefix = "hq radius";
else if (key == "antiAirChancePercent") prefix = "hq aa";
else if (key == "antiPersonnelMineChancePercent") prefix = "hq mines";
else if (key == "heliTimerMinutes") prefix = "hq heli timer";
else if (key == "convoyCount") prefix = "hq convoy count";
value = value.Trim();
if (prefix.IsEmpty() || value.IsEmpty() || value.Length() > 32) return "";
return prefix + " set " + value;
}
}
@@ -0,0 +1,46 @@
// Generated by tools/build_admin_menu.py; edit ui/admin_menu/navigation.json.
enum ERR_AdminPage
{
MISSION = 0,
HQ = 1,
JOURNAL = 2,
MODS = 3,
FACTIONS = 4,
WORLD = 5,
INTEL = 6,
STATUS = 7,
TERRAINS = 8,
ITEMS = 9,
SPEECH = 10,
FORCES = 11,
GUARDS = 12,
SITES = 13,
PLANE = 14,
RECOMMENDATIONS = 15,
WEIGHTS = 16,
ALLOWED_ITEMS = 17,
ITEM_PRESETS = 18,
AI = 19,
STARTER = 20,
SARKA = 21,
}
class ERR_AdminMenuPages
{
static array<string> Pages() { return {"SettingsPage", "HQPage", "JournalPage", "ModsPage", "FactionsPage", "WorldPage", "IntelPage", "StatusPage", "TerrainsPage", "ItemsPage", "TTSPage", "ForcesPage", "GuardsPage", "SitesPage", "PlanePage", "RecommendationsPage", "WeightsPage", "AllowedItemsPage", "ItemPresetsPage", "AIPage", "StarterPage", "SarkaPage"}; }
static array<string> Navigation() { return {"OpenSettings", "OpenHQ", "OpenJournal", "OpenMods", "OpenFactions", "OpenWorld", "OpenIntel", "OpenStatus", "OpenTerrains", "OpenItems", "OpenTTS", "OpenForces", "OpenGuards", "OpenSites", "OpenPlane", "OpenRecommendations", "OpenWeights", "OpenAllowedItems", "OpenItemPresets", "OpenAI", "OpenStarter", "OpenSarka"}; }
static array<string> MainButtons() { return {"Main_Mission", "Main_Forces", "Main_HQ", "Main_Intel", "Main_Items", "Main_Mods", "Main_Audio", "Main_Status"}; }
static array<string> Sidebars() { return {"Sidebar_Mission", "Sidebar_Forces", "Sidebar_HQ", "Sidebar_Intel", "Sidebar_Items", "Sidebar_Mods", "Sidebar_Audio", "Sidebar_Status"}; }
static int Group(int page)
{
array<int> groups = {0, 2, 7, 5, 1, 0, 3, 7, 0, 4, 6, 1, 1, 1, 2, 5, 4, 4, 4, 2, 4, 1};
if (page < 0 || page >= groups.Count()) return -1;
return groups[page];
}
static int MainPage(int group)
{
array<int> pages = {ERR_AdminPage.MISSION, ERR_AdminPage.FORCES, ERR_AdminPage.HQ, ERR_AdminPage.INTEL, ERR_AdminPage.ITEMS, ERR_AdminPage.MODS, ERR_AdminPage.SPEECH, ERR_AdminPage.STATUS};
if (group < 0 || group >= pages.Count()) return -1;
return pages[group];
}
}
@@ -0,0 +1,26 @@
// Widget presentation only. No settings writes, authorization or transport here.
class ERR_AdminMenuView
{
static bool ShowPage(Widget root, int page)
{
int group = ERR_AdminMenuPages.Group(page);
if (!root || group < 0) return false;
array<string> pages = ERR_AdminMenuPages.Pages();
array<string> navigation = ERR_AdminMenuPages.Navigation();
for (int i = 0; i < pages.Count(); i++)
{
root.FindAnyWidget(pages[i]).SetVisible(i == page);
root.FindAnyWidget("Selected_" + navigation[i]).SetVisible(i == page);
ERR_SelectionStyle.Choice(SCR_ButtonTextComponent.GetButtonText(navigation[i], root), i == page);
}
array<string> buttons = ERR_AdminMenuPages.MainButtons();
array<string> sidebars = ERR_AdminMenuPages.Sidebars();
for (int section = 0; section < buttons.Count(); section++)
{
root.FindAnyWidget(sidebars[section]).SetVisible(section == group);
root.FindAnyWidget("Selected_" + buttons[section]).SetVisible(section == group);
ERR_SelectionStyle.Choice(SCR_ButtonTextComponent.GetButtonText(buttons[section], root), section == group);
}
return true;
}
}
@@ -7,7 +7,7 @@ modded class ERR_AdminMenu
{
Open();
ERR_AdminMenu menu = GetOpenMenu();
if (menu) menu.ShowPage(3);
if (menu) menu.ShowPage(ERR_AdminPage.MODS);
}
override void OnMenuOpen()
{
@@ -0,0 +1,13 @@
// Generated by tools/build_admin_menu.py; edit ui/admin_menu/theme.json.
class ERR_MenuTheme
{
static const int NEUTRAL = 0xFF454B52;
static const int NEUTRAL_HOVER = 0xFF606870;
static const int POSITIVE = 0xFF246B3C;
static const int POSITIVE_HOVER = 0xFF31894F;
static const int NEGATIVE = 0xFF8F2929;
static const int NEGATIVE_HOVER = 0xFFB43B3B;
static const int MIXED = 0xFF805B17;
static const int READY_TEXT = 0xFF69BE8C;
static const int UNAVAILABLE_TEXT = 0xFFDC7373;
}
@@ -19,16 +19,33 @@ modded class ERR_AdminMenu
{
GetRootWidget().Update();
if (m_Page != expected) return false;
int activeGroup = ERR_AdminMenuPages.Group(expected);
Widget content = GetRootWidget().FindAnyWidget(Pages()[expected]);
if (!content) return false;
float contentX, contentY, contentWidth, contentHeight;
content.GetScreenPos(contentX, contentY); content.GetScreenSize(contentWidth, contentHeight);
array<string> mainButtons = ERR_AdminMenuPages.MainButtons();
array<string> sidebars = ERR_AdminMenuPages.Sidebars();
for (int group = 0; group < mainButtons.Count(); group++)
{
Widget mainTab = GetRootWidget().FindAnyWidget(mainButtons[group]);
Widget marker = GetRootWidget().FindAnyWidget("Selected_" + mainButtons[group]);
Widget sidebar = GetRootWidget().FindAnyWidget(sidebars[group]);
if (!mainTab || !marker || !sidebar || !mainTab.IsVisible() || marker.IsVisible() != (group == activeGroup) || sidebar.IsVisible() != (group == activeGroup)) return false;
float x, y, width, height;
mainTab.GetScreenPos(x, y); mainTab.GetScreenSize(width, height);
if (width <= 1 || height <= 1 || y + height > contentY) return false;
}
for (int i = 0; i < Pages().Count(); i++)
{
Widget panel = GetRootWidget().FindAnyWidget(Pages()[i]);
Widget tab = GetRootWidget().FindAnyWidget(Navigation()[i]);
Widget selected = GetRootWidget().FindAnyWidget("Selected_" + Navigation()[i]);
if (!panel || !tab || !selected || panel.IsVisible() != (i == expected) || !tab.IsVisible() || selected.IsVisible() != (i == expected)) return false;
float x, y, width, height, px, py;
tab.GetScreenPos(x, y); tab.GetScreenSize(width, height); panel.GetScreenPos(px, py);
if (width <= 1 || height <= 1 || (i == expected && y + height > py))
{ PrintFormat("ERR ADMIN NAV BOUNDS: page=%1 tab=%2 bounds=%3,%4,%5,%6 panelY=%7", expected, i, x, y, width, height, py); return false; }
if (!panel || !tab || !selected || panel.IsVisible() != (i == expected) || selected.IsVisible() != (i == expected)) return false;
if (ERR_AdminMenuPages.Group(i) != activeGroup) continue;
float x, y, width, height;
tab.GetScreenPos(x, y); tab.GetScreenSize(width, height);
if (!tab.IsVisible() || width <= 1 || height <= 1 || x + width > contentX || y < contentY || y + height > contentY + contentHeight) return false;
}
return true;
}
@@ -38,6 +38,7 @@ modded class ERR_HQ
modded class ERR_GameMode
{
protected int m_ERRSarkaStage;
protected int m_ERRSarkaMenuPage;
protected int m_ERRSarkaDeadline;
protected bool m_ERRSarkaOrder;
protected override void EOnInit(IEntity owner)
@@ -66,7 +67,27 @@ modded class ERR_GameMode
if (m_ERRSarkaStage == 0)
{
if (!ERR_SarkaProbeCheck(Settings().Get("sarkaCarsPerAnger") == 1, "default one car per anger point")) return;
ERR_AdminMenu.Open(); m_ERRSarkaStage = 10; return;
ERR_AdminMenu.Open(); m_ERRSarkaStage = 9; return;
}
if (m_ERRSarkaStage == 9)
{
ERR_AdminMenu menu = ERR_AdminMenu.GetOpenMenu();
if (!menu || !menu.ERR_SarkaProbeReady()) return;
menu.ERR_ProbePage(m_ERRSarkaMenuPage); m_ERRSarkaStage = 90; return;
}
if (m_ERRSarkaStage == 90)
{
ERR_AdminMenu menu = ERR_AdminMenu.GetOpenMenu();
if (!ERR_SarkaProbeCheck(menu && menu.ERR_ProbeNavigation(m_ERRSarkaMenuPage), "top main tabs and left sidebar reach page " + m_ERRSarkaMenuPage.ToString())) return;
System.MakeScreenshot("$logs:err-menu-sidebar-" + m_ERRSarkaMenuPage.ToString());
m_ERRSarkaStage = 91; return;
}
if (m_ERRSarkaStage == 91)
{
m_ERRSarkaMenuPage++;
if (m_ERRSarkaMenuPage < ERR_AdminMenu.Pages().Count()) m_ERRSarkaStage = 9;
else m_ERRSarkaStage = 10;
return;
}
if (m_ERRSarkaStage == 10)
{
@@ -4,8 +4,8 @@ class ERR_SelectionStyle
static void Action(SCR_ButtonTextComponent button, bool enable)
{
if (!button) return;
Color normal = Color.FromInt(0xFF8F2929), hover = Color.FromInt(0xFFB43B3B);
if (enable) { normal = Color.FromInt(0xFF246B3C); hover = Color.FromInt(0xFF31894F); }
Color normal = Color.FromInt(ERR_MenuTheme.NEGATIVE), hover = Color.FromInt(ERR_MenuTheme.NEGATIVE_HOVER);
if (enable) { normal = Color.FromInt(ERR_MenuTheme.POSITIVE); hover = Color.FromInt(ERR_MenuTheme.POSITIVE_HOVER); }
button.SetBackgroundColors(normal, hover, normal, hover, hover);
button.ColorizeBackground(false);
}
@@ -14,11 +14,11 @@ class ERR_SelectionStyle
static void Choice(SCR_ButtonTextComponent button, bool selected, bool positive = true)
{
if (!button) return;
Color normal = Color.FromInt(0xFF454B52), hover = Color.FromInt(0xFF606870);
Color normal = Color.FromInt(ERR_MenuTheme.NEUTRAL), hover = Color.FromInt(ERR_MenuTheme.NEUTRAL_HOVER);
if (selected)
{
normal = Color.FromInt(0xFF246B3C); hover = Color.FromInt(0xFF31894F);
if (!positive) { normal = Color.FromInt(0xFF8F2929); hover = Color.FromInt(0xFFB43B3B); }
normal = Color.FromInt(ERR_MenuTheme.POSITIVE); hover = Color.FromInt(ERR_MenuTheme.POSITIVE_HOVER);
if (!positive) { normal = Color.FromInt(ERR_MenuTheme.NEGATIVE); hover = Color.FromInt(ERR_MenuTheme.NEGATIVE_HOVER); }
}
button.SetDisabledOpacity(1);
button.SetBackgroundColors(normal, hover, normal, hover, hover);
@@ -34,10 +34,10 @@ class ERR_SelectionStyle
{
SCR_ButtonTextComponent badge = SCR_ButtonTextComponent.GetButtonText(name, parent);
if (!badge) return;
string label = "Waiting..."; Color color = Color.FromInt(0xFF454B52);
if (state == 0) { label = "Disabled"; color = Color.FromInt(0xFF8F2929); }
if (state == 1) { label = "Enabled"; color = Color.FromInt(0xFF246B3C); }
if (state == 2) { label = "Mixed"; color = Color.FromInt(0xFF805B17); }
string label = "Waiting..."; Color color = Color.FromInt(ERR_MenuTheme.NEUTRAL);
if (state == 0) { label = "Disabled"; color = Color.FromInt(ERR_MenuTheme.NEGATIVE); }
if (state == 1) { label = "Enabled"; color = Color.FromInt(ERR_MenuTheme.POSITIVE); }
if (state == 2) { label = "Mixed"; color = Color.FromInt(ERR_MenuTheme.MIXED); }
badge.SetText(label); badge.SetDisabledOpacity(1); badge.SetEnabled(false, false);
badge.SetBackgroundColors(color, color, color, color, color); badge.ColorizeBackground(false);
}
@@ -216,7 +216,7 @@ modded class ERR_AdminMenu
if (button.GetRootWidget() != row.FindAnyWidget("Enable")) continue;
SCR_ComboBoxComponent combo = SCR_ComboBoxComponent.GetComboBoxComponent("ItemsMod", GetRootWidget());
if (combo) combo.SetCurrentItem(m_ServerAddonKeys.Find(addon), false, false, false);
ShowPage(17); SubmitItems("items list " + addon + " 0"); return;
ShowPage(ERR_AdminPage.ALLOWED_ITEMS); SubmitItems("items list " + addon + " 0"); return;
}
}
protected void SubmitItems(string command)
File diff suppressed because it is too large Load Diff