Голосование

Как вы оцениваете Смуту?












Оформление



Пользователей
  • Всего: 28884
  • Последний: Menfred
Сейчас на форуме
Пользователи: 0
Гостей: 621
Всего: 621

Просмотр сообщений

В этом разделе можно просмотреть все сообщения, сделанные этим пользователем.


Темы - Негодяй

Страницы: [1]
1
   Девятая часть.
  Следующим файлом, требующим нашего внимания, станет module_game_menus.py. Он содержит все игровые меню МиБа. Игровые меню служат для интеракции между игроком и возникающей сценой. Кроме того игровые меню представляют из себя достаточно гибкий инсрумент, способный послужить в самым целям. Внутригровая система переходов также использует игровые меню для реализации своих функций.


9.1 Файл module_game_menus.py.

Файл начинается с python-овского списка, затем описаны два меню, используемых при создании персонажа, в частности выбор пола персонажа и выбор класса персонажа. Для нас это может оказаться пока сложновато для первого знакомства, поэтому для примера мы поищем что-нибудь полегче.

Пример игрового меню :
(прим: У него талант выбирать примеры, которых нет в оригинальной версии МиБа. Этот не исключение)
(прим2: зато есть в Варбанде)

    (
    "salt_mine",mnf_auto_enter,
    "You enter the salt mine.",
    "none",
    [(reset_price_rates,0),(set_price_rate_for_item,"itm_salt",55)],
    [
      ("enter",[],"Enter.",[[set_jump_mission,"mt_visit_town_horseback"],[jump_to_scene,"scn_salt_mine"],[change_screen_mission]]),
      ("leave",[],"Leave.",[[leave_encounter],[change_screen_return]]),
    ]
  ),



Это меню появляется при входе в соляные шахты. Оно очень простое, обладает меткой mnf_auto_enter. Любое меню с такой меткой автоматически активирует первую опцию меню, для всех игровых ситуаций, не ожидая выбора игрока. В случае соляных шахт, этой опцией является  “войти”.


Описание полей записи игрового меню.

1) Идентификатор игрового меню. Используется для ссылки на игровое меню.
2) Метки игрового меню.
3) Текст игрового меню. Это текст, который игрок увидит в игровом меню в кнопке.
4) Идентификатор 3D модели. В настоящий момент не используется, потому в таких полях везде должно стоять значение "none".
5) Блок операций. Блок операций, выполняющихся когда активируется меню.
6) Список опций меню. Каждая опция игрового меню, состоит из следующих элементов:
             6.1) Идентификатор опции меню. Используется для ссылок на опцию игрового меню из других файлов.
             6.2) Блок условий. Условия, оговоривающие когда будет выведенна на экран данная опция игрового меню.
             6.3) Текст игровго меню. Кликабельный текст опций игрового меню. Подстановка подчёркивающей линии ("_") - изящный трюк для того, чтобы сделать кнопку некликабельной.
             6.4) Блок последствий. Блок последствий, активируемый при выборе данной опции игрового меню.


Разбор записи меню соляных шахт.

1) Идентификатор игрового меню = "salt_mine"
2) Метки игрового меню = mnf_auto_enter
2) Текст игрового меню = "You enter the salt mine."
3) Идентификатор 3D модели = "none"
4) Блок операций = (reset_price_rates,0),(set_price_rate_for_item,"itm_salt",55)
5) Список опций меню
             6.1) Идентификатор опции меню = "enter", "leave"
             6.2) Блок условий = нет условий
             6.3) Текст игровго меню = "Enter.", "Leave."
             6.4) Блок последствий = [set_jump_mission,"mt_visit_town_horseback"],[jump_to_scene,"scn_salt_mine"],[change_screen_mission], [leave_encounter],[change_screen_return]
                                                                                   # в Варбанде такой метки нет
                                                                                   # вместо неё mt_town_center


Первое, что делает это меню – сброс всех торговых ставок на 0 (не обнуление цен, а сброс именно наценки торговца и установление настоящей цены вещам) и затем установка цены соли в 55% от обычной (то есть соль будет продаваться не за 100 динаров, а за 55). Следущее действие, реализованнное с помощью метки mnf_auto_enter, это то, что меню автоматически, за игрока, выберет первую (и единственную) опцию в меню. В данном случае ("enter") и выполнит ее, переместив игрока в солянные шахты, не ожидая клика мышки.


В следуещем разделе мы используем соляные шахты, как шаблон для меню города, который мы создали во 2 части, таким образом завершив его создание.

2
  Восьмая часть
  Теперь, когда мы приобрели довольно значительный опыт в работе с модульной системой, мы можем перейти к разбору более сложных компонентов - триггеров. Триггеры это блоки операций, активируемые по прошествию определенного отрезка времени или при наличии специфической игровой ситуации.


8.1 Файл module_simple_triggers.py.

В настоящий момент файл module_simple_triggers.py выполняет только одну фунцию: при столконовении игрока с другими отрядами на глобальной карте, направить его к соответствующему меню. (прим: это неправда. По видимому, автор ссылается на файл из устаревшей версии МиБа (ниже 1.0))  Для реализации этого эффекта используется python-овский список, состоящий из всего одной, но действительно длинной записи:

  (ti_on_party_encounter,
   [
       (store_encountered_party, reg(1)),
       (store_encountered_party2,reg(2)), # encountered_party2 is set when we come across a battle or siege, otherwise it's a minus value
       (try_begin),
         (lt, reg(2),0), #Normal encounter. Not battle or siege.
         (try_begin),
           (ge, reg(1), towns_begin),
           (lt, reg(1), towns_end),
           (jump_to_menu, "mnu_town"),
         (else_try),
         (else_try),
           (ge, reg(1), castles_begin),
           (lt, reg(1), castles_end),
           (jump_to_menu, "mnu_castle"),
         (else_try),
           (eq, reg(1), "p_zendar"),
           (jump_to_menu, "mnu_zendar"),
         (else_try),
           (eq, reg(1), "p_salt_mine"),
           (jump_to_menu, "mnu_salt_mine"),
         (else_try),
           (eq, reg(1), "p_four_ways_inn"),
           (jump_to_menu, "mnu_four_ways_inn"),
         (else_try),
           (eq, reg(1), "p_dhorak_keep"),
           (jump_to_menu, "mnu_dhorak_keep"),
         (else_try),
           (eq, reg(1), "p_training_ground"),
           (jump_to_menu, "mnu_training_ground"),
         (else_try),
           (store_current_hours, reg(7)),
           (le, reg(7), "$defended_until_time"),
           (assign, "$defended_until_time", 0),
           (jump_to_menu, "mnu_in_castle_under_attack"),
         (end_try),
       (else_try), #Battle or siege
       (try_end)
    ]),



Этот список состоит из одного единственного триггера. Запись триггера имеет очень простую конструкцию, состоящую из двух полей.

Разбор записи простого триггера:

1) Интервал проверки. Задает как часто или в каких случаях будет проверяться триггер.
2) Блок операций. Для подробного разбора видов операций смотри файл header_operations.py


Разбор записи триггера из примера, приведенного выше:

1) Интервал проверки = ti_on_party_encounter.
2) Блок оперции. Мы разберем его чуть ниже.

Значение ti_on_party_encounter в поле интервала проверки означает, что триггер будет проверен всякий раз, когда игрок встретит другой отряд на глобальной карте. Рассмотрим по очереди каждую секцию блока операций данного триггера:


Секция 1:

       (store_encountered_party, reg(1)),
       (store_encountered_party2,reg(2)), # encountered_party2 is set when we come across a battle or siege, otherwise it's a minus value

       # encountered_party2 – задается, когда игровая ситуация соответствует осаде или сражению в поле, иначе обладает отрицательным значением

1) (store_encountered_party, reg(1)),
Во-первых, индикатор еncountered_party - это индикатор подобный encountered_party2. Он хранит в себе идентификатор отряда, который встретил игрок. Второе, store_ + индикатор  encountered_party говорит системе: “помести значение еncountered_party в <регистре или переменной, что будет указана после запятой>”.  В данном случае это reg(1)
2) (store_encountered_party2,reg(2)),
Делает почти то же самое. Говорит системе взять значение из индикатора encountered_party2 и поместить его в <регистр после запятой>,  в данном случае в reg(2). Но используется только в том случае, если вы встретили битву либо осаду, то есть в регистры будут записаны идентификаторы нападающего и защищающегося отрядов.


Секция 2:

       (try_begin),
         (lt, reg(2),0), #Normal encounter. Not battle or siege.

         #Нормальное столкновение. Ни битва ни осада.
         # lt - "lower than" - "меньше чем"

Выражение (try_begin) - это операция, открывающая блок ветвления. Блок ветвления является обычным блоком операций, но с одним отличием – если за выражением (try_begin) следуют операции условия и если хотя бы одно из них не удовлетворяется, то система прерывает обработку блока ветвления. Т.е. система переходит к выполнению операции, стоящей после выражения (try_end), закрывающего блок ветвления. Если мы имеем дело с  блоками ветвления, вложенными один в другой, то следует учитывать, что система перебирает выражение за выражением, пропуская встреченные вложенные блоки ветвления, пока не находит соответствующий (try_end) (либо выражение (else_try), но о нем позже) и возобновляет выполнение кода сразу после него.

Выражение (lt, reg(2),0) это условие, требующие чтобы значение регистра “reg(2)” было меньше нуля: (lt, “первое значение”, “второе значение”) ==  “первое значение” < “второго значения”. Как было сказано выше, значение регистра всегда будет меньше нуля, если игрок не столкнулся с осадой или с полевым сражением. Таким образом, если значение в регистре reg(2) меньше нуля, система перейдет к выполнению операций секции 3.  Если это не так, то система прекратит выполнение блока ветвления, и  возобновит работу с операции, расположенной сразу после ближайщего выражения (try_end).

Кроме того, существует операция (else_try). Она входит в состав блока ветвления (т.е. находится между выражениями (try_begin) и (try_end)) и открывает блок операций, который будет обработан лишь в том случае, если требования  других активных операций, находящихся перед (else_try), не были удовлетворенны. Это также касается и других блоков (else_try), если таковые находятся выше. Другими словами, если в блок ветвления входят сразу несколько блоков (else_try), выполнен будет лищь один из них. Это как максимум, а как минимум – ни один.


Секция 3:

         (try_begin),
           (ge, reg(1), towns_begin),
           (lt, reg(1), towns_end),   
           (jump_to_menu, "mnu_town"),


           # В операторах сравнения и рассчёта главное не путаться:
           # ge - "greater or equal" - "больше или равно"
           # gt - "greater than" - "больше чем"
           # le - "lower or equal" - "меньше или равно"
           # lt - "lower than" - "меньше чем"


Эта секция начинается с выражения открывающего блок ветвления, расположенного внутри предыдущего блока ветвления (см. Секция 2). Секция будет обработана если значение регистра “reg(1)” -- встреченного игроком отряда -- находится между значениями констнат towns_begin (идентификатор “p_town_1” из файла module_parties.py) и towns_end ("p_salt_mine"). Если эти условия выполняются, игрок будет направлен к меню города "mnu_town". Если нет, то система прервет обработку этой части блока (try_begin), и перейдёт к секции 4.


Секция 4:

         (else_try),
           (ge, reg(1), castles_begin),
           (lt, reg(1), castles_end),
           (jump_to_menu, "mnu_castle"),


Секция 4 будет расмотренна лишь в случае, если условия  предыдущей секции не удовлетворены. Иными словами, она будет рассмотренна лишь в случае, если значение в reg(1) не соответствует идентификатору города. Таким образом придя к выводу, что встреченный игроком отряд это не город, в этой секциии система проверит соответствие значения регистра идентификатору замка. Он должен лежать в промежутке ограниченном константами castles_begin ("p_castle_1") и castles_end  ("p_river_pirate_spawn_point"). Если проверка успешна, игрок будет направлен в меню замка "mnu_castle". Если нет, система перейдет к обработке секции 5. Все это реализованно теми же самыми операциями, что и в секции 3.


Секция 5:

         (else_try),
           (eq, reg(1), "p_zendar"),
           (jump_to_menu, "mnu_zendar"),


Прийдя к выводу, что это и не город и не замок, в этой секции проверяется возможность столкновения игрока с отрядом, определяющимся идентификатором "p_zendar". Если проверка завершается успешно, игрок будет направлен в меню Зендара "mnu_zendar". Если нет, система перейдет к обработке секции 6. Все это реализованно с помощью операции eq.


Секция 6:

         (else_try),
           (eq, reg(1), "p_salt_mine"),
           (jump_to_menu, "mnu_salt_mine"),


Проверка на соответствие между идентификатором встреченной группы и  соляными шахтами  - "p_salt_mine" .


Секция 7:

         (else_try),
           (eq, reg(1), "p_four_ways_inn"),
           (jump_to_menu, "mnu_four_ways_inn"),


Проверка на соответствие между идентификатором встреченной группы и таверной четыре дороги  - "p_four_ways_inn".


Секция 8:

         (else_try),
           (eq, reg(1), "p_dhorak_keep"),
           (jump_to_menu, "mnu_dhorak_keep"),


Проверка на соответствие между идентификатором встреченной группы и усадьбой Дхорак  - "p_dhorak_keep".


Секция 9:

         (else_try),
           (eq, reg(1), "p_training_ground"),
           (jump_to_menu, "mnu_training_ground"),


Проверка на соответствие между идентификатором встреченной группы и учебным лагерем  - "p_training_ground".


Секция 10:

         (else_try),
           (store_current_hours, reg(7)),
           (le, reg(7), "$defended_until_time"),
           (assign, "$defended_until_time", 0),
           (jump_to_menu, "mnu_in_castle_under_attack"),


В этой секции количество часов, прошедших с момента начала игры, помещается в регистр "reg(7)" (операция (store_current_hours, reg(7))). Затем значение регистра "reg(7)" сравнивается со значением переменной "$defended_until_time", значение регистра должно быть меньше либо равно $defended_until_time (операция (le, reg(7), "$defended_until_time")). Если это так, система присвоит значение "$defended_until_time" равным нулю (операция (assign, "$defended_until_time", 0)) и направит игрока в меню "замок под атакой" -- "mnu_in_castle_under_attack". Если же проверка не пройдена, система приступит к обработке секции 11. (прим: скорее всего эта секция используется как часть скрипта "замок в осаде". Из-за того, что триггер относится к устаревшей версии МиБа, у меня нет возможности сказать что-то более конкретное.)


Секция 11:
         (end_try),

Сигнализирует системе о завершение второго, вложенного оператора ветвления, см. секция 3.


Секция 12:

       (else_try), #Battle or siege (битва или осада)
       (try_end)


Эта последняя операция (else_try) из блока ветвления нашего триггера. Она будет обработанна если игрок сошелся в бою с другой группой. Так как блок не включает в себя никаких других операций, он завершится боевым столкновением (а не меню) -- в точности как в случае с городом "new_town", который мы создали во второй части документации. Он все еще не имеет операций привязывающих его к собственному меню. Мы исправим это в следущем разделе.


8.2 Редактирование триггера столкновенний групп.
При создании нашего нового города мы должны побеспокоиться о том, чтобы любые столкновения с ним на глобальной карте перенаправлялись в соответствующие меню. Это оставляет нам два варианта: либо привязывать новые объекты к одному из существующих нативовских меню (стандартное mnu_town например), либо создать свое собственное меню.

Для нашего  new_town мы создадим свое уникальное меню, но позже. Сейчас мы разберем, что потребуется сделать для привязки нового объекта на карте к стандартному нативовскому меню города.


Во-первых, любые дальнейшие изменения в триггере столкновений отрядов следует добавлять перед секцией 10. Если вы добавите любые данные после нее, то создадите проблеммы при обработке столкновения с отрядом в полночь. Чтобы этого не произошло, мы добавим наши новые данные перед этой секцией, и сразу после секции 9.

Создайте немного пространства между секциями 9 и 10:

         (else_try),
           (eq, reg(1), "p_training_ground"),
           (jump_to_menu, "mnu_training_ground"),

         # Здесь мы сделаем новую секцию
         (else_try),
           (store_current_hours, reg(7)),
           (le, reg(7), "$defended_until_time"),
           (assign, "$defended_until_time", 0),
           (jump_to_menu, "mnu_in_castle_under_attack"),



Любые новые добавления в этот триггер должны начинаться с (else_try). После else_try, мы добавим операции условия -- когда наш блок должен сработать, а когда нет. Затем, когда все условия определены и записаны, мы должны добавить операцию последствия, такую как например jump_to_menu – операцию, перенаправляющую в меню. Операции последствия могут быть различны и не ограничиваться одним лишь jump_to_menu.

Скопируйте и вставьте секцию 9 на освобожденное для этого место. Затем, замените "p_training_ground" на "p_new_town" и "mnu_training_ground" на "mnu_new_town". Плов готов.

