Make empty-server restarts configurable in the mission menu
Add persistent last-disconnect and idle-interval controls with enabled/five-hour defaults. Apply saved settings immediately, reset idle time while occupied, and cover migration, routing, layout and restart behavior.
This commit is contained in:
@@ -10,7 +10,7 @@ source = BASE / 'addons/core/Scripts/Game/ERR/00_Core/ERR_AdminMenu.c'
|
||||
layout = BASE / 'addons/core/UI/layouts/ERR/AdminMenu.layout'
|
||||
keys = re.findall(r'"([A-Za-z]+)"', body(source, '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", "planeReconEnabled"}
|
||||
assert len(keys) == len(set(keys)) and set(settings) - set(keys) == {"automaticConvoyEnabled", "keepWeaponOnUnconscious", "planeReconEnabled", "restartOnEmpty"}
|
||||
assert set(keys) <= set(settings) # Every menu field must have a server setting.
|
||||
# Native layouts place child widget declarations inside an anonymous Children
|
||||
# block. A missing brace can preserve most widget names yet prevent menu load.
|
||||
@@ -53,7 +53,7 @@ assert 'Apply_planeReconEnabled' not in names # Saved-state toggle pair, not a
|
||||
assert len(names) == len(set(names)), 'Menu widget names must be unique'
|
||||
for key in keys:
|
||||
assert all(name in names for name in (key, 'Apply_' + key, 'Saved_' + key, 'Help_' + key))
|
||||
if key not in ('aiDoctrine','aiShadow','motorbikeCount','planeAltitudeMetres','planeReplacementSeconds','planeSectorSeconds','fieldCampCount','fieldHospitalCount','startMode','civilianTrafficCount','roadblockCount','hunterSpawnDistanceMetres','objectGuardCount','objectGuardEvery','objectGuardHQMode','startWeaponCount','startMagazineCount'): assert ('Default_' if key == 'viewDistanceMetres' else 'Random_') + key in names
|
||||
if key not in ('emptyRestartHours','aiDoctrine','aiShadow','motorbikeCount','planeAltitudeMetres','planeReplacementSeconds','planeSectorSeconds','fieldCampCount','fieldHospitalCount','startMode','civilianTrafficCount','roadblockCount','hunterSpawnDistanceMetres','objectGuardCount','objectGuardEvery','objectGuardHQMode','startWeaponCount','startMagazineCount'): assert ('Default_' if key == 'viewDistanceMetres' else 'Random_') + key in names
|
||||
else: assert 'Random_startMode' not in names
|
||||
for name in ('Close', 'SettingsPage', 'HQPage', 'JournalPage', 'JournalText', 'JournalContent', 'JournalScroll', 'PauseJournal', 'LatestJournal', 'Status'):
|
||||
assert name in names
|
||||
@@ -100,7 +100,7 @@ int main() {
|
||||
assert(Fields::FollowJournal(false,0,200,400,false));
|
||||
assert(Fields::FollowJournal(true,0,0,400,false));
|
||||
std::vector<std::pair<string,string>> cases={
|
||||
{"aiDoctrine","mission doctrine"},{"aiShadow","mission aishadow"},{"automaticEquipmentChancePercent","hq equipment"},{"startMode","mission start"},{"radioDefenceSeconds","mission radio"},{"aiIntelChancePercent","intel carrying"},
|
||||
{"emptyRestartHours","mission emptyhours"},{"aiDoctrine","mission doctrine"},{"aiShadow","mission aishadow"},{"automaticEquipmentChancePercent","hq equipment"},{"startMode","mission start"},{"radioDefenceSeconds","mission radio"},{"aiIntelChancePercent","intel carrying"},
|
||||
{"intelRevealChancePercent","intel reveal"},{"startTimeMinutes","mission time"},
|
||||
{"timeMultiplier","mission speed"},{"startDateCode","mission date"},
|
||||
{"weatherProfile","mission weather"},{"viewDistanceMetres","mission viewdistance"},{"reportIntervalSeconds","hq interval"},
|
||||
@@ -135,6 +135,45 @@ assert 'm_Controller.ERR_AdminSubmit("mission keepweapon set " + retain, true)'
|
||||
assert click.index('!m_Ready') < click.index('"mission keepweapon set "')
|
||||
print('PASS: explicit weapon retention buttons use the same ready/authorized server command path')
|
||||
|
||||
assert all(name in names for name in ('RestartEmptyOn', 'RestartEmptyOff', 'Saved_restartOnEmpty'))
|
||||
assert click.index('!m_Ready') < click.index('"mission restartempty set "')
|
||||
toggle = body(source, 'if (name == "RestartEmptyOn" || name == "RestartEmptyOff")')
|
||||
toggle = toggle.replace('m_Controller.', 'm_Controller->')
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
using string=std::string;
|
||||
struct Controller {int calls=0;string last;void ERR_AdminSubmit(string command,bool menu){assert(menu);calls++;last=command;}};
|
||||
struct Menu {
|
||||
int m_RestartEmptyState=-1;float m_ClickDelay=0;Controller controller;Controller* m_Controller=&controller;
|
||||
void SetText(string,string){}
|
||||
void Click(string name){
|
||||
''' + toggle + r'''
|
||||
}
|
||||
};
|
||||
int main(){
|
||||
Menu m;m.Click("RestartEmptyOn");assert(m.controller.calls==0);
|
||||
m.m_RestartEmptyState=0;m.Click("RestartEmptyOn");
|
||||
assert(m.controller.last=="mission restartempty set 1"&&m.controller.calls==1);
|
||||
m.m_RestartEmptyState=1;m.Click("RestartEmptyOff");
|
||||
assert(m.controller.last=="mission restartempty set 0"&&m.controller.calls==2);
|
||||
}
|
||||
''')
|
||||
# All four rows remain inside the visible Mission page with no overlapping controls.
|
||||
page = body(layout, 'FrameWidgetClass { Name "SettingsPage"')
|
||||
rectangles = []
|
||||
for line in page.splitlines()[1:]:
|
||||
if not line.startswith(('TextWidgetClass', 'ButtonWidgetClass')):
|
||||
continue
|
||||
name = re.search(r'Name "([^"]+)"', line)[1]
|
||||
x, y, width, height = (int(re.search(r'\b' + prop + r' (\d+)', line)[1])
|
||||
for prop in ('PositionX', 'PositionY', 'SizeX', 'SizeY'))
|
||||
assert x + width <= 1000 and y + height <= 486, name
|
||||
for other, ox, oy, ow, oh in rectangles:
|
||||
assert x + width <= ox or ox + ow <= x or y + height <= oy or oy + oh <= y, (name, other)
|
||||
rectangles.append((name, x, y, width, height))
|
||||
print('PASS: restart toggle submits authorized settings commands; Mission controls fit without overlap')
|
||||
|
||||
# The edit page must carry every control bound by the extension, with a valid row.
|
||||
items_menu = (ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_ServerItemsMenu.c').read_text()
|
||||
for name in ('ItemsMod','ItemsShow','ItemsEnableAll','ItemsDisableAll','ItemsPrevious','ItemsNext','ItemsPresets','ItemsPresetLoad','ItemsPresetDelete','ItemsPresetName','ItemsPresetSave','ItemsStorage','ItemsContent'):
|
||||
|
||||
Reference in New Issue
Block a user