Lua scripting

| Моддинг |
|---|
| Токены |
| Audio • Biome • Graphics • Tile page • Interaction • Mod info • Plant • Speech • Sphere • Syndrome • World |
| Токены тела |
| Body • Body detail plan • Bodygloss • Tissue |
| Токены существ |
| Creature • Creature mannerism • Personality • Creature variation • Procedural graphics layer |
| Дескрипторы |
| Descriptor color • Color • Descriptor pattern • Descriptor shape |
| Токены цивилизаций |
| Entity • Ethic • Language • Position |
| Токены работ |
| Building • Labor • Reaction • Skill • Unit type |
| Токены предметов |
| Item • Item definition • Ammo • Armor • Instrument • Tool • Trap component • Weapon |
| Токены материалов |
| Material • Material definition • Inorganic material definition |
|
Lua |
| Скриптинг • Примеры • Функции |
Эта статья посвящена процедурной генерации сырых данных. Информацию о скриптах Utility:DFHack можно найти по адресу https://docs.dfhack.org/en/stable/.
Lua-скрипты — это один из инструментов моддинга Dwarf Fortress. Они используются для записи определений процедурно генерируемых объектов и со временем станут основой для будущих магических обновлений. [1]
Подержка Lua-скриптов была добавлена в качестве экспериментальной функции в версии 51.06 и полностью включена в игру версии 52.01.
Неорганические материалы, языки, существа, взаимодействия, предметы (в настоящее время за исключением музыкальных инструментов), реакции, сообщества и растения доступны для этой системы.
Скрипты загружаются из файла scripts/init.lua мода и могут require() требовать другие файлы.
Видео
Видео-анонс:
Уроки:
Структура
На данный момент работа скриптов на Lua ограничено генерацией процедурных объектов. Это осуществляется с помощью функции generate() — глобальной функции, загружаемой в data/init/generators.lua. Она запускает модульные тесты, предварительную обработку, do_once (или do_once_early), материалы, предметы, языки, существа, взаимодействия, сущности и постобработку — именно в таком порядке.
При первой генерации случайных объектов игра заполняет две глобальные таблицы: world и random_object_parameters.
worldсодержит информацию о мире, который в данный момент генерируется. Она содержит параметры генерации мира, определения raw-объектов и несколько других полей.
random_object_parametersсодержит то, что игра ожидает сгенерировать в текущем вызове генерации.
Вы можете вывести содержимое этих таблиц в лог, чтобы увидеть доступные данные. Мод Runtime Dataminer включает скрипт для чтения этих таблиц.
У DFHack также есть версия df.world, хотя эти таблицы не обязательно эквивалентны. Заголовки и пути могут различаться, даже если они ссылаются на одни и те же данные.
Отладка
Вы можете задать глобальную переменную debug_level для вывода отладочной информации. Это число, но его значения совершенно арбитрарно. Если оно >0, будут запускаться модульные тесты; если >=0.5, на каждом этапе генерации будет отображаться текущий этап. Вы можете использовать get_debug_logger(x) для возврата функции, которая записывает данные в lualog.txt, если debug_level не ниже x.
Модульные тесты — это функции, возвращающие таблицу, содержащую значение good, и если оно истинно, тест считается пройденным, и info — строку с информацией о прохождении или провале теста. Эти модульные тесты не должны иметь побочных эффектов, то есть не должны влиять на глобальное состояние. Вот пример модульного теста, добавленного по умолчанию:
unittests.get_random_creature |
|---|
get_random_creature=function()
local cr=world.creature.get_random_creature()
local res={}
res.good=type(cr)=='table'
res.info=res.good and ("Got a random creature: "..cr.token) or "No random creature could be gotten, even at most permissive!"
return res
end
|
Генерация объектов
При вызове generate() переменная random_object_parameters определяет, что именно будет сгенерировано.
Перед генерацией карты мира переменная random_object_parameters.pre_gen_randoms имеет значение true на одну генерацию. После завершения создания карты переменная random_object_parameters.main_world_randoms имеет значение true на одну генерацию при "генерации предыстории"; большая часть начальной рандомизации происходит именно здесь. Дальнейшие вызовы генерации, например, для создания экспериментов, не устанавливают эти переменные в значение true.
Если вы регистрируете совершенно новый тип процедурного объекта, вы можете сгенерировать его на этих этапах. Игра включает в себя ряд таблиц, в которые можно добавлять функции; игра запускает каждую из них при генерации raw-данных.
do_onceзапускается только в вызовеrandom_object_parameters.main_world_randomsи является наиболее безопасным вариантом для добавления новых объектов.do_once_earlyзапускается в вызовеrandom_object_parameters.pre_gen_randomsи может использоваться для объектов, которые необходимо разместить на карте, например, минералов или популяций животных на поверхности.preprocessзапускается перед любой из первых таблиц, но запускается во время каждого вызоваgenerate(), и невозможно предсказать, когда это произойдет.postprocessзапускается в конце каждого вызоваgenerate(), после завершения остальных шагов.
Примеры регистрации объектов с помощью этих шагов можно найти на странице Lua script examples.
Если вы хотите использовать preprocess или postprocess для генерации raw-объектов, вы можете проверить правильность выбранного этапа генерации, прочитав вышеупомянутый random_object_parameters. Пример "Адамантиновые сплавы" включает такую проверку.
Вы также можете экспериментировать с random_object_parameters в препроцессинге. Здесь назначаются типы демонов из обычной игры, и вы можете изменить пропорции, если хотите.
Генерация из списка
После preprocess и do_once игра генерирует все отдельные объекты, ожидаемые random_object_parameters. Общая процедура заключается в том, что игра вызывает функцию generate_from_list() для таблицы функций, которая вызывает каждую функцию и выбирает одно из полученных значений случайным образом в зависимости от их весов.
Например, таблица interactions.secrets содержит одну запись для некромантов; она возвращает таблицу из трёх записей: {raws=tbl,weight=1,spheres=spheres}.
raws— это полный raw-текст взаимодействия.weight— это случайный вес взаимодействия, т.е. если добавить ещё одну функцию, возвращающую таблицу, содержащуюweight=2, вероятность такого взаимодействия будет вдвое выше, чем у некромантов.spheres— это дополнительные данные, которые может использовать генератор. На самом деле, на данный момент это не так, но можно переопределитьgenerate_random_interactions()собственной версией, которая учитываетspheresи, например, пытается равномерно распределить сгенерированные секреты по доступным сферам. (Это не вошло в обычную игру, в первую очередь из-за опасений возникновения ошибок).
Языки
Однако языки — это нечто особенное; как видно из Divine language/script или языка идентичности. Таблица languages просто ожидает возврата таблицы с переводами, например, tbl["ABBEY"]="abbey". Если вы хотите процедурно добавлять слова или символы (и да, оба варианта реализуемы), вы можете сделать это с помощью raws.register_languages() в другой таблице функций.
Существа
Существа обладают гораздо большей специфичностью, чем другие процедурные объекты. Забытые твари в некотором смысле самые простые из них:
creatures.fb.default |
|---|
creatures.fb.default=function(layer_type,tok)
local tbl={}
local options={
strong_attack_tweak=true,
spheres={CAVERNS=true},
is_evil=true,
sickness_name="beast sickness",
token=tok
}
tbl=split_to_lines(tbl,[[
[FEATURE_BEAST]
[ATTACK_TRIGGER:0:0:2]
[NAME:forgotten beast:forgotten beasts:forgotten beast]
[CASTE_NAME:forgotten beast:forgotten beasts:forgotten beast]
[NO_GENDER]
[CARNIVORE]
[DIFFICULTY:10]
[NATURAL_SKILL:WRESTLING:6]
[NATURAL_SKILL:BITE:6]
[NATURAL_SKILL:GRASP_STRIKE:6]
[NATURAL_SKILL:STANCE_STRIKE:6]
[NATURAL_SKILL:MELEE_COMBAT:6]
[NATURAL_SKILL:DODGING:6]
[NATURAL_SKILL:SITUATIONAL_AWARENESS:6]
[LARGE_PREDATOR]
]])
add_regular_tokens(tbl,options)
tbl[#tbl+1]=layer_type==0 and "[BIOME:SUBTERRANEAN_WATER]" or "[BIOME:SUBTERRANEAN_CHASM]"
if layer_type==0 then options.spheres.WATER=true end
options.spheres[pick_random(evil_spheres)]=true
options.do_water=layer_type==0
populate_sphere_info(tbl,options)
local rcp=get_random_creature_profile(options)
add_body_size(tbl,math.max(10000000,rcp.min_size),options)
tbl[#tbl+1]="[CREATURE_TILE:"..tile_string(rcp.tile).."]"
build_procgen_creature(rcp,tbl,options)
return {creature=tbl,weight=1}
end
|
Сначала создаётся таблица options; можно создать полный список опций, используемых в игре, но другие моды также могут использовать вполне произвольные опции. Затем добавляются все обычные токены special-to-forgotten-beast в большую строку, после чего вызываеся add_regular_tokens(tbl,options), что добавляет некоторые вещи, общие для всех (ванильных) процедурных существ, на основе предоставленных опций.
Также добавляется do_water и сфера WATER, если тварь находится в водной пещере, опция, которая добавляет в белый список определённые случайные профили существ, а также случайную злую сферу.
populate_sphere_info() аналогична add_regular_tokens(); она добавляет все сферы из options.spheres к существу, используя токен [SPHERE], а затем, если заданы определённые параметры, выполняет дополнительные действия.
Затем выбирается случайный профиль существа с помощью get_random_creature_profile() и параметров, используется add_body_size() для установки токенов [BODY_SIZE] и сопутствующих параметров, задаётся тайл существа и, наконец, запускается большая функция build_procgen_creature(), которая создаёт описание, тело, ткани и так далее.
Случайные профили существ
Случайный профиль существа — это шаблон, которые определяет в первую очередь форму тела для процедурного существа. Например:
random_creature_types.GENERAL_QUADRUPED |
|---|
GENERAL_QUADRUPED={
name_string="quadruped",
tile='Q',
body_base="QUADRUPED",
c_class="UNIFORM",
cannot_have_get_more_legs=true,
min_size=70000,
weight=1000
},
|
Из этого кода только cannot_have_get_more_legs является необязательным. build_procgen_creature() имеет прямой доступ к профилю в качестве первого аргумента, поэтому дополнительные записи в таблице можно использовать по своему усмотрению.
body_base указывает на ключ в body_base_fun, который используется для настройки параметров существа (ходьба и слой процедурной графики задаются таким образом) и возвращает список токенов тела. Четвероногие используют специальную функцию для изменения спрайта, поэтому вот базовая функция тела для обычного гуманоида:
body_base_fun.HUMANOID |
|---|
HUMANOID=function(rcp,options)
options.pcg_layering_base="BEAST_HUMANOID"
options.walk_var="STANDARD_BIPED_GAITS"
options.walk_speed=900
return {"RCP_UPPER_BODY","RCP_LOWER_BODY","RCP_NECK","RCP_HEAD","RCP_TWO_PART_ARMS","RCP_TWO_PART_LEGS"}
end
|
c_class также относится к другому классу, который определяет тип слоёв тканей существа. "FLESHY", "MAMMAL", "CHITIN_EXO" и т. д. подразумевают биологическое существо с сухожилиями, кровью, различными органами, нервами и так далее. "UNIFORM" описывает существо, состоящее из одного материала, выбор которого зависит от его параметров. Таблицы random_creature_class и random_creature_material хранят информацию об этих характеристиках.
Органических существ можно модифицировать, изменяя их внешний вид, например, делая их безкожими, волосатыми или даже однородными по материалу.
Твики
В самом широком смысле, твики (tweaks) — это любое отклонение от профиля существа. Вышеупомянутые изменения внешнего вида, новые части тела и взаимодействия при атаке — всё это примеры изменений.
Ряд опций изменяют доступные твики или принудительно вызывают их, например:
options.no_tweakотключает случайные твики.options.strong_attack_tweakпозволяет существу всегда выбирать из таблицыattack_tweaks; например: "Beware its webs!"options.humanoidable_onlyделает существо "искажённым до гуманоидной формы" (если оно злое) или "<существом> в гуманоидной форме" (в противном случае).
Одним из возможных вариантов использования btc1_tweaks (см. Lua functions#Патчинг существ) является добавление пользовательских кандидатов на твик, указывая на ключи в tweaks.
Выбор цвета
Функции выбора цвета (color pickers) могут предоставлять более подходящие варианты цветов на основе параметров, вместо полностью случайного спектра по умолчанию. Существуют функции выбора цвета для некоторых злых сфер, придающие им тёмный вид. Оборотни имеют специальный флаг, чтобы получать только натуральные коричневые или чёрные цвета.
Если существо соответствует условию cond и заданный дескриптор цвета соответствует color, то оно добавляется в список кандидатов. Цвета имеют значения h, s, v (цветовой тон, насыщенность и значение) и r, g, b (красный, зелёный и синий) в диапазоне от 0 до 1 (за исключением цветового тона, который находится в диапазоне от 0 до 360 градусов).
color_picker_functions |
|---|
color_picker_functions={
death_misery={
cond=function(options)
return options.spheres.DEATH or options.spheres.MISERY
end,
color=function(color)
-- GRAY TO BLACK/BLUEISH GREEN THAT ARE SOMEWHAT GRAYISH AND MORE BLUE
return (color.v<=0.75 and color.s<=0.001) or (color.v==color.b and color.s<=0.25)
end
},
darkness_night={
cond=function(options)
return options.spheres.DARKNESS or options.spheres.NIGHT
end,
color=function(color)
-- GRAY TO BLACK OR DARK BLUISH
return color.v<=0.4 and (color.s < 0.001 or (color.h>180 and color.h<=240))
end
},
werebeast={
cond=function(options)
-- werebeasts only, in vanilla
return options.animal_coloring_allowed
end,
color=function(color)
--BROWN OKAY TOO
return color.h>=30 and color.h<=48 and color.b<=0.15 and color.v<=0.75 and color.v > 0
end
}
}
|
options.blood_color работает как функция выбора цвета. Если какой-либо цвет соответствует её функции, то кровь будет окрашена в один из них. Например, у бугименов и кошмаров есть функция, которая даёт им пурпурный цвет крови, но вы можете создать свои собственные функции цвета крови.
Существа с другими типами крови, например, ихором, не подвержены этому эффекту.
options.blood_color (Бугимен) |
|---|
blood_color=function(cl)
-- DARKER MAGENTA COLORS
return cl.h>=260 and cl.h <= 340 and cl.v <= 0.5 and cl.v >= 0.1
end
|