Для того чтобы сделать наш город функциональным, нам остается закончить только одну деталь – создать его меню. Но прежде чем мы перейдем к изучению файла module_game_menus.py, гораздо разумней будет закончить с триггерами. Окройте файл module_triggers.py и мы переходим к следующему разделу.


8.3 Файл module_triggers.py.

В модульной системе МиБа существует два типа триггеров: простые триггеры, они записаны в module_simple_triggers.py, и расширенные триггеры, записанные в module_triggers.py. Расширенные триггеры работают по тому же принципу, но имеют несколько больше возможностей.


  (0.1, 0, ti_once, [(map_free,0)], [(tutorial_box,"str_tutorial_map1")]),

Это первый триггер из этого файла, и он прост и приятен. Когда игрок появляется в первый раз на глобальной карте, этот триггер вызывает туторитал по глобальной карте.


Разбор записи обычного (не простого) триггера:

1) Интервал проверки. Как часто будет проверен триггер.
2) Интервал задержки. Сколько пройдет времени, прежде чем будут примененны последствия триггера (если разумеется, до этого все проверки блока условий были проведенны успешно).
3) Интервал перезарядки. Сколько времени должно пройти, прежде чем триггер будет готов к повторной ативации.
4) Блок условий. Это должен быть блок операций. Если этот блок пуст, то значит, что проверка условий проходит вседа успешно.
5) Блок последствий. Должен быть блоком операций.


Разбор полей триггера вызывающего окно туторитала по карте.

1) Интервал проверки = 0.1
2) Интервал задержки = 0
3) Интервал перезарядки = ti_once
4) Блок условий = (map_free,0)
5) Блок последствий = (tutorial_box,"str_tutorial_map1")


Как видно из примера, триггер проверяется каждые 0.1 часа внутригрового времени; он не имеет интервала задержки; он никогда не появится снова -- реализованно с помощью метки ti_once. Из блока условий следует, что триггер сработает если игрок будет находится на глобальной карте, неважно где именно. Таким образом, окно туториала появится на экране всего один раз - в начале игры.


Теперь мы создадим наш собственный триггер. Скопируйте триггер, вызывающий туторитал по глобальной карте, и вставьте в конец python-овского списка.

Наш новый триггер должен инициировать появление отряда банды Джоффри на глобальной карте. Замените (map_free,0) в блоке условий триггера на следущие операции:

(eq,"$geoffrey_duel",1),
(store_time_of_day,reg(1)),
(gt,reg(1),19),


Если вы помните, в седьмой части этой документации, после того как Джоффри вызывает игрока на дуэль, мы присваивали переменной $geoffrey_duel" значение 1. Таким образом операция (eq,"$geoffrey_duel",1), проверяет вызвал ли Джоффри игрока на дуэль. Если условие не удовлетворено, проверка заканчивается неуспешно.

Следующие две операции сохраняют текущее дневное внутригровое время в регистр reg(1), и затем проверяют его значение. Если оно больше 19:00, то проверка блока условий завершается успешно и триггер срабатывает. Банда Джоффри "Красные Всадники" появится на карте между 19:00 и 00:00.


Теперь заменим (tutorial_box,"str_tutorial_map1") в блоке последствий на следующий блок операций:

(set_spawn_radius,0),
(spawn_around_party,"p_zendar","pt_red_riders"),
(assign,"$geoffrey_party_id",reg(0)),
(party_set_ai_behavior,"$geoffrey_party_id",ai_bhvr_track_party),
(party_set_ai_object,"$geoffrey_party_id","p_main_party"),


Первая операция устанавливает радиус появления. Перед тем, как инициировать появление нового отряда, вам следует его указать, иначе будет использован последний заданный радиус появления, что представляет из себя весьма непредсказуемую величину.

Вторая операция инициирует появление отряда, генерируемого шаблоном "pt_red_riders" около отряда "p_zendar". Кроме того, в результате этой операции, в регистр reg(0) помещается идентификатор вызванного отряда. Чтобы он не потерялся при следующем изменении reg(0), мы сохраним его значение в пременную "$geoffrey_party_id". (операция (assign,"$geoffrey_party_id",reg(0))).

Ну и, наконец, мы установим поведение ИИ отряда и зададим объект-цель для ИИ. Мы реализуем это с помощью очень простых операций: party_set_ai_behavior и party_set_ai_object. В первой мы задаем для группы Джоффри тип поведения  ai_bhvr_track_party (преследовать отряд). А во второй задаем объектом-целью отряд игрока -- "p_main_party". Когда вы задаете ИИ отряду установку преследовать другой отряд --  ai_bhvr_track_party –- это заставит отряд преследовать и атаковать свою цель, даже несмотря на отношения их фракций.

Сохраните ваш прогресс. Мы еще не закончили, т.к. не создали меню для к нашего нового города new_town. Мы сделаем это в следующей, девятой части документации.

3
   Седьмая часть.

 В этой части мы разберем файл module_dialogs.py, самый большой файл в модульной системе и один из самых важных, поскольку содержит все диалоги МиБа. Любой новый диалог, которой вы создадите войдет в этот файл. Также файл содержит специальные служебные диалоги, их используют войска, не (совсем) обладющие собственными диалогами.


7.1 Разбор файла  module_dialogs.py.

Файл начинается со списка dialogs = [, он сопровожден первой частью диалога из игры – заявления констебля Харека касательно речных пиратов (прим: как уже неоднократно упоминалось, документация устарела. Никакого констебля Харека Вы не найдете.) (прим 2: в модульной системе Warband такого диалога нет вообще, но как пример имеет шансы на жизнь) Важный факт - система выбирает первую строку из файла module_dialogs.py и проверяет относительно каждой детали, составляющей текущую игровую ситуацию, надо ли выводить эту строку на экран. Если да, то выводит ее, если нет, то двигается дальше, ко второй строчке. Т.е. если есть две линии диалога, соответствующие игровой ситуации, приоритет получит та, что расположенна выше. Далее по тексту, вы найдете более подробное объяснение.

По сути, в этом файле описаны не диалоги как таковые, а построение диалогов из отдельных реплик. Каждая реплика диалога это отдельная запись, и эти записи связываются в привычные нам диалоги при помощи особых идентификаторов - статусов реплик.


Пример записи реплики:

  [trp_constable_hareck,"start", [], "What do you want?", "constable_hareck_talk",[]],

Это простая запись, она хорошо послужит нам в качестве примера. Она (запись) делает все, что должна делать реплика; при обращении к констеблю Хареку внутри сцены, она выводит на экран текст “What do you want?” (“Что ты хочешь?”),  а затем, после клика левой кнопкой мышки, выводит стартовое окно диалога "constable_hareck_talk".


Разбор записи реплики:

1) Идентификатор собеседника. Конкретная персона или отряд, которые игрок должен встретить для начала диалога.
2) Статус вызова реплики. Определяет когда/где текст будет выведен на экран.
3) Блок условий. Блок дополнительных условий (помимо статуса вызова), который должен быть выполнен, для вывода текста на экран. Это должен быть блок операций.
4) Текст диалога. Текст, который появится в игре.
5) Статус завершения реплики. Определяет, что случится после вывода этого текста.
6) Блок последствий. Операции, которые произведутся после того, как щелкнете мышкой. Это должен быть блок операций.


Разбор записи констебля Харека

1) Идентификатор собеседника = trp_constable_hareck
2) Статус вызова реплики = "start"
3) Блок условий = []
4) Текст диалога = "What do you want?"
5) Статус завершения реплики = "constable_hareck_talk"
6) Блок последствий = []


Важная деталь, которую стоит отметить - статусы реплики. Здесь мы копнем чуть глубже.

Статус завершения реплики ("constable_hareck_talk") это то, что ведет переход от одной реплики к другой (от записи к записи). Статус завершения диалога может быть всем, чем захотите -- но обязана существовать другая запись с соответсвующим, парным статусом вызова реплики. Для примера, если мы сделали запись, имеющую статус завершения "blue_oyster", это отправит нас к записи со статусом вызова "blue_oyster".  Обязано выдерживаться строгое соответветсвие; если соответствующей пары нету, то build_module.bat выдаст ошибку при попытке собрать мод.

Если есть несколько записей, имеющих статус вызова "blue_oyster", то случится нечто особенное. Если эти записи - реплики произносимые игроком, они суммируются в виде меню, где игрок сможет выбрать одну из предлагаемых реплик. Если произносятся НПС, модульная система будет использовать первую запись, которую найдет удовлетворяющей условию (а если конкретно, запись расположенную ближе к верхушке файла).

Для остановки разговора вы должны использовать статус завершения "close_window".


Для начала разговора существует несколько специальных статусов вызова:

 - "start" -- применяется когда беседа инициирована внутри сцены (Разговор с НПС).
 - "party_encounter" -- применяется в случае столкновения с другим отрядом на глобальной карте .
 - “party_relieved" -- применяется если игрок окажет помощь отряду ведущему битву, сражение должно быть выигранно.
 - "prisoner_liberated" -- применяется когда игрок победил вражеский отряд,  держащий в плену одного или более hero (диалог с освобождёнными hero). (прим: метка "hero" описывалась в предыдущих частях)
 - "enemy_defeated"  -- применяется когда игрок победил вражеский отряд под предводительством типа hero (диалог с пленёнными hero).
 - "event_triggered" – применяется когда диалог инициирован какой либо операцией, но не внутри сцены.
 - "member_chat" -- применяется когда разговариваешь с членами своей группы.
 - "prisoner_chat" -- применяется когда игрок разговаривает с пленными своей группы.

Каждый статус вызова реплики оформлен для специфической ситуации. Запись реплики не будет рассмотрена системой если нет ситуации, соответствующей ее (записи) статусу вызова.


Т.к. разобранная нами запись инициируется обращением к констеблю Хареку, она обладает статусом вызова "start".


7.2. Блоки условий и последствий.

Диалоговый интерфейс очень гибок, есть множество путей его применения. Он позволяет создать диалог в любом месте, где только захотите.

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

Как мы подчеркнули в предыдущем разделе, реплика будет выведена на экран, только если все требования удовлетворенны.

Во-первых, игрок должен разговаривать с нужным персонажем. Реплика с trp_constable_hareck не будет выведена на экран если игрок обращается, скажем, к trp_ramun_the_slave_trader (Рамуну работорговцу). А вот константа anyone может быть использована для реплики, которая должна появляться всякий раз, когда игрок обращается к кому-либо.

Второе, статус вызова реплики должен быть в соответствие с ситуацией -- или реплика будет инициирована одним из статусов вызова (таким как start), или же реплика последует в соответствии со статусом завершения предыдущей реплики. Если статус вызова реплики не имеет спецификации (в поле статуса вызова ничего нет), она (реплика) не будет использоваться нигде.

Втретьих, похожая логика действует и на блок условий. Пока все из перечисленных в записи условий реплики не будут удовлетворенны, реплика не будет рассмотрена системой как вариант для вывода на экран. И если блок условий или статус вызова реплики ошибочны, у вас возникнут проблемы; одно из двух – либо build_module.bat выдаст ошибку, либо реплика просто повиснет по ходу игры, так как система не будет в состоянии найти следущую запись для активации.

По этой причине будьте осторожны с блоком условий. Убедитесь что вы не сломали ваш диалог “вживив” в него блок условий с неточными установками.


Если все работает гармонично, блок условий может стать мощным инструментом в ваших руках. В него можно включить блоки ветвления (try blocks): внутри блока условий можно описать операцию, и использовать её результат в качестве условия для вывода реплики (всё это будет в том же блоке условий). Для этого можете задавать обычные и строковые регистры, чтобы использовать их в качестве параметра для операции внутри блока условий данной записи реплики. Это не так сложно как может показаться из вышенаписанного, и всё это мы с вами сейчас по очереди рассмотрим и опробуем.


Изучим использование блока условий внутри записи из самого начала module_dialogs.py:

  [trp_constable_hareck,"start", [(eq,"$constable_announcement",0)]
  # eq - "equal" - "равно"

Здесь есть лишь одно условие (операция eq), требующее чтобы переменная "$constable_announcement" была равна 0. Смысл в том, что все регистры и переменные в начале игры равны нулю. Если вы посмотрите на окончание этой записи (на блок последствий), то заметите следующее:

  "constable_hareck_introduce_1",[(assign,"$constable_announcement",1)]]

После того, как реплика выведена на экран, операция assign (присвоить) задаёт переменной "$constable_announcement" значение 1. Другими словами, появившись раз, эта реплика никогда не отобразится вновь – после выполнения блока последствий значение перменной "$constable_announcement" уже отличное от 0. Теперь система будет игнорировать эту запись и вместо нее пойдет к следущей записи, расположенной в списке ниже и также удовлетворяющей нужным условиям:

  [trp_constable_hareck,"start", [], "What do you want?", "constable_hareck_talk",[]],

Единственным требованием вывода этой реплики на экран является факт обращения к констеблю Хареку внутри сцены. Если игрок обратится к Хареку, то после того как тот сказал вступительную речь, система всегда выведет эту линию.


7.3 Добавление диалогов.

Наконец у нас появилась возможность сделать Джоффри несколько уникальных реплик и связать их в уникальный диалог. Но, как и в module_troops.py, вы не можете просто добавить новые записи в конец файла. Окончание python-овского списка в module_dialogs.py состоит из служебных реплик, они вызываются по умолчанию для войск и героев, не обладающих своими уникальными репликами. И так как система в поисках соответствия сканирует файл сверху вниз, все реплики, добавленные после служебных, будут проигрорированны.

Рекомендуется добавлять новые реплики сразу после фразы:

# Random quests
######################################################



Начнем с создания вступительной реплики Джоффри. Таким образом, мы сможем набраться опыта, прежде чем приступим к работе с полноценными квестами. 

Скопируйте нижеследующую запись и вставьте в module_dialogs.py в место, рекомендованое выше:

  [trp_geoffrey,"start", [], "What? What do you want? Leave me be, {sir/wench}, I have no time for beggars.", "geoffrey_talk",[]],
  (“Вам что-то надо? Оставьте меня в покое, {сударь/сударыня}, у меня нет времени для попрошаек”)

Это первая реплика нашего диалога. Она активируется с помощью статуса вызова "start" и произносится Джоффри. Приводит к вызову реплики со статусом вызова "geoffrey_talk". Также, система отобразит обращение “сударь” или “сударыня” в зависимости от пола персонажа игрока.

