Формат файлов миров

Файлы миров лежат в /worldgen/worlds и имеют расширение .world. Формат у них похож на формат var-ов.

Пример (artifacts_v1.0.1.world):

Identifier: artifacts_v1.0.1
Name: Мир артефактов
#Редкость артефактов уменьшена на 2#

/
Filename: item.var
Attribute: Rarity
Value: -2
Condition: Rarity>2 and Rarity<8

/
Filename: item.var
Attribute: Rarity
Value: -1
Condition: Rarity=2

где:

Identifier - Уникальный идентификатор мира (Идентификатор текущего мира сохраняется в worldgen.cfg).
Имя и описание думаю понятно и так.

Дальше идут модификации со следующими параметрами:

Filename - var-файл, в котором мы производим изменения

Attribute - атрибут из var-файла, значение которого меняет данная модификация.
Если в названии атрибута в var-е содержится что-то кроме a-z, 0-9 и "_", то нужно это заменить (пробелы на подчеркивание, остальное удалить). К примеру "Gold income" из raсe.var нужно писать как "Gold_income".

Value - новое значение. Можно использовать синтаксис Javascript или принятые сокращения. Примеры:
-2 : уменьшить на 2
+2 : увеличить на 2
2 : установить на 2 (абсолютное значение)
+20% : увеличить на 20%
-20% : уменьшить на 20%
Fn.random(1, 5) : случайное число от 1 до 5
(Attack + CounterAttack)*2 + Fn.random(1, 2) : формула с использованием других аттрибутов, и функции random

Condition - условие (опционально, можно убрать всю строку). Можно использовать синтаксис Javascript.
Примеры:
Life>10
ShootingRange>=4 or UnitClass!=2
contains([9, 14, 123], Identifier) : идентификатор входит в указаный список
contains([1, 2], UnitClass) and (Speed>2 or Level!=2) : более сложное условие, со скобками

Номер элемента в var-е называется Identifier (число, которое стоит после "/"), так что если мы хотим поменять что-то скажем у Пегаса, то делаем условие "Identifier=24"

Формулы

Во многих местах в файлах миров используются формулы (к примеру в Condition).
Эти формулы основаны на Javascript и используют синтаксис этого языка.
Вот тут есть справочник.

Есть следующие отличия:

Встроенные формулы

В теле фрагментов и в некоторых других местах можно использовать так называемые встроенные формулы. Такие формулы заключены в фигурные скобки, к примеру {Fn.random([92, 170])}. В этих формулах нет доступа к атрибутам и спискам, но можно к примеру сделать что-нибудь в этом роде: {LastId_unit+1}

Дополнительные функции

Сразу на примерах:

Регулярные выражения в Java

Функции списков

Эти функции используются только в модификациях списков (смотри ниже) и с префиксом в виде названия списка (к примеру Effects.containsAttr(...) или Abilities.anyIn(...)).

Фрагменты (Snippets)

После всех модификаций находится опциональный раздел "snippets". Он содержит фрагменты текста/варов, которые можно использовать в некоторых модификациях, задав в атрибуте "Snippet" имя соответствующего фрагмента.

Пример из мира трупоедов:

/
Filename: unit.var
Action: add
ListName: Abilities
Snippet: cannibal_ability_5
Condition: Identifier>0 and !Abilities.anyIn('Value', [79, 535]) and Level<3

/
Filename: unit.var
Action: add
ListName: Abilities
Snippet: cannibal_ability_10
Condition: Identifier>0 and !Abilities.anyIn('Value', [79, 535]) and Level>2

=== snippets
-- cannibal_ability_5
Cannibalise: 535
-- cannibal_ability_10
Cannibalise: 79

Строка "=== snippets" обозначает начало раздела фрагментов (должна идти после всех модификаций и только один раз).
Каждый фрагмент начинается с "-- " и дальше идет его название, которое можно использовать в модификациях.
"Тело" фрагмента начинается со следующей строки и идет до следующего фрагмента/конца файла.
При написании фрагментов, учитывайте, что они копируются "как есть", то есть с пустыми строками или без них, если они не заданы. Там где нужно, необходимо добавить в начале пустую строку.

В теле фрагментов можно использовать встроенные формулы, к примеру {Fn.random([92, 170])} (смотри Встроенные формулы)

Списки

Под списками подразумеваются списки элементов, в которых каждый элемент состоит из одного или больше атрибута (примеры списков: эффекты, способности, виды местности и т.п.).

Вот, к примеру, список способностей из unit.var:

Abilityes:
Lesser_Undead: 61
Skeletal: 576
Round_attack: 108;

В данном случае каждый элемент состоит лишь из одной строки/одного атрибута.

Другой пример:

Effects:
Attack: 2
Power: 2
Duration: 5

CounterAttack: 3
Power: 2
Duration: 5

Тут уже каждый элемент состоит из нескольких атрибутов.

Названия списков

Answers, Effects, Abilities, Units, Upgrades, Loot, Guards, Events, ResourceBoni, Objectives, Hero_Upgrades, Squad_Upgrades, Terrains, Talents, Items.

Hero_Upgrades и Squad_Upgrades: skill.var
Objectives: quest.var
ResourceBoni: outer_build.var
Talents: hero_class.var
Остальные встречаются как правило в нескольких варах и понять где какой там не сложно.

Действия со списками

Добавление элемента списка

/
Filename: guard.var
Action: add
ListName: Units
Snippet: new_guard_unit_1
Condition: Identifier=18

...

=== snippets
-- new_guard_unit_1
Unit6: {Fn.random([92, 170])}, 3, 1, 0

