Port a legacy mutator
KroggyComm 1.1 from its archives to a package, and a TA: Mutation rule file kept with sm or rewritten as Lua.
This chapter summarises docs/mutators/authoring.md and docs/mutators/oamut-standard.md.
The original archives
KroggyComm 1.1, by Immerman, shipped as }{KroggyComm11.tam and }{KroggyComm11.ccx. The .tam is the TA: Mutation record: the name, the text that credits the author, and the compatibility codes TACC and UH. The .ccx holds the Krogoth model, one commander script stored twice (for Arm and for Core), the two commanders' unit files, the script's source and a corpse. This chapter does not paste those archives. It shows what the curated package keeps, and where each piece went.
Start from the import
oa-tool mutator import reads the .tam and finds the .ccx beside it. Write the folder somewhere other than the folder that holds the .tam. The warnings say what to curate: the tag is the default, TACC becomes ta-3.1c, and UH has no mod to map to.
$ oa-tool mutator import 'originals/}{KroggyComm11.tam' --out imports
imported kroggycomm-1-1 from originals/}{KroggyComm11.tam: 0 rule files, 1 archives
tags default to gameplay
TACC maps to ta-3.1c; Core Contingency's units are only there when it is on
UH is not mapped to a mod
The import keeps the whole .ccx at files/kroggycomm-1-1.ccx, and the script it writes only adds that folder. Its manifest has the version 1.0, the gameplay tag, works-with ta-3.1c, the files layer and the legacy record. That folder plays, and it is the start of the package, not the finished mutator.
Import ·mutator.lua-- Made by oa-tool mutator import from the TA: Mutation mutator "KroggyComm 1.1".
-- Its rule file is in rules/ and its archives in files/; files OA protects are left out.
function mutate(ctx)
return {
files = { add = { "files" } },
}
end
Model and scripts become files/
The curated package keeps only what the game reads from the archive: three paths under files/, each where the game looks for it.
files/objects3d/KROGCOM.3dothe model, from objects3d/KROGCOM.3do in the .ccxfiles/scripts/armcom.cobArm's script, from scripts/armcom.cobfiles/scripts/corcom.cobCore's script: the same bytes as Arm's, stored in the archive as scripts/Corcom.cobThe two unit files are not shipped either: their fields become rules, below. Two more things in the archive are left out. scripts/KroggyCom.bos is the script's source, and the game never reads it. features/corpses/Corkrog_dead.tdf is Core Contingency's corpse, unchanged.
FBI changes become rules
The Krogoth's unit fields become the rules in the curated mutator.lua. Both commanders take the object KROGCOM, the weapons CORKROG_FIRE, CORKROG_ROCKET and CORKROG_HEAD, the costs, the footprint and the movement. Cloak and bad-target fields are removed, which belongs to that design. HealTime, OvrAdjust and DefaultMissionType keep their 3.1c values: the 2001 unit files predate those fields, so their absence is not the Krogoth's design. Arm's sound category becomes KROGOTH. Core keeps CORE_COM, so the script passes nil and does not set soundcategory. The units hook refuses the match when those three weapons are missing.
Entry script ·mutator.lua-- HealTime, OvrAdjust, DefaultMissionType and the localized names stay.
-- The 2001 unit files predate those 3.1c fields, so their absence is not the Krogoth's design.
local function krogoth_changes(sound)
local changes = {
acceleration = set(0.09),
badtargetcategory = remove(),
brakerate = set(0.19),
buildcostenergy = set(116664),
buildcostmetal = set(29489),
builddistance = set(120),
buildtime = set(382193),
cantbetransported = set(1),
cloakcost = remove(),
cloakcostmoving = remove(),
corpse = set("corkrog_dead"),
downloadable = set(1),
energymake = set(32),
energyuse = set(7),
explodeas = set("NUCLEAR_MISSILE"),
footprintx = set(3),
footprintz = set(3),
maxdamage = set(29918),
maxslope = set(17),
maxvelocity = set(1),
maxwaterdepth = set(12),
mincloakdistance = remove(),
movementclass = set("TANKDH3"),
noautofire = set(0),
objectname = set("KROGCOM"),
onoffable = set(1),
selfdestructas = set("NUCLEAR_MISSILE"),
sightdistance = set(255),
turnrate = set(960),
weapon1 = set("CORKROG_FIRE"),
weapon2 = set("CORKROG_ROCKET"),
weapon3 = set("CORKROG_HEAD"),
wpri_badtargetcategory = remove(),
}
if sound ~= nil then
changes.soundcategory = set(sound)
end
return changes
end
function mutate(ctx)
return {
files = { add = { "files" } },
rules = {
named("ARMCOM"):change(krogoth_changes("KROGOTH")),
named("CORCOM"):change(krogoth_changes(nil)),
},
units = function(units, ctx)
local weapons = ctx.weapons
if weapons["CORKROG_FIRE"] == nil or weapons["CORKROG_ROCKET"] == nil or weapons["CORKROG_HEAD"] == nil then
ctx.incompatible("KroggyComm needs Core Contingency's Krogoth weapons")
end
end,
}
end
The legacy block, and the credit
legacy names the original. Keep the record the import wrote: the name KroggyComm 1.1, the prefix }{, compatibility TACC and UH, and files }{KroggyComm11.tam and }{KroggyComm11.ccx. The author stays Immerman, and the description names the original it replaces and its author: that is the credit. The rest is curated: version 1, the tags commanders and new-units, works-with a star, so the package fits any mod and the base game, and the layers files and units for the files, the rules and the hook. conflicts-with names anni-comm, special-tech-comm, commander-blast and tribe-commander, which write the same commander fields or ship the same script. The manifest below is the whole file.
Manifest ·oamut.yamloamut: 1
id: "kroggy-comm"
name: "Krogoth Commander"
version: "1"
author:
name: "Immerman"
description: "Use an amphibious Krogoth as a commander. Turn it off to disable the radar, the sonar and the head laser in exchange for 7 energy. The D-gun aims the head laser. Arm and Core are both named because one script is shipped for each commander. This package replaces KroggyComm 1.1 by Immerman. The model and the scripts stay as files, and the Krogoth's stats are rules, including the cloak and bad-target removals that belong to that design. Heal time, the override adjust, the default mission and the localized names stay, because the 2001 unit files left them out. It needs Core Contingency's Krogoth weapons."
tags: ["commanders", "new-units"]
works-with: ["*"]
requires:
engine: ">= 0.8.5"
conflicts-with: ["anni-comm", "special-tech-comm", "commander-blast", "tribe-commander"]
layers: ["files", "units"]
legacy:
- name: "KroggyComm 1.1"
prefix: "}{"
compatibility: ["TACC", "UH"]
files: ["}{KroggyComm11.tam", "}{KroggyComm11.ccx"]
Check, and diff against the import
From outside the folder, pack writes kroggy-comm-1.oamut. oa-tool mutator check reads that file. On this package the last line is result: ok. The diff compares the curated folder with the import kroggycomm-1-1. It was run from the commanders build folder, with the family's allow file, and DIR stands for the game folder. The allow file is what may remain.
$ oa-tool mutator pack kroggy-comm
$ oa-tool mutator check kroggy-comm-1.oamut
$ oa-tool mutator diff --game-dir DIR --mutator ./kroggy-comm --versus ./_imports/kroggycomm-1-1 --allow _verify/kroggy-comm.allowed
With that allow file the comparison exits 0. After its header lines, which start with #, it prints the lines below: 0 differences, 7 allowed, 0 unused allowances. The seven lines are the import, the right side, dropping defaultmissiontype, healtime and ovradjust on both commanders, and adding scripts/kroggycom.bos. The curated package keeps those three fields and does not ship the .bos. On the game folder of that run the units hook did not refuse the package: the three Krogoth weapons were present.
allowed right only field units/armcom.fbi [unitinfo] defaultmissiontype: Standby -> (absent)
allowed right only field units/armcom.fbi [unitinfo] healtime: 27 -> (absent)
allowed right only field units/armcom.fbi [unitinfo] ovradjust: 1 -> (absent)
allowed right only field units/corcom.fbi [unitinfo] defaultmissiontype: Standby -> (absent)
allowed right only field units/corcom.fbi [unitinfo] healtime: 27 -> (absent)
allowed right only field units/corcom.fbi [unitinfo] ovradjust: 1 -> (absent)
allowed right only file added scripts/kroggycom.bos aef95595e9dc1a1e543cf87c954c0b19e6523a227e89c4abaa40e6d264043889
mutator diff: 0 differences, 7 allowed, 0 unused allowances
A TA: Mutation rule file
The curated packages do not ship a .sm file. oa-tool mutator import keeps the original rule file and names it from the plan with sm. Hide Damage, by Kirk, is the short one. The import writes rules/hide-damage.sm and a mutator.lua whose rules entry is sm "rules/hide-damage.sm". The .sm below is that file, including its line endings.
Import ·mutator.lua-- Made by oa-tool mutator import from the TA: Mutation mutator "Hide Damage".
-- Its rule file is in rules/ and its archives in files/; files OA protects are left out.
function mutate(ctx)
return {
rules = { sm "rules/hide-damage.sm" },
}
end
Rule file · rules/hide-damage.sm[*all*]
{
HideDamage=1;
}The same rule as Lua is all():change { hidedamage = set(1) }. That is what [*all*] and HideDamage=1 become. The curated package ships that script, not the .sm. Its legacy record still names {}HideDamage.tam and {}HideDamage.sm, and the author stays Kirk.
Entry script ·mutator.luafunction mutate(ctx)
return {
rules = {
all():change { hidedamage = set(1) },
},
}
end
Manifest · oamut.yamloamut: 1
id: "hide-damage"
name: "Hide Damage"
version: "1"
author:
name: "Kirk"
description: "Opponents cannot see how much health a unit has left.\n\nKirk's Hide Damage sets that flag on every unit.\n\nThis package keeps the flag."
tags: ["gameplay"]
works-with: ["*"]
requires:
engine: ">= 0.8.5"
layers: ["units"]
legacy:
- name: "Hide Damage"
prefix: "{}"
compatibility: ["*"]
files: ["{}HideDamage.tam", "{}HideDamage.sm"]
The command is oa-tool mutator import INPUT... --out DIR. Add --id, --version, --tag, --works-with, --pack and --force when you need them. A .sm is copied under rules/, and the plan names it with sm. The import is the start of the package. Before you publish, turn variants into options, turn rule files into the Lua forms or leave them named with sm, and keep a file only where a model or a script needs one.