Далее мы сделаем реплику, следущую сразу за первой и произносимую игроком. Скопируйте и вставьте в файл вслед за нашей первой записью следущую:

  [trp_geoffrey|plyr,"geoffrey_talk", [], "Nothing, never you mind.", "close_window",[]],
  (“Ничего особенного, не бери в голову.”

Эта линия диалога будет выведена на экран сразу после первой, и будет произносится игроком. Она обладает статусом завершения диалога "close_window", т.е. прервает беседу сразу после своего вывода, при нажатии левой кнопки мыши. Метка |plyr означает что говорит игрок.


Как мы уже объясняли выше, добавление сразу нескольких записей реплик, произносимых игроком и обладающих одинаковым статусом вызова, приведет к “суммированию” этих записей в виде меню с разными вариантами ответа, ведущими диалог в разные ветки.  Еще раз напоминаю, что это работает лишь с репликами, произносимыми игроком. Скопируйте следущие записи и вставьте их в файл module_dialogs.py:

  [trp_geoffrey|plyr,"geoffrey_talk", [], "And who are you supposed to be?", "geoffrey_talk_2",[]],
  ”И кто же ты такой?
  [trp_geoffrey,"geoffrey_talk_2", [], "Why, I'm Geoffrey Eaglescourt, son of the Baron Eaglescourt! Leader of the Red Riders, bane of bandits, and crusher of pirates!", "geoffrey_talk_3",[]],
  ”Что же, я Джоффри Иглскорт (дословно – орел ощипанный), сын барона Иглскорта! Лидер Красных Всадников, гроза бандитов и сокрушитель пиратов!”
  [trp_geoffrey|plyr,"geoffrey_talk_3", [], "Oh, I see. And how many pirates have you killed?", "geoffrey_talk_4",[]],
  "О да, я вижу. И сколько пиратов сокрушила ваша светлость?"
  [trp_geoffrey,"geoffrey_talk_4", [], "See for yourself! I scalp every one of the dogs I kill. They are my battle trophies.", "geoffrey_talk_5",[]],
  "Смотри сам! Я снимаю скальп с каждого из тех псов, убитых мною. Их скальпы -  мои боевые трофеи.”
  [trp_geoffrey|plyr,"geoffrey_talk_5", [], "That's nice. I'll be going now.", "close_window",[]],
  "Как здорово. Ну я пошел."

Вот все возможные части беседы; небольшая интеракция между игроком и Джоффри. Ничего важного еще не происходит, но сейчас мы добавим несколько вариантов продолжения беседы, повлияющих на продвижение по сюжетной линии квеста. Добавьте две следущие записи:

  [trp_geoffrey|plyr,"geoffrey_talk_5", [(check_quest_active,"qst_speak_with_troublemakers"),(eq,"$geoffrey_duel",0)], "Really? Those scalps look suspiciously like horse tails to me.", "geoffrey_hostile",[]],
  "Правда? А вон тот скальп разве не похож на лошадиную голову?”
  [trp_geoffrey|plyr,"geoffrey_talk", [(check_quest_active,"qst_speak_with_troublemakers"),(eq,"$geoffrey_duel",0)], "You look familiar. Haven't I seen your face in a pigsty before?", "geoffrey_hostile",[]],
  "Ты выглядишь знакомо. Слушай, может я уже видел твою харю в свинарнике?"

Эти две реплики создадут дополнительные варианты ответа для игрока, со статусами вызова "geoffrey_talk_5"  и “geoffrey_talk" соответственно. Техничекси не важно в каком месте файла module_dialogs.py вы вставите записи диалога игрока с одинаковыми статусами вызова. Система найдет их всех и, если требования блока условий были выполенны, добавит в меню, к остальным вариантам ответа. Но лучше все-таки, располагать записи как можно ближе к друг другу, это сделает ваш код читаемым и легкомодифицируемым для вас.

Примечательное свойство наших двух новых линий – это блок условий. Обе реплики появятся только в том случае, если квест "qst_speak_with_troublemakers" активен и значение переменной "$geoffrey_duel" равно 0. Если хоть какое-то из условий не выполняется, реплики не будут отображены. Теперь скопируйте и вставьте последние несколько записей:

  [trp_geoffrey,"geoffrey_hostile", [], "What?! I'll see you dead for that insult, peasant! Don't you know who I am?", "geoffrey_hostile_2",[]],
  "Что?! Ты сдохнешь за это оскорбление, крестьянин! Ты что, не знаешь кто я такой?!”
  [trp_geoffrey|plyr,"geoffrey_hostile_2", [], "No . . . Was I supposed to remember?", "geoffrey_hostile_3",[]],
  (Нет... А я должен знать?)
  [trp_geoffrey,"geoffrey_hostile_3", [], "Why, I'm Geoffrey Eaglescourt, son of the Baron Eaglescourt, and you have delivered the gravest insult to my family's honour! I demand satisfaction! Meet me outside the town walls tonight, or you will be known a coward to every man in this countryside. Good day, {sirrah/wench}.", "geoffrey_hostile_4",[]],
  “Что же, я Джоффри Иглскорт, сын барона Иглскорта, и ты нанес смертельное оскорбление моей фамильной чести! Я требую сатисфакции! Встреть меня сегодня ночью за городскими стенами или каждый человек в окрестностях Зендара будет знать, что ты трус! Доброго дня, {сударь/сударыня}.”
  [trp_geoffrey|plyr,"geoffrey_hostile_4", [], "Charming lad. Tonight, eh . . . ? I shouldn't miss it . . .", "close_window",[(assign,"$geoffrey_duel",1)]],
  "Прелестный пацанчик. Сегодня ночью, значит?... Надо бы не пропустить...”

Этими строками мы закончим наш диалог. Теперь у нас есть беседа с некоторым количеством условий и последствий, непосредственно связанных с квестом. Мы вплотную подошли к записи квестовой линии диалога; но мы еще не прописали условия, позволяющие игроку активировать квест в диалоге с констеблем Хареком. Мы сделаем это в следующем разделе.


7.4. Диалоги и квесты.

Добавляя ветки диалога к уже существующей беседе, мы должны быть очень осторожны, необходимо правильно совместить наши новые реплики с написаными ранее. Помните, что система сканирует файл module_dialogs.py сверху вниз; не забывайте проверять ваш блок условий; следите за правильностью синтаксиса ваших записей. Одно упущение может повлиять на работу всего блока кода. Не ленитесь конструировать ваш модуль заново (щелкать на файл module.bat), это поможет сразу же выявить синтаксические ошибки.

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


В настоящий момент первая из веток диалога Джоффри это:

  [trp_geoffrey,"start", [], "What? What do you want? Leave me be, {sir/wench}, I have no time for beggars.", "geoffrey_talk",[]],
  “Вам что-то надо? Оставьте меня в покое, {сударь/сударыня}, у меня нет времени для попрошаек”


Теперь, скопируйте и вставьте следущее, перед упомянутой выше записью:

  [trp_geoffrey,"start", [(eq,"$geoffrey_duel",1)], "Begone! I have nothing to say to you, varlet.", "close_window",[]],
  ”Пшел прочь! Мне не о чем разговаривать с тобой, каналья.”


Таким образом, если вы уже вызвали Джоффри на дуэль, система не выведет обычный диалог, описанный в предыдущем разделе. Этим способом можно видоизменить диалог в зависимости от ситуации; создать любое число вариантов диалога с блоками условий, контролирующими когда каждый из вариантов будет отображен.


Последняя запись беседы с Констеблем Хареком это:

  [trp_constable_hareck|plyr,"constable_hareck_talk", [], "Nothing. Good-bye.", "close_window",[]],
  ”Ничего. Всего хорошего.”


Статус вызова "constable_hareck_talk" принадлежит к статусу вызова меню диалога с набором разных вариантов ответа для игрока, из которых данная запись является последним ответом в списке. Для того чтобы эта запись в меню выбора ответов (вариант ответа ведущий к завершению диалога) оставалась последней мы должны добавить нашу новую линию диалога перед ней.

Скопируйте и вставьте следущее перед вышеупомянотой записью:

  [trp_constable_hareck|plyr,"constable_hareck_talk", [(neg|check_quest_active,"qst_speak_with_troublemakers"),(neg|check_quest_finished,"qst_speak_with_troublemakers")], "Is something wrong? You look worried.", "constable_hareck_troublemakers",[]],
  "Все в порядке? Ты выглядишь обеспокоенным.”

Эта запись не будет отображена если квест "qst_speak_with_troublemakers" активен или завершен. Реализуется с помощью операции отрицания “neg” привязанной к проверке активности и завершенности квеста "qst_speak_with_troublemakers" в поле статуса вызова. К примеру, если для истинности операции сравнения “eq” требуется, чтобы два значения были равны друг другу, то для истинности комбинированной операции отрицания-сравнения “neg|eq” значения должны отличаться.


Теперь, вслед за новой записью скопируем и вставим следущие (уже не так критично куда именно, советую ставить рядом с остальным диалогом Харека, для вашего удобства):

  [trp_constable_hareck,"constable_hareck_troublemakers", [], "Oh, it's nothing, just . . .", "constable_hareck_troublemakers_2",[]],
  ”О...Все в порядке, просто...”
  [trp_constable_hareck|plyr,"constable_hareck_troublemakers_2", [], "You can tell me, sir.", "constable_hareck_troublemakers_3",[]],
  "Ты можешь мне довериться, сэр"
  [trp_constable_hareck,"constable_hareck_troublemakers_3", [], "No harm in it, I suppose. The trouble is, a few of the town's young nobles . . . spoiled dandies and fops, the lot of them . . . they've decided that suddenly they're men to be respected, and that they should 'take matters into their own hands', to 'take action where the official government has failed'. They say they're going to kill all the river pirates that have been troubling Zendar of late. Of course, they've not actually gone out to fight any river pirates, but they've been making a great ruckus in town and there's not a thing I can do about it.", "constable_hareck_troublemakers_4",[]],
  "Да ничего особенного не произошло, я полагаю. Есть несколько мололых дворян... избалованые пижоны и щеглы, в большинстве своем... они вдруг решили, что являются уважаемыми людьми и что должны взять дела в городе в собственные руки. Дела, где по их мнению городские власти не справляются. Они говорят: “Мы убьем всех речных пиратов в окрестностях Зендара!” Конечно же  никто из них не собирается идти сражаться с пиратами, но они поднимают слишком много шума в городе и я ничего не могу с этим поделать.”
  [trp_constable_hareck|plyr,"constable_hareck_troublemakers_4", [], "Hmm . . . Would there be a reward for solving this problem?", "constable_hareck_troublemakers_5",[]],
  ”Хмм... А полагается ли награда, за решение этой проблемы?”
  [trp_constable_hareck,"constable_hareck_troublemakers_5", [], "What? What are you saying?", "constable_hareck_troublemakers_6",[]],
  ”Что? О чем ты говоришь?"
  [trp_constable_hareck|plyr,"constable_hareck_troublemakers_6", [], "Nothing, sir. However, it sounds to me like a neutral third party might be just what you need. I could talk to them.", "constable_hareck_troublemakers_7",[]],
  ”Ничего, сэр. Просто мне кажется,что нейтральная третья сторона это, то что тебе нужно. Я мог бы поговорить с ними.”
  [trp_constable_hareck,"constable_hareck_troublemakers_7", [], "Heh. Well, you can try, friend. If you manage to do any good, I'll even throw in a few coins for getting the sand out of my breeches. Their leader is a boy named Geoffrey, spends most of his time on watered-down ale and whores in the Happy Boar. Chances are you'll find him there.", "constable_hareck_troublemakers_8",[]],
  "Хех. Что же, ты можешь попытаться, друг. И если ты хорошо управишься с этим делом, я даже подкину несколько монет за то, что ты сдул песок с моих бриджей. Их лидер, это мальчишка по имени Джоффри, он тратит  большую часть времени на эль и проституток. Все шансы, что ты встретишь его в трактире Счастливый Вепрь.“
  [trp_constable_hareck|plyr,"constable_hareck_troublemakers_8", [], "Thank you, constable. I shall return.", "close_window",[(setup_quest_text,"qst_speak_with_troublemakers"),(start_quest,"qst_speak_with_troublemakers")]],
  "Благодарю, констэбль. Я еще вернусь за деньгами.”

  [trp_constable_hareck|plyr,"constable_hareck_talk", [(check_quest_active,"qst_speak_with_troublemakers"),(eq,"$geoffrey_duel",2)], "Constable, I've taken care of the toublemakers for you. They shouldn't be a worry any longer.", "constable_hareck_troublemakers_10",[]],
  "Констэбль, я позаботился о вашей проблемме. Эти нарушителя порядка вас больше не побеспокоят.”
    [trp_constable_hareck,"constable_hareck_troublemakers_10", [], "Truly? Thank God! A few more days and I would've thrown them all into a cell and thrown away the key. Here, take this. You've earned it.", "constable_hareck_troublemakers_11",[(troop_add_gold,"trp_player",100),(add_xp_as_reward,750),(succeed_quest,"qst_speak_with_troublemakers")]],
  ”Правда? Слава Богу! Еще несколько дней и я бы запер их тюремную камеру, а ключ от дверей выбросил бы куда подальше. Вот, возьми это. Ты честно заработал эти деньги.”
  [trp_constable_hareck|plyr,"constable_hareck_troublemakers_11", [], "My pleasure, constable. If you've any other jobs that need doing, please let me know. Farewell.", "close_window",[]],
  ”Моя благодарность, констэбль. Если у тебя появится другое задание, дай мне знать. До встречи”

  [trp_constable_hareck|plyr,"constable_hareck_talk", [(check_quest_active,"qst_speak_with_troublemakers"),(eq,"$geoffrey_duel",3)], "Constable, I failed. I'm sorry.", "constable_hareck_troublemakers_15",[]],
  ”Констэбль, я потерпел неудачу. Сожалею.”
  [trp_constable_hareck,"constable_hareck_troublemakers_15", [], "Oh . . . Oh well. I suppose you did the best you could. Thanks anyway, friend. Perhaps some other job will suit you better. I shall let you know when I have any. Farewell.", "close_window",[(fail_quest,"qst_speak_with_troublemakers")]],
  ”О... Ну что-же, я полагаю ты сделал все возможное. Спасибо в любом случае, друг. Может с другими заданиями ты справишься лучше. Я дам тебе знать, если у меня появится что-то. До встречи.”

Первый блок активирует квест и объясняет его детали. Второй блок - окончание квеста с приятной маленькой наградой в виде золота и опыта, требует наличия у переменной "$geoffrey_duel" значения 2, которое появится только в случае победы над Джоффри. Третий блок задает окончание квеста, если игрок не смог победить Джоффри, что означает отсутствие награды.


Мы разобрали возможности module_dialogs.py, но но мы все еще на полпути. Мы написали все диалоги, однако мы все еще должны задать триггеры событий. Время двигаться к следущей части нашей документации.

4
  Шестая часть.
 В этой части документации мы разберем самые маленькие файлы модульной системы. Но не смотря на размер, эти файлы вы будете модифицировать очень часто, и для разных задач. В конце этой главы, вы будете знать как использовать эти файлы с наибольшей отдачей.


6.1 Файл module_constants.py.

module_constants.py очень простой файл. Он заполнен константами; если вы читали 3-ю главу, то наверное представляете как они работают. Константы в файле module_constant.py в точности подобны любым другим константам из любого другого файла модульной системы. Но те из них, что будут использованны в нескольких module-файлах рекомендуется писать здесь, для того, чтобы модульная система всегда знала, где их искать.

Файл module_constant.py также служит как упорядоченный, легкодоступный список констант, где вы легко можете присвоить каждой из них нужное значение. Любые изменения сразу же отразятся на всех операциях, использующих эти константы, т.е. вам не нужно искать вручную каждую из этих операций и производить там замену.

К примеру, замена marnid_inn_entry = 2 на marnid_inn_entry = 4 многвенно отразится на операциях использующих константу marnid_inn_entry, они будут трактовать значение константы как 4, а не 2.


6.2 Файл moodule_factions.py.

Файл moodule_factions.py содержит все фракции, которые используются модульной системой и ИИ-ом МиБа. Это маленький файл, но он задает несколько важных установок, мы разберем их здесь.

Файл начинается с python-овского списка factions = [. Как и в большинстве module-файлов первые несколько записей вмонтированы в игру и не рекомендуются к редактированию.


Пример записи фракции:

  ("innocents","Innocents", 0, 0.5,[("outlaws",-0.05)]),

Разбор полей записи:

1) Идентификатор фракции. Используется для ссылок  из других файлов.
2) Имя фракции. Название фракции в игре.
3) Метки фракции.
4) Фракционная согласованность. Отношения между членами фракции. Числовое значение.
5) Меж-фракционные отношения. Каждый элемент этого поля состоит из:
     5.1) Идентификатор фракции, отношение к которой мы хотим задать.
     5.2) Отношение. Значение, характеризует отношение к указанной фракции. Задается из отрезка между -1 и 1.


Разбор записи “innocents” (невиновные):

1) Идентификатор фракции = "innocents"
2) Имя фракции = "Innocents"
3) Метки фракции = 0
4) Фракционная согласованность = 0.5
5) Меж-фракционные отношения
     5.1) Идентификатор фракции  = "outlaws"
     5.2) Отношение = 0.05

Исходя из разбора записи можно сказать следущие: у фракции нет меток, значение согласованости фракции нейтральное, фракция не имеет врагов заданных в полях ее записи, кроме "outlaws" ("преступники"). Однако, если мы немного осмотримся, то найдем, что фракция "dark_knights" (к примеру) имеет с "innocents" отношения -0.9 (согласно данным ее записи). Все это потому, что фракционные отношения работают в обе стороны – стоит только задать уровень отношений между любыми двумя фракциями в полях записи одной из них, как оно тут же распространится и на вторую. Если же отношения между фракциями не указаны вообще нигде (в т.ч. и в module_constants.py), то отношения меж ними по умолчанию устанавливаются на 0, то есть они друг к другу будут нейтральны.

Давайте теперь создадим новую фракцию, скопировав и вставив запись "innocents" в конец списка. Заменим идентификатор и имя нашей новой фракции на “geoffrey”, заменим значение "outlaws" в списке межфракционных отношений на "player_faction".

Сохраняемся. Как вы уже делали это однажды, откройте файлы module_party_templates.py и module_troops.py и замените значение в поле фракция у шаблона отряда "new_template" и героя "Geoffrey" на "fac_geoffrey". Сохраните и закройте файлы. Кликните два раза на файле build_module.bat. Если все прошло хорошо, все группы шаблона "new_template" будут показывать принадлежность к фракции "Geoffrey", и будут враждебны игроку.


6.3 Файл module_strings.py.

Файл module_strings.py возможно самый простой из всех файлов модульной системы. Он состоит из строк – блоков текста, которые различным образом могут быть выведенны на экран. Строки используются во многих файлах модульной системы, начиная с файла module_dialogs.py и заканчивая module_quests.py; везде, где необходимо вывести блок текста на экран. Файл module_strings.py на деле является хранилищем независимых блоков информации, не принадлежащих отдельному одиночному файлу. Напротив, они (блоки текста) могут быть вызваны с помощью определенных операций из любого файла модульной системы.

Вы можете увидеть использование строк (блоков текста) в различных частях игры, - читая сообщения в левой части экрана или блок объяснений (туторитал бокс),  расматривая игровое меню или содержание диалогов.

