Use direct buttons for fixed mission setting choices; record Workshop 0.0.149
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -12,7 +12,7 @@ navigation = source.with_name('ERR_AdminMenuPages.c')
|
||||
layout = BASE / 'addons/core/UI/layouts/ERR/AdminMenu.layout'
|
||||
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) == {"telemetryEnabled", "automaticConvoyEnabled", "keepWeaponOnUnconscious", "selfEpinephrine", "planeReconEnabled", "restartOnEmpty", "showAllSites"}
|
||||
assert len(keys) == len(set(keys)) and set(settings) - set(keys) == {"crashLootMode", "telemetryEnabled", "automaticConvoyEnabled", "keepWeaponOnUnconscious", "selfEpinephrine", "planeReconEnabled", "restartOnEmpty", "showAllSites"}
|
||||
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.
|
||||
@@ -48,12 +48,21 @@ else:
|
||||
names = re.findall(r'\bName "([^"]+)"', layout.read_text())
|
||||
assert 'RandomDay_startTimeMinutes' in names
|
||||
assert 'PlaneOn' in names and 'PlaneOff' in names
|
||||
assert 'CrashLootRandom' in names and 'CrashLootCustom' in names
|
||||
assert 'crashLootMode' not in names and 'Apply_crashLootMode' not in names
|
||||
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(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'
|
||||
choice_sets = {key: re.findall(r'"([^"]+)"', values) for key, values in re.findall(r'if \(key == "([^"]+)"\) return \{([^}]+)\}', body(fields, 'static array<string> Choices('))}
|
||||
assert set(choice_sets) == {'startMode', 'aiDoctrine', 'aiShadow', 'objectGuardEvery', 'objectGuardHQMode', 'weatherProfile'}
|
||||
for key in keys:
|
||||
if key in choice_sets:
|
||||
assert key not in names and 'Apply_' + key not in names
|
||||
assert all('Choice_' + key + '_' + choice in names for choice in choice_sets[key])
|
||||
assert all(prefix + key in names for prefix in ('Saved_', 'Help_'))
|
||||
continue
|
||||
assert all(name in names for name in (key, 'Apply_' + key, 'Saved_' + key, 'Help_' + key))
|
||||
if key not in ('shoreBoatCount','shoreBoatChancePercent','shoreBoatCrossingChancePercent','crashLootMode','crashSiteCount','crashHelicopterChancePercent','crashRifleCount','crashMagazineCount','crashAntiAirChancePercent','crashRocketChancePercent','radioIntelSiteCount','droneReconCount','droneCooldownSeconds','sarkaAngerPerCar','emptyRestartHours','aiDoctrine','aiShadow','motorbikeCount','planeAltitudeMetres','planeReplacementSeconds','planeSectorSeconds','fieldCampCount','fieldHospitalCount','startMode','garageVehicleCount','garageChancePercent','garageRadiusMetres','garageSpawnIntervalSeconds','civilianTrafficCount','roadblockCount','largeRoadblockCount','mainRoadWidthMetres','hunterSpawnDistanceMetres','objectGuardCount','objectGuardEvery','objectGuardHQMode','startWeaponCount','startMagazineCount'): assert ('Default_' if key == 'viewDistanceMetres' else 'Random_') + key in names
|
||||
else: assert 'Random_startMode' not in names
|
||||
@@ -66,14 +75,19 @@ menu_format = body(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_JournalText.c'
|
||||
follow = body(fields, 'static bool FollowJournal(')
|
||||
command = body(fields, 'static string Command(')
|
||||
available = body(fields, 'static bool Available(').replace('Keys().Contains(key)', 'Known(key)')
|
||||
choices = body(fields, 'static array<string> Choices(').replace('array<string>', 'Array<string>')
|
||||
choice_key = body(fields, 'static string ChoiceKey(').replace('array<string>', 'Array<string>')
|
||||
known = ' || '.join('key == "' + key + '"' for key in keys)
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
struct string : std::string {
|
||||
using std::string::string;
|
||||
string(std::string s):std::string(s){}
|
||||
bool StartsWith(string prefix){return rfind(prefix,0)==0;}
|
||||
template<class T> void Split(string separator,T& result,bool skipEmpty){assert(!skipEmpty);size_t start=0,end;while((end=find(separator,start))!=npos){result.push_back(substr(start,end-start));start=end+separator.size();}result.push_back(substr(start));}
|
||||
bool IsEmpty(){return empty();}
|
||||
int Length(){return size();}
|
||||
int IndexOf(string s){auto i=find(s);return i==npos?-1:int(i);}
|
||||
@@ -81,13 +95,25 @@ struct string : std::string {
|
||||
void Replace(string a,string b){size_t i=0;while((i=find(a,i))!=npos){replace(i,a.size(),b);i+=b.size();}}
|
||||
string Trim(){auto a=find_first_not_of(" \t\r\n");if(a==npos)return "";auto b=find_last_not_of(" \t\r\n");return substr(a,b-a+1);}
|
||||
};
|
||||
template<class T> struct Array:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}bool Contains(T v){return std::find(this->begin(),this->end(),v)!=this->end();}};
|
||||
struct ERR_JournalText {
|
||||
''' + menu_format + r'''
|
||||
};
|
||||
struct Fields {
|
||||
static bool Known(string key) {return ''' + known + ';}\n' + command + '\n' + available + '\n' + follow + '\n' + journal + r'''
|
||||
static bool Known(string key) {return ''' + known + ';}\n' + choices + '\n' + choice_key + '\n' + command + '\n' + available + '\n' + follow + '\n' + journal + r'''
|
||||
};
|
||||
int main() {
|
||||
assert(Fields::ChoiceKey("Choice_startMode_prison")=="startMode");
|
||||
assert(Fields::ChoiceKey("Choice_startMode_crash")=="startMode");
|
||||
assert(Fields::ChoiceKey("Choice_aiDoctrine_coordinated")=="aiDoctrine");
|
||||
assert(Fields::ChoiceKey("Choice_aiShadow_off")=="aiShadow");
|
||||
for(string choice:{"authored","clear","overcast","rain","random"})
|
||||
assert(Fields::ChoiceKey(string("Choice_weatherProfile_")+choice)=="weatherProfile");
|
||||
assert(Fields::ChoiceKey("Choice_objectGuardEvery_4")=="objectGuardEvery");
|
||||
assert(Fields::ChoiceKey("Choice_objectGuardHQMode_2")=="objectGuardHQMode");
|
||||
for(string invalid:{"Apply_weatherProfile","Choice_weatherProfile_4","Choice_objectGuardEvery_0","Choice_objectGuardHQMode_3","Choice_startMode_random","Choice_aiShadow_on_extra","Choice_"}) assert(Fields::ChoiceKey(invalid).empty());
|
||||
assert(Fields::Choices("crashRifleCount").empty());
|
||||
assert(Fields::Choices("startDateCode").empty());
|
||||
string raw="#24 t=7s M3 observe guard";
|
||||
assert(Fields::JournalText(raw)=="No. 24 t=7s M3 observe guard");
|
||||
assert(raw=="#24 t=7s M3 observe guard");
|
||||
|
||||
Reference in New Issue
Block a user