01Что такое мод в Alem
Почти во всех играх мод — это кусок программы. В Alem не так, и на это есть причина: игра собирается для Android через IL2CPP, а такая сборка физически не умеет запускать чужой C#. Поэтому моддинг здесь устроен иначе — через данные.
Почти все числа и списки игры вынесены из кода в текстовые таблицы: урон каждого оружия, состав каждого рецепта, броня одежды, цена построек, дерево технологий, черты характера, пулы имён, вес и стоимость каждого ресурса. Эти таблицы лежат внутри игры и читаются при запуске.
Мод — это папка с такими же таблицами. Игра сначала читает свою, потом кладёт сверху вашу, запись к записи. Вы можете:
- поменять числа у существующих вещей — урон ножа, цену стены, вес стали;
- добавить новые вещи — оружие, одежду, рецепты, технологии, черты характера, имена;
- завести свой ресурс, которого в игре не было — свою руду, свой металл;
- подменить любую картинку своей;
- перевести всё добавленное на английский.
Всё это — обычные текстовые файлы. Их можно писать в блокноте на телефоне, и никакой Unity для этого не нужен.
02Первый мод за пять минут
Самый быстрый способ понять устройство мода — посмотреть на живой. В игре есть кнопка, которая его создаёт.
- Главное меню → МОДЫ.
- Внизу справа — СОЗДАТЬ ПРИМЕР. Нажмите. В списке появится «Пример мода».
- Нажмите у него ВЫКЛ, чтобы стало ВКЛ.
- Начните новую игру и загляните в плавильню — там появился рецепт «Выплавить мифрил». А нож стал слабее.
- Теперь откройте папку мода (её путь написан прямо на экране «Моды», голубой строкой) и посмотрите файлы внутри. Это и есть весь мод.
Дальше проще всего работать так: скопировать папку примера, переименовать, поправить
mod.json и править файлы под себя.
На телефоне нет ни редактора кода, ни удобного файлового менеджера. Кнопка «Создать пример» — способ получить рабочий мод, не имея компьютера вообще.
03Из чего состоит мод
Мод — это папка. Обязателен в ней ровно один файл: mod.json. Всё остальное
добавляется по необходимости — берите только те таблицы, которые собираетесь трогать.
МойМод/ mod.json ← паспорт, обязателен weapons.json ← оружие recipes.json ← рецепты станков apparel.json ← одежда и броня resources.json ← ресурсы (в том числе свои) buildings.json ← цены и прочность построек techs.json ← дерево технологий pawns.json ← черты (с эффектами), биографии, имена events.json ← свои события с выбором plants.json ← дикие растения на карте animals.json ← дикие звери livestock.json ← домашний скот furniture.json ← своя мебель и станки factions.json ← державы на глобусе biomes.json ← настройка биомов quests.json ← заказы держав needs.json ← свои потребности колонистов storytellers.json ← свои рассказчики challenges.json ← свои испытания info.json ← описания для карточки «?» loc.json ← английский перевод ваших строк scripts/ ← скрипты на Lua (см. разделы 32–35) myrules.lua textures/ ← картинки, повторяют пути игры Guns/knife/knife-right.png Items/mithril.png char/hat/3_down.png
mod.json — паспорт
Единственный обязательный файл. Без него папка не считается модом и в списке появится с ошибкой «нет mod.json».
{
"id": "steppe_pack",
"name": "Степной набор",
"author": "Ермахан",
"version": "1.0",
"gameVersion": "0.22",
"desc": "Степное оружие, доспех и новая руда."
}
| Поле | Обязательно | Что это |
|---|---|---|
| id | да | Внутреннее имя мода. Только латинские буквы,
цифры, - и _. По нему игра запоминает, включён ли мод, и по нему
же называет папку при установке из архива. Двух модов с одним id быть не должно. |
| name | нет | Название для списка. Если не указать —
покажется id. |
| author | нет | Ваше имя. |
| version | нет | Версия мода, любая строка. По умолчанию
1.0. |
| gameVersion | нет | Для какой версии игры сделан. Пока справочно — игра ничего по нему не запрещает. |
| desc | нет | Описание в одну-две строки. Показывается в карточке мода. |
Не используйте в id точки, пробелы и слэши. Игра их вырезает, и мод может
оказаться в папке с неожиданным именем.
04Куда класть мод и как его включить
Игра ничего не качает из интернета. Мод — это файл, который уже лежит на устройстве. Есть два пути.
Способ 1. Архивом (для игроков)
- Соберите папку мода в .zip. Можно паковать и саму папку, и её содержимое — игра разберётся с обоими вариантами.
- Скачайте файл на телефон.
- Меню → МОДЫ → ВЫБРАТЬ ФАЙЛ. Откроется системное окно, найдите свой .zip и подтвердите.
- Мод распакуется и появится в списке. Включите его кнопкой ВКЛ.
Игра также сама показывает архивы, которые нашла в «Загрузках», «Документах» и в своей папке — их можно установить нажатием на имя файла. Если список пуст, это не поломка: начиная с Android 11 система закрывает чужие папки, и надёжен только выбор файла вручную.
Способ 2. Папкой (для авторов)
Распакованную папку мода можно положить прямо в папку модов — архив для этого не нужен. Точный путь написан на экране «Моды» голубой строкой, скопируйте его оттуда. Выглядит он примерно так:
/storage/emulated/0/Android/data/<пакет игры>/files/Mods/
Положили папку → вернулись в игру → ОБНОВИТЬ → мод в списке. Это самый удобный режим работы, пока вы мод пишете: поправил файл, нажал «Обновить», проверил.
Порядок модов
У включённых модов есть стрелки ▲▼. Порядок решает споры: если два мода правят одно и то же, побеждает тот, кто ниже в списке. То же правило действует и для картинок.
Меняйте набор модов до начала партии. Сохранение, сделанное с другими модами, может недосчитаться вещей: если мод выключен, его предметы изымаются из колонии, и игра честно об этом напишет. Перед сменой набора модов игра предложит сделать копию сохранения — соглашайтесь.
05Главное правило: слияние по ключу
Это самое важное, что нужно понять про моды Alem. Файл мода не заменяет файл игры. Он на него накладывается — построчно, по ключу записи.
У каждой таблицы есть ключевое поле (обычно id). Игра берёт вашу запись и
ищет свою с таким же ключом:
- Ключ совпал → игра меняет только те поля, которые вы написали. Всё остальное остаётся её.
- Ключа нет → ваша запись добавляется как новая.
Поэтому чтобы ослабить нож, не нужно переписывать всю таблицу оружия. Достаточно двух строк:
{
"weapons": [
{ "id": "knife", "dmg": 9 }
]
}
Урон стал 9, а скорость, дальность, цена, картинка и всё прочее у ножа остались игровыми — включая те, что появятся в будущих версиях.
Если бы мод подменял таблицу целиком, то мод «нож слабее» обязан был бы тащить с собой всё оружие игры — и при первом же обновлении молча вернул бы старые числа всему остальному. Слияние по ключу избавляет от этого.
Ключи таблиц
| Файл | Список внутри | Ключ |
|---|---|---|
| weapons.json | weapons | id |
| weapons.json | materials | key |
| recipes.json | recipes | station + id |
| apparel.json | apparel | id |
| resources.json | resources | id |
| buildings.json | buildings | kind + variant |
| techs.json | techs | id |
| pawns.json | traits | name |
| pawns.json | childhoods / adulthoods | name |
| pawns.json | списки имён | без ключа — просто дополняются |
| events.json | events | id |
| plants.json | plants | id |
| animals.json | animals | id |
| livestock.json | livestock | species |
| furniture.json | furniture | id |
| factions.json | factions | name |
| biomes.json | biomes | id |
| quests.json | quests | id |
| needs.json | needs | id |
| storytellers.json | storytellers | name |
| challenges.json | challenges | id |
| info.json | entries | ключ записи |
06Как писать JSON, чтобы он не сломался
Все таблицы — это JSON. Формат простой, но придирчивый. Пять правил покрывают почти все ошибки новичков:
- Кавычки только прямые —
", а не «ёлочки» и не ’. Телефонная клавиатура любит подставлять красивые — следите. - Запятая между элементами, но не после последнего.
{ "a": 1, "b": 2 }— верно;{ "a": 1, }— ошибка. - Дробные числа через точку:
0.5, не0,5. - Комментариев в JSON нет. Строчку
// так нельзяфайл не переживёт. - Файл в кодировке UTF-8. Иначе русские названия превратятся в кракозябры.
Значения перечислений (тип урона, слот одежды, ресурс) пишутся именами в
кавычках, а не числами: "Sharp", "Head",
"Iron". Числа сюда писать нельзя — игра ругнётся в лог и возьмёт запасное
значение.
Игра проверяет каждый JSON мода сразу при показе списка модов, а не в момент загрузки мира. Если файл битый, вы увидите в карточке мода красную строку «ОШИБКА: weapons.json: …» с точной причиной. Это ваш главный инструмент отладки.
07weapons.json — оружие
Файл состоит из двух списков: materials (из чего делают клинки) и
weapons (само оружие).
{
"weapons": [
{
"id": "kylysh",
"name": "Кылыш",
"melee": true,
"family": true,
"dmg": 17,
"interval": 1.7,
"range": 1.5,
"dtype": "Sharp",
"hitLabel": "Удар кылышем",
"drawScale": 0.8,
"craftWork": 6
}
]
}
| Поле | По умолч. | Что делает |
|---|---|---|
| id | — | Ключ записи. Латиницей. |
| name | = id | Название в игре. |
| spriteId | = id | Чья картинка используется. Позволяет
сделать оружие без своего рисунка: "spriteId": "sword". |
| melee | false | Ближний бой. |
| family | false | Семейство: игра сама породит варианты по материалам (деревянный, железный, стальной…) и рецепты к ним. |
| handOnly | false | Не существует как предмет (гранаты в руках). |
| dmg | 1 | Урон за удар или выстрел. |
| interval | 1 | Секунд между ударами. Больше — медленнее. |
| range | 1.4 | Дальность в клетках. |
| accMul | 1 | Множитель точности. |
| pen | 0 | Пробитие брони (0…1 — доля брони, которую оружие игнорирует). |
| burst | 0 | Выстрелов в очереди. |
| burstCd | 0 | Пауза между выстрелами внутри очереди. |
| dtype | Sharp | Sharp режущий ·
Blunt дробящий · Heat ожог. |
| hitLabel | — | Как удар назовётся в описании раны. |
| drawScale | 1 | Размер картинки в руке, за спиной и на земле. 1 = длиной со ствол. У ножа 0.55, у копья 0.88. |
| spriteAngle | 0 | Ручной поворот картинки. Обычно не нужен: игра сама разворачивает вертикально нарисованный клинок. |
| cost1Type / cost1 | — | Основной материал и сколько его. |
| cost2Type / cost2 | — | Второй материал. |
| more | — | Ещё ингредиенты списком:
[{ "res": "Rivets", "count": 4 }]. |
| craftWork | 2 | Сколько работы на изготовление. |
Материалы клинков
Список materials — из чего игра порождает варианты оружия с
"family": true. Ключ записи — key.
{
"materials": [
{ "key": "iron", "dmgMul": 1.25 }
]
}
Здесь dmgMul — множитель урона, res — ресурс, amount —
сколько его уходит, name — как называется вариант («железный»).
Мало добавить запись — оружие нужно ещё открыть. Иначе его не будет ни в
одном станке. Как это делается — раздел 12, техи: ключ
weapon:kylysh или, для семейства, weaponfam:kylysh@iron.
08recipes.json — рецепты станков
Один список recipes, ключ записи — пара station + id.
Один и тот же id у разных станков — законно (простое блюдо есть и на плите, и
на костре).
Станки
| station | Что это |
|---|---|
| campfire | Костёр |
| cook | Кухонная плита |
| butcher | Разделочный стол |
| furnace | Плавильная печь |
| steel | Сталеплавильная печь |
| stonecut | Камнерезный стол |
| chem | Химический стол |
| tailor | Швейный стол |
| craft | Верстак |
{
"recipes": [
{
"station": "furnace",
"id": "mithril_ingot",
"name": "Выплавить мифрил",
"in1Type": "SilverOre", "in1": 10,
"in2Type": "Coal", "in2": 2,
"outType": "Mithril", "outCount": 1,
"work": 4
}
]
}
| Поле | Что делает |
|---|---|
| station | На каком станке. Из таблицы выше. |
| id | Ключ рецепта. Им же он открывается техом:
recipe:mithril_ingot. |
| name | Название заказа. |
| in1Type / in1 | Первый ингредиент и его количество. |
| in2Type / in2 | Второй ингредиент. |
| in1Group / in2Group | Вместо точного вида — группа:
0 точный тип · 1 любое мясо · 2 любая растительная
еда. Так написаны блюда: «мясное» берёт оленину, крольчатину или что нашлось. |
| outType / outCount | Что получится и сколько. |
| outType2 / outCount2 | Побочный продукт (например, уголь). |
| more | Дополнительные ингредиенты списком. |
| work | Сколько работы на одну порцию. |
| weaponId | Рецепт делает оружие с этим id. |
| apparelId | Рецепт шьёт одежду с этим id. |
| colorPick | Показать выбор цвета при пошиве. |
| medicinePool | Рецепт берёт лекарства из общего пула. |
Для оружия с "family": true и для одежды рецепты создаются
сами — по таблицам weapons и apparel. Писать их руками в recipes.json нужно
только для особых случаев.
09apparel.json — одежда и броня
Один список apparel, ключ — id. Порядок записей в файле = порядок
в окне станка.
{
"apparel": [
{
"id": "steppecoat",
"name": "Степной кафтан",
"slot": "ArmorOver",
"armorSharp": 0.3,
"armorBlunt": 0.22,
"insCold": 6,
"maxHp": 130,
"look": 3,
"matType": "Leather",
"matCost": 28,
"more": [ { "res": "Rivets", "count": 6 } ],
"tint": "#6B4B2E"
}
]
}
| Поле | По умолч. | Что делает |
|---|---|---|
| id | — | Ключ. Им же вещь открывается:
apparel:steppecoat. |
| name | = id | Название. |
| slot | Torso | Куда надевается — см. ниже. |
| armorSharp | 0 | Доля блокируемого режущего
урона: 0.3 = 30%. |
| armorBlunt | 0 | То же для дробящего. |
| armorHeat | 0 | То же для ожога. |
| insCold | 0 | Насколько градусов сдвигает вниз порог холода. Это и есть «тепло» в описании. |
| insHeat | 0 | Насколько поднимает порог жары. |
| maxHp | 100 | Прочность вещи. |
| look | 0 | Номер картинки слоя. См. раздел 16. |
| matType | Cloth | Основной материал пошива. |
| matCost | 10 | Сколько его нужно. |
| more | — | Дополнительные материалы списком. |
| waterproof | 0 | Защита от намокания, 0…1. |
| tendBonus | 0 | Прибавка врачу к качеству лечения (халат врача). |
| colorful | false | Вещь красится в цвет при пошиве — игрок выбирает цвет. |
| tint | — | Постоянный цвет вещи, "#7A5A38".
Такая вещь не красится никогда и нигде. |
| coversFace | false | Закрытый шлем: под ним не видно бороды. |
Слоты
| slot | Что это | Где шьётся |
|---|---|---|
| Head | Голова: шапки, шлемы | швейный стол; железные и стальные — верстак |
| Torso | Тело: рубаха, куртка, парка | швейный стол |
| TorsoOver | Поверх тела: сорочка | швейный стол |
| Legs | Ноги: штаны | швейный стол |
| ArmorOver | Доспех поверх всего | верстак |
Правило простое: всё, что считается бронёй, делается на верстаке, остальное —
на швейном столе. Бронёй считается вещь в слоте ArmorOver, а также головной убор
из железа или стали.
Новая одежда видна в станке только если выполнены оба условия: правильный
станок по слоту и выданный ключ apparel:<id> — через тех в
techs.json. Забыли ключ — вещь есть в таблице, но её нигде нельзя сшить.
10resources.json — ресурсы, в том числе свои
Здесь живут вес, цена, сытость и признаки каждого вида. Ключ — id. У этой
таблицы две работы.
Работа первая: поправить существующий ресурс
Если id совпал с игровым (список — раздел 17), меняются
свойства этого вида:
{
"resources": [
{ "id": "Steel", "value": 14, "weight": 0.5 }
]
}
Работа вторая: завести свой ресурс
Незнакомый id — это новый вид. Он получит собственный номер
(модовые номера начинаются с 1000), будет лежать на складах, носиться, гореть, торговаться и
съедаться — по тем признакам, которые вы укажете.
{
"resources": [
{
"id": "Mithril",
"name": "Мифрил",
"nameEn": "Mithril",
"weight": 0.3,
"value": 40,
"mineral": true,
"icon": "Items/mithril",
"shape": "ingot",
"color": "#8FD8E8"
}
]
}
| Поле | По умолч. | Что делает |
|---|---|---|
| id | — | Ключ. Должен начинаться с буквы. Имя вида, которым на него ссылаются рецепты. |
| weight | 0.2 | Вес единицы. |
| value | 1 | Цена для торговли и оценки богатства колонии. |
| nutrition | 0 | Сытость одной штуки. Только у еды. |
| name | — | Название по-русски. Только для своих видов — у встроенных имя живёт в переводе игры. |
| nameEn | = name | Название по-английски. |
| icon | — | Путь к картинке, как в игре:
"Items/mithril". Файл кладётся в
textures/Items/mithril.png. |
| shape | nugget | Форма заглушки, если картинки нет — см. ниже. |
| color | — | Цвет заглушки, "#8FD8E8". |
Признаки
Это не ярлыки для красоты — каждый читается конкретной частью игры: кухней, огнём, торговлей, хирургией, скотом.
Формы заглушек (shape)
Без картинки вещь не станет розовым квадратом: игра нарисует силуэт указанной формы вашим цветом. Мод без художника всё равно получает вещь, которую видно и не спутать с соседней стопкой.
Его никто не добывает и ни из чего не делает, пока вы не добавите рецепт в
recipes.json. Жилы в горе и растения на грядке задаёт генератор мира, а не
таблица — их модом пока не расширить.
11buildings.json — цена и прочность построек
Эта таблица не создаёт новые постройки — она перекрывает числа существующих.
Ключ — kind плюс необязательный variant.
{
"buildings": [
{ "kind": "Wall", "costAmount": 4, "hp": 220 },
{ "kind": "Turret", "work": 40,
"extra": [ { "res": "SteelMechanism", "count": 2 } ] }
]
}
| Поле | Что делает |
|---|---|
| kind | Вид постройки. Имя должно быть игровым — незнакомое даст ошибку в логе. |
| variant | Вариант вида: у осадных орудий и машин энергосети своя цена у каждого. Пусто — запись на весь вид. |
| costType | Основной ресурс. |
| costAmount | Сколько его. |
| work | Секунд работы строителя. |
| hp | Прочность. |
| extra | Дополнительные компоненты списком. |
Названия видов подсмотрите в файле игры buildings.json — он лежит внутри
сборки и покрывает все 53 вида. Правьте только те строки, которые нужны: остальные останутся
игровыми.
12techs.json — технологии и ключи доступа
Это самая важная таблица для авторов контента. Именно тех выдаёт ключ, по которому вещь становится доступной. Нет ключа — вещь есть в таблицах, но её нигде нельзя сделать.
{
"techs": [
{
"id": "steppe_smithing",
"name": "Степная ковка",
"branch": "Ремёсла",
"era": 2,
"cost": 600,
"desc": "Кылыш и степной кафтан.",
"prereq": [ "craft_smithy" ],
"unlocks": [ "weaponfam:kylysh@iron", "apparel:steppecoat" ]
}
]
}
| Поле | По умолч. | Что делает |
|---|---|---|
| id | — | Ключ теха. |
| name | — | Название в окне «Технологии». |
| branch | — | Ветка. Новое название = новая колонка в окне. |
| era | 1 | 1 ремёсла · 2 железо · 3 порох · 4 сталь · 5 пар и электричество. |
| cost | 100 | Очков исследования. |
| desc | — | Описание. |
| needAdvanced | false | Требует построенного продвинутого стола исследований. |
| prereq | [] | Список id техов, которые нужно изучить раньше. |
| unlocks | [] | Что открывает — ключи из таблицы ниже. |
Словарь ключей
| Ключ | Открывает |
|---|---|
| building:Furnace | Постройку (имя вида). |
| recipe:mithril_ingot | Рецепт станка (его id). |
| weapon:bow | Конкретное оружие. |
| weaponfam:kylysh@iron | Семейство + материал: «железный кылыш». |
| apparel:steppecoat | Одежду или броню. |
| bed:Bedroll | Вариант кровати. |
| wallmat:Wood | Материал стен. |
| power:Generator | Машину энергосети. |
| siege:Mortar | Осадное орудие. |
| crop:oak | Культуру грядки. |
| mech:hunting | Механику игры (охота, рыбалка, приручение, торговля, первая помощь…). |
Сделали новую вещь — задайте три вопроса: 1) есть ли запись в таблице, 2) есть ли рецепт или подходящий станок, 3) кто выдаёт ключ. Три «да» — вещь в игре. Любое «нет» — её не будет, и игра об этом не предупредит.
13pawns.json — черты, биографии, имена
Самая безопасная таблица для мода: черты лежат в сохранении именами, поэтому добавлять и убирать их можно свободно.
{
"traits": [
{ "name": "Степняк",
"desc": "Вырос в седле: ходит на 10% быстрее." },
{ "name": "Белоручка", "blocks": "Уборка",
"desc": "За грязную работу не берётся." }
],
"traitPairs": [
{ "a": "Степняк", "b": "Белоручка" }
],
"maleNames": [ "Ермахан", "Актос" ],
"femaleNames": [ "Айгерим" ],
"lastNames": [ "Сериков" ],
"nicknames": [ "Белый" ],
"chronic": [ "Старая рана" ]
}
| Список | Ключ | Что это |
|---|---|---|
| traits | name | Черты характера.
blocks — работа, за которую человек не берётся. desc —
описание в карточке «Персона». |
| traitPairs | a + b | Взаимоисключающие черты: обе одному человеку не выпадут. |
| childhoods | name | Детские биографии:
bonus — прибавки к навыкам, blocks — навсегда закрытые
работы. |
| adulthoods | name | Взрослые биографии, те же поля. |
| maleNames / femaleNames | — | Пулы имён. Дополняются, ваши имена добавляются к игровым. |
| lastNames / nicknames | — | Фамилии и клички. |
| rareMale / rareFemale | — | Редкие имена. |
| rareFull | first | Редкие пары «имя + фамилия» целиком. |
| chronic | — | Хронические болячки из генерации. |
Эффекты черты
Черта мода — не просто ярлык: у неё может быть настоящий эффект. Блок
effects задаёт до двадцати параметров. Проценты пишутся долями
(0.15 = +15%), не указанное поле ни на что не влияет, эффекты разных черт
складываются.
{
"traits": [
{ "name": "Кочевник",
"desc": "Вырос в пути: ходит быстрее и легче терпит холод.",
"effects": { "moveSpeed": 0.15, "cold": 6, "hunger": -0.12, "mood": -2 } }
]
}
| Поле | Что меняет | Поле | Что меняет |
|---|---|---|---|
| workSpeed | скорость работы | moveSpeed | скорость ходьбы |
| hunger | как быстро голодает | damageTaken | входящий урон |
| aim | точность стрельбы | melee | урон в ближнем бою |
| quality | качество изделий (1.2 = как Аккуратист) | poison | риск отравиться |
| pain | чувствительность к боли | immunity | скорость иммунитета |
| sickChance | шанс заболеть | cold / heat | терпит холод / жару, °C |
| carry | грузоподъёмность | research | скорость исследований |
| rest | качество сна | darkWork | работа в темноте |
| social | общительность | mood | очки настроения (8 = Оптимист) |
| joyDrain | как быстро скучает | blocks | работа, за которую не берётся |
Запись { "name": "Трудяга", "effects": { "carry": 0.1 } } добавит Трудяге
+10% к грузу, а его родные +15% к скорости работы останутся. Если описания нет, игра
соберёт его сама из эффектов.
14loc.json — английский перевод
В игре две локали. Все ваши названия написаны по-русски — и в английской сборке они останутся русскими, пока вы не дадите перевод. Формат простой: русская строка → английская.
{
"en": {
"Кылыш": "Kylysh",
"Степной кафтан": "Steppe coat",
"Степная ковка": "Steppe smithing",
"Удар кылышем": "Kylysh strike",
"Выплавить мифрил": "Smelt mithril"
}
}
Ключ — ровно та строка, которую вы написали в таблице, символ в символ. Лишний пробел или другая буква «ё» — и перевод не найдётся.
- Перевод мода не может переписать строки игры — свои названия у неё останутся своими.
- Выключили мод — его строки уходят из таблицы вместе с ним.
- У ресурсов есть более короткий путь: поле
nameEnпрямо вresources.json.
15Папка textures — свои картинки
Правило одно: в моде путь картинки такой же, как у игры, плюс
.png. Игра при загрузке спрайта сначала спрашивает моды, потом свои
файлы.
| Что заменяем | Путь в моде |
|---|---|
| Картинку ножа в руке | textures/Guns/knife/knife-right.png |
| Картинку своего оружия | textures/Guns/kylysh/kylysh-right.png |
| Предмет на земле | textures/Items/mithril.png |
| Обломок гранита | textures/granite-chunk.png |
| Слой облика (шапка №3) | textures/char/hat/3_down.png |
Как рисовать
- PNG с прозрачностью, пиксель-арт (игра не сглаживает картинки моды — они останутся чёткими).
- Оружие рисуется горизонтально, остриём вправо. Если нарисовать вертикально (выше, чем шире в 1.4 раза), игра сама повернёт картинку на −90° — она считает, что клинок нарисован вверх. Это касается только ближнего боя: лук так и остаётся вертикальным.
- Точка отсчёта у оружия — центр картинки; размер задаётся полем
drawScale, а не пикселями. Картинка 400×100 и 800×200 будут в игре одного размера. - Обломки камня рисуйте квадратными и без «верха»: игра поворачивает их на 0/90/180/270°, чтобы россыпь не выглядела штампованной. Тень с одной стороны выдаст поворот.
- Если два включённых мода кладут картинку по одному пути — побеждает тот, кто ниже в списке модов.
16Облик колониста: слои и номера look
Колонист собирается из слоёв-картинок. Каждый слой — папка, внутри пронумерованные варианты, каждый в четырёх видах.
char/hat/3_down.png ← вид спереди (обязателен) char/hat/3_up.png ← со спины char/hat/3_left.png ← влево char/hat/3_right.png ← вправо char/hat/3_down_w.png ← женский вариант (необязателен)
Поле look в apparel.json — это и есть номер варианта в папке
своего слоя. Слоты и папки связаны так:
| Слот вещи | Папка слоя | Занято игрой |
|---|---|---|
| Head | char/hat/ | 0, 1, 2 |
| Torso · TorsoOver · ArmorOver | char/top/ | 0, 1, 2 |
Значит, ваша новая шапка — это "look": 3 плюс файл
textures/char/hat/3_down.png.
Игра набирает варианты слоя по порядку — 0, 1, 2, 3… — и останавливается на
первом отсутствующем. Если положить 5_down.png, пропустив 3 и 4, его
не увидит никто. Берите следующий свободный номер.
Правила покраски
"colorful": true— вещь красится в выбранный цвет при пошиве. Рисуйте её белой: цвет накладывается кодом."tint": "#7A5A38"— вещь всегда одного цвета. Тоже рисуется белой.- Броня (
ArmorOverи железные/стальные шлемы) не красится никогда — её рисуют сразу в нужном цвете. - Под любым головным убором волосы скрываются автоматически, а под
"coversFace": true— ещё и борода.
17Справочник: все ресурсы игры
Эти имена пишутся в полях in1Type, outType,
costType, matType, res. Пишите точно, с заглавной
буквы.
Сырьё и камень
Руда и металл
Детали и механизмы
Еда
Ткани и шкуры
Медицина
Порох и боеприпасы
Деньги и прочее
Всего встроенных видов — 83. Ваши добавляются сверх них.
18Справочник: ключи и значения
| Где | Поле | Возможные значения |
|---|---|---|
| оружие | dtype | Sharp · Blunt · Heat |
| оружие | materials.key | wood · lime · gran · iron · bronze · steel |
| одежда | slot | Head · Torso · TorsoOver · Legs · ArmorOver |
| рецепт | station | campfire · cook · butcher · furnace · steel · stonecut · chem · tailor · craft |
| рецепт | in1Group | 0 точный · 1 любое мясо · 2 любая растительная еда |
| ресурс | shape | ingot · bar · powder · vial · coin · meat · grain · tuber · cloth · herb · gem · nugget |
| тех | era | 1 ремёсла · 2 железо · 3 порох · 4 сталь · 5 пар и электричество |
| тех | unlocks | building: · recipe: · weapon: · weaponfam: · apparel: · bed: · wallmat: · power: · siege: · crop: · mech: |
Что открыто с самого начала, без исследований
Полезно знать, чтобы не выдавать ключ второй раз: костёр, пол, дверь, стена, разделочный стол, деревянные стены, спальник, стол исследований, уголок ремесла, рубаха, шапка, штаны, пелёнка, простая еда на костре, разделка, а также нож, копьё и дубина из дерева, известняка и гранита.
19Сейвы и совместимость
Тут стоит понимать, как игра хранит ваши вещи, — это объясняет все странности при смене набора модов.
Номера ресурсов
В сохранении предмет записан номером: «предмет 5, штук 20». У встроенных видов номера вечные. Ресурсам модов номера выдаются по порядку, начиная с 1000 — а значит, они зависят от того, какие моды включены и в каком порядке.
Чтобы это не ломало партии, каждое сохранение носит с собой свою карту «номер → имя». При загрузке она главнее: ваши ресурсы получат ровно те номера, что были в этой партии. Переставили моды местами — колония не пострадает.
Если мод выключили
Вещи, чей вид больше никто не описывает, изымаются из колонии, а игра пишет об этом письмо. Изъятие затрагивает склады, подсумки, груз караванов и задания — там, где был мифрил, останется пустое место, а не «дерево».
Перед сменой набора модов игра предлагает сделать копию сохранения. Соглашайтесь: это один тап, а откатиться потом уже нельзя.
Что безопасно менять на середине партии
| Безопасно | Опасно |
|---|---|
| Числа существующих вещей: урон, цена, вес, работа | Убрать ресурс, который уже лежит на складе |
| Добавить черты, имена, биографии | Убрать одежду, которая уже надета |
| Добавить техи и рецепты | Поменять id своей вещи (для игры это
«старая исчезла, новая появилась») |
| Заменить картинки | Выключить мод посреди партии |
20Когда что-то не работает
| Симптом | Что проверить |
|---|---|
| Мода нет в списке | Есть ли в папке mod.json. Лежит ли папка
именно в Mods/. Нажали ли «ОБНОВИТЬ». |
| В карточке красная строка «ОШИБКА» | Там же написан файл и причина — обычно лишняя запятая или неровные кавычки. |
| Мод включён, но ничего не поменялось | Совпадает ли id с игровым
(регистр важен: knife, не Knife). Правильное ли имя списка —
"weapons", а не "weapon". |
| Новой вещи нет ни в одном станке | Нет ключа. Нужен тех с
unlocks — см. раздел 12. |
| Новая броня не появилась у швейного стола | Броня делается на верстаке, а не у швейного стола. |
| Вместо предмета непонятный камешек | Картинка не найдена — проверьте путь в
поле icon и файл в textures/. Заглушка рисуется по
shape. |
| Новая шапка не надевается | Номер look идёт не подряд, или нет
файла <номер>_down.png. |
| Оружие держат боком | Картинка нарисована не остриём вправо. Либо
перерисуйте, либо задайте spriteAngle. |
| Русские буквы стали кракозябрами | Файл сохранён не в UTF-8. |
| В английской сборке русские названия | Нет loc.json или ключ в
нём написан не символ в символ. |
Правьте по одному файлу за раз и после каждой правки жмите «ОБНОВИТЬ» на экране модов. Тогда вы всегда знаете, какая именно строка сломала мод. Когда меняешь пять файлов и потом ищешь ошибку — уходит вечер.
21Чего мод пока не может
Честный список границ — чтобы вы не потратили день на то, чего в игре ещё нет.
- Своего вида работ в приоритетах. Колонист не возьмёт «рыбалку» сам —
его отправляет скрипт (
alem.task, раздел 34). - Своего поведения у зверя. Зверь мода ведёт себя как олень, кролик или волк — какой основе вы его научите.
- Постройки со своей работой. Мебель мода берёт поведение нашей постройки (раздел 26): станок, склад, кровать, турель. Совсем новой механики у неё нет.
- Нового биома на глобусе. Биом — это генерация планеты; модом можно только настроить наши пять (раздел 27).
- Своей жилы в горе. Руду расставляет генератор мира. Свой ресурс выдаётся рецептом, растением, торговцем или скриптом.
- Своих окон интерфейса. Есть письма и окно с кнопками
(
alem.dialog), но не свои экраны. - Кода на C#. На Android чужой C# не запускается в принципе — это ограничение сборки. Именно поэтому в игру встроен Lua.
Всё остальное уже есть: двадцать таблиц, свои звери, растения, постройки, державы, события, заказы, потребности, рассказчики и испытания — и скрипты, которые связывают это в свои правила.
22Готовый шаблон и чек-лист
Ниже — маленький, но полный мод: новое оружие, доспех, свой ресурс, рецепт, тех, который всё это открывает, и перевод. Скопируйте, переименуйте, правьте.
{
"id": "steppe_pack",
"name": "Степной набор",
"author": "ваше имя",
"version": "1.0",
"desc": "Кылыш, степной кафтан и мифрил."
}
{
"weapons": [
{ "id": "kylysh", "name": "Кылыш", "melee": true, "family": true,
"dmg": 17, "interval": 1.7, "range": 1.5, "dtype": "Sharp",
"hitLabel": "Удар кылышем", "drawScale": 0.8, "craftWork": 6 }
]
}
{
"apparel": [
{ "id": "steppecoat", "name": "Степной кафтан", "slot": "ArmorOver",
"armorSharp": 0.3, "armorBlunt": 0.22, "insCold": 6, "maxHp": 130,
"look": 3, "matType": "Leather", "matCost": 28,
"more": [ { "res": "Rivets", "count": 6 } ],
"tint": "#6B4B2E" }
]
}
{
"resources": [
{ "id": "Mithril", "name": "Мифрил", "nameEn": "Mithril",
"weight": 0.3, "value": 40, "mineral": true,
"shape": "ingot", "color": "#8FD8E8" }
]
}
{
"recipes": [
{ "station": "furnace", "id": "mithril_ingot", "name": "Выплавить мифрил",
"in1Type": "SilverOre", "in1": 10, "in2Type": "Coal", "in2": 2,
"outType": "Mithril", "outCount": 1, "work": 4 }
]
}
{
"techs": [
{ "id": "steppe_smithing", "name": "Степная ковка", "branch": "Ремёсла",
"era": 2, "cost": 600, "desc": "Кылыш, кафтан и выплавка мифрила.",
"unlocks": [ "weaponfam:kylysh@iron", "apparel:steppecoat",
"recipe:mithril_ingot" ] }
]
}
{
"en": {
"Кылыш": "Kylysh",
"Степной кафтан": "Steppe coat",
"Степная ковка": "Steppe smithing",
"Мифрил": "Mithril",
"Выплавить мифрил": "Smelt mithril",
"Удар кылышем": "Kylysh strike"
}
}
Чек-лист перед раздачей мода
- Мод включается и выключается без ошибок в карточке.
- Все новые вещи реально появляются в станках — проверено в новой игре.
- У каждой новой строки есть перевод в
loc.json. - Картинки на месте: в руке, за спиной, на земле, в списке заказов.
idмода уникален и не совпадает с чужими.- В архиве лежит папка мода или её содержимое — и внутри есть
mod.json. - В описании написано, для какой версии игры мод и что он меняет.
- Если есть скрипты — в логе нет строк
[Lua] …с ошибками. - Мод проверен на новой партии: звери, растения и державы появляются только при создании карты.
Начинайте с малого: один мод — одна идея. Набор из трёх видов оружия, который работает, полезнее большого мода, который не включается.
23events.json — свои события
Событие — это письмо, которое приходит игроку посреди партии: находка, беда, предложение. Мод может добавлять свои, и они встанут в общий поток рассказчика.
{
"events": [
{
"id": "steppe_caravan_gift",
"category": "good", "weight": 1.2, "cooldownDays": 12, "minDay": 4,
"title": "Караван из Таавуна",
"text": "Старший говорит: «{colonist}, степь помнит добро» — и оставляет кумыс.",
"effects": [
{ "type": "give", "resource": "Kumys", "count": 12 },
{ "type": "mood", "value": 4, "days": 2, "text": "Гостили купцы" }
]
}
]
}
Когда событие может выпасть
| Поле | Что задаёт |
|---|---|
| category | good · neutral ·
threat. Категория решает, в какой момент рассказчик может его прислать. |
| weight | Как часто выпадает. Все встроенные события категории весят вместе как 4, так что вес 1 — это примерно каждый пятый раз. |
| cooldownDays | Не чаще раза в столько дней. |
| once | true — только один раз за игру. |
| minDay | С какого игрового дня. |
| minColonists / maxColonists | Сколько живых людей в колонии. |
| minWealth / maxWealth | Богатство колонии. |
| season | spring · summer ·
autumn · winter. |
| minTemp / maxTemp | Температура на улице. |
| needResource / needCount | Сколько такого ресурса должно быть в колонии. |
Письмо и выбор
title и text — заголовок и текст; {colonist} в тексте
заменится именем случайного колониста. Дальше одно из двух: либо effects — и
событие просто случается, либо choices — ровно два варианта, у
каждого свои последствия.
"choices": [ { "text": "Купить", "effects": [ { "type": "take", "resource": "ImperialMark", "count": 200 }, { "type": "animal", "species": "Верблюд", "count": 1 } ] }, { "text": "Отказать", "effects": [ { "type": "relation", "value": -3 } ] } ]
Последствия (effects)
| type | Что делает | Поля |
|---|---|---|
| give | Кладёт ресурс у колонии | resource, count |
| take | Забирает ресурс | resource, count |
| mood | Воспоминание всем колонистам | value, days, text |
| relation | Меняет отношения с державой | value, faction |
| raid | Приближает налёт | hours |
| animal | Приводит домашний скот | species, count |
| letter | Ещё одно письмо | title, text |
| event | Запускает другое событие по id | text = id |
Вариантов должно быть ровно два — событие с тремя игра пропустит и
напишет ошибку. И цепочка event не должна замыкаться сама на себя: игра
оборвёт её на пятом шаге.
24plants.json — дикие растения
Растение мода ведёт себя как ягодный куст: его помечают на сбор, колонист собирает урожай, растение отрастает. Отличается всё остальное — имя, картинка, что даёт, где растёт.
{
"plants": [
{ "id": "saxaul", "name": "Саксаул",
"desc": "Корявый пустынный куст: его ветки горят жарко и долго.",
"texture": "plants/saxaul_full", "emptyTexture": "plants/saxaul_empty",
"resource": "SaxaulWood", "yieldMin": 8, "yieldMax": 14,
"work": 2.0, "regrowDays": 4, "scale": 1.2, "blocks": false,
"biomes": [ "steppe", "desert" ],
"terrain": [ "sand", "dirt" ], "chance": 0.004 }
]
}
| Поле | Что задаёт |
|---|---|
| name / desc | Имя на карте и описание в карточке «?». |
| texture / emptyTexture | Картинка со спелыми плодами и после сбора. Без второй растение просто темнеет. |
| resource · yieldMin · yieldMax | Что даёт и сколько за сбор. |
| work | Секунд работы при навыке 0. |
| regrowDays | За сколько дней урожай отрастает снова. |
| scale · blocks | Размер и проходимость. |
| biomes | forest · temperate ·
steppe · desert · tundra. Пусто — везде, кроме
ледника. |
| terrain | grass · dirt ·
sand · mud. Пусто — трава и земля. |
| chance | Доля подходящих клеток: 0.004 — примерно
одна из двухсот пятидесяти. |
Растения расставляются при создании карты: в уже начатой партии они не появятся. Сами по себе они не разрастаются, а мороз останавливает отрастание. Если мод выключить, растения станут малиновыми кустами — еда не пропадёт.
25animals.json и livestock.json — звери и скот
Дикие звери
Своё поведение зверю задать нельзя — он берёт его у одного из наших видов
(behavior): deer ходит стадом и отбивается, если ранить;
rabbit мелкий и пугливый, попадается в силки; wolf — хищник,
охотится на дичь и скот.
{
"animals": [
{ "id": "wild_camel", "name": "Дикий верблюд", "nameFemale": "Дикая верблюдица",
"desc": "Двугорбый зверь степей. Лягается, если его ранить.",
"behavior": "deer", "texture": "animals/camel_side",
"hp": 160, "bodySize": 1.6, "hpMul": 2.4, "speed": 2.6, "scale": 1.25,
"damageMin": 6, "damageMax": 12,
"meat": "Beef", "meatMin": 70, "meatMax": 100, "leather": 35,
"biomes": [ "steppe", "desert" ],
"groups": 1, "groupMin": 2, "groupMax": 4, "restock": 0.12,
"tameTo": "Верблюд" }
]
}
groups — сколько групп поставить при создании карты, restock —
шанс, что зверей этого вида подселят по ходу игры (так они появляются и в начатой партии).
tameTo — вид скота, которым зверь станет после приручения; без него приручить
нельзя.
Картинка рисуется сбоку, мордой вправо — при ходьбе влево игра отразит её сама. Отдельных кадров для движения вверх и вниз у зверей мода нет.
Домашний скот
Запись в livestock.json добавляет новый вид скота или правит наш. Пустое поле
означает «оставить как есть», поэтому для правки хватит одной строки.
{
"livestock": [
{ "species": "Верблюд", "ruName": "верблюд",
"femaleName": "Верблюдица", "maleName": "Верблюд", "babyName": "Верблюжонок",
"spriteKey": "camel", "scale": 1.25, "bodySize": 1.6, "hpMul": 2.5,
"adultDays": 200, "appetite": 0.8, "pregnancyHours": 960,
"product": "milk", "productHours": 14, "productAmount": 6,
"meatType": "Beef", "meat": 120, "leather": 50,
"price": 420, "maxHerd": 8, "minTempC": -6, "mount": true },
{ "species": "Курица", "price": 75 }
]
}
product — milk, egg, wool или
none. mount: true делает вид верховым: на нём ездят и он ускоряет
караван, как лошадь. Картинки скота кладутся в
textures/Animals/<spriteKey>_down.png и остальные стороны.
species лежит в сохранении строкой. Переименуете — в старых партиях скот
исчезнет. Для показа есть отдельные поля ruName, maleName и
прочие, их правьте свободно.
26furniture.json — своя мебель и станки
Эта таблица добавляет постройки, в отличие от
buildings.json, который только меняет числа наших.
Секрет в поле base: предмет мода берёт поведение нашей
постройки, а имя, картинку, цену, прочность и свет задаёт мод. Так получаются свои
склады, кровати, станки, турели и очаги — без единой строчки кода.
{
"furniture": [
{ "id": "steppe_chest", "name": "Сундук кочевника",
"desc": "Расписной сундук для утвари. Работает как полка-склад.",
"base": "Shelf", "texture": "furniture/chest",
"cost": [ { "resource": "Wood", "count": 15 },
{ "resource": "Leather", "count": 5 } ],
"hp": 130, "research": "building:steppe_chest" },
{ "id": "steppe_hearth", "name": "Очаг юрты", "base": "Brazier",
"light": 7, "lightColor": "#FF9A50" },
{ "id": "felt_bed", "name": "Войлочная постель",
"base": "Bed", "variant": "Single" }
]
}
| Поле | Что задаёт |
|---|---|
| base | Имя нашей постройки, чьё поведение берётся. Пусто — простой предмет 1×1 без пользы (декор, светильник, преграда). |
| variant | Вариант основы: у кровати Single,
Double, Hospital, Cradle…; у машины энергосети
Lamp, Heater, Cooler…; у осадного орудия
Ballista, Mortar… |
| name · desc · texture | Имя, подсказка в меню и картинка. |
| cost | Список материалов. Пусто — как у основы. |
| work · hp | Время стройки и прочность. 0 — как у основы. |
| light · lightColor | Радиус и цвет света. |
| blocks · scale | Непроходимость и размер картинки (у предмета без основы). |
| research | Ключ технологии: пока не изучена, плитка в меню заперта. Пусто — доступна сразу. |
Какие основы бывают
Нельзя брать за основу стены, пол, двери, мост, заборы, провода, телеги и шахтный ствол — они ставятся не как мебель.
Сделали станок на основе, например, TailorBench — рецепты для него пишутся
в recipes.json с обычной станцией tailor. Плитки мебели модов
стоят в конце вкладки «Мебель».
27factions.json и biomes.json — державы и биомы
Державы
Запись либо правит нашу державу (если name совпал), либо
добавляет новую на глобус — со столицей, городами и территорией.
{
"factions": [
{ "name": "Орда Каракум", "archetype": "raiders", "color": "#C07A38",
"alwaysHostile": true, "tech": 1.8, "citiesMin": 2, "citiesMax": 4 },
{ "name": "Таавун", "archetype": "traders", "relationMin": 5, "relationMax": 25 }
]
}
archetype — raiders, tribal, traders,
smiths, healers, syndicate: от него зависят караваны,
товары и скорость развития. outlaw — «вне закона»: мало поселений и техника с
самого низа. tech — стартовый уровень 0…5.
Глобус строится заново по зерну планеты при каждой загрузке. Наши державы расставляются как раньше, а державы модов дописываются в конец своим генератором — включение мода не сдвинет столицы в уже идущей партии. Правки наших держав (отношения, архетип, техуровень) применяются только в новой игре: в старой всё это хранит сейв.
Биомы
Новый биом добавить нельзя — это генерация планеты. Зато наши пять настраиваются:
{
"biomes": [
{ "id": "steppe", "trees": 0.7, "bushes": 1.5, "richSoil": 0.03,
"wildlife": 1.4, "temp": 2 }
]
}
| Поле | Что меняет | Когда действует |
|---|---|---|
| trees · rocks · bushes | густота деревьев, валунов, ягодных кустов | новая карта |
| richSoil | сколько богатой почвы (0.05 — заметно) | новая карта |
| temp | прибавка к температуре места высадки, °C | сразу |
| wildlife | сколько дичи и какой её потолок | карта и сразу |
Имена биомов: forest, temperate, steppe,
desert, tundra.
28quests.json — свои заказы держав
Шаблон заказа на поставку: кто просит, что, сколько, за какой срок и чем платит. Письмо, окно «Задания», сдача груза гонцу и штраф за просрочку — всё это делает игра.
{
"quests": [
{ "id": "steppe_felt_order",
"archetypes": [ "tribal", "traders" ], "minRelation": 5, "minDay": 6, "weight": 1.2,
"items": [ "FeltCloth", "DriedMeat" ], "amountMin": 15, "amountMax": 35,
"daysMin": 6, "daysMax": 10, "moneyMin": 300, "moneyMax": 520,
"rewards": [ { "resource": "Iron", "min": 15, "max": 30 } ],
"relation": 12, "research": 120,
"title": "Кочевникам: {0} × {1}",
"text": "«{giver}» просят {amount} × {item}. Срок — {days} дн." }
]
}
В заголовке {0} — количество, {1} — ресурс. В тексте доступны
{giver}, {amount}, {item} и {days}. Ниже
игра сама допишет срок, награду и предупреждение о провале.
weight работает как у событий: обычный заказ игры весит 3.
factions позволяет ограничить заказ конкретными державами по имени.
Заказ «убить зверя» или «привести беженца» пока не сделать. И заказы дают только дружественные державы — не больше двух активных сразу.
29needs.json — свои потребности
Новая шкала у каждого колониста: сама убывает, видна в окне «Нужды» и портит настроение, когда падает. Чем её наполнять — решает мод.
{
"needs": [
{ "id": "thirst", "name": "Жажда", "color": "#4FA8E8",
"start": 85, "drainPerHour": 3.2, "lowAt": 25,
"moods": [
{ "below": 45, "mood": -3, "text": "Хочется пить" },
{ "below": 20, "mood": -9, "text": "Мучает жажда" }
] }
]
}
Когда шкала падает ниже lowAt, скриптам приходит
on_need_low(имя, id) — там и решается, что делать: отправить к воде, дать выпить,
прислать письмо. Значения хранятся в сохранении отдельно по каждому колонисту.
30Рассказчики и испытания
storytellers.json
Рассказчик задаёт ритм всей партии: как быстро копится напряжение, какая доля налётов, какие налёты чаще, какая передышка в начале, насколько крупные отряды, с какого дня конница и осады.
{
"storytellers": [
{ "name": "Буран", "character": "Беспощадный",
"styleLine": "бьёт волнами и почти не даёт продохнуть",
"style": "waves", "tensionPerDay": 15, "raidShare": 0.65,
"sizeMin": 1.2, "sizeMax": 1.7, "graceMin": 4, "cavalryDay": 18 }
]
}
style — calm (по напряжению), waves (кризис, затем
передышка), chaos (ровный случайный шанс), relentless. Ноль или
пустое поле значит «как у игры», поэтому нашего рассказчика можно поправить одной строкой.
Наши рассказчики всегда идут первыми, модовые дописываются в конец: номер рассказчика лежит в сейве. Выключили мод — партия возьмёт ближайшего нашего, а не чужого.
challenges.json
Своё испытание: правила старта и цель. Цель бывает четырёх видов —
raids (отбить столько налётов), days (прожить столько дней),
wealth (накопить богатство) и script — когда счёт ведёт сам мод.
{
"challenges": [
{ "id": "steppe_herd", "name": "Хозяин табуна",
"desc": "Соберите табун из восьми верблюдов и удержите его.",
"rules": [ "Цель считает мод", "Рассказчик «Буран»" ],
"goal": "script", "goalValue": 8, "goalText": "Собрать табун из 8 верблюдов",
"colonists": 3, "difficulty": 2, "graceDays": 4, "starDay": 40,
"planetSeed": 40404,
"cargo": [ { "resource": "Food", "count": 120 } ],
"weapons": [ "bow" ], "startTech": [ "steppe_craft" ] }
]
}
Для цели script скрипт сам считает прогресс и объявляет исход — см.
alem.challenge_progress, alem.win и alem.lose в
разделе 34.
31info.json — описания для карточки «?»
У каждой вещи, постройки, растения и зверя в игре есть кнопка «?» — окно с описанием сверху и характеристиками снизу. Характеристики игра считает сама, а описание берёт отсюда.
{
"entries": {
"res:Kumys": { "ru": "Кислый напиток из кобыльего молока.",
"en": "A sour drink of mare's milk." },
"weapon:saber": { "ru": "Изогнутый клинок для конного боя.",
"en": "A curved blade for fighting from the saddle." }
}
}
Ключи: res:<ресурс>, weapon:<id>,
apparel:<id>, build:<вид>,
plant:<id>, animal:wild:<id>. У растений, зверей и мебели
есть путь короче — поле desc прямо в их таблице.
32Скрипты на Lua: первый скрипт
Таблицы описывают вещи. Скрипт описывает правила: «если
случилось это — сделай то». Файлы .lua кладутся в папку scripts/
внутри мода и грузятся по алфавиту.
-- Голодная зима: если зимой мало еды, приходит письмо и портится настроение
function on_day(day)
if alem.season() == "winter" and alem.resource("Food") < 50 then
alem.letter("Голодная зима", "Старики говорят: духи уходят из холодных домов.")
alem.mood_all(-4, 2, "Страх голода")
end
end
Это весь скрипт. Lua — простой язык: переменные, if, циклы и функции. Всё, что
нужно знать для модов, помещается в этот раздел и два следующих.
Что нужно помнить сразу
- Каждый мод живёт отдельно — переменные одного мода не видны другому.
- Ошибка не роняет игру. Она попадает в лог строкой
[Lua] …с именем мода. После трёх ошибок подряд скрипты этого мода выключаются до перезапуска. - Бесконечный цикл не повесит игру — вызов прервётся по лимиту в 200 000 шагов, а в лог попадёт ошибка.
- Песочница. Скрипту недоступны файлы, интернет и что-либо за пределами
таблицы
alem. Нетio,require,os.execute. - Изменения подхватываются при перезапуске игры или при смене набора модов.
Все скрипты мода живут в общей таблице функций. Если два файла объявят
function on_day, второй затрёт первый. Когда файлов несколько, подписывайтесь
так:
alem.on("on_day", function(day)
-- этот обработчик не мешает соседнему файлу
end)
33Хуки: когда игра зовёт скрипт
| Хук | Когда вызывается |
|---|---|
| on_hour(hour) | Каждый игровой час. |
| on_day(day) | Начало нового дня. |
| on_event(id) | Выпало событие — наше (Blizzard,
Windfall…) или мода. |
| on_raid_start() / on_raid_end() | Начало и конец налёта. |
| on_hit(кого, кто, урон, тип, часть, это_колонист) | Каждое попадание, когда записывается рана. Здесь делаются эффекты оружия: яд, поджог, оглушение. |
| on_kill(имя, вид, причина) | Смерть человека; вид —
colonist или enemy. |
| on_colonist_died(имя) | Умер колонист. |
| on_need_low(имя, id) | Своя потребность упала ниже порога. |
| on_building_built(id, x, y) | Достроена постройка; id — ваш предмет или имя нашего вида. |
| on_challenge_start(id) | Началось испытание. |
on_hit случается десятки раз в секунду в бою. Если модов со скриптами нет,
он не стоит игре ничего, но внутри своего обработчика не делайте тяжёлой работы: длинные
циклы там ни к чему.
34Команды alem.*
Узнать
| Команда | Что возвращает |
|---|---|
| alem.day() · alem.hour() | День и час. |
| alem.season() · alem.temp() | Сезон (spring…) и
температуру. |
| alem.wealth() | Богатство колонии. |
| alem.colonists() | Список имён живых колонистов. |
| alem.colonist_count() | Сколько их. |
| alem.health(имя) · alem.mood(имя) | Здоровье и настроение. |
| alem.has_trait(имя, черта) | Есть ли черта. |
| alem.need(имя, "hunger"|"rest"|"recreation"|своя) | Значение шкалы; третьим аргументом — задать. |
| alem.resource(ресурс) | Сколько лежит в колонии. |
| alem.relation(держава) | Отношения, −100…100. |
| alem.enemies() | Список врагов: имя, x, y, здоровье. |
| alem.count_animals(вид) · alem.count_wild(вид) | Сколько скота и дичи. |
| alem.count_buildings(id) | Сколько таких построек. |
| alem.colony_center() · alem.pos(имя) · alem.map_size() | Координаты. |
| alem.challenge() | Что за испытание идёт: id, цель, прогресс. |
Сделать
| Команда | Что делает |
|---|---|
| alem.give(ресурс, n) · alem.take(ресурс, n) | Выдать или забрать. |
| alem.memory(имя, значение, дни, текст) | Воспоминание одному колонисту. |
| alem.mood_all(значение, дни, текст) | Всем сразу. |
| alem.letter(заголовок, текст) | Письмо игроку. |
| alem.dialog(заголовок, текст, {кнопки}, функция) | Окно с двумя–пятью кнопками; функция получает номер нажатой. |
| alem.log(текст) | Строка в журнал колонии. |
| alem.go(имя, x, y) · alem.draft(имя, вкл) | Приказ идти; мобилизовать. |
| alem.task(имя, x, y, сек, функция) | Колонист идёт и работает там указанное время, потом вызывается ваша функция. Так делаются свои занятия. |
| alem.attack_nearest(имя) · alem.hunt_nearest(имя) | Атаковать ближайшего врага; охотиться на ближайшего зверя. |
| alem.hurt(имя, урон, тип, подпись) | Ранить. |
| alem.heal(имя) · alem.tend(имя) | Вылечить; перевязать. |
| alem.disease(имя, название, скорость, иммунитет, смертельна, боль) | Своя болезнь. |
| alem.damage_at(x, y, радиус, урон, тип, подпись) | Урон по всем в круге — взрыв, молния, облако яда. |
| alem.ignite(x, y) · alem.extinguish(x, y) | Поджечь и потушить. |
| alem.spawn_item · alem.spawn_weapon | Положить предмет или оружие. |
| alem.spawn_wild(вид, x, y) · alem.spawn_livestock(вид, x, y) | Дикий зверь; скот. |
| alem.spawn_enemy(x, y, оружие, навык) · alem.raid(очки) | Одиночный враг; настоящий налёт через рассказчика. |
| alem.spawn_wanderer(x, y) | Путник у ворот. |
| alem.build(вид, x, y, материал, сразу) | Постройка: наша или мебель мода; чертежом или готовой. |
| alem.spawn_plant(что, x, y) | tree,
bush, rock или id растения мода. |
| alem.relation_add(держава, n) · alem.raid_in(часы) | Отношения; приблизить налёт. |
| alem.event(id) | Запустить событие из events.json. |
| alem.challenge_progress(n) · alem.win() · alem.lose() | Своя цель испытания. |
Память мода
Скрипту нужно помнить своё: сколько раз что случилось, на каком шаге сюжет. Для этого есть две команды, и всё, что в них положено, лежит в сохранении.
alem.set("deaths", alem.get("deaths", 0) + 1)
if alem.get("deaths", 0) == 3 then alem.event("cursed_land") end
Хранить можно строки, числа и true/false. Ключи у каждого мода свои — чужие
не перепутаются.
35Правила и границы скриптов
Пример: жажда и водопой
Связка «таблица + скрипт» выглядит так. В needs.json заведена шкала
«Жажда», а скрипт решает, как её утолять:
function on_need_low(name, need)
if need ~= "thirst" then return end
local cx, cy = alem.colony_center()
alem.task(name, cx, cy, 6, function(who)
if alem.resource("Kumys") > 0 then
alem.take("Kumys", 1)
alem.need(who, "thirst", 100)
alem.memory(who, 3, 1, "Пил кумыс")
else
alem.need(who, "thirst", 65) -- вода утоляет хуже
end
end)
end
Чего скрипт не может
- Отменить или изменить урон.
on_hitприходит уже после расчёта: можно добить, поджечь, отравить или вылечить — но не «не дать попасть». - Завести новый вид работ. Колониста на своё занятие отправляет
alem.task, а не список приоритетов. - Сделать своё окно интерфейса. Есть письма и
alem.dialog— до пяти кнопок. - Достучаться до внутренностей игры. Наружу выведены только команды из раздела 34; всё остальное закрыто песочницей.
print("что-то") из скрипта попадает в лог строкой
[Lua:<id мода>], а alem.log() — в журнал колонии, который
видно прямо в игре. Этого хватает, чтобы понять, где скрипт остановился.