Action: действие ("add" означает, что мы добавляем элемент к списку).
В ListName задано название списка.
В Snippet указано название добавляемого фрагмента.
Condition - условие, которое может содержать специальные функции списков (смотри Функции списков).

Удаление элементов списка

/
Filename: site.var
Action: remove
ListName: Guards
Condition: contains([100, 101, 102], $.Guard)

Action: действие ("remove" означает, что мы удаляем элементы из списка).
В ListName задано название списка.
Condition - условие, которое может содержать специальные функции списков (смотри Функции списков).

$.Guard означает атрибут "Guard" сравниваемого элемента списка (в данном случае списка "Guards")

Добавление нового контента

Добавление записей в var-файл

/
Filename: unit_upg.var
Action: add_entry
Snippet: unit_upg_1

...

=== snippets
-- unit_upg_1

/{LastId_unit_upg+1}
Name: Посланник смерти;
...

Action: действие ("add_entry" означает добавление записи в var-файл)
Snippet: название фрагмента для добавления
В теле фрагмента вместо идентификатора мы используем константу с последним идентификатором и добавляем +1. Так мы можем не привязываться к конкретному моду и если количество записей изменится, не нужно будет подстраивать мир. При добавлении нескольких записей в один и тот же var можно продолжать увеличивать идентификатор: {LastId_unit_upg+2}, {LastId_unit_upg+3} и т.д.

Добавление записей в txt-файл

/
Filename: Site.txt
Action: add_text
Snippet: text_site_1

...

=== snippets
-- text_site_1

{LastId_site+1}. Кладбище
...

Action: действие ("add_text" означает добавление записи в txt-файл)
Snippet: название фрагмента для добавления

Добавление картинок в dat-файл

/
Filename: Units.dat
Action: add_image
ImageFile: envoy_of_death.bmp
ImageName: Unit{Fn.leftZeroPad(LastId_unit+1, 3)}

Action: действие ("add_image" означает добавление картинки в dat-файл)
ImageFile: имя файла с картинкой (сама картинка должна лежать в /worlds/images/ в подкаталоге с тем же именем, что и идентификатор мира, к примеру в /worlds/images/undead_v1.0.2)
ImageName: Название картинки внутри dat-файла (как видно на примере, можно использовать встроенные формулы)

При добавлении юнитов необходимо помнить, что нужно несколько картинок (для Units.dat, Unit_icons.dat, Unit_shadow.dat и Unit_shadowf.dat)

Добавление звуков в dat-файл

/
Filename: unit_sound.dat
Action: add_sound
SoundFile: new_unit_sound.ogg
SoundName: S{Fn.leftZeroPad(LastDatId_unit_sound+1, 3)}

Action: действие ("add_sound" означает добавление звука в dat-файл)
SoundFile: имя файла со звуком (сама картинка должна лежать в /worlds/images/ в подкаталоге с тем же именем, что и идентификатор мира, к примеру в /worlds/images/undead_v1.0.2)
SoundName: Название звука внутри dat-файла (как видно на примере, можно использовать встроенные формулы)

Звуки должны быть в формате OGG.

Замена контента

Замена записей в var-файле

/
Filename: unit.var
Action: replace_entry
Snippet: units

...

=== snippets
-- units

/31
Name: Гарпия;
...

/32
Name: Василиск;
...

Action: действие ("replace_entry" означает замену записей в var-файле)
Snippet: название фрагмента для добавления
В теле фрагмента могут быть несколько записей, они будут автоматически найдены через ID и заменены в соответствующем var-файле.
Обычно модификации проще делать через изменения конкретных атрибутов, элементов списка и т.д. (см. выше), но если мы меняем очень многое, то бывает проще заменить всю запись целиком и для этого и существует данный тип действия.

Замена записей в txt-файле

/
Filename: Defender.txt
Action: replace_text
Snippet: defender_texts
...

=== snippets
-- defender_texts
2. Шайка воров
#...#

3. Племя
#...#
...

Action: действие ("replace_text" означает замену записей в txt-файле)
Snippet: название фрагмента для добавления
В теле фрагмента могут быть несколько записей, они будут автоматически найдены через ID и заменены в соответствующем txt-файле.

Замена картинок в dat-файле

/
Filename: PortraitsMen.dat
Action: replace_image
ImageFile: normal/000Warrior.bmp
ImageName: 000Warrior

Action: действие ("replace_image" означает замену картинки в dat-файле)
ImageFile: имя файла с картинкой (сама картинка должна лежать в /worlds/images/ в подкаталоге с тем же именем, что и идентификатор мира, к примеру в /worlds/images/portraits_men_v1)
ImageName: Название картинки внутри dat-файла

Замена звуков в dat-файле

/
Filename: unit_sound.dat
Action: replace_sound
SoundFile: phoenix_attack.ogg
SoundName: S165

Action: действие ("replace_sound" означает замену звука в dat-файле)
SoundFile: имя файла со звуком (сама картинка должна лежать в /worlds/images/ в подкаталоге с тем же именем, что и идентификатор мира, к примеру в /worlds/images/undead_v1.0.2)
SoundName: Название звука внутри dat-файла (как видно на примере, можно использовать встроенные формулы)

Звуки должны быть в формате OGG. Заменять звуки можно только в spell_sound.dat и unit_sound.dat.

Комментарии

Строка, начинающаяся с "§", является комментарием.

На русской клавиатуре, к сожалению, нет этого символа, поэтому пока что можно либо копировать из имеющихся миров, либо использовать следующую комбинацию клавиш: Зажав "Alt", набрать 0167, отпустить "Alt".

Рекомендации