Reorganize mission menu and separate design from logic
This commit is contained in:
+15
-11
@@ -7,8 +7,10 @@ from pathlib import Path
|
||||
BASE=Path(os.environ.get("ERR_CONVOY_SETTINGS_ROOT", Path(__file__).resolve().parents[1]))
|
||||
from policy_harness import ROOT, body, run
|
||||
source = BASE / 'addons/core/Scripts/Game/ERR/00_Core/ERR_AdminMenu.c'
|
||||
fields = source.with_name('ERR_AdminMenuFields.c')
|
||||
navigation = source.with_name('ERR_AdminMenuPages.c')
|
||||
layout = BASE / 'addons/core/UI/layouts/ERR/AdminMenu.layout'
|
||||
keys = re.findall(r'"([A-Za-z]+)"', body(source, 'static array<string> Keys('))
|
||||
keys = re.findall(r'"([A-Za-z]+)"', body(fields, 'static array<string> Keys('))
|
||||
settings = re.findall(r'"([A-Za-z]+)"', body(BASE / 'addons/core/Scripts/Game/ERR/00_Core/ERR_Settings.c', 'static array<string> Keys('))
|
||||
assert len(keys) == len(set(keys)) and set(settings) - set(keys) == {"automaticConvoyEnabled", "keepWeaponOnUnconscious", "selfEpinephrine", "planeReconEnabled", "restartOnEmpty"}
|
||||
assert set(keys) <= set(settings) # Every menu field must have a server setting.
|
||||
@@ -46,9 +48,9 @@ else:
|
||||
names = re.findall(r'\bName "([^"]+)"', layout.read_text())
|
||||
assert 'RandomDay_startTimeMinutes' in names
|
||||
assert 'PlaneOn' in names and 'PlaneOff' in names
|
||||
assert all(n in names for n in ('OpenPlane','PlaneBack','PlanePage','Selected_OpenPlane'))
|
||||
assert all(n in names for n in ('OpenPlane','Sidebar_HQ','PlanePage','Selected_OpenPlane'))
|
||||
for signature in ('static array<string> Pages(', 'static array<string> Navigation('):
|
||||
for n in re.findall(r'"([A-Za-z]+)"', body(source, signature)): assert n in names
|
||||
for n in re.findall(r'"([A-Za-z]+)"', body(navigation, signature)): assert n in names
|
||||
assert 'Apply_planeReconEnabled' not in names # Saved-state toggle pair, not a numeric edit.
|
||||
assert len(names) == len(set(names)), 'Menu widget names must be unique'
|
||||
for key in keys:
|
||||
@@ -59,11 +61,11 @@ for name in ('Close', 'SettingsPage', 'HQPage', 'JournalPage', 'JournalText', 'J
|
||||
assert name in names
|
||||
for name in re.findall(r'"([A-Za-z]+)"', body(source, 'override void OnMenuOpen(').split('foreach')[0]):
|
||||
assert name in names
|
||||
journal = body(source, 'static string JournalText(').replace('ERR_JournalText.Menu', 'ERR_JournalText::Menu')
|
||||
journal = body(fields, 'static string JournalText(').replace('ERR_JournalText.Menu', 'ERR_JournalText::Menu')
|
||||
menu_format = body(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_JournalText.c', 'static string Menu(')
|
||||
follow = body(source, 'static bool FollowJournal(')
|
||||
command = body(source, 'static string Command(')
|
||||
available = body(source, 'static bool Available(').replace('Keys().Contains(key)', 'Known(key)')
|
||||
follow = body(fields, 'static bool FollowJournal(')
|
||||
command = body(fields, 'static string Command(')
|
||||
available = body(fields, 'static bool Available(').replace('Keys().Contains(key)', 'Known(key)')
|
||||
known = ' || '.join('key == "' + key + '"' for key in keys)
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
@@ -160,7 +162,7 @@ int main(){
|
||||
}
|
||||
''')
|
||||
# All four rows remain inside the visible Mission page with no overlapping controls.
|
||||
page = body(layout, 'FrameWidgetClass { Name "SettingsPage"')
|
||||
page = body(layout, 'FrameWidgetClass { "Is Visible" 1 Name "SettingsPage"')
|
||||
rectangles = []
|
||||
for line in page.splitlines()[1:]:
|
||||
if not line.startswith(('TextWidgetClass', 'ButtonWidgetClass')):
|
||||
@@ -183,8 +185,10 @@ print('PASS: item editor, preset controls and dynamic item-row layout are struct
|
||||
|
||||
# Run the production selected-state styling: retain selected color at disabled
|
||||
# opacity 1, while unselected controls remain neutral rather than looking active.
|
||||
choice=body(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SelectionStyle.c','static void Choice(').replace('SCR_ButtonTextComponent button','SCR_ButtonTextComponent* button').replace('button.','button->').replace('Color.FromInt','Color::FromInt')
|
||||
run(r'''
|
||||
choice=body(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SelectionStyle.c','static void Choice(').replace('SCR_ButtonTextComponent button','SCR_ButtonTextComponent* button').replace('button.','button->').replace('Color.FromInt','Color::FromInt').replace('ERR_MenuTheme.', 'ERR_MenuTheme::')
|
||||
palette=(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_MenuTheme.c').read_text().split('class ERR_MenuTheme',1)[1]
|
||||
palette='struct ERR_MenuTheme'+palette.replace('static const int', 'static constexpr uint32_t')+';\n'
|
||||
run('#include <cstdint>\n'+palette+r'''
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
struct Color{uint32_t value;static Color FromInt(uint32_t v){return {v};}};
|
||||
@@ -213,7 +217,7 @@ weight_layout=(ROOT/'addons/core/UI/layouts/ERR/ItemWeightRow.layout').read_text
|
||||
validate_layout_structure(weight_layout)
|
||||
weight_names=re.findall(r'\bName "([^"]+)"', weight_layout)
|
||||
assert len(weight_names)==len(set(weight_names))
|
||||
for name in ('EquipmentNavigation','EquipmentOverview','OpenAllowedItems','AllowedItemsPage','OpenItemPresets','ItemPresetsPage','OpenWeights','WeightsPage','WeightsMod','WeightsShow','WeightsPrevious','WeightsNext','ModWeight','ModWeightApply','ModWeightNormal','ModWeightHigh','ModWeightSaved','WeightsChance','WeightsPageState','WeightsContent','WeightsScroll'):
|
||||
for name in ('Sidebar_Items','OpenItems','OpenAllowedItems','AllowedItemsPage','OpenItemPresets','ItemPresetsPage','OpenWeights','WeightsPage','WeightsMod','WeightsShow','WeightsPrevious','WeightsNext','ModWeight','ModWeightApply','ModWeightNormal','ModWeightHigh','ModWeightSaved','WeightsChance','WeightsPageState','WeightsContent','WeightsScroll'):
|
||||
assert name in names, name
|
||||
for name in ('WeightLabel','WeightState','WeightValue','WeightApply','WeightNormal'):
|
||||
assert name in weight_names, name
|
||||
|
||||
Reference in New Issue
Block a user