Lua 示例
来自真实包的六份可运行脚本:一条规则、带上限的缩放、按阵营、可以拒绝本局的单位钩子、地物钩子,以及分层文件夹的文件计划。
本章概述 docs/mutators/authoring.md 和 docs/mutators/oamut-standard.md。
六个完整脚本,从 mutator-packages 原样复制。每一块先展示该包的 oamut.yaml,再展示它的 mutator.lua。文件按写下的样子展示,不作翻译。
一条规则,带一个选项
Commander Radar,作者 Drinken,改动能移动的指挥官上的一个字段。where 块要求 commander = 1 并且 bmcode = 1,因此只带着指挥官标记的传送门不会被改到。选项 radar 在 advanced 和 none 之间选择。advanced 把 radardistance 设为 2880,即 3.1c 高级雷达塔的范围,none 则设为 0。
清单 ·oamut.yamloamut: 1
id: "commander-radar"
name: "Commander Radar"
version: "1"
author:
name: "Drinken"
description: "Commanders gain the advanced radar tower's range, or lose radar entirely. Advanced sets the radar distance to 2880, the range of 3.1c's advanced radar tower, and none sets it to 0. This package replaces Advanced Radar Comm and No Radar Comm, both by Drinken. It also requires a unit that can move, which those originals did not, so Warp Gate 3.1's gates keep their own radar."
tags: ["commanders"]
works-with: ["*"]
requires:
engine: ">= 0.8.5"
layers: ["units"]
options:
radar:
type: choice
label: "Radar"
values: ["advanced", "none"]
default: "advanced"
legacy:
- name: "Advanced Radar Comm"
prefix: "{}"
compatibility: ["*"]
files: ["{}AdvRadarComm.tam", "{}AdvRadarComm.sm"]
- name: "No Radar Comm"
prefix: "{}"
compatibility: ["*"]
files: ["{}NoRadarComm.tam", "{}NoRadarComm.sm"]
入口脚本 · mutator.luafunction mutate(ctx)
local distance = ctx.options.radar == "none" and 0 or 2880
return {
rules = {
where { commander = 1, bmcode = 1 }:change {
radardistance = set(distance),
},
},
}
end
按比例缩放,并带上下限
ÜberComm,作者 Immerman,是缩放的例子。选项 strength 是 full 或 lite,脚本据此选出各个因子。full 是两倍的装甲和速度,以及六倍的建造力。lite 是一点五倍的装甲和速度,以及三倍的建造力。maxdamage 写成 scale(health, 1, 29900):新值保持在 1 到 29900 之间。builddistance 是 scale(2),没有界限。界限只作用于写着它的那一次乘法,不作用于后面变异器的乘法。
清单 ·oamut.yamloamut: 1
id: "uber-comm"
name: "ÜberComm"
version: "1"
author:
name: "Immerman"
description: "All commanders have twice the armor and speed, and six times the building power. This makes the commander a much more central unit, rather than having it become mostly obsolete after the early game. The increased usefulness means that playing \"Game Continues\" is no longer an invitation to bomb the commander. Strength lite is ÜberComm Lite: half again the armor and speed, and three times the building power, for the same reason. This package replaces ÜberComm and PowerComm, both by Immerman. The strength choice is the only difference between those two originals."
tags: ["commanders", "statistics"]
works-with: ["*"]
requires:
engine: ">= 0.8.5"
layers: ["units"]
options:
strength:
type: choice
label: "Strength"
values: ["full", "lite"]
default: "full"
legacy:
- name: "ÜberComm"
prefix: "{}"
compatibility: ["*"]
files: ["{}šberComm.tam", "{}šberComm.sm"]
- name: "PowerComm"
prefix: "{}"
compatibility: ["*"]
files: ["{}PowerComm.tam", "{}PowerComm.sm"]
入口脚本 · mutator.luafunction mutate(ctx)
local lite = ctx.options.strength == "lite"
local health = lite and 1.5 or 2
local worker = lite and 3 or 6
local move = lite and 1.5 or 2
local accel = lite and 2.25 or 4
return {
rules = {
where { commander = 1, bmcode = 1 }:change {
maxdamage = scale(health, 1, 29900),
builddistance = scale(2),
workertime = scale(worker),
maxvelocity = scale(move),
turnrate = scale(move),
brakerate = scale(accel),
acceleration = scale(accel),
},
},
}
end
按阵营区分的规则
Super Commander,作者 MAC (macraig),在 where 块里加上 side = is("ARM") 或 side = is("CORE"),因此每一块只作用于一方的指挥官。选项 side 是 all、arm 或 core,脚本为它点名的每一方各建一块。Arm 把 maxvelocity 乘以 7,上限 30,把 brakerate 和 acceleration 乘以 49。Core 把 maxvelocity 乘以 5,上限 30,把那两项乘以 25。双方都把 maxdamage 乘以 1.5,范围 1 到 29900,builddistance 乘以 2,workertime 乘以 3,turnrate 乘以 5,上限 29900。side 为 all 时写下两块,因此第三方的指挥官保持原样。
清单 ·oamut.yamloamut: 1
id: "super-commander"
name: "Super Commander"
version: "1"
author: { name: "MAC (macraig)" }
description: "Commanders have half again the armor, build three times as fast and reach twice as far. Arm commanders move seven times as fast, and Core commanders five times as fast.\n\nSuper Commander (Arm) and Super Commander (Core), by MAC (macraig), both promise 50% more armor, 500% more speed, 300% faster construction and 200% more build distance, so the commander stays useful after the early game. The two copies do not use the same speed numbers. Arm multiplies top speed by 7 and braking and acceleration by 49. Core multiplies top speed by 5 and braking and acceleration by 25. Both multiply damage by 1.5, from 1 to 29900, build distance by 2, worker time by 3 and turn rate by 5, up to 29900. This version keeps each side's numbers. The idea is Immerman's."
tags: ["commanders", "statistics"]
works-with: ["*"]
requires:
engine: ">= 0.8.5"
layers: ["units"]
options:
side:
type: choice
values: ["all", "arm", "core"]
default: "all"
label: "Side"
legacy:
- name: "[MAC] Super Commander (Arm)"
prefix: "{}"
compatibility: ["*"]
files: ["{}MAC Super Commander (Arm).tam", "{}MAC Super Commander (Arm).sm"]
- name: "[MAC] Super Commander (Core)"
prefix: "{}"
compatibility: ["*"]
files: ["{}MAC Super Commander (Core).tam", "{}MAC Super Commander (Core).sm"]
入口脚本 · mutator.luafunction mutate(ctx)
local side = ctx.options.side
local rules = {}
local function add(side_name, speed, accel)
rules[#rules + 1] = where {
commander = 1,
bmcode = 1,
side = is(side_name),
}:change {
maxdamage = scale(1.5, 1, 29900),
builddistance = scale(2),
workertime = scale(3),
turnrate = scale(5, 0, 29900),
maxvelocity = scale(speed, 0, 30),
brakerate = scale(accel),
acceleration = scale(accel),
}
end
-- Both texts promise the same percentages. The copies use different speed
-- factors. Side all is both blocks, so another side's commander is left
-- alone, as the two originals leave it.
if side ~= "core" then
add("ARM", 7, 49)
end
if side ~= "arm" then
add("CORE", 5, 25)
end
return { rules = rules }
end
一个可以拒绝本局的 units 钩子
Krogoth Commander 在 units 钩子里读取 weapons 集合。如果缺少 CORKROG_FIRE、CORKROG_ROCKET 或 CORKROG_HEAD,它就调用 ctx.incompatible,理由就是玩家看到的那句。读取 weapons 不需要单独的一层。钩子旁边的规则是 Krogoth 的数值。下一章从这个包的原始归档讲起,并在那里署上 Immerman。
清单 ·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"]
入口脚本 · 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
一个 features 钩子
No Scars,作者 MAC (macraig),是 maps-and-features 组里的 features 钩子。它清掉每个地物上的两个键。钩子用 each 走遍集合,把 featuredead 和 featurereclamate 设为 nil,因此摧毁或回收地物、单位或建筑不会留下疤痕,残骸也不会留下碎堆。清单声明了 features 层,写操作因此被允许。
清单 ·oamut.yamloamut: 1
id: "no-scars"
name: "No Scars"
version: "2"
author: { name: "MAC (macraig)" }
description: "Destroying or reclaiming a feature, a unit or a building leaves no scar and no smudge. Wrecks leave no heap either, because those two keys are what names the heap.\n\nNo Fugly Scars, by MAC (macraig), named 41 feature folders and cleared featuredead and featurereclamate on every feature in them. This version clears those two keys on every feature."
tags: ["gameplay"]
works-with: ["*"]
requires:
engine: ">= 0.8.5"
layers: ["features"]
legacy:
- name: "[MAC] No Fugly Scars"
prefix: "{}"
compatibility: ["*"]
files: ["{}MAC No Fugly Scars.tam", "{}MAC No Fugly Scars.sm"]
入口脚本 · mutator.luafunction mutate(ctx)
return {
features = function(features, ctx)
features:each(function(feature)
feature.featuredead = nil
feature.featurereclamate = nil
end)
end,
}
end
分层的文件目录
Warp Gate,作者 Immerman,返回一份 files 计划。选项 arrival 是 at-gate、near、near-twin、chosen 或 chosen-twin。脚本先加入 files/common,再加入 files/plain 或 files/target,arrival 为 near-twin 时再加入 files/near,最后加入 files/ 加上到达方式的名字。后面的文件夹胜出一条前面也有的路径。同一份计划还锁住指挥官的建造花费,并改写 sides 和 menus。这些层和 files 一起声明。
清单 ·oamut.yamloamut: 1
id: "warpgate"
name: "Warp Gate"
version: "2"
author:
name: "Immerman"
description: "Start from a gate and teleport in the commander you choose.\n\nArm starts with the Normal gate and Core with the Easy gate, which makes 40 energy and 3 metal and stores 3000 energy and 1000 metal. With a chosen arrival, Arm's gate is the Target gate: attack-order the ground and the commander is built there. Core's gate is the Random gate, which picks the spot. When the battle room says Game ends, losing the gate ends your game. Some computer profiles may not use the gate. If the match does not start on a gate, that profile is the likely cause. With a chosen arrival, a computer player on Arm's side never gets a commander: the Target gate builds only where a player attack-orders. On Core's side the Random gate works for a computer.\n\nCommander arrives: at-gate puts the commander on the gate, as Warp Gate 3.1 did, and the gate is itself a commander. near puts the commander near the gate, as 4.0 and 4.1 did, and the gate's script places the build point 8 to 30 units out. near-twin gives two commanders, as TwinComm did. chosen builds the commander where you choose, as Targetting did. chosen-twin gives two commanders, at a chosen spot or a random one, as TwinTarget did.\n\nThis package replaces * Warp Gate 3.1 *, * Warp Gate 4.0 *, * Warp Gate 4.0, Targetting *, * Warp Gate 4.0, TwinComm *, * Warp Gate 4.0, TwinTarget * and * Warp Gate 4.1 *, all by Immerman. It follows 4.1. One package holds the six versions, the sides keep their names and prefixes, TwinTarget's two commanders may both exist, and the battle-room screens, their art and the instruction plate are left out."
tags: ["gameplay", "new-units"]
works-with: ["*"]
requires:
engine: ">= 0.8.5"
layers: ["files", "units", "sides", "menus"]
clashes: "replace"
options:
arrival:
type: choice
values: ["at-gate", "near", "near-twin", "chosen", "chosen-twin"]
default: "near"
label: "Commander arrives"
legacy:
- name: "* Warp Gate 3.1 *"
prefix: "{}"
compatibility: ["*"]
files: ["{}Warpgate31.tam", "{}Warpgate31.sm", "{}Warpgate31(1).ccx"]
- name: "* Warp Gate 4.0 *"
prefix: "{}"
compatibility: ["*"]
files: ["{}Warpgate4_0.tam", "{}Warpgate4_0.sm", "{}Warpgate4_0(1).ccx"]
- name: "* Warp Gate 4.0, Targetting *"
prefix: "{}"
compatibility: ["*"]
files:
- "{}Warpgate4_0-Targetting.tam"
- "{}Warpgate4_0-Targetting.sm"
- "{}Warpgate4_0-Targetting(1).ccx"
- name: "* Warp Gate 4.0, TwinComm *"
prefix: "{}"
compatibility: ["*"]
files:
- "{}Warpgate4_0-TwinComm.tam"
- "{}Warpgate4_0-TwinComm.sm"
- "{}Warpgate4_0-TwinComm(1).ccx"
- name: "* Warp Gate 4.0, TwinTarget *"
prefix: "{}"
compatibility: ["*"]
files:
- "{}Warpgate4_0-TwinTarget.tam"
- "{}Warpgate4_0-TwinTarget.sm"
- "{}Warpgate4_0-TwinTarget(1).ccx"
- name: "* Warp Gate 4.1 *"
prefix: "{}"
compatibility: ["*"]
files: ["{}Warpgate4_1.tam", "{}Warpgate4_1.sm", "{}Warpgate4_1(1).ccx"]
入口脚本 · mutator.lua-- Arrival chooses one archive's files. 4.1's side rule runs for every arrival.
-- The originals' download files say MENU=2 BUTTON=0 for ARMCOM and BUTTON=1
-- for CORCOM. A player's page is MENU minus 1 and a slot is BUTTON plus 1,
-- so those places are page 1 slot 1 and page 1 slot 2. Slot 0 is not a place
-- this hook can name. The gates have no build page of their own, so the
-- slots stay where they are asked.
-- Gates have BMCode 0, so the commander selector does not treat them as
-- commanders. near-twin and chosen-twin clear the commander flag. TwinTarget
-- shipped no such line; TwinComm's line was a plain assignment.
local function offer(menus, gate)
menus:add(gate, "ARMCOM", { page = 1, slot = 1 })
menus:add(gate, "CORCOM", { page = 1, slot = 2 })
end
function mutate(ctx)
local arrival = ctx.options.arrival
local buildtime = 100
if arrival == "at-gate" then
buildtime = 1
end
local commanders = {
buildcostenergy = lock(0),
buildcostmetal = lock(0),
buildtime = lock(buildtime),
extractsmetal = lock(0.001),
}
if arrival == "near-twin" or arrival == "chosen-twin" then
commanders.commander = set(0)
end
local gate = "plain"
if arrival == "chosen" or arrival == "chosen-twin" then
gate = "target"
end
-- Shared folders first. A later folder replaces a path an earlier one
-- added. near and near-twin share unit files; near-twin then replaces
-- the scripts those files sit beside.
local add = { "files/common", "files/" .. gate }
if arrival == "near-twin" then
table.insert(add, "files/near")
end
table.insert(add, "files/" .. arrival)
local plan = {
files = { add = add },
rules = {
where { commander = 1, bmcode = 1 }:change(commanders),
all():change { side = lock(0) },
},
sides = function(sides, ctx)
if ctx.units.ARMCOM == nil or ctx.units.CORCOM == nil then
ctx.incompatible("Warp Gate needs the Arm and Core commanders, ARMCOM and CORCOM")
return
end
sides:each(function(side)
local commander = side.commander
if commander == nil then
return
end
local folded = commander:lower()
if folded == "armcom" then
side.commander = "CORGATE"
elseif folded == "corcom" then
side.commander = "ARMGATE"
end
end)
end,
menus = function(menus, ctx)
offer(menus, "ARMGATE")
offer(menus, "CORGATE")
end,
}
return plan
end