Подобно moodule_factions.py, файл начинается с python-овского списка: strings = [. И опять-таки первые несколько записей вмонтированы в игру и не рекомендуются к редактированию.


Пример строки:

  ("door_locked","The door is locked."),


Описание полей записи:

1) Идентификатор строки. Используется для ссылок на строку из других файлов.
2) Текст строки.


Одно свойство строки, которое стоит отметить это способность хранить данные регистров (register value) и другие строки. Вы можете проверить это, добавив к примеру, {reg0} в строку. Тогда, при выводе строки, вы также увидите текущие значение reg(0). {reg10}, к примеру, при выводе строки добавит текущие значение reg(10), и т.д.

Для добавления строк, вы должны использовать строковые регистры вместо обычных; это потому, что строки записаны иначе чем данные обычного регистра. Для примера, {s2} выведет содержимое string register 2, а не содержимое reg(2). Вы можете свободно использовать string register 2 и reg(2) одновременно для разных вещей, они не будут перекрываться или смешиваться друг с другом.


(прим: для усвоения, и чтобы не путаться, уточним
reg(X) и его вывод {regX} - это обычный регистр
string register X и его вывод {sX} - это строковый регистр)



В диалогах, есть возможность высвечивать часть (или все) содержания строки в зависимости от пола персоны, к которой она (строка) адресована. Для примера вставка {sir/madam} в строку заставит вывести слово "sir" , когда она адресованна персоне мужского пола и "madam"  персоне женского пола.

Все эти возможности (исключая различие полов) будут работать без проблем при выводе строки в любое время и в любом месте. Различие полов работает только в диалоговом окне.


Кстати, с помощью операции "display_message"  возможно вывести строку в различном цвете, присоединив код цвета в 16-ой системе исчисления (hexadecimal colour code) к операции. Для примера:

 (display_message,<идентификатор строки>, <код цвета>),

А это список используемых кодов цвета в 16-ой системе исчисления для операции display_message:

синий = 0xFFAAAAFF
светло синий = 0xFFAAD8FF
красный = 0xFFFFAAAA
желтый = 0xFFFFFFAA
розовый = 0xFFFFAAFF
фиолетовый = 0xFF6AAA89
черный = 0xFFAAAAAA
белый = 0xFFFFFFFF
зеленый = 0xFFAAFFAA
коричневый = 0xFF7A4800

прим: В принципе, вы можете сами сделать нужный цвет, если зададите его код. Разберём запись кода цвета. После "0х" есть четыре группы, они отвечают за цвета примерно таким образом: 0xFFFFFFFF (за что отвечает первая (чёрная) группа я не нашёл, возможно за яркость или что-то вроде). Все эти значения варьируются от 0 до 256 (в шестнадцатиричной системе от 0 до FF). Подставляя туда свои цифры, можно получать интересные комбинации. Но помните что число не должно превышать 256 в десятичной! Например, чтобы сделать просто красный цвет, код будет 0xFFFF0000)

 (display_message,"door_locked",0xFFFFAAAA),

Эта операция выведет нам на экран следующее сообщение:

The door is locked.


6.4 Файл module_quests.py.

Файл module_quests.py это последний из наших маленьких и простых module-файлов. Он состоит из квестов, включая все блоки текста, относящиеся к этим квестам. Добавление сюда нового квеста позволит ему быть активированным модульной системой, чтобы операции смогли считать текущий статус квеста и использовать этот статус как операцияю условия.


Пример квеста:

("hunt_down_river_pirates", "hunt down river pirates", qf_show_progression,
  "Constable Hareck asked you to hunt down the river pirates that plague the country around Zendar.\
 He promised to pay you 5 denars for each river pirate party you eliminate."
  ),


(“hunt_down_river_pirates “, “охота за речными пиратами”, qf_show_progression,
     “Констэбль Харек просит вас поймать речных пиратов, наводнивших окрестности Зендара.\
      Он обещает заплатить Вам по 5 денаров за каждую банду речных пиратов,\
      которую вы уничтожите.” ),


Этот квест не нуждается в представлении. Мы должны отловить речных пиратов в одной или другой точке карты. Интересными деталями являются метка qf_show_progression, и факт использования обратной косой черты -- \ -- (бэкслэша) позволяющей сделать перенос строки в этом месте. Примечание: использование бэкслэша не принуждает перенос строки при выводе на экран. Используется только для аккуратности и удобства работы с текстом в файле module_quests.py.


Разбор полей записи.

1) Идентификаторр квеста. Служит для ссылок на квест из других файлов.
2) Название квеста, отображаемое в списке в левой части на квестовом экране.
3) Метки квеста.
4) Описание квеста. Длинное описание квеста, отбражаемое в большом текстовом окне в верхней правой части квестового экрана.


Разбор записи квеста речных пиратов:

1) Идентификаторр квеста = "hunt_down_river_pirates"
2) Название квеста = "hunt down river pirates"
3) Метки квеста = qf_show_progression
4) Описание квеста = "Constable Hareck asked you to hunt down the river pirates that plague the\ country around Zendar. He promised to pay you 5 denars for each river pirate party you eliminate."


Метка qf_show_progression это то, что придает квесту процентный статус завершённости, видимый справа в списке квестов. Список квестов может быть найден в левой части квестового экрана. Процентный статус не присваивается автоматически; вы должны задать его (апдейт процентного статуса завершенности квеста) с помощью специальных операций, если хотите его использовать.

Если вы хотите сделать квест без меток вообще, просто поставьте 0 в поле меток.


А сейчас, мы сделаем первый шаг в создание нашего собственного квеста. Скопируйте следущюю запись  квеста и вставьте его в конец python-овского списка:

("speak_with_troublemakers", "Parley with the troublemakers", 0,
  "Constable Hareck asked you to deal with a bunch of young nobles making trouble around town. How you want to tackle the problem is up to you; Hareck has promised a purse of silver if you succeed."
  ),


("speak_with_troublemakers", “Переговоры с нарушителями порядка”, 0 , “Констебль Харек просит вас разобраться с бандой молодого дворянина, нарушающей порядок в окрестностях  города. Каким образом вы разберетесь с проблеммой, полностью зависит от вас; Харек пообещал вам кошель серебра, если справитесь”)

(прим: описание квеста должно идти одной, неразрывной строкой, или используйте “\”)
Замечание по структуре этой записи. Ка вы видите, потребовалось внести всего несколько изменений, чтобы сделать новый квест, но присоединение его (квеста) к игре требует гораздо больше работы. По этой причине, нашей следующей целью будет module_dialogs.py – где мы вставим и подготовим к использованию наш новый квест, войска и шаблон отряда.

Сохраните ваш прогресс и дважды кликните на файле build_module.bat. Если все пройдет хорошо, то мы будем готовы двигаться к седьмой части этой документации.

5
  Пятая часть.
  В 3-ей и 4-ой частях мы изучили как создавать новых персонажей и шаблоны, по которым из них формируются войска. С этими знаниями, мы можем приступить к следующей задаче - созданию новых предметов экипировки для наших войск.


5.1 Описание файла module_items.py.

Файл module_items.py начинается с некоторого количества констант, которые используются для задания модификаторов предметов в игре. Bent polearms/погнутые копья, chipped swords/зазубренный мечи, heavy axes/тяжелые топоры, все эти предметы созданны с помощью записей из module_items.py и присоединенных к ним модификаторов, уменьшающих или увеличивающих свойства предметов.

Константы, определенные здесь, состоят из стандартных модификаторов предметов. Предметы, которые вы найдете у торговцев и среди добычи будут “вытягивать” случайным образом модификаторы указанные в этих константах. Модификаторы, которые не включенны в константы, не будут применяться к инвентарям торговцев и боевым трофеям.

Для более опытных мододелов важно отметить, что модификаторы, которые не упомянуты в соответствующей константе, могут быть использованны с помощью специальной операции - “troop_add_item”. Для примера, длинный лук (longbow) обычно появляется в простом (“plain”), погнутом (“bent”) или сломанном (“cracked”) виде; но если мы добавим к длинному луку (лежащему в инвентаре игрока) модификатор сбалансированный (“balanced”), то игрок получит сбалансированный длинный лук.

После констант начинается список записей. Первая интересующая нас запись это оружие “practice_sword”, которая послужит нам в качестве хорошего примера.

Итак, пример:

["practice_sword","practice_sword", [("practice_sword",0)], itp_type_one_handed_wpn|itp_melee|itp_primary|itp_secondary, itc_longsword, 3,weight(1.5)|spd_rtng(103)|weapon_length(90)|swing_damage(16,blunt)|thrust_damage(10,blunt),imodbits_none],

Это базовое учебное оружие, используемое в Зендаре на тренировках и арене.


Разберем поля записи:

1) Идентификатор предмета. Используется для ссылок на предмет из других файлов.
2) Название предмета. Название предмета, которое вы увидите в игре.
3) Список meshes (3d моделей). Каждое из полей отведенного под meshes состоит из:
         3.1) Идентификатора 3d модели из resource-файла.
         3.2) Меток, задающих где и когда для отбражения предмета будет использована модель с данным идентификатором. К примеру, если мы имеем дело с моделью меча в ножнах, то здесь следует поставить метку -  ixmesh_carry.  В этом случае модель будет применяться всякий раз, когда меч закреплен на поясе у персонажа. Замечание: если моделей у предмета несколько, то по умолчанию всегда используется та, что указана первой. (прим: здесь я заменил объяснение автора своим)
4) Метки предмета.
5) Потенциальные возможности. (прим: по сути тоже метки). Определяют список анимаций, используемых с этим предметом.
6) Цена предмета. В динарах. Замечание – реальная цена предмета в игре будет заметно выше, покрайней мере до тех пор, пока ваше умение торговаться не вырастет до 10-ти и более.
7) Свойства предмета. Вес, распространенность, прочность, очки брони и т.д.
8) Список модификаторов. Модификаторы которые могут быть примененны относительно предмета.
9) Триггеры (опционально, не обязательно к заполнению). Список простых триггеров, связанных с этим предметом.

Важно! Все доступные метки находятся в файле header_items.py, а модификаторы описываются в начале module_items.py. Но об этом ниже.


Запись practice_sword состоит из:

1) Идентификатор предмета =  “practice_sword”
2) Название предмета = ”practice_sword”
3) Список 3d моделей использующихся для отбражения предмета:
         3.1) Идентификатор модели = ”practice_sword”
         3.2) Метки для данной модели = 0
4) Метки предмета = itp_type_one_handed_wpn|itp_melee|itp_primary|itp_secondary
5) Потенциальные возможности = itc_longsword
6) Цена = 3
7) Свойства предмета = weight(1.5)|spd_rtng(103)|weapon_length(90)|swing_damage(16,blunt)|thrust_damage(10,blunt)
8) Список модификаторов = imodbits_none
9) Триггеры = не заполнено

Теперь мы можем  рассказать все о "practice_sword", исходя из параметров его записи:
 - Всегда использует 3d модель "practice_sword" для отображения (задано по умолчанию).
 - Метки указывают, что это одноручное оружие (itp_type_one_handed_wpn) ближнего боя (itp_melee). Войска экиппированные "practice_sword" примут его во внимание, когда будут выбирать основное оружие ближниго боя из предметов своего инвентаря (itp_primary). Они также будут рассматривать "practice_sword" и при определении запасного (вторичного) оружия (itp_secondary). (Замечание: функция запасного оружия в настоящее время расплывчата. Неясно когда оно будет использовано, если будет использованно вообще. Однако можно быть уверенным в том, что войска не станут менять оружие во время ближнего боя.)
 - Вся анимация связанная с предметом определена константой itc_longsword, т.е. мы можем предположить, что "practice_sword" способен на тоже, что и длинный меч.
 - Его вес 1.5 кг, его скорость 103, его длинна 90 см. Он причиняет 16 едениц blunt (дробящего, оглушающего) урона при рубящем ударе и 10 едениц blunt урона при колющем (без учета различных модификаторов).
 - К предмету не применяются модификаторы.

(прим: написанное в этой части есть неофициальное дополнение, взятое из стороннего ресурса)

Важно! В Варбанде добавлены новые метки, которых не было M&B:

itp_type_horse, эта метка и раньше была, но появилась:
itp_type_animal,

Наиболее важные нововведения:
itp_can_penetrate_shield, для боеприпасов стрелкового/метательного. Пример: стальные болты пробивают лёгкие щиты.
itp_next_item_as_melee, "следующее-оружие-рукопашное", используется для ограничения возможности использовать следующее оружие как стрелковое/метательное, оно будт использовано как рукопашное, если имеет такую возможность
itp_offset_lance, для полеармов, добавляет возможность использовать для таранного удара, и после удара поднять. Чтобы таранный удар заработал, необходима следующая метка:
itp_couchable, возможность переключить лансу по кнопке X.
itp_crush_through, способность пробивать блок.
itp_unbalanced, для характеристики Unbalanced, делающей оружие несбалансированным. Пример: бердыши, топоры, молоты.
itp_merchandise, для появления предмета в магазинах.



5.2 Типы урона.

Как мы видели в разборе записи "practice_sword" , он причиняет дробящий урон. Логично, т.к. является учебным оружием. Самое время взглянуть на различные типы урона, реализованные в МиБе.

Первый, это рубящий (cut) тип урона. Рубящий тип урона возникает при нанесении рубящего удара острым лезвием, таким как у меча или у топора. Удар рубящяго типа урона по легкобронированному врагу будет иметь бонус, и напротив получит штраф против тяжелобронированного врага.

Далее, дробящий/оглушающий (blunt) тип урона. Получается при ударе тупым предметом, не имеющим лезвия, как например молот или древко копья. Удар с дробящим типом урона по тяжелобронированному врагу получит 50% бонус, но дробящее оружие как правило короче рубящего, и имеет более маленький базовый урон. Самое большое преимущество удара причиняющего дробящий урон в том, что он нокаутирует противника, а не убивает. (прим: это верно только для одиночной игры; в мультиплеере же именно убивает как и любой другой вид оружия) Поверженный таким образом враг, может быть захвачен в плен, а затем продан в рабство. Урон, причиняемый конем, также относится к дробящему типу ущерба. (прим: оружию с дробящим уроном часто добавляют шанс пробить блок и/или опрокинуть персонажа на землю при помощи метки itp_can_knock_down)

Ну и наконец, колящий (pierce) тип урона, наносимый стрелами, болтами и некоторыми видами оружия ближнего боя. Есть 50% бонус к сумме урона если наносится удар по тяжелобронированному противнику, но базовый урон меньше, чем у оружия с рубящим типом урона. Удар с колющим типом урона убьет противника, если сведет его здоровье до нуля.


5.3 Создание предмета.

Скопируйте запись "practice_sword" и вставьте в конец файла, перед закрывающей квадратной скобкой. Поменяйте название и идентификатор нового предмета на "new_mace":

["new_mace","new_mace", [("practice_sword",0)], itp_type_one_handed_wpn|itp_melee|itp_primary|itp_secondary, itc_longsword, 3,weight(1.5)|spd_rtng(103)|weapon_length(90)|swing_damage(16,blunt)|thrust_damage(10,blunt),imodbits_none],

МиБ очень гибок для модификации; понадобится сделать лишь малое колличество согласований, чтобы превратить меч в булаву. В записи "practice_sword" уже указан дробящий тип урона оружия. Это делает нашу работу еще более легкой.

Первое, что мы изменим, это потенциальные возможности предмета itc_longsword на itc_scimitar. Это не позволит нашей булаве наносить колящие удары, т.к. анимация колящего удара не включена в константу itc_scimitar:

["new_mace","new_mace", [("practice_sword",0)], itp_type_one_handed_wpn|itp_melee|itp_primary|itp_secondary, itc_scimitar, 3,weight(1.5)|spd_rtng(103)|weapon_length(90)|swing_damage(16,blunt)|thrust_damage(10,blunt),imodbits_none],

Далее, мы поменяем 3d модель на "mace_pear" и занесем в поле применяемых к предмету модификаторов imodbits_mace вместо imodbits_none. Это даст возможность нашей булаве использовать все модификаторы, присвоенные константе imodbits_mace в начале файла.

["new_mace","new_mace", [("mace_pear",0)], itp_type_one_handed_wpn|itp_melee|itp_primary|itp_secondary, itc_scimitar, 3,weight(1.5)|spd_rtng(103)|weapon_length(90)|swing_damage(16,blunt)|thrust_damage(10,blunt),imodbits_mace],

Осталось провести два изменения, и мы закончим с этим предметом. Во-первых мы увеличим ущерб от рубящих ударов (swing damage, левый-правый-верхний удары), а во-вторых добавим предмету новую метку.

Смотрим:

["new_mace","new_mace", [("mace_pear",0)], itp_type_one_handed_wpn|itp_melee|itp_primary|itp_secondary|itp_unique, itc_scimitar, 3,weight(1.5)|spd_rtng(103)|weapon_length(90)|swing_damage(26,blunt)|thrust_damage(10,blunt),imodbits_mace],

