Adopting a Foothold mission onto the v6 toolchain (the "moulinette")¶
Foothold is a community mission (by Lekaa) that ships new versions several times a month. This guide describes the moulinette: a reproducible procedure any VEAF member can re-run on each new upstream version to produce the VEAF
.miz(Modern and Cold-War) from a single mission folder.Architecture: generic code, author-specific knowledge as data (see ADR 0007) and untouched upstream config + lexically-validated partial override (see ADR 0008). The per-command detail lives in CONVERT_OTHER and the mission.yaml reference.
Prerequisites¶
- An up-to-date
veaf-tools(orveaf-tools.exe). - The Foothold release archive for the target map (e.g. Caucasus) — see below.
- The
footholdconversion profile (shipped with the tools).
Where the upstream comes from¶
Foothold is published on GitHub: leka1986/Lekas-Foothold,
Releases tab. A release ships one .zip per map (Caucasus, Persian Gulf, Sinai, Syria,
Cold War Germany, Kola, Iraq, Afghanistan, WWII Normandy), each holding:
| File | What we do with it |
|---|---|
Foothold_<map>_<version>.miz |
this is the moulinette's input |
Foothold Config Manager <version>.exe |
ignored — see the warning |
Foothold_Manual_v<x>.pdf |
upstream documentation, worth reading |
Getting Started.url |
video shortcut |
Pass the .zip straight to convert-other: it adopts the .miz inside and ignores the
rest. No need to unzip by hand.
All nine maps at once. A release ships one archive per map. From a clone of this repository,
tools/Convert-FootholdBatch.ps1adopts them all in one pass, picking the right profile for each (it looks inside the archive, not at its name):.\tools\Convert-FootholdBatch.ps1 -InputFolder <zip-folder> -OutputFolder <missions-folder> -ValidateOn later releases add
-Update: the scripts are refreshed and every tunedmission.yamlis preserved. Budget roughly a minute per mission.Your mission folders need not be named after the archives: the script matches each archive to the folder of the same map (it reads the theatre on both sides), so
Foothold_CA_4.7.0_Multi_Language….zipdoes refreshVEAF-Foothold-Caucasus. It prints the folder it picked, and why, for each mission.Only folders
convert-otheradopted count (those with aconvert-other-state.yaml), so the output folder may also hold other missions on the same maps, such as the Open Training ones. One exception: Sinai and Sinai North are two Foothold missions on one map. Copy their archives under their folder names (VEAF-Foothold-Sinai.zip,VEAF-Foothold-SinaiNorth.zip): a folder named after the archive comes first. The same goes for every map on the first refresh of a mission adopted beforeconvert-other-state.yamlexisted.
Which profile for which map¶
| Map | Profile |
|---|---|
| Caucasus, Persian Gulf, Sinai, Syria, Cold War Germany, Kola, Iraq, Afghanistan | foothold |
| WWII Normandy | foothold-ww2 |
Normandy is a different family, hence its own profile: its config file is called
Foothold Config WW2.lua, it has no Era global (WWII has no era switch) and no
StartNormal, and the mission ships no Foothold CTLD — so the VEAF CTLD is not
incompatible there (it stays OFF by default, but you may enable it).
Adopt Normandy with the foothold profile and validate stops you: the config_override
would target Foothold Config.lua, absent from this mission, and the override would then be
loaded last — after the setup script has already read the settings — so it would have no
effect at all.
Overview¶
release .zip ──(1) convert-other --profile foothold──► v6 mission folder
│
(2) tune mission.yaml ◄──────┘
│
(3) validate ──► (4) build ──► <base>_MODERN.miz
<base>_COLD_WAR.miz
│
(5) DCS test ◄────┘
New Lekaa version ──► convert-other --update ──► (re)validate ──► build
1. Initialise (adopt)¶
veaf-tools convert other Foothold_CA_4.4.1_Multi_Language_Coldwar-Modern-Vietnam.zip <mission-folder> --profile foothold
convert-other extracts the .miz (out of the archive when needed), detects the scripts
loaded by its native triggers (in order), and generates a mission.yaml with:
- an ordered
custom_scripts:block (Moose, zoneCommander, Foothold Config, setup, Foothold CTLD, Splash, AIEN, EWRS… — the original load order), delays included: Lekaa does not load everything at once — 5 scripts arrive 3 seconds after the first ones, and AIEN 12 seconds later.convert-otherreads those delays out of the source triggers and writesdelay_seconds:on the scripts concerned, reproducing the staging (seedelay_seconds); - a
strip_native_triggers:list of the native loader triggers (the build removes them so nothing is loaded twice); - the profile's VEAF modules (RADIO, SPAWN, WEATHER, SHORTCUTS, SECURITY, REMOTE);
- a
conversion_profile: footholdmarker (build/validate reject an incompatible module — Foothold ships its own CTLD, so the VEAF CTLD stays OFF); - a commented
config_override:scaffold targetingFoothold Config.lua.
Why the staging matters¶
This is not fidelity for its own sake. AIEN inventories ground groups exactly once, at load time (its own comment: "launched once at mission start and collect everything relevant that is already there"). Foothold, meanwhile, creates part of its groups afterwards, from scheduled tasks starting at around 2 seconds.
Loading AIEN at time zero therefore hands it a world those tasks have not populated yet — and the
symptom is silent: no log error, just ground AI that never manages the groups Foothold created.
That is what Lekaa's 12 seconds are for, and why a detected delay_seconds: should not be removed
casually.
2. Tune mission.yaml¶
Three adjustments, all config-only (the upstream scripts are never touched):
a. VEAF community scripts (already turned off by the profile)¶
Foothold ships its own libraries (Moose, its own CTLD, AIEN, EWRS, Splash…)
as custom_scripts. The VEAF community scripts must therefore stay OFF — otherwise,
for example, VEAF's AIEN clobbers Foothold's and the mission crashes. The foothold
profile already scaffolds these disables inside the modules: block (not in
a separate community_scripts: block, which is ignored once modules: exists) —
nothing to do, just confirm they are present:
modules:
# … VEAF modules …
# ── Community scripts OFF ──
stts: false
ctld: false
aien: false
csar: false
hercules: false
skynet: false
tum: false
MiST is not in the list: it is no longer injected by default. The VEAF scripts stopped calling it, and so did every community script shipped here, which saves 336 KB in every mission. If one of your own scripts calls
mist., the build notices and injects it for you, naming the file that asked for it.
b. Partial override of the Foothold config¶
Uncomment the config_override: block and put in only the globals you change
(the rest of Foothold Config.lua stays untouched and updates by itself on the
next version). validate checks two things:
- each key is validated lexically against the injected code — a typo or an upstream rename
fails
validateand the build; - the
targetmust name one of the mission's scripts. Otherwise the override would be loaded last, after the setup script has already read the settings, and would have no effect — a silent failure, now blocked.
config_override:
target: "Foothold Config.lua"
values:
Era: Modern # default; overridden per variant below
AutoRestart: false
FootholdLocale: FR # language of Foothold's on-screen messages
FootholdLocale (upstream config V1.0.9) accepts EN, DE, FR, ES, RU, PT-BR,
TR, IT, zh-CN, zh-TW. It does not force the players' radio-menu language, which each
of them still sets in game.
⚠️ The Config Manager's external config (do not install it)¶
Since config V1.0.9, Foothold Config.lua looks for an external config file and, if it
finds one, applies it on top of the values embedded in the .miz:
<Saved Games>\DCS…\Missions\Saves\Foothold Config.lua
This is the Foothold Config Manager's channel (the executable shipped in the archive): its Import MIZ Config button installs that file. What follows from it:
- our
config_overridestill wins: the generatedveaf-config-override.luais loaded after the config script, so it reassigns the globals last — ADR 0008 still holds; - but such a file dropped into a server's
Saved Gamessilently changes every Foothold mission on that instance, ours included, for every setting we do not explicitly override; - Foothold also nags on screen (
FOOTHOLD_CONFIG_EXTERNAL_OUTDATED) when that external file is older than the embedded config.
So: do not install the Config Manager's external config on a VEAF server. A VEAF mission's configuration lives in its
mission.yaml, versioned with the mission folder and validated at build time — which a file inSaved Gamesis not. The Config Manager stays useful offline, to explore the available settings and see what each option does.
c. Modern / Cold-War variants¶
Foothold's era is driven by the Era global, which accepts four values — "Modern",
"Coldwar", "Gulfwar" (the Cold-War era's name on the Iraq map) and "Vietnam". It is a
pure config difference, so we emit several .miz in a single build via
build_variants::
mission:
name: VEAF-Foothold-Caucasus
build_variants:
- MODERN
- COLD_WAR
profiles:
MODERN:
mission:
era: MODERN
config_override:
values:
Era: Modern
COLD_WAR:
mission:
era: COLD_WAR
config_override:
values:
Era: Coldwar
The same shape gives a Vietnam variant if we want one (Era: Vietnam): recent Lekaa missions
ship the period's aircraft and loadouts, selected by Era. On the Iraq map, the Cold-War era
is called Gulfwar.
3. Validate¶
veaf-tools mission validate <mission-folder>
Checks the syntax, module semantics, custom_scripts existence, profile
incompatibilities, and that every config_override key segment exists in the
injected Foothold code.
4. Build both variants¶
veaf-tools mission build <mission-folder>
A single build produces two .miz: …_MODERN.miz and …_COLD_WAR.miz. Each
variant: untouched upstream config → small veaf-config-override.lua reassigning
Era (loaded between config and setup) → native triggers stripped →
custom_scripts loaded in declaration order → VEAF spawns/data injected.
For
--profile <X>alone (a single variant, suffixed with its name) or build options, see the mission.yaml reference.
5. Test in DCS¶
Open each .miz in DCS and confirm iso-functional behaviour (VEAF F10 radio
menu present, Foothold engine running, equipment consistent with the era). This is
the final validation step before publishing the mission folder.
Updating (new Lekaa version)¶
When Foothold ships a new release, download the new archive and re-import it into the same folder:
veaf-tools convert other <new_release.zip> <mission-folder> --profile foothold --update
--update refreshes the third-party scripts and mission base, preserves your
tuned mission.yaml, and normalises versioned names (Moose_<date>.lua → Moose.lua,
Splash_Damage_<version>_leka.lua → Splash_Damage.lua).
It also reconciles delay_seconds: with the new release's staging, deletes the scripts Lekaa no longer ships, and rewrites custom_scripts: to what the new release loads, in its load order.
That is what covers an upstream rename: in 5.3.0, zoneCommander.lua becomes zoneCommanderv2.lua on all ten maps, and the old list would have loaded the previous engine against the new config.
Your own scripts stay in the list, after Lekaa's.
The report (convert-other-report.md) lists all of it: scripts added, updated, deleted, custom_scripts: entries added or removed, and every delay written.
Review it, then re-validate and rebuild. See CONVERT_OTHER.
convert-other-state.yamlappears in the mission folder: it is the list of scripts the upstream release loads, and it is what tells a script Lekaa dropped from one you added yourself. Commit it with the mission.