Как видите, мы подняли ущерб от рубящих ударов с 16 до 26, это сделает нашу булаву более опаной в битве. И, что более примечательно, мы добавили метку itp_unique в поле для меток. Любой предмет с меткой itp_unique никогда не окажется среди добычи, в обычном инвентаре трофеев, что появляется после битвы. Это не позволит игроку завладеть новой булавой слишком рано, так как у нас для нее есть особая задумка.

Нашим последним согласованием станет смена названия предмета с "new_mace" на "Geoffrey's_mace". Теперь откройте module_trooops.py и замените itm_club в инвентаре Джоффри на itm_new_mace.

Сохраните проделанные измениния в обоих файлах, а затем дважды кликнете на build_module.bat. Поздравляю! Мы создали новый предмет и добавили его в инвентарь войска. Войско, а точней герой, будет всегда иметь эту булаву в своём инвентаре, а кроме того он будет использовать лучшее оружие какое только может из своего инвентаря.

Обычные же войска, в отличие от героя, будут выбирать свою экипировку из своего инвентаря случайным образом. Это причина, по которой большинство обычных войск имеют различный выбор оружия в инвентаре -- иначе они все будут выглядеть одинаково.

Теперь, когда мы знаем как создавать предметы, мы разберем свойство предметов и опишем, что все они значат.


5.4 Свойства предметов.

В этом раздере вы найдете исчерпывающий список свойств и объяснение их функций. Т.к. некоторые свойства  для разных типов имеют отличное значение, мы организовали список по типам.


Общие (General)

abundance (распространенность) -- процентное значение.
Это свойство определяет как часто предмет будет появляться в инвентаре торговцев и среди военной добычи. 100% это стандартное значение; может быть как больше так и меньше 100 (минимум 0).

weight (вес) - значение в килограммах.
Определяет вес предмета в килограммах.


Лошади (itp_type_horse)

body_armor (броня тела) - числовое значение.
Определяет величину бронированности  лошади и колличество здоровья. Чем больше значение, тем больше уровень брони и здоровья.

difficulty (сложность) - числовое значение.
Задает минимальный уровень навыка верховой езды, необходимый наезднику, чтобы оседлать данную лошадь

horse_speed (скорость лошади) - числовое значение.
Скорость лошади в бою. Большее значение – быстрее лошадь.

horse_maneuver (маневренность лошади) - числовое значение.
Маневренность лошади в бою.

horse_charge (натиск лошади) - числовое значение.
Определяет сколько урона наносит лошадь, тараня пехоту и сколько будет терять в скорости после каждого столкновения. Чем выше значение - тем больше урон, и тем больше пехоты способна сбить лошадь, до полной остановки.


Одноручное оружие (itp_type_one_handed_wpn)

difficulty (сложность) - значение.
Минимум силы, который потребуется для использование данного оружия.

spd_rtng (уровень скорости) - числовое значение.
Скорость атаки оружия, как бокового замаха, так и укола.

weapon_length (длинна оружия) - сантиметры.
Длинна оружия в сантиметрах. Это свойство определяет как далеко будет способно достать оружие в игре, ну и размер модели.

swing_damage (урон от бокового удара и удара сверху) - числовое значение, тип урона.
Определяет величину базового урона и его тип для рубящих ударов.

thrust_damage (урон от колющего удара) - числовое значение, тип урона.
Определяет величину базового урона и его тип для колющего удара.


Двуручное оружие (tp_type_two_handed_wpn)

Абсолютно идентично одноручному оружию.


Древковое оружие (itp_type_polearm)

Абсолютно идентично одноручному оружию.


Стрелы (itp_type_arrows)

weapon_length (длинна оружия) - сантиметры.
Длина в сантиметрах.

thrust_damage (урон от колющего удара) - числовое значние, тип урона.
Определяет величину базового урона и добавляет его к величине базового урона лука, а также определяет тип урона.

max_ammo (максимальный запас) - числовое значение.
Колличество стрел в колчане.


Болты (itp_type_bolts)

Абсолютно идентичны стрелам.


Ядра (itp_type_bullets)

Абсолютно идентичны стрелам.


Луки (itp_type_bow)

difficulty (сложность) - числовое значение.
Минимальное значение навыка сильный выстрел, которое потребуется для использование данного лука.

spd_rtng (уровень скорости) - числовое значение.
Скорость перезарядки (взять стрелу + натянуть тетиву + выпустить стрелу) лука. Большее значение – более быстрая перезарядка.

shoot_speed (скорость выстрела) - числовое значение.
Скорость полета стрелы. Высокое значение – высокая скорость.

thrust_damage (урон от колющего удара) - числовое значение, тип урона.
Определяет величину базового ущерба и его тип.

Accuracy (точность) - процентное значение.
Вероятность, что лук выстрелит именно туда, куда направлен прицел. Значение 100 это 100%-ная вероятность точного выстрела. Меньшая, чем 100%-ная вероятность понижает шанс попасть в цель.


Арбалеты (itp_type_crossbow)

difficulty (сложность) - числовое значение.
Минимум силы, который потребуется для использование данного арбалета.

spd_rtng (уровень скорости) - числовое значение.
Скорость перезарядки (закрепить болт + натянуть тетиву + спустить тетиву ) арбалета. Большее значение – более быстрая перезарядка.

shoot_speed (скорость выстрела) - числовое значение.
Скорость полета болта. Высокое значение – высокая скорость.

thrust_damage (урон от колющего удара) - числовое значние, тип урона.
Определяет величину базового уронаа и его тип.

max_ammo (максимальный запас) - числовое значение.
Количество болтов в пачке.

Accuracy (точность) - процентное значение.
Вероятность, что арбалет выстрелит именно туда, куда направлен пицел. Значение 100 это 100%-ная вероятность точного выстрела. Меньшая, чем 100%-ная вероятность понижает шанс попасть в цель.


Метательное оружие (itp_type_thrown)

difficulty (сложность) - числовое значение.
Минимальное значение навыка power throw/сильный бросок, которое потребуется для использования данного оружия.

spd_rtng (уровень скорости) - числовое значение.
Скорость перезарядки. Определяет насколько быстро метательный снаряд может быть подготовлен для броска. Большее значение – более быстрая перезарядка.

shoot_speed (скорость выстрела) - числовое значение.
Скорость полета метательного снаряда. Высокое значение – большая скорость.

thrust_damage (урон от колющего удара) - числовое значние, тип урона.
Определяет величину базового урона и его тип.

max_ammo (максимальный запас) - числовое значение.
Число метательных снарядов в одной связке.

weapon_length (длинна оружия) - сантиметры.
Длина в сантиметрах.


Щит (itp_type_shield)

hit_points (хитпойнты) - числовое значение.
Базовое колличество хитпойнтов щита. Сколько урона способен выдержать щит прежде чем его разобьют.

body_armor (броня тела) - числовое значение.
Колличество урона, который будет поглощен при каждом ударе по щиту.

spd_rtng (уровень скорости) - числовое значение.
Скорость с которой щит может быть переведен в положение защиты. Чем больше значение, тем выше скорость.

weapon_length (длинна оружия) - сантиметры.
Площадь щита. Большое значения позволяет щиты прикрывать больший участок, защищая дополнительные части тела от стрел.


Броня головы (itp_type_head_armor)

head_armor (броня головы) - числовое значение.
Значение урона, которое предотвратит эта часть доспеха, если удар будет направлен в голову.

body_armor (броня тела) - числовое значение.
Значение урона, которое предотвратит эта часть доспеха, если удар будет направлен в тело.

leg_armor (броня ног) - числовое значение.
Значение урона, которое предотвратит эта часть доспеха, если удар будет направлен в ноги.


Броня тела (itp_type_body_armor).

Абсолютно идентична броне головы.


Броня ног (itp_type_foot_armor).

Абсолютно идентична броне головы.


Броня рук (itp_type_hand_armor).

Абсолютно идентична  броне головы.


Товар (itp_type_goods)

food_quality (качество еды).
Отражает как этот предмет влияет на мораль в группе. При значениях больше 50 будет повышать мораль в группе по мере потребления, если значение меньше 50, будет снижать мораль.

max_ammo (максимальный запас).
Количество частей предмета в одной пачке, готовых к употреблению


Пистоль (itp_type_pistol)

difficulty (сложность) - числовое значение.
Минимум силы, который потребуется для использование данного оружия.

spd_rtng (уровень скорости) - числовое значение.
Скорость перезарядки пистоля. Определяет насколько быстро войско способно перезарядить пистоль и выстрелить снова. Большее значение – более быстрая перезарядка.

shoot_speed (скорость выстрела) - числовое значение.
Скорость полета пули. Высокое значение – большая скорость.

thrust_damage (урон от колющего удара) - числовое значение, тип ущерба.
Определяет величину базового ущерба и его тип.

max_ammo (максимальный запас) - числовое значение.
Число выстрелов, которое может быть сделанно из пистоля, до того как он потребует перезарядки.

Accuracy (точность) - процентное значение.
Вероятность, что пистоль выстрелит именно туда, куда направлен пицел. Значение 100 это 100%-ная вероятность точного выстрела. Меньшая, чем 100%-ная вероятность понижает шанс попасть в цель.


Мушкет (itp_type_musket)

Абсолютно идентичен пистолю.

6
   Четвертая часть.
   Во второй части документации мы изучили как создавать новые отряды (parties), представляюoие собой уникальные объекты/локации на карте. Не стоит их путать с шаблонами отрядов (party templates), о которых мы будем говорить здесь, в четвертой части.

Шаблоны отрядов, если в общем, - это набор установок о том какие отряды будут появляться на карте. Еще раз отмечу наиболее наглядное различие между отрядами и шаблонами отрядов -- отряды являются уникальными объектами на карте, в то время как шаблоны не имеют "физического тела" в игровом мире. Они представляют собой лишь список установок о "производстве" отрядов определенного типа. Кроме того, некоторые операции, в которых используются идентификаторы отрядов в качестве аргумента не будут работать с идентификаторами шаблонов отрядов.

Отряды, созданные шаблонами, не могут быть уникальными. В игре может существовать множество отрядов, принадлежащих к одному и тому же шаблону (созданных по этому шаблону); каждый будет иметь различное колличество войск в зависимости от уровня игрока и минимума/максимума войскового ограничения указанного в шаблоне.


4.1. Описание файла Module_Party_Templates.py.

Файл начинается с обычного python-овского списка party_templates = [, сопровожденного несколькими шаблонам, которые вмонтированы в игру и не рекомендуются к редактированию. Вы отметите, что записи в файле module_party_templates.py очень похожи на те, что мы видели в module_parties.py, но не являются взаимозаменямыми.

Пример шаблона отрядов:

  ("farmers","farmers",icon_peasant|pf_auto_start_dialog,0,fac_innocents,merchant_personality,[(trp_farmer,11,22),(trp_peasant_woman,16,44)]),

Это пример шаблона, с которым мы можем столкнуться в игре. Отряды, сгенерированные по этому шаблону, будут именоваться "farmers", они автоматически вступят в диалог, когда вы встретите их, они трусливо ведут себя в игре, и каждый из таких отрядов включает в себя два стэка войск, состоящих из фермеров и крестьянок.


Описание полей записи шаблона группы:

1) Идентификатор шаблона отряда. Используется для ссылок на шаблон из других файлов.
2) Имя шаблона отряда. Имя, которое будут носить отряды, сгенерированные этим шаблоном в игре.
3) Метки отряда. Вы можете заметить, что все шаблоны в файле module_party_templates.py имеет метку pf_auto_start_dialogue. Эта метка заставит отряд автоматически начать диалог, всякий раз когда игрок повстречает такой отряд на карте.
4) Меню. Не используется, всегда ставьте значение 0 в этом поле.
5) Фракция.
6) Характеристика. Это поле включает в себя метки, которые определяют поведение группы на карте.
7) Список стэков. Стэк - войско одного типа; в отряде может быть несколько разных стэков. Поле каждого стэка содержит следущие элементы:
  7.1) Идентификатор войска.
  7.2) Минимальное колличество солдат в стэке.
  7.3) Максимальное колличество солдат в стэке.
  7.4) Метки/флаги (не обязательно к заполнению). (прим: там может быть например уже освещённая ранее метка (trp_swadian_crossbowman,5,14,pmf_is_prisoner).)

Максимальное колличество стэков в отряде - 6.


Разберем подробней шаблон отрядов "farmers":

1) Иденитфикатор шаблона групп = "farmers"
2) Имя шаблона групп = "farmers"
3) Метки шаблона групп = icon_peasant|pf_auto_start_dialog
4) Меню = 0
5) Фракция = fac_innocents
6) Характеристика = merchant_personality
7) Список стэков:
  7.1) Идентификатор войска = trp_farmer, trp_peasant_woman
  7.2) Минимальное колличество солдат в стэке = 11, 16
  7.3) Максимальное колличество солдат в стэке = 22, 44
  7.4) Метки/флаги = незаполнено

(прим: мы видим, что данный отряд может состоять из двух стеков - видов войск - (trp_farmer,11,22) и (trp_peasant_woman,16,44), соответственно 11-22 крестьянина и 16-44 крестьянок.)


Если вы изучали эту документацию с первой части, вы наверное стали настоящим специалистом в чтении записией, и поэтому, должно быть уже заметили, что одно из полей этой записи отлично от всех других, с которыми до сих пор мы имели дело: поле номер 6, характеристика. Давайте рассмотрим это поле и его функции в следующем разделе.


4.2 Характиристика.

(прим: personality - "личность" в переводе с англ., то есть это набор личностных характеристик, определяющих поведение)
Как было упомяното выше, в описании записи шаблона поле характеристик определяет поведение отряда на карте. Здесь вы можете задать определенное колличество очков храбрости и агрессии, или использовать одну из констант, каждая из которых содержит свое распределение очков храбрости и агрессии. Они (константы) определенны в файле header_parties.py, давайте откроем его, прокрутим вниз до самого дна и посмотрим какие значения там есть. Вы также найдете список возможных установок храбрости и агрессии.

Константа merchant_personality используется во многих шаблонах на протяжение всего файла module_party_templates.py. Отряды с этой характеристикой будут дружественны, они не будут атаковать врага или преследовать более слабые отряды. Это потому, что merchant_personality ставит метку агрессии отряда на ноль - aggresiveness_0, т.е. фактически убирает всю агрессивность. Отряды с aggresiveness_0 никогда не будут атаковать другие отряды. В отличие от них, у нормальных боевых отрядов выставлена характеристика soldier_personality, с меткой aggresiveness_8. Это позволяет им атаковать другие отряды, если их фракции находятся в плохих отношениях, а также если отряд противника не превосходит их в силе.

Значение Храбрости определяет будет ли отряд убегать от других более крупных отрядов. Высокая храбрость означает, что отряд не будет торопиться убегать, даже если не обладает преимуществом в силе. Группы с merchant_personality будут обладать храбростью 8, тогда как soldier_personality дает своему носителю храбрость 11.

Значения, которые могут принимать храбрость и агрессивность, варьируются от 0 до 15. Исходя из этого вы можете точно задать желаемое поведение отрядам, генерируемым вашим шаблоном. Однако, имеющим мало опыта мододелам рекомендуется для начала использовать уже заданные в header_parties.py метки-пресеты: они охватывают весьма широкий спектр характеристик, и могут покрыть любые нужды для вашего первого мода.

Кроме всего прочего, существует метка banditness. Она заставляет отряды принадлежащие к данному шаблону рассматривать все окружающие отряды как добычу, и если добыча везет значительное количество золота или товара, бандитские отряды будут ее атаковать. В идеале, бандитским отрядам следует обладать низкой агрессивностью или маленьким количеством войск, что не позволит им атаковать обычные боевые отряды фракций/дезертиров.


4.3 Создание новых шаблонов.

Скопируйте запись "farmers" и вставьте ее на дно файла, перед закрывающей квадратной скобкой:

  ("red_riders","The Red Riders",icon_peasant|pf_auto_start_dialog,0,fac_innocents,merchant_personality,[(trp_farmer,11,22),(trp_peasant_woman,16,44)]),

В этом примере мы поменяли идентификатор "farmers" на "red_riders", и сделали то же самое с именем. Теперь мы можем приступить к редактированию более специфичных деталей этого шаблона.

В качестве первого шага, давайте изменим фракционную принадлежность шаблона на fac_neutral, и merchant_personality сменим на soldier_personality:

  ("red_riders","The Red Riders",icon_peasant|pf_auto_start_dialog,0,fac_neutral,soldier_personality,[(trp_farmer,11,22),(trp_peasant_woman,16,44)]),

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

Теперь поиграемся с составом войск:

  ("red_riders","The Red Riders",icon_peasant|pf_auto_start_dialog,0,fac_neutral,soldier_personality,[(trp_geoffrey,1,1),(trp_red_rider,16,44)]),

В этом примере есть несколько примечательных изменений -- самое важное, что с этого момента отряд данного шаблона будет под предводительством trp_geoffrey, героя которого мы создали в третьей части документации. Т.к. в одном стэке может быть только один Джоффри, мы изменили минимум и максимум солдат на 1.

Мы придали ему в качестве подчиненных контингент trp_red_rider, обычных войск, которых мы тоже создали в третьей части. Их (red_rider) никогда не будет меньше чем 16, но когда игрок будет расти в уровне, то и размер отряда вырастет в пропорции. Фактически, этот новый шаблон может породить отряд, насчитывающий до 44 солдат войска в стеке "red_rider", но не более.

Сохранитесь, и кликните дважды на файле build_module.bat. Если все прошло хорошо, вы сможете использовать новый шаблон в вашем моде, но нужно еще "произвести" группы этого шаблона -- они не смогут появиться просто по своему желанию. И если мы запустим сейчас игру, мы не найдем ни одной группы нового шаблона на карте.

Мы научимся как "производить" группы в следующей части этой документации.

7
 Третья часть.
  В этой части документации мы затронем файл module_troops.py и его функции. Module_troops включает в себя такие объекты как обычные войска, герои, сундуки и городских НПС. Эти объекты полностью определены - с заданными лицами, возможностями, очками опыта и инвентарем. Когда бы вам ни захотелось создать новый характер или тип войска, вы будете модифицировать именно этот файл.

3.1 Разбор записей troops/войск.

Файл module_troops.py начинается с маленького блока кода, отвечающего за расчет навыков владения оружияи, и включающего в себя некоторую часть немодифицируемого кода. Проматываем это и направляемся сразу к списку troops = [.

Здесь мы можем найти записи игрока и нескольких других важных для игры персонажей. А сразу ниже описаны различные бойцы. Они являются отличными примерами уровневого прогресса войск, потому изучим их подробно.

Пример записи из списка объектов troops:

["novice_fighter","novice_fighter","novice_fighters",tf_guarantee_boots|tf_guarantee_armor,no_scene,reserved,fac_commoners,
   [itm_sword,itm_hide_boots],
   str_6|agi_6|level(5),wp(60),knows_common,swadian_face1, swadian_face2],


Эта запись описывает объект troop называемый "novice fighter". "novice fighter" это низкоуровневый боец, не очень хорош как боец, имеет маленькое число очков способностей, в остальном вообще почти не заметен.


Описание элементов записи:

1) Идентификатор troop/войска. Используется для ссылки на запись войска из других файлов.
2) Название troop/войска.
3) Название во множественном числе.
4) Метки для troop/войска. Метка с префиксом tf_guarantee_  + тип детали экипировки (как например, boots (сапоги), armor (броня) и т.д.) указывает, что если в инвентаре войска находится предмет соответствующего типа, он гарантировано будет надет и использован в бою. Если вы не ставите соответствующую метку, то войско может появиться в бою без предметов экипировки, чей тип не закреплен этой меткой. Только оружие ближнего боя будет экипировано без указания соответсвующего флага/метки. (прим: в Warband появилась новая метка tf_guarantee_all, позволяющая войску одеваться полностью и забивать все слоты под завязку)
5) Сцена. Применяется только к записям объектов Heroes/герои. Определяет в какой сцене и в какой entry point/входной точке герой появится. Для примера, метка scn_reyvadin_castle|entry (1) поместит героя во входную точку (1) внутри замка Рейвадин.
6) Зарезервированный, неиспользуемый элемент. Должен быть либо reserved либо 0.
7) Фракция. Принадлежность войска к фракции, используется префикс fac_.
8) Инвентарь. Список предметов в инвентаре войска. Эти предметы будут надеты обычными войсками случайным образом.
9) Атрибуты. Очки атрибутов войска (сила, ловкость, интелект, харизма), а также уровень персонажа/солдата_войска. Работает также как и для игрока.
10) Навык владения оружием. Представляет собой очки владения оружием данного войска. Функция wp(x) случайным образом задаст навык оружия близкий к значению "x", но вы также можете поместить точное значение одного или более навыков используя префикс wp_ + название навыка + (значение навыка). Например, если вы хотите создать умелого лучника, с остальными навыками владения оружием близкими к 60, то вы пишете:
wp_archery(160)|wp(60) .
11) Умения. Такие же умения как и у игрока. Стоит отметить, что в дополнение к атрибутам и умениям которые вы определили, войско получает 1 очко к атрибутам и 1 очко к умениям за каждый уровень (случайным образом).
12) Код для лица. Игра генерирует лицо согласно этому коду. Вы можете экспортировать новый код для лица из игры зажав CTRL + E в редакторе лиц, пока редактируете мод.
13) Код для лица 2. Применим только для обычных войск, можно пренебречь для героев. Игра будет создавать случайным образом лицо со значением кода, лежащим между первым и вторым кодами для каждого солдата в отдельности.

Рассмотрим значение элементов записи Novice_fighter:

1) Идентификатор войска= "novice_fighter"
2) Имя войска = "novice_fighter"
3) Множественное число солдат данного типа = "novice_fighters"
4) Метки = tf_guarantee_boots|tf_guarantee_armor0
5) сцена = no_scene
6) Зарезервиравонно = reserved
7) Фракции = fac_commoners (простолюдин)
8) Инвентарь = [itm_sword, itm_hide_boots]
9) Атрибуты = str_6|agi_6|level(5)
10) Навык владения оружием = wp(60)
11) Умения = knows_common (метка, прописывающая общие знания)
12) Код для лица (1) = swadian_face1
13) Код для лица (2) = swadian_face2

Три вещи, которые стоит отметить:

1. Наш "novice_fighter"/молодой_боец обладает меткой tf_guarantee_armor, но никакой брони в его собственности нету. Однако, это не делает метку tf_guarantee_armor бессмысленной. Войско наденет любую броню, какую только сможет заполучить по ходу игры.
2. Вначале, (на уровне 1) "novice_fighter" обладает силой (str) 6 и ловкостью (agi) 6. Как только игра стартует, он автоматически поднимается в уровне до пятого, что в свою очередь отразится ростом его атрибутов и умений (в атрибутах у "novice-fighter" прописан 5-ый уровень)
3. Он обладает умениями, определяемыми как knows_common или общие знания. knows_common это набор умений, заданный вначале файла module_troops.py; прокрутите вверх и посмотрите на этот набор:

knows_common = knows_riding_1|knows_trade_2|knows_inventory_management_2|knows_prisoner_management_1|knows_leadership_1

Войско, которое обладает knows_common/общими знаними, будет иметь значения навыков перечисленных выше; knows_riding_1 - значит умение верховой езды = 1; knows_trade_2 - торговля=2; knows_inventory_management_2 - управление инвентарем = 2; knows_prisoner_management_1 - управление пленными = 1; knows_leadership_1 - лидерство = 1. knows_common является константой. Подобным образом, можно определить любую другую константу, которая будет состоять из любого колличества объектов, чисел, идентификаторов или даже других констант.

В данном случае, knows_common/общие знания определены как: knows_riding_1|knows_trade_2|knows_inventory_management_2|knows_prisoner_management_1|knows_leadership_1
Таким образом в результате вставки knows_common в поле умений, модульная система будет функционировать точно так, как если бы вы задали в поле умений следущие метки: knows_riding_1|knows_trade_2|knows_inventory_management_2 |knows_prisoner_management_1|knows_leadership_1

Теперь давайте взглянем на следущую запись в списке:
["regular_fighter","regular_fighter","regular_fighters",tf_guarantee_boots|tf_guarantee_armor,no_scene,reserved,fac_commoners,
   [itm_sword,itm_hide_boots],
   str_8|agi_8|level(11),wp(90),knows_common|knows_ironflesh_1|knows_power_strike_1|knows_athletics_1|knows_riding_1|knows_shield_2,swadian_face1, swadian_face2],


В этом примере вы видите слегка усиленного "regular fighter"/"обычного бойца"; он имеет повышенное значение способностей, он 11-го уровня и некоторые из его умений выходят за рамки knows_common/общих знаний. В игре, если некоторые из "novice fighters"/ "молодых бойцов" достигают 11-го уровня, мы сделаем возможность проапгрейдить их до "regulars fighter"/"обычных бойцов". (см. далее)


3.2 Апгрэйд/прокачка войск.

Представляет собой список, указывающий какие войска и в кого могут быть проапгрейдены ("повышены в звании", если вам удобнее). Он находится на дне файла module_troops.py. Прокрутите вниз, в конец файла.

Как вы можете увидеть, каждый из вариантов апгрейда для любого типа войск должен быть объявлен операцией upgrade(troops). Сначала идентификатор войска подлежащего апгрейду, а затем идентификатор войска образующегося в результате апгрейда. К примеру: upgrade(troops,"farmer", "watchman") позволит фермерам (farmer) апгрейдится в караульных (watchman), если первые наберут достаточно опыта.

Существует два типа операций апгрейда:

1) upgrade(troops,"source_troop", "target_troop_1")  предлагает вам только один апгрейд, из "source_troop" в "target_troop_1".
2) upgrade2(troops,"source_troop", "target_troop_1", "target_troop_2") позволит игроку выбрать между апгрейдом  "source_troop" в "target_troop_1" или в "target_troop_2". Два варианта это текущий максимум возможностей выбора апгрейда.

Сейчас в этом блоке нет данных о "novice_fighter", так давайти добавим их. Скопируйте upgrade(troops,"farmer", "watchman") и вставьте в конец блока. Измените "farmer" на "novice_fighter" и "watchman" на "regular_fighter". Любой "novice fighters" в вашей группе способен теперь проапгрейдится до "regular fighters".

Теперь мы пойдем немного дальше. Сделайте еще одно погружение на дно списка, а затем поднимитесь до фразы: # Add Extra Quest NPCs below this point. Здесь вы заметите квадратную скобку "]", закрывающую python-овский список troops. Новые типы войск следует добавлять перед этой скобкой, чем мы сейчас и займемся. (прим: никакой скобки рядом с этой фразой я не нашел. Она (“]”) есть, но находится ниже, ищите ее перед фразой #Troop upgrade decloration)


3.3 Добавление новых войск.

Сдвиньте скобку вниз на две строки, затем на освободившеесе место скопируйте и вставьте следующий код:

  ["new_troop","new_troop","new_troops",tf_guarantee_boots|tf_guarantee_armor,no_scene,reserved,fac_commoners,
   [itm_sword,itm_hide_boots],
   str_6|agi_6|level(5),wp(60),knows_common,swadian_face1, swadian_face2],


Это данные с которыми мы планируем поиграться, создавая новый род войск. Для начала давайте назовём новый отряд по-человечески:

  ["red_rider","red rider","red riders",tf_guarantee_boots|tf_guarantee_armor,no_scene,reserved,fac_commoners,
   [itm_sword,itm_hide_boots],
   str_6|agi_6|level(5),wp(60),knows_common,swadian_face1, swadian_face2],



Теперь дадим ему немного брони и шлем:

  ["red_rider","red rider","red riders",tf_guarantee_boots|tf_guarantee_armor,no_scene,reserved,fac_commoners,
   [itm_sword,itm_hide_boots,
itm_leather_jerkin,itm_skullcap],
   str_6|agi_6|level(5),wp(60),knows_common,swadian_face1, swadian_face2],


Теперь каждый солдат из рода войск определенного как "red_rider" будет носить кожанную безрукавку. Однако, только у некоторых появится itm_skullcap, потому что в поле меток у этого рода войск гарантированны только броня и сапоги. Если мы хотим быть увереными, что все наши солдаты наденут шлемы на головы, мы должны добавить tf_guarantee_helmet в поле меток:

  ["red_rider","red rider","red riders",tf_guarantee_boots|tf_guarantee_armor|tf_guarantee_helmet,no_scene,reserved,fac_commoners,
   [itm_sword,itm_hide_boots,itm_leather_jerkin,itm_skullcap],
   str_6|agi_6|level(5),wp(60),knows_common,swadian_face1, swadian_face2],


Давайте сделаем некоторые изменения в свойствах войска. Введем силу равную 9 и ловкость 9. Затем изменим уровень на 4-ый и навык владения оружием установим на 80. Наш "new troop" будет выглядеть так:

  ["red_rider","red rider","red riders",tf_guarantee_boots|tf_guarantee_armor|tf_guarantee_helmet,no_scene,reserved,fac_commoners,
   [itm_sword,itm_hide_boots,itm_leather_jerkin,itm_skullcap],
   str_9|agi_9|level(4),wp(80),knows_common,swadian_face1, swadian_face2],


Теперь он готов быть помещеным в игру, в качестве эксперимента.


3.4 Наемники.

(прим: в версии 1.011 никакого Зендара нет, как нет и наемников Зендара. Туторитал немного устарел.) Сохраните ваш прогресc, затем откройте файл module_parties.py. Опускайтесь вниз, пока не увидете группу "zendar_mercs" (наемники Зендара):

("zendar_mercs","zendar_mercs",pf_disabled, no_menu, pt_none, fac_commoners,0,ai_bhvr_hold,0,(0,0),[(trp_farmer,15,0)]),

Это группа наемников; группа не отображенна на глобальной карте, но может быть нанята как наемники через хозяина таверны Зендара. Этот момент задан в свойствах самого хозяина таверны.

В данном случае, "zendar_mercs" состоят из 15 фермеров. Если вы запустите игру прямо сейчас, вы сможете их нанять. Однако, если мы изменим "trp_farmer" на "trp_red_rider", то сможем нанять 15 солдат, определенных как "red riders". Попробуйте сделать эти изменения.

Сохраните ваш прогресс, закройте файл module_parties.py и дважды щелкните на файле build_module.bat. Если сборка мода будет завершена без проблем, вы найдете ваших новых солдат доступных к найму через содержателя Зендарской таверны. (Вы должны начать новую игру, чтобы ваши действия возымели эффект).

Начните новую игру и наймите ваших "red riders". Когда выйдете из города, посражайтесь немного. Когда ваши новые войска наберутся опыта проапгрейдите их в "regular fighters".


3.5 NPCs

Различные наемники и NPC, которых вы видите в игре, очень похожи на обычные войска. Самый значимый, выделяющий их элемент это метка tf_hero; Каждый NPC с которым вы столкнетесь в игре отностся к типу “герой”, даже если это торговец. Главная разница между обычными войсками и героями:

1) Герои бессмертны, их здоровье представленно процентым значением, и вы не можете нанять двух или более одинаковых героев, если только они не были склонированны по ошибке или намеренно. Даже если вы намеренно создали несколько клонов, эта явно плохая идея.
2) Каждый герой занимает отдельный стэк.
3) Герой четко показан в сцене, когда его нанимают в группу.
4) Герои остаются с игроком когда он побежден врагами -- герои не захватываются врагами в плен -- но игрок может захватывать вражеских героев.

Т.к. нам следует иметь только один образец для каждого героя, то название во множественном числе не используется -- содержимое поля для названия во множественном числе будет идентично содержимому поля с названием.

Пример записи объекта героя:

["Marnid","Marnid","Marnid", tf_hero, scn_the_happy_boar|entry(4),reserved, fac_commoners,[itm_linen_tunic,itm_hide_boots,itm_club],def_attrib|level(6),wp(60),knows_trade_3|knows_inventory_management_2|knows_riding_2,0x00000000000c600301c2510211a5b292],

Вот это и есть наш друг Марнид, преданный компаньон на протяжении всей игры. Он отмечен как герой меткой tf_hero в его поле меток. Мы можем найти его в entry point/входной точке (4) в таверне счастливый вепрь. Он несколько слабоват в бою, но действительно хорош в торговле, что весьма пригодится в наших ранних странствиях, и как любой герой он бессмертен, покрайней мере если не будет удален особым скриптовым событием. Вы можете также заметить что у Марнида собственный, уникальный код лица 0x00000000000c600301c2510211a5b292. Его модификация возможна с помощью игрового редактора лиц, вам будет нужно экспортировать оттуда полученный кода лица в ваш мод. Это объясненно в части 10, "Использование внутриигрового редактора".

Другая важная деталь: несмотря на то, что идентификатор объекта Марнид в файле --"Marnid"-- использует заглавную букву M, мы должны всегда ссылаться на идентификатор начинающийся со строчной буквы. Модульная система выдаст ошибку, если вы попытаетесь использовать заглавную букву в ссылке на идентификатор из другого файла. Итак, для ссылки на Марнида мы будем использовать идентификатор "trp_marnid".

Последняя интересная деталь это данные, которые вы можете заметить в поле атрибутов Марнида - def_attrib. def_attrib такая же константа как и knows_common, но определенная в файле header_troops.py. Ее функция похожа на функцию выполняемую knows_common -- заранее заданный набор атрибутов для записей troop. Взято из header_troops.py:

def_attrib = str_5 | agi_5 | int_4 | cha_4

Это говорит нам о том, что на старте герой с def_attrib будет обладать силой 5, ловкостью 5, интелектом 4, харизмой 4. Если задать отдельно любой другой атрибут после отметки def_attrib, то указанное значение без проблем заменит соответствующее ему из def_attrib.

Теперь мы готовы создать нашего собственного героя. Скопируйте запись героя "Марнид" и вставьте сразу же за записью "red_rider" сделанной раннее.

  ["Geoffrey","Geoffrey","Geoffrey", tf_hero, scn_the_happy_boar|entry(4),reserved, fac_commoners,[itm_linen_tunic,itm_hide_boots,itm_club],def_attrib|level(6),wp(60),knows_trade_3|knows_inventory_management_2|knows_riding_2,0x00000000000c600301c2510211a5b292],

Здесь мы поменяли идентификатор нового героя и имя  на "Geoffrey"/Джоффри. Сделайти эти изменения и вы.


Теперь, если мы кликнем два раза на файл build_module.bat, модульная система скомпилирует наш мод без проблем. Наша запись не будет конфликтовать ни с чем. Но все же, существует одна главная проблемма -- оба, Джоффри и Марнид используют одну и ту же точку входа в одной и той же сцене. В результате мы найдем там только Марнида, поскольку его запись в файле расположенна выше, и Джоффри не появится, так как точка входа будет занята.

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

  ["Geoffrey","Geoffrey","Geoffrey", tf_hero, scn_the_happy_boar|entry(6),reserved, fac_commoners,[itm_linen_tunic,itm_hide_boots,itm_club],def_attrib|level(6),wp(60),knows_trade_3|knows_inventory_management_2|knows_riding_2,0x00000000000c600301c2510211a5b292],

Пытаясь поговорить с Джоффри, вы заметите что он не имеет диалогов связанных с ним. Это еще один пример взаимосвязи отдельных module-файлов.


Важно заметить, что так как мы просто копировали запись Марнида, то код его лица будет применён и к Джоффри. Как уже говорилось ранее, отредактировать его можно с помощью внутриигрового редактора, но не будем забегать вперёд, всему своё время.

Мы оставим Джоффри как он есть, и вернемся к нему позже, когда будем работать с файлами module_quests.py и module_dialogs.py.


3.6 Торговцы.

(прим: вы не найдете внутри файла module_troops.py версии 1.011 записи,  указанной ниже)
Торговцы это особый тип героя. В дополнению к метке tf_hero, они также обладают меткой tf_is_merchant. Эта метка не позволяет им надевать предметы из их инвентаря, за исключением тех, что изначально указанны внутри записи. Другими словами, торговцы получают многие виды предметов в течении игры, но они не надевают и не используют их. Все эти предметы будут выставленны на продажу.

Пример торговца:

  ["zendar_weaponsmith","Dunga","Dunga",tf_hero|tf_is_merchant, scn_zendar_center|entry(3),0,  fac_commoners,[itm_linen_tunic,itm_nomad_boots],def_attrib|level(2),wp(20),knows_inventory_management_10, 0x00000000000021c401f545a49b6eb2bc],

Это торговец оружие в Зендаре, по имени Dunga/ Дунга. Он почти идентичен другим торговцам из версии найтив МиБа. Если вы рассмотрите их (торговцев) поближе, единственными отличиями будут идентификатор, имя, местоположение и лицо.

(прим: с этого места и до конца раздела, я взял на себя смелость немного адаптировать текст к файлу module_troops.py версии 1.011) Добавить торговца не так просто, как оно может показаться. Они разбиты на группы по определенной причине. Скрипты МиБа обновляют инвентарь торговцев каждый день и для каждого типа по своему --делая это, скрипты сортируют торговцов исходя из положения их записей в файле module_troops.py относительно друг друга. Для примера, интервал в котором должны быть описаны торговцы броней включает в себя все начиная с "town_1_armorer" и заканчивая "town_1_weaponsmith". "town_1_weaponsmith" является вверхней границей интервала, но не входит в сам интервал, т.е. для добавления новой записи необходимо поставить ее перед "town_1_weaponsmith" (на строку выше), если мы хотим, чтобы она (новая запись) была включена в интервал отведенный для торговцев броней.

По этой причине новый торговец броней должен быть добавлен перед "town_1_weaponsmith". Новый торговей оружием перед "town_1_tavernkeeper". Новый торговец общими товарами перед строкой "merchants_end". (прим: последнюю строку я оставил так, как она есть в документации).


3.7 Сундуки.

 Сундуки являются особым "типом войск",  используемым только для игрового отображения инвентаря сундука (того что в нем лежит). Т.е. они реализуют лишь инвентарь, для сундука, но не сам сундук. Сундук, который вы видите в игре, является частично костяком сцены, частично данными сцены, частично войском и частично программным кодом. Сундук в целом это нечто сложное для реализации, т.к. требует работы с различными module-файлами. Здесь мы опишем только информацию касающуюся файла module_troops.py.

Пример сундука:

  ["zendar_chest","zendar_chest","zendar_chest",tf_hero|tf_inactive, 0,reserved,  fac_vaegirs,[],def_attrib|level(18),wp(60),knows_common, 0],

Все записи сундуков должны следовать этому примеру. Только имя, индентификатор, (возможно) уровень и навыки, и инвентарь могут быть измененны при создании нового сундука. Как уже отмеченно, запись объекта типа сундук в файле module_troops.py служит лишь для внутригрового отбражения инвентаря сундука. Более того, любые предметы, которые вы добавите в инвентарь записи сундука окажутся внутри сундука на старте игры.

Сундуки требуют записи как тип войска "сундук" в файле module_troops.py для функционирования. Однако они также требуют и другие модификации в различных module-файлах, которые мы опишем в специальной по-файловой документации.

Изучив эту часть документации, вы узнаете все, что следует знать касательно файла module_troops.py. Список всех доступных меток для работы с рассмотренными здесь типами записей находится в файле header_troops.py. Не стесняйтесь экспериментировать с ними, когда будете готовы. Давайте перейдем к следущей части документации.

8
   Вторая часть.
Как было отмеченно в предыдущей главе, ваша работа с модульной системой заключается в:
  1) Редактировании одного или более module-файлов (которые начинаются с “module_ ...“ и заканчиваются с  “.py”) и вносить в них любые изменения на ваш вкус. (Обычно, вам нужно щелкнуть правой кнопкой мышки и выбрать Edit with IDLE/редактировать с IDLE чтобы сделать это).
  2) Затем, дважды щелкнуть на файле build_module.bat в папке где расположена модульная система. Это действие позволит сконструировать ваш модуль (и доложит об ошибках, если они есть).
  3) Если ошибок не отмечено, вы можете загрузить ваш модуль с помощью загрузчика Mount&Blade и проверить измения, которые вы сделали. Иногда вам будет необходимо начать новую игру,  для того чтобы изменения возымели эффект.

2.1 Редактирование module-файлов.

Модульная система использует python-овские списки (прим: по-простому - это блоки данных; для более продвинутых - список это составной тип данных, в конкретном случае составными элементами для него послужит тип данных определямый как запись). Python-овские списки начинаются  с квадратных скобок, внутри скобок находится список объектов, отделенных друг от друга запятыми, и заканчиваются они (списки) закрытием квадратных скобок. Если вы откроете и посмотрите любой module-файл, вы увидете, что он состоит из подобных списков. Для примера, файл module_map_icons.py (он описывает значки на глобальной карте) включает:

map_icons = [
  ("player",0,"player", 0.2, snd_footstep_grass),
  ("player_horseman",0,"player_horseman", 0.2, snd_gallop),
  ("gray_knight",0,"knight_a", 0.2, snd_gallop),
  ("vaegir_knight",0,"knight_b", 0.2, snd_gallop),
  ("peasant",0,"peasant_a", 0.2,snd_footstep_grass),
  ("khergit",0,"khergit_horseman", 0.2,snd_gallop),
  ("axeman",0,"bandit_a", 0.2,snd_footstep_grass),
  ("woman",0,"woman_a", 0.2,snd_footstep_grass),
  ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
]


Описанный выше список, с именем  map_icons , является обычным python-овским списком и каждый его элемент это значок на карте. Разберем первый объект в списке - (“player”,0,'player”,0.2,snd_footstep_grass) . В дальнейшем мы будем определять тип таких объектов как “запись” (прим: в английском руководстве "tuple").  (прим. 2:  запись/tuple это объект, состоящий из нескольких элементов. Эти элементы могут быть разных типов). Записи, как и списки, включают в себя элементы отделённые запятой, но в отличе от списков, они заключены в круглые скобки. Структура каждого объекта типа  “запись” объясняется вначале module-файла и отличается от файла к файлу.

Запись каждого значка на карте содержит следущие элементы:

1) название значка, которое мы увидим на карте (набор символов)
2) метки - задаваемые условия, влияющие на отображение (это определённые параметры, о них позже)
3) название модели, которая будет отображена (должно соответствовать названию файла модели без расширения)
4) размеры модели (число; 1 соответствует 100% величине)
5) идентификатор звука - идентификатор записи, содержащей данные звукового сопровождения (должен соответствовать названию звукового файла без расширения)

Итак, первая запись это - (“player”,0,'player”,0.2,snd_footstep_grass) -
1) Название значка на карте = “player”
2) Флаги = 0
3) mesh name/название модели = “player”
4) mesh scale/масштаб = 0.2
5) индентификатор звука = snd_footstep_grass

Изучая шапку файла с описаниями и анализируя затем содержимое списков, вы можете самостоятельно разложить все структуры игровых объектов в каждом из module-файлов.


2.2 Добавление новых игровых объектов

Изучив структуру записи значков на карте, мы можем приступить к добавлению наших собственных значков. Давайте посмотрим на следущий python-овский список:

map_icons = [
  ("player",0,"player", 0.2, snd_footstep_grass),
  ("player_horseman",0,"player_horseman", 0.2, snd_gallop),
  ("gray_knight",0,"knight_a", 0.2, snd_gallop),
  ("vaegir_knight",0,"knight_b", 0.2, snd_gallop),
  ("peasant",0,"peasant_a", 0.2,snd_footstep_grass),
  ("khergit",0,"khergit_horseman", 0.2,snd_gallop),
  ("axeman",0,"bandit_a", 0.2,snd_footstep_grass),
  ("woman",0,"woman_a", 0.2,snd_footstep_grass),
  ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
]


Новый игровой объект в любом module-файле должен быть добавлен внутрь списка. Как мы видим, список в module_map_icons заканчивается сразу после ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass). Давайте освободим место под наш новый значок, для этого мы должны сдвинуть квадратную скобку “]” на одну строку ниже.

Теперь мы можем добавить в освободившееся место наш новый объект. Простейший способ сделать это – скопировать и вставить предыдущюю запись, а затем отредактировать ее содержимое. Для примера:

map_icons = [
  ("player",0,"player", 0.2, snd_footstep_grass),
  ("player_horseman",0,"player_horseman", 0.2, snd_gallop),
  ("gray_knight",0,"knight_a", 0.2, snd_gallop),
  ("vaegir_knight",0,"knight_b", 0.2, snd_gallop),
  ("peasant",0,"peasant_a", 0.2,snd_footstep_grass),
  ("khergit",0,"khergit_horseman", 0.2,snd_gallop),
  ("axeman",0,"bandit_a", 0.2,snd_footstep_grass),
  ("woman",0,"woman_a", 0.2,snd_footstep_grass),
  ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
  (
"new_icon",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
]


В этом примере, мы скопировали ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass) и задали новое название для нашего значка на карте; "new_icon". У этого объекта есть метка-флаг. Метки указывают на дополнительные свойства объекта, (каждое свойство имеет соответствующюю метку-переключатель и может быть “включенно” или  “выключенно”  при помощи добавления или удаления такой метки). В нашем примере метка mcn_no_shadow на новом объекте указывает, что он не отбрасывает тени на землю.

Удалим метку mcn_no_shadow с нашего нового значка. Для этого заменим метку mcn_no_shadow на 0. Это скажет модульной системе, что меток для данного объекта нет.

map_icons = [
  ("player",0,"player", 0.2, snd_footstep_grass),
  ("player_horseman",0,"player_horseman", 0.2, snd_gallop),
  ("gray_knight",0,"knight_a", 0.2, snd_gallop),
  ("vaegir_knight",0,"knight_b", 0.2, snd_gallop),
  ("peasant",0,"peasant_a", 0.2,snd_footstep_grass),
  ("khergit",0,"khergit_horseman", 0.2,snd_gallop),
  ("axeman",0,"bandit_a", 0.2,snd_footstep_grass),
  ("woman",0,"woman_a", 0.2,snd_footstep_grass),
  ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
  ("new_icon",
0,"City", 0.9,snd_footstep_grass),
]


Оба значка - “town” и “new_icon” - используют меш/модель “City”, это значит что оба используют одну и ту же 3D модель “City” в resource-файлах игры. Изменяя этот элемент мы можем сменить графическое отображение значка на любую 3D модель из resourse-файлов.

Т.к. оба значка используют одну и ту же 3D модель, то наш новый значок "new_icon" будет выглядеть в точности как значок "town".

Давайте попробуем задать нашему значку немного отличный от "town" облик.

map_icons = [
  ("player",0,"player", 0.2, snd_footstep_grass),
  ("player_horseman",0,"player_horseman", 0.2, snd_gallop),
  ("gray_knight",0,"knight_a", 0.2, snd_gallop),
  ("vaegir_knight",0,"knight_b", 0.2, snd_gallop),
  ("peasant",0,"peasant_a", 0.2,snd_footstep_grass),
  ("khergit",0,"khergit_horseman", 0.2,snd_gallop),
  ("axeman",0,"bandit_a", 0.2,snd_footstep_grass),
  ("woman",0,"woman_a", 0.2,snd_footstep_grass),
  ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
  ("new_icon",0,"City",
5.0,snd_footstep_grass),
]


В этом примере мы изменили масштаб значка с 0.9 до 5.0. Это значит, что значок будет отображен в пять раз крупнее обычного. Это поможет нам отличить наш значок от обычного значка “town", когда мы добавим его в игру.

Далее мы создадим новый отряд (party) в файле module_parties.py  (прим: все объекты на игровой карте ислючая ландшафт являются “отрядами”), который будет использовать наш значок на карте. Чтобы сделать это, нам необходимо “сослаться” на созданный нами значок из файла module_parties.py.


2.3 Ссылки на игровые объекты

Откройте файл module_parties.py в папке с модульной системой. Нам предстоит разобрать другой python-овский список – parties = [.

Файл module_parties.py

Как видим, структура записей в module_parties.py отличается от module_icons.py. Впрочем как и многие - если не все - другие "module_" файлы. Пока есть возможность, изучим структуру этого.

В начале файла идёт расшифровка записи партий на английском.


 Далее идёт описание флагов:

...
no_menu = 0
#pf_town = pf_is_static|pf_always_visible|pf_hide_defenders|pf_show_faction
pf_town = pf_is_static|pf_always_visible|pf_show_faction|pf_label_large
pf_castle = pf_is_static|pf_always_visible|pf_show_faction|pf_label_medium
pf_village = pf_is_static|pf_always_visible|pf_hide_defenders|pf_label_small
...


 Поговорим подробнее о Флагах:

 Флаги:

 pf_town
 pf_castle
 pf_village 

 В свою очередь содержат в себе несколько других флагов

 pf_is_static
 pf_always_visible
 pf_show_faction
 pf_hide_defenders
 pf_label_large
 pf_label_medium
 pf_label_small


 Насколько понимаю можно создать свой флаг и прописать ему нужные флаги, например как в module_parties.py из MS от TLD:

...
no_menu = 0
pf_tld_down  = pf_is_static|pf_always_visible|pf_show_faction|pf_label_large
...


 Таким образом например запись

...
  ("town_1","Sargoth",  icon_town|pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-17.6, 79.7),[], 170),
...

 Реально выглядит

...
  ("town_1","Sargoth",  icon_town|pf_is_static|pf_always_visible|pf_show_faction|pf_label_large, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-17.6, 79.7),[], 170),
...

 Увидеть это можно сравнив флаги города Zendar с другими городами

...
  ("zendar","Zendar",pf_disabled|icon_town|pf_is_static|pf_always_visible|pf_hide_defenders, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(18,60),[]),

  ("town_1","Sargoth",  icon_town|pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-17.6, 79.7),[], 170),
...
  ("castle_1","Culmarr_Castle",icon_castle_a|pf_castle, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-101.3, -21),[],50),
...
  ("village_1", "Yaragar",  icon_village_a|pf_village, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-60, -9.5),[], 100),
...



 Рассмотрим свойства флагов

 Флаги разделяются между собой  |


 pf_disabled  -  исключает партию из игрового процесса

 pf_is_static  -  делает партию статичной на глобальной карте

 pf_always_visible  -  если убрать этот флаг, то объект будет виден на глобальной карте только при приближении игрока. Растояние видимости зависит от навыка ГГ - spotting , и от параметра seeing_range = 6.5(по умолчанию) который прописан в module.ini(находится...Warband\Modules\название мода)

 pf_show_faction  -  не могу точно сказать, как я понял от его наличия зависит возможность захватить замок или город

 pf_hide_defenders  -  не могу точно сказать, как я понял он противоположность к pf_show_faction его наличие отменяет возможность захватить замок или город

 pf_label_large, pf_label_medium, pf_label_small - по логике задают размер иконки, но на практике при присвоении иконки города(icon_town) - замку, уменьшения иконки не наблюдалось.



 Пару слов о "Повороте партии в градусах"

...
  ("town_1","Sargoth",  icon_town|pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-17.6, 79.7),[], 170),
...

 Поворот партии в градусах изменяется от 0 до 360 (360=0, то есть фактически от 0 до 359 градусов), против часовой стрелки, при нуле ворота города смотрят на юг карты. Отсутсвие параметра в свойствах партии говорит о том, что он равен 0(как например у Zendar).

...
  ("zendar","Zendar",pf_disabled|icon_town|pf_is_static|pf_always_visible|pf_hide_defenders, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(18,60),[]),
...

Пример записи отряда:

("zendar","Zendar",icon_town|pf_is_static|pf_always_visible|pf_hide_defenders, "zendar", pt_none, fac_neutral,0,ai_bhvr_hold,0,(2,46),[(trp_swadian_knight,6,0)]),

Эта запись определяет местонахождение города Zendar на карте. Свойства объекта Zendar описываются в соответствующих полях – довольно похожие на те, что мы успели разобрать в файле module_icons.py.

Итак:

1) Идентификатор отряда. Служит для обращения к объекту из разных частей файла и из других файлов вообще.
2) Имя отряда. Это имя будет отображенно как название объекта в игре. Может быть отлично от индентификатора отряда, если пожелаете.
3) Метки отряда. Первая метка для каждого отряда (их может быть несколько, отделяются друг от друга значком “|”) должна указывать на значок, используемый при отображении отряда на карте.
4) Меню. Это поле устарело и с версии M&B 0.730 больше не используется.
5) Шаблон отряда. Индентификатор (ID) шаблона, к которому описываемый отряд принадлежит. Используйте метку “pt_none”, как значение по умолчанию.
6) Фракция отряда. Может иметь любое значение входящее в файл module_factions.py.
7) Характеристики отряда. Для объяснения значения Personality/характеристика смотри и разбирай записи из файла header_parties.py.
8) Поведение ИИ (искуственный интелект). Определяет как ИИ отряд будет действовать на глобальной карте.
9) Группы-цели для ИИ. Определяет цели для ИИ.
10) Инициализация координат. Определяет стартовое положение отряда на карте; X,Y.
       11.1) Идентификатор войска. Любое значение (как регулярные войска, так и герои) из файла module_troops.py.
       11.2) Число войск в описываемом стеке/отряде; не варьируется. То колличество, что введете, то и будет. (прим: имеетсе ввиду, что нельзя задать количество войск, определяющееся из заданного интервала, скажем: ..., (свадийские рыцари, от50..до60),...)
       11.3) Метки войска. Опционально, т.е. необязательно к заполнению. Используйте метку pmf_is_prisoner если хотите отметить, что заявленный стек – заключенный.

Рассмотрим запись объекта Zendar:
 
("zendar","Zendar",icon_town|pf_is_static|pf_always_visible|pf_hide_defenders, "zendar", pt_none, fac_neutral,0,ai_bhvr_hold,0,(2,46),[(trp_swadian_knight,6,0)]),

1) Идентификатор отряда = “zendar"
2) Имя отряда = “Zendar”
3) Метки = icon_town|pf_is_static|pf_always_visible|pf_hide_defenders
4) Меню = "zendar"
5) Шаблон отряда = pt_none
6) Фракция отряда = fac_neutral
7) Характеристики отряда = 0
8) Поведение ИИ = ai_bhvr_hold
9) Отряды-цели ИИ = 0
10) Координаты отряда на глобальной карте = (2,46)
11) Список войск
       11.1) Индентификатор = trp_swadian_knight
       11.2) Число войск в этом стеке = 6
       11.3) Метки войск = 0

Рассматривая поле (3), мы можем заметить, что Zendar ссылается на значок  “town”  из файла module_icons.py, добавляя перед его именем префикс icon_. Этот префикс указывает системе на соответствующий module-файл, где описан данный объект. Для того, чтобы сослаться на файл module_icons.py мы будем использовать префикс icon_; для того, чтобы сослаться на module_factions.py мы будем использовать префикс fac_ ; для ссылки на module_parties.py префикс p_; и так далее. В конце статьи вы найдете список префиксов для каждого module-файла.

Теперь, когда мы знаем как "устроены" отряды, мы можем добавить свой. Но перед этим, стоит отметить следущее: в файле module_parties.py, как и в некоторых других module-файлах, не следует добавлять ваш новый объект в конец списка. Внутри самого файла вы даже найдёте предупреждение об этом. Добавление записи в неправильном месте может дизорганизовать работу кода игры. В файле module_parties.py, рекомендуется добавлять ваш новый объект в список parties между строками "training_ground"  и "castle_1".

Теперь, скопируем и вставим в место оговоренное выше строку начинающуюся как: ("town_14" ... 

  ("training_ground","Training Ground",  icon_town|pf_town|pf_disabled, "training_ground", pt_none, fac_vaegirs,0,ai_bhvr_hold,0,(-2,-3),[(trp_vaegir_knight,6,0)]),

  ("new_town","Mod_Town",  icon_town|pf_town, "town", pt_none, fac_vaegirs,0,ai_bhvr_hold,0,(-4,-37),[(trp_vaegir_knight,6,0)]),

  ("castle_1","Culmarr_Castle",icon_town|pf_is_static|pf_always_visible, "castle", pt_none, fac_outlaws,0,ai_bhvr_hold,0,(-47,-51),[(trp_swadian_knight,5,0),(trp_swadian_crossbowman,25,0)]),

В этом примере мы изменили идентификатор новой группы с “town_14” на “new_town”, и название группы с “Halmar” на “Mod_Town”.

Глядя на запись, можно установить следующее:.

1) Ссылаясь на этот отряд из другого файла, мы должны использовать идентификатор "new_town" с префиксом "p_". Суммируя, получаем "p_new_town" .
2) В игре мы будем видеть имя объекта только как "Mod Town" и никогда не увидим идентификатор.
3) Этот отряд использует icon_town (объект town из файла icons_modules.py) и метку pf_town – метку, она добавляет общие свойства города. С полем меток мы ещё поэкспериментируем.
4) Mod Town принадлежит королевству Вегир.
5) Если мы добавим наш город в игру, его местонахождение будет идентично координатам города Halmar. Это мы тоже изменим в дальнейшем.

  ("training_ground","Training Ground",  icon_town|pf_town|pf_disabled, "training_ground", pt_none, fac_vaegirs,0,ai_bhvr_hold,0,(-2,-3),[(trp_vaegir_knight,6,0)]),

  ("new_town","Mod_Town",  icon_new_icon|pf_town, "town", pt_none, fac_neutral,0,ai_bhvr_hold,0,(-1,-1),[(trp_vaegir_knight,6,0)]),

  ("castle_1","Culmarr_Castle",icon_town|pf_is_static|pf_always_visible, "castle", pt_none, fac_outlaws,0,ai_bhvr_hold,0,(-47,-51),[(trp_swadian_knight,5,0),(trp_swadian_crossbowman,25,0)]),

Мы поменяли значок нашего города на значок icon_new_icon, вставив вместо метки icon_town метку icon_new_icon. (прим. для несообразительных: префикс icon_ + название значка на карте;  icon_ +  new_icon =  icon_new_icon). А также, мы изменили координаты местоположения объекта на карте на (-1,-1) и изменили фракцию города на fac_neutral.

Город теперь использует наш значок при отображении на экран и обладает собственным местоположением на карте. И все это без особых проблемых. Побробуйте это сделать сами.

Сохраните ваш прогресс, а затем два раза щелкните на build_module.bat. И если все прошло хорошо, вы можете запустить ваш мод и увидеть новый город в центре карты.

Если все не прошло хорошо, внимательно проверьте синтаксические ошибки. Неправильно, в разных местах по разному, указаны свои же названия,  запятые и скобки не на своих местах. Плохой синтаксис наиболее распространенная причина всех ошибок. Не забывайте пробелы в начале записей, а также что надо ставить не кавычки на русской раскладке по shift+2, а двойной апостроф по shift+э на английской.

В игре, попробуйте зайти в город. Вы попадете в боевую стычку, потому что город в настоящий момент не имеет привязаного к нему игрового меню. Создание игрового меню это более сложная задача и поэтому пока мы оставим наш город в том виде, в котором он есть. Позже мы вернемся к нему, но в последней части этой документации.

Как вы можете видеть, взаимосвязь различных файлов модульной системы может быть внешней. Каждая часть работы должна быть сделана в соответсвующем module-файле.


fac_   -- module_factions.py
icon_   -- module_map_icons.py
itm_   -- module_items.py
mnu_   -- module_game_menus.py
mno_   -- module_game_menus.py -- References an individual menu option in module_game_menus.
mt_   -- module_mission_templates.py
psys_   -- module_particle_systems.py
p_      -- module_parties.py
pt_   -- module_party_templates.py
qst_   -- module_quests.py
script_   -- module_scripts.py
scn_   -- module_scenes.py
spr_   -- module_scene_props.py
str_   -- module_strings.py
trp_   -- module_troops.py


Теперь, когда мы покрепче схватили базовые знания, можем перейти к более глубокому изучению module-файлов. Открывайте 3 часть.

9
Первая часть


1.1 Что такое модульная система?

 Модульная система МиБ - это набор python-вских скриптов, позволяющих вам  модифицировать содержимое МиБа. Мы использовали эту систему при создании официальной версии МиБа. Модульная система позволяет Вам добавлять новые типы войск, новые характеры, новые квесты, новые диалоги и т.д. Или же вы можете редактировать существующее наполнение найтивовской версии.

 Это важно отметить -  МиБ не использует python и не работает с python-вскими скриптами напрямую. Вместо этого python-овские скрипты указывают как создавать особые текстовые файлы ,а уже с ними  работает МиБ.

МиБ считывает содержимое текстовых файлов находящихся в папках Mount&Blade/Modules . Теоретически вы можете делать все те же модификации, что позволяет вам  модульная система, путем редактирования текстовых файлов. (Более того, некоторые мододелы своими работами доказывают, что редактируя текстовые файлы мы можем реально создавать замечательные моды). Однако, текстовые файлы сложночитаемы и очень непрактичны для работы с ними. На данный момент существует два варианта написания новых модулей. Первый – это официальная модульная система описанная в этой документации. Другой вариант – это эффидианский неофициальный редактор (Effidian's unofficial editor), который в последнее время не обновлялся и является  не совместимым с текущей v.1.011 версие МиБа, но работает со старыми версиями МиБа, такими как v.0.751 .


1.2 Требования для использования модульной системы.

Модульная ситема базируется на python-скриптах, и как результат, вам необходимо иметь установленную среду python на вашем компьютере. Вы можете скачать python с сайта python.org из раздела downloads.
Там есть более чем несколько версий для скачивания, но вам нужна 2.6.6 или 2.7.1 2.7.10.

После того как вы скачали и установили Python, вам также необходимо добавить Python как windows path enviroment variable. В разных версиях Windows это делается по-разному:



1.3 Установка модульной системы.

Последняя версия модульной системы может быть скачана с нашего сайта в разделе редакторов.

Вам нужно скачать архив с модульной системой и распаковать его (требует архиваторов, таких как WinRAR или 7-zip ). Распакуйте модульную систему в любую удобную вам папку, это не имеет принципиального значения.


1.4 Файлы модульной системы.

Теперь давайте посмотрим на файлы включенные в модульную систему. Это файлы c расширением .py , они есть следующих типов:

 - файлы начинающиеся с приставки  “header_”
 - файлы начинающиеся с приставки  “process_ “
 - файлы начинающиеся с приставки  “ID_ “
 - файлы начинающиеся с приставки  “module_ “

Первые два типа файлов (“header_” и “process_ “) необходимы для действия модульной системы. Вы не должны модифицировать их вовсе. Третий тип (“ID_ “) временные файлы создающиеся во время постройки (генерации) модификации. Вы можете даже удалить их, если захотите, модульная система сгенерирует их снова при генерации модуля. Последний, четвертый тип (“module_ “) и есть те самые файлы, что содержат актуальную для нас информацию. Именно их мы и будем модифицировать.


1.5 Создание новых модификаций.

Прежде чем идти дальше, давайте сначала создадим папку для вашего нового мода. Для этого нам нужно зайти в папку Mount&Blade/Modules/ (по умолчанию это  "c:/Program Files/Mount&Blade/Modules" ). Теперь, в папке  “Modules /”  должна быть папка с названием “Native”. Это, к слову, и есть офицальный мод (пресловутый "натив"). Для вашего собственного мода вы должны создать новую папку в директории  /Modules и скопировать в нее файлы из папки “Native”. Эта новая папка будет папкой для вашего собственного мода, так что называйте ее как хотите. Для простоты и ясности, я предлагаю назвать ее “MyNewModule” .

Вы можете проверить все ли правильно вы сделали попытавшись запустить МиБ. В меню загрузчика МиБа должно быть специальное окошко, позволяющие вам выбрать мод, который вы хотите запустить. Попытайтесь среди предложенных модификаций отыскать ваш новый мод и запустить его. Так как мы скопировали содержимое папки “Naitve” в папку вашей новой модификации, игра в которую вы сыграете будет идентична нативу.

Следущий шаг, мы должны заставить модульную систему использовать вашу новую папку в качестве цели (для экспорта генерируемого модуля). Что бы сделать это, откройте файл module_info.py (из папки модульной системы) для редактирования (щелкните правой кнопкой мышки на файле и выберите Edit with IDLE /редактировать с IDLE; или откройте файл используя Notepad /блокнот или любой другой, предпочитаемый вами, текстовый редактор). Найдите строку начинающуюся с export_dir = ”...” (либо если ее  нет, то добавьте) и присвойте export_dir адрес папки с вашим новым модом. К примеру, если ваша папка находится по адресу:  "C:/Program Files/Mount&Blade/Modules/MyNewModule" То вы должны вставить:

export_dir = "C:/Program Files/Mount&Blade/Modules/MyNewModule/"

На этом, установка модульной системы закончена. Чтобы проверить правильность установки, удалите из папки вашего мода файл conversation.txt  и затем два раза щелкните на файле build_module.bat. Вы должны увидеть командную строку  выводящую на экран что-то вроде этого:

Initializing...
Compiling all global variables...
Exporting strings...
Exporting skills...
Exporting tracks...
Exporting animations...
Exporting meshes...
Exporting sounds...
Exporting skins...
Exporting map icons...
Creating new tag_uses.txt file...
Creating new quick_strings.txt file...
Exporting faction data...
Exporting item data...
Exporting scene data...
Exporting troops data
Exporting particle data...
Exporting scene props...
Exporting tableau materials data...
Exporting presentations...
Exporting party_template data...
Exporting parties
Exporting quest data...
Exporting scripts...
Exporting mission_template data...
Exporting game menus data...
exporting simple triggers...
exporting triggers...
exporting dialogs...
Checking global variable usages...

______________________________

Script processing has ended.
Press any key to exit. . .


Если вместо этого вы получили сообщение об ошибке/error, проверьте что вы точно следовали всем шагам описанным в этом туториале. Если уверены в правильности своих действий, попробуйте с помощью поисковой функции найти на форумах taleworlds.com кого-то c похожей проблемой; есть шанс, что там же вы найдете и решение. Некоторые ошибки описаны в посте по ссылке (англ) http://forums.taleworlds.com/index.php/topic,6575.msg96347.html#msg96347

Если же ошибки не возникло/были успешно устраненны – поздравляю! Вы продвинулись на один шаг в создании вашего собственного мода, настроив модульную систему. Теперь давайте перейдем ко второй части.

Страницы: [1]
СиЧЪ Total War СиЧЪ Total War
Сайт "Всадники Кальрадии" не является СМИ. Администрация не несет ответственность за высказывания и публикацию каких-либо материалов, сделанные любыми пользователями форума, в том числе посредством личных и публичных сообщений. Материалы, размещенные на ресурсе третьими лицами, могут содержать информацию, не предназначенную для лиц, не достигнувших совершеннолетия. При обнаружении на ресурсе материалов, нарушающих законодательство Российской Федерации, необходимо обращаться к администрации.
Сайт работает на быстром VPS/VDS хостинге от FASTVPS


Powered by SMF 2.0 | SMF © Simple Machines LLC