en|General Information|ru|Общая информация|

en|You will need|ru|Для моддинга Вам понадобится| MMArchive. ru|Также могут пригодиться |mm8leveleditor en|and|ru|и| MM Map Vieweren| may also come in handy|.
en|Extract all <'>*.txt files from appropriate LOD archive: <'>icons.lod in MM6, <'>events.lod in MM7, <'>EnglishT.lod in MM8. You will need them.| ru|Извлеките все файлы <'>*.txt из соответствующих архивов LOD: <'>icons.lod в MM6, <'>events.lod в MM7, <'>EnglishT.lod в MM8. Вам они понадобятся.|
en|Easiest thing to start with is editing text tables with|ru|Проще всего начать с редактирования текстовых таблиц с помощью| TxtEdit. en|In addition to <'>*.txt files that you've extracted from LOD archives MMExtension will create some tables in Data\Tables folder on first launch. You can change any values in them and add new lines to many of them.| ru|В добавок к файлам <'>*.txt, которые Вы извлекли из LOD-архива MMExtension создаст некоторые таблицы в папке Data\Tables при первом запуске. Вы можете менять в них любые значения и добавлять новые строки ко многим из них.|

en|Lua is a simple, yet powerful scripting language. You can read about it here, here and here. MMExtension exposes all standard Lua and LuaJIT libraries.
I used to use SciTE to edit Lua scripts before I switched to Sublime Text. Here are some of my settings for SciTE:| ru|Lua - это простой, но гибкий скриптовый язык. Вы можете прочитать о нём здесь, здесь и здесь. MMExtension предоставляет все стандартные библиотеки Lua и LuaJIT.
Пока я не перешёл на Sublime Text, я использовал SciTE для редактирования скриптов Lua. Вот некоторые мои настройки для SciTE:|
en|Open|ru|Откройте| SciTEGlobal.properties.

en|Find and set up these settings:|ru|Найдите и установите следующие параметры:|
tabsize=2
indent.size=2
braces.autoclose=0

en|Find|ru|Найдите| user.shortcuts=\
en|delete line with|ru|удалите строку с| Alt+X
en|add line|ru|добавьте строку|: Ctrl+Shift+Z|IDM_REDO|\
en|add line|ru|добавьте строку|: Ctrl+R|IDM_REPLACE|\

en|All scripts are located in Scripts directory. Subfolders determine when scripts get loaded and unloaded:| ru|Все скрипты находятся в директории Scripts. Скрипты в разных подпапках загружаются и выгружаются в разные моменты:|
 en|Subfolder|ru|Подпапка|  en|Description|ru|Описание|
Core en|MMExtension core scripts. Don't put your scripts here.|ru|Скрипты ядра MMExtension. Не кладите сюда свои скрипты.|
General en|These scripts are loaded before the game starts and are never unloaded.| ru|Эти скрипты загружаются перед началом игры и никогда не выгружается.|
Global en|These scripts are loaded when a new game is started or a saved game is loaded and are unloaded when user exits to main menu or loads another game. This is a good place for most of your scripts that don't belong to specific maps. Quests scripts in particular.| ru|Эти скрипты загружаются, когда запускается новая игра или загружается сохраненная игра, и выгружаются, когда пользователь выходит в главное меню или загружает другую игру. Это хорошее место для большинства скриптов, которые не относятся к конкретной карте. В частности, для скриптов заданий.|
Localization en|These scripts and text files are loaded on game start just before "General" scripts and can be reloaded with <#>ReloadLocalization():ReloadLocalization function. They should be used for mods localization.| ru|Эти скрипты загружаются при старте игры перед загрузкой скриптов из папки "General" и могут быть загружены снова вызовом функции <#>ReloadLocalization():ReloadLocalization. Они предназначены для локализации модов.|
Maps en|These scripts correspond to maps. For example, scripts named <'>oute3.lua and <'>some_name.oute3.lua would be loaded when <'>oute3.odm map (New Sorpigal) is loaded and unloaded when it is left.| ru|Эти скрипты соответствуют картам. Например, скрипты под названием <'>oute3.lua и <'>любое_имя.oute3.lua будет загружаться при загрузке карты <'>Oute3.odm (Нью-Сорпигал) и выгружаться при выходе из неё.|
Modules en|These scripts aren't loaded automatically. Instead they can be loaded with require function.| ru|Эти скрипты не загружаются автоматически. Вместо этого, их можно загружать при помощи функции require.|
Structs en|These scripts are intended for structs definitions. These things are low-level, so I won't describe them yet. If you find some interesting address with ArtMoney or disassembler, contact me.| ru|Эти скрипты предназначены для определения структур. Эти вещи низкого уровня, так что пока я не буду описывать их. Если вы нашли интересный адрес с помощью ArtMoney или дизассемблера, свяжитесь со мной.|
en|You can create your own subfolders. Scripts in them won't be loaded automatically and won't be unloaded either.| ru|Вы можете создавать свои собственные подпапки. Скрипты в них не будут загружаться автоматически и не будет выгружаться.|

en|The word 'unload' means removing all events of a script. As a consequence of global and maps scripts being unloaded and then loaded again, you can change them and test changes by simply reloading a saved game. No need to exit Might and Magic for this.| ru|Слово "выгружать" означает удаление всех событий скрипта. Благодаря выгрузке и последующей повторной загрузке глобальных скриптов и скриптов карт, вы можете их изменить и протестировать изменения, просто перезагрузив сохраненную игру. Не нужно для этого выходить из игры Меч и Магия.|

en|Debug console is a convenient way to quickly test things. Press Ctrl+F1 to open it. You can write any script here. For example, dump(Party[0].Stats) would output first player's stats.
Press Ctrl+Enter to execute a script.
Press Ctrl+E to repeat the last script.
Press Esc to cancel debug console.
| ru|Отладочная консоль - это удобный способ быстро проверить что-нибудь. Нажмите Ctrl + F1, чтобы открыть её. Здесь Вы можете писать любые скрипты. Например, dump(Party[0].Stats) выведет значения характеристик первого персонажа.
Нажмите Ctrl + Enter, чтобы выполнить скрипт.
Нажмите Ctrl + E, чтобы повторить последний скрипт.
Нажмите Esc, чтобы закрыть консоль.
|
en|Variables used by your scripts can be of 4 types:
Local variables, declared with the word local in Lua. They can be used in the place where they were declared (i.e. function, script or code block) and aren't stored is saved games.
Global variables. They can be accessed from anywhere, but aren't stored in saved games.
Variables in <#>vars table. They can be accessed from anywhere and are stored in savegames.
Variables in <#>mapvars table. They belong to current map. When a new map is entered the appropriate variables table is set as <#>mapvars table. They are stored in savegames. When a map is refilled, new <#>mapvars table is created, but the old one is accessible at this moment as <#>Map.Refilled:structs.GameMap.Refilled.
| ru|Переменные, используемые скриптами, могут быть 4 типов:
Локальные переменные, объявленные со словом local в Lua. Они могут быть использованы в месте, где они были объявлены (т.е. функция, скрипт или блок кода) и не хранятся в сохранении игры.
Глобальные переменные. Они могут быть доступны из любого места, но они не хранятся в сохранении игры.
Переменные в таблице <#>vars. Они могут быть доступны из любого места и хранятся в сохранении игры.
Переменные в таблице <#>mapvars. Они относятся к текущей карте. При загрузке карты, соответствующая таблица с переменными устанавливается, как <#>mapvars. Они хранятся в сохранении игры. При перезаполнении карты таблица <#>mapvars пересоздаётся, старая таблица в этот момент доступна, как <#>Map.Refilled:structs.GameMap.Refilled.
|

en|Hello world!|ru|Привет мир!|

en|Let's first use the debug console. In the game press Ctrl+F1 and paste this script:| ru|Для начала используем отладочную консоль. В игре нажмите Ctrl+F1 и вставьте следующий скрипт:| Message("Hello world!") en|Press Ctrl+Enter to execute your welcome script.| ru|Нажмите Ctrl+Enter, чтобы исполнить свой приветственный скрипт.|

en|To make your first game script create <'>out01.lua file (or <'>oute3.lua for MM6) in Scripts\Maps directory and write this text there:
| ru|Для того, чтобы сделать свой первый скрипт игры, создайте файл <'>out01.lua (или <'>oute3.lua для MM6) в каталоге Scripts\Maps и напишите в нём этот текст:
| MessageBox("Hello world!") en|This script will show a <#>system message box:MessageBox each time you enter the first map.

To show a native MM message you can use a script like this:| ru|Этот скрипт будет показывать <#>системное окно сообщения:MessageBox каждый раз при входе в первую карту.

Чтобы показать родное сообщение МM, вы можете использовать подобный скрипт:| Game.NeedRedraw = true -- I remember having problems with minimap not getting drawn Sleep(1) -- sleep for 1 tick to let it be drawn Message("Hello world!") en|The reason for the first two lines of code is that Might and Magic doesn't support showing simple messages when the map is just loaded.
| ru|Первые 2 строки в нём понадобились из-за того, что Меч и Магия не поддерживает показ простых сообщений в момент загрузки карты.
|

en|Decompiled Scripts|ru|Декомпилированные скрипты|

en|Original Might and Magic scripts are stored in binary format in files with extension <'>.evt. MMExtension can decompile these scripts.|ru|Оригинальные скрипты Меча и Магии хранятся в двоичном формате в файлах с расширением <'>.evt. MMExtension может декомпилировать эти скрипты.| en|Get decompiled scripts here.|ru|Ссылка на декомпилированные скрипты.|
en|Decompiled scripts come in the form of Lua scripts and also pseudo code that directly corresponds to binary format.
Just so you know, here are the differences between pseudo code commands and real MMExtension commands:
1) "evt." isn't written in them.
2) They use jumps instead of if-then and loops. They have their lines numbered.
3) They almost always supply parameter names in calls to Evt functions. You can do the same, but you can also use calls without parameter names.
4) Some commands are replaced by better analogs in MMExtension. For example, you cannot use <'>evt.OnTimer command, instead you should use <#>Timer function.
5) There are other differences, like declaration of events.
| ru|Декомпилированные скрипты представлены в виде скриптов Lua и в псевдокоде, который напрямую соответствует бинарному формату.
Просто для информации, далее приведены различия между командами псевдокода и реальными командами MMExtension:
1) "evt." в них не пишется.
2) Они используют goto, а не структурные условные операторы и циклы. Их строки пронумерованы.
3) Они почти всегда проставляют имена параметров в вызовах функций Evt. Вы можете сделать то же самое, но вы также можете использовать вызовы без имен параметров.
Некоторые команды заменены лучшими аналогами в MMExtension. Например, вы не можете использовать команду <'>evt.OnTimer, вместо этого вы должны использовать функцию <#>Timer.
5) Есть и другие отличия в синтаксисе скриптов - например, объявление событий.
|
en|Here's an example (a Lua script)|ru|Пример (в формате Lua)|:
Game.MapEvtLines:RemoveEvent(60) -- remove original event evt.map[60] = function() local i if evt.Cmp("MapVar0", 4) then evt.StatusText(5) -- "Nothing here" else evt.Add("MapVar0", 1) evt.StatusText(4) -- "You found something!" i = Game.Rand() % 6 if i == 1 then evt.Add("Inventory", 1) -- "Longsword" elseif i == 2 then evt.Add("Inventory", 15) -- "Dagger" elseif i == 3 then evt.Add("Inventory", 58) -- "Club" elseif i == 4 then evt.Add("Inventory", 161) -- "Phirna Root" elseif i == 5 then evt.Add("Inventory", 309) -- "Inferno" else evt.Add("Inventory", 94) -- "Cloth Hat" end end end en|It comes from a decompiled script <'>ZNWC.lua of MM6. I just added the first line to make it remove original event handler, so that this code fragment can be used on its own. Decompiled scripts remove all standard event handlers at once with Game.MapEvtLines.Count = 0 command. If you want to modify parts of decompiled scripts, you should also extract them like I did here. See <#>RemoveEvent:structs.EventLine.RemoveEvent method for more info.
| ru|Это код из декомпилированного скрипта <'>ZNWC.lua из MM6. Я только добавил первую строчку, которая убрает оригинальный обработчик события, чтобы этот фрагмент кода можно использовать сам по себе. Декомпилированные скрипты убирают сразу все оригинальные обработчики командой Game.MapEvtLines.Count = 0. Если вы хотите изменить некоторые события в декомпилированных скриптах, вытаскивайте эти события в отдельный скрипт, как сделал здесь я. Подробнее см. в описании метода <#>RemoveEvent:structs.EventLine.RemoveEvent.
|
en|Now it's time for a script doing the same thing (if you play the game from start with it), but written using more fitting MMExtension features:|ru|Если же полноценно использовать возможности MMExtension, то скрипт, делающий то же самое (если играть с ним с самого начала игры), будет выглядеть так:|
local TXT = Localize{ NothingHere = "Nothing here", FoundSomething = "You found something!", } local items = {1, 15, 58, 161, 309, 94} Game.MapEvtLines:RemoveEvent(60) -- remove original event evt.map[60] = function() if mapvars.Shelf60 == 4 then return Game.StatusText(TXT.NothingHere) end mapvars.Shelf60 = (mapvars.Shelf60 or 0) + 1 Game.StatusText(TXT.FoundSomething) local i = math.random(1, #items) evt.Add("Inventory", items[i]) -- alternatively: evt.GiveItem{Id = items[i]} end
en|You can decompile scripts by yourself if you ever need to. To do so, extract all <'>*.evt and <'>*.str files from the same LOD archive you used to extract <'>*.txt files. Create "Decompile" sub-folder inside the game folder, put these files there and run this simple script:| ru|Вы можете декомпилировать скрипты самостоятельно, если Вам это когда-то понадобится. Для этого извлеките все <'>*.evt и <'>*.str файлы из того же архива LOD, что вы использовали для извлечения <'>*.txt файлов. Создайте папку "Decompile" в папке с игрой, положите их туда, и выполните этот простой скрипт:|
local dir = "Decompile/" for f in path.find(dir.."*.evt") do evt.Decompile(f, 0, dir.."Scripts/"..path.setext(path.name(f), ".lua")) evt.Decompile(f, 0, dir.."Scripts/txt/"..path.setext(path.name(f), ".txt"), true) end
en|Note: You can't turn a decompiled script back into EVT file, even if it's in text form. In case you do need to edit raw EVT files, you can use|ru|Примечание: Невозможно преобразовать декомпилированный скрипт даже в текстовой форме обратно в файл EVT. Если Вам надо отредактировать EVT, используйте| en|my 010Editor Templates|ru|мои шаблоны для 010Editor|.

en|Short functions syntax|ru|Сокращённый синтаксис объявления функций|

en|MMExtension features one language extension - short syntax for functions declaration. Here's an example:|ru|MMExtension вносит одно дополнение в синтаксис Lua - cокращённый синтаксис объявления функций. Пример:| sum = |x, y| x + y -- en|translates into|ru|превращается в| sum = function(x, y) return x + y end en|Return operator is only added when appropriate. For example,|ru|Оператор return добавляется только когда он уместен. Например,| check = |b| if b then print(b) end -- en|translates into|ru|превращается в| check = function(b) if b then print(b) end end en|Since such short functions can return only one value, you'll have no problems using them in function calls and table declarations:|ru|Поскольку не заключённые в скобки короткие функции возвращают одно значение, нет проблем с передачей их в функции и конструкторы таблиц:| Timer(|| evt.DamagePlayer{Damage = 1}, const.Minute) -- en|translates into|ru|превращается в| Timer(function() return evt.DamagePlayer{Damage = 1} end, const.Minute) en|If you want to return more than one value, put the whole short function into parentheses:|ru|Если Вы хотите вернуть более одного значения, заключите всю короткую функцию в скобки:| switch = (|x, y| y, x) -- en|translates into|ru|превращается в| switch = (function(x, y) return y, x end) en|You can even put multiple statements inside a short function in parentheses:|ru|Внутри заключённой в скобки короткой функции можно даже использовать несколько строчек кода:| print2 = (|x, y| print(x) print(y) ) -- en|translates into|ru|превращается в| print2 = (function(x, y) print(x) print(y) end) en|Using <'>do block for the same effect would look better:|ru|Для того же эффекта лучше будет выглядить блок <'>do:| print2 = |x, y| do print(x) print(y) end -- en|translates into|ru|превращается в| print2 = function(x, y) do print(x) print(y) end end en|But don't go too wild with short functions, it's best to use traditional syntax for such multiline functions.|ru|Но не стоит перебарщивать с короткими функциями - для многострочных функций предпочтителен традиционный синтаксис.|

en|Quests|ru|Задания|

en|Original MM quests were spread across multiple files and manually programmed, which is inconvenient and error prone. My quests support has undergone a few iterations and I'm very happy with it now.|ru|Оригинальные задания MM были разбросаны по нескольким файлам и программировались вручную, это неудобно и чревато ошибками. Поддержка заданий в MMExtension прошла несколько итераций и сейчас я ею очень доволен. |
en|Here are 6 examples|ru|Здесь 6 примеров|. en|They are for MM8. At the end of most examples there is extra information about functions used. Most examples utilize <#>short functions syntax:Short-functions-syntax.| ru|Они предназначены для MM8. В конце большинства примеров есть дополнительная информация об использованных функциях. В большинстве примеров используется <#>сокращённый синтаксис объявления функций:Short-functions-syntax.|

en|Start with Quest Example Simple.lua. It contains a simple <#>quest:Quest and two <#>text topics:NPCTopic, as well as a <#>greeting:Greeting.| ru|Начните с Quest Example Simple.lua. Это простое <#>задание:Quest с двумя дополнительными <#>пунктами разговора:NPCTopic, а также <#>приветствием:Greeting.| %MMExtScripts%\Quests Examples (for MM8)\Scripts\Global\Quest Example Simple.lua Quest Example.lua en|contains basically the same quest, but it is more complex. It has different NPC greetings depending on <#>quest state:vars.Quests and conditionally shown topics, one of which lets you buy items from NPC.| ru|содержит копию того же задания, и кое-что дополнительно: приветствие NPC зависит от <#>состояния задания:vars.Quests, есть пункты диалога, показываемые при определённых условиях, один из которых - покупка предмета у NPC.| %MMExtScripts%\Quests Examples (for MM8)\Scripts\Global\Quest Example.lua Quest With 2 NPCs.lua en|is an example of a quest that requires going to another NPC.| ru|- это пример задания, для выполнения которого надо пойти к другому NPC.| %MMExtScripts%\Quests Examples (for MM8)\Scripts\Global\Quest With 2 NPCs.lua Quest Alchemy.lua en|demonstrates a quest similar to "collect ingredients for a potion" quests with a little extra twist.| ru|- это пример задания, подобного стандартным "собери ингредиенты для зелья" с небольшой дополнительной фишкой.| %MMExtScripts%\Quests Examples (for MM8)\Scripts\Global\Quest Alchemy.lua Quest Kill Monsters.lua en|has two <#>"kill monsters":KillMonstersQuest quests, MMExtension supports them natively so you don't have to add all the checks for slain monsters into effected maps.| ru|содержит 2 задания <#>"убить определённых монстров":KillMonstersQuest - MMExtension поддерживает такие задания "из коробки", так что Вам не придётся прописывать проверки убийства вручную во все задействованные карты.| %MMExtScripts%\Quests Examples (for MM8)\Scripts\Global\Quest Kill Monsters.lua Multilevel Quest.lua en|is a complex tree-style dialog, yet elegant in execution. <#>QuestBranch function is utilized.| ru|создаёт сложный древовидный диалог, но код остаётся вполне элегантным. Используется функция <#>QuestBranch.| %MMExtScripts%\Quests Examples (for MM8)\Scripts\Global\Multilevel Quest.lua en|Some examples also work in MM7 if you find the NPCs they operate on. As for MM6, it lacks NPC greetings and only has 3 dialog topics, plus two text-only topics. The first quest should work in it.| ru|В MM7 часть примеров тоже будет работать, если Вы найдёте персонажей, к которыми они применяются. Что касается MM6, в нём нет приветствий персонажей и всего 3 пункта диалога, плюс 2 исключительно текстовых пункта. Первый пример задания на нём должен работать.|

en|Localization|ru|Локализация|

en|In order to make a script localizeable you should call <#>Localize function in it, passing a table with default strings. The function will return a table with localized strings. See | ru|Чтобы сделать скрипт локализуемым, Вы должны в нём вызвать функцию <#>Localize, передав ей таблицу со строками по умолчанию. Она вернёт таблицу с локализованными строками. См. | en|an example above|ru|предыдущий пример|.
en|For strings shared by multiple scripts you should use <#>LocalizeAll function which works the same way, but normally you should only use the table returned by <#>Localize. It automatically falls back to common localization strings if there is no script-specific one.
Also note that you aren't limited to strings, localization table can contain any values, including subtables.
Quests are localized automatically.| ru|Для общих строк, используемых несколькими скриптами, используйте функцию <#>LocalizeAll. Она работает так же, но Вам не потребуется возвращаемая ей таблица. Таблица, возвращаемая <#>Localize, автоматически берёт отсутствующие строки из таблицы общих строк.
Вообще, таблицы локализации могут содержать любые значения, а не только строки. В том числе, подтаблицы.
Задания локализуются автоматически.|

en|Localization is very much automated. <#>GenerateLocalization():GenerateLocalization function automatically extracts localization information from all scripts and generates localization files. It requires scripts in Maps and Modules folders not to do anything prior to calling <#>Localize and/or <#>LocalizeAll functions. Also, each function should be called at most once per script, otherwise strings passed in subsequent calls may get ignored.| ru|Локализация максимально автоматизирована. Функция <#>GenerateLocalization():GenerateLocalization автоматически извлекает информацию о локализации из всех скриптов и генерирует файлы локализации. Она накладывает ограничения на скрипты в папках Maps и Modules. Эти скрипты не должны ничего делать до вызова функций <#>Localize и/или <#>LocalizeAll. К тому же, каждую функцию они должны вызывать только 1 раз, иначе строки, переданные в последующих вызовах, могут быть проигнорированы.|
en|To generate localization files for all scripts, load any game, press Ctrl+F1, write GenerateLocalization() and press Ctrl+Enter. It will generate the following files in Scripts\Localization folder:| ru|Чтобы сгенерировать файлы локализации для всех скриптов, загрузите любую игру, нажмите Ctrl+F1, напишите GenerateLocalization() и нажмите Ctrl+Enter. В папке Scripts\Localization будут созданы следующие файлы:|
 en|File|ru|Файл| en|Description|ru|Описание|
Quests.txten|Quests localization.|ru|Локализация заданий.|
Common.txten|Strings passed to <#>LocalizeAll function.|ru|Строки, переданные в функцию <#>LocalizeAll.|
Scripts.txten|Strings passed to <#>Localize function.|ru|Строки, переданные в функцию <#>Localize.|
en| These files should be edited with |ru| Для редактирования этих файлов используйте |Txt Tables Editor.
en| Alternatively, you can run GenerateLocalization(true) to generate <'>*.lua localization files. Choose whichever format you prefer.| ru| Или же можно запустить команду GenerateLocalization(true), чтобы сгенерировать файлы локализации <'>*.lua. Выбирайте тот формат, который Вам удобнее.|
en| To generate localization only for quests you can use <#>GenerateQuestsLocalization():GenerateQuestsLocalization command.
To generate localization excluding quests you can use GenerateLocalization(false, false) command.| ru| Чтобы сгенерировать локализацию только для заданий, можно использовать команду <#>GenerateQuestsLocalization():GenerateQuestsLocalization.
Чтобы сгенерировать локализацию для всего, кроме заданий, можно использовать команду GenerateLocalization(false, false).|

en| To test localization changes without restarting the game you can create a script in Global folder with this line:| ru| Чтобы тестировать изменения локализации без перезапуска игры, можно создать скрит в папке Global со следующей строкой:|
ReloadLocalization() en| Then you'll only need to reload a saved game to test localization changes.| ru| После этого для для тестирования изменений локализации достаточно будет перезагрузить сохраненную игру.|

en|Special Codes in Texts|ru|Особые коды в сообщениях|

en|In any text you can use special codes inserted with <#>StrLeft, <#>StrRight and <#>StrColor functions.|ru|В любом тексте можно использовать особые коды, создаваемые функциями <#>StrLeft, <#>StrRight и <#>StrColor.|
en|In various NPC messages and History.txt file you can also use these codes:|ru|В сообщениях NPC и в файле History.txt также используется следующий набор особых кодов:| %01 NPC name %02 Current player name %03 "his"/"her" (depending on NPC sex) %04 Bribe cost %05 "day"/"evening"/"morning" %06 "lady"/"sir" (depending on sex of current player) %07 "Lady"/"Sir" (depending on sex of current player) %08 Random achieved award from a hardcoded list %09 Same as %03 %10 "Lady"/"Lord" (depending on sex of current player) %11 Reputation category of the party %12 Reputation category required by the NPC %13 Any name starting with the same letter as that of current player %14 "sister"/"brother" (depending on NPC sex) %15 "daughter" %16 Same as %14 %17 Gold percentage an NPC takes %23 Map name %24 Item name (in yellow color) %25 Standard buy/sell/repair cost %27 Actual buy/sell/repair cost with regards to Merchant skill %28 Shop owner title %29 Identification price %30 History entry date %31 Player 1 name %51 - %70 <#>Special date:structs.GameParty.SpecialDates %32 Player 2 name %33 Player 3 name %34 Player 4 name %32 "his"/"her" (depending on player 1 sex) %33 "he"/"she" (depending on player 1 sex) %34 "him"/"her" (depending on player 1 sex)
en|Code|ru|Строка|en|Meaning|ru|Значение|
MM7, MM8:
MM7:
MM8:

en|More on Data Tables|ru|Ещё о Data\Tables|

en|Having the Data\Tables folder is great for development of a mod, but simply distributing it with a big mod may present challenges for subsequent creation of smaller mods for it, because they'd be overwritten by any update of the big mod.| ru|Папка Data\Tables - это отличный инструмент при разработке мода, но простое распространение её с большим модом может вызвать сложности при последующем создании меньших модов для него, поскольку любое обновление большого мода их перезапишет.|
en|First, I'd recommend setting up reading tables that don't correspond to *.bin files from LOD archive of your mod if it's a big one. This will allow other mods or power users to override the table more safely. Here's an example (for Scripts\General):| ru|Во-первых, я советую настроить чтение таблиц, по которым не генерируются файлы *.bin, из LOD-архива мода, если он большой. Это позволит другим модам их перекрывать более безопасно. Вот пример, как это сделать (для Scripts\General):| table.copy({ ['Chest'] = 'Chest.txt', ['Class HP SP'] = 'ClsHPSP.txt', ['Class Skills'] = 'ClsSkill.txt', ['Class Starting Skills'] = 'ClsSkilB.txt', ['Class Starting Stats'] = 'ClsStats.txt', ['House Movies'] = 'HouseMov.txt', ['Monster Kinds'] = 'MonKind.txt', ['Spells2'] = 'Spells2.txt', ['Town Portal'] = 'TownPort.txt', ['Transport Index'] = 'TripIdx.txt', ['Transport Locations'] = 'TripLoc.txt', ['Shops'] = 'Shops.txt', ['Faces'] = 'Faces.txt', ['Face Animations'] = 'FaceAnim.txt', }, DataTables.Files, true) -- DataTables.LazyMode = true -- see below for an explanation en|You can also disable any data table by setting it to an empty string here.| ru|Можно таким же образом отключить любую таблицу, присвоив ей здесь пустую строку.|
en|Second, for tables that generate *.bin files there are 2 ways to go:| ru|Во-вторых, для таблиц, которые генерируют файлы *.bin, есть 2 пути:|
  • en|Distributing Data\Tables folder with them with your mod. Including DataFiles with corresponding *.bin files is also a good practice. This will lead to the discussed problem for smaller mods, but you won't have to make an installer.| ru|Распространять папку Data\Tables с ними вместе с модом. Также хорошей практикой является включение в мод папки DataFiles с соответствующими файлами *.bin. Это приведёт к описанной проблеме с меньшими модами, но Вам не обязательно будет создавать инсталлятор для мода.|
  • en|Placing generated *.bin files into a LOD archive of your mod and setting DataTables.LazyMode = true, which makes it so that tables can still be overridden with Data\Tables, but they aren't automatically generated (calling UpdateDataTables() would generate them). You will also need to move or delete preexisting Data\Tables and DataFiles on first installation of the mod, because files there will override those of your mod.| ru|Распространять сгенерированные файлы *.bin в LOD-архиве мода и включить DataTables.LazyMode = true - она делает так, что таблицы из Data\Tables читаются, если они есть, но не генерируются автоматически (их можно сгенерировать, вызвав UpdateDataTables()). Вам также надо будет перенести или удалить существующие папки Data\Tables и DataFiles при первой установке мода, поскольку файлы в них будут иметь приоритет над файлами мода.|




  • en|Examples|ru|Примеры|

    en|You can find more examples in the|ru|Вы найдёте больше примеров в| en|MMExtension discussion thread|ru|теме для обсуждения MMExtension|.

    en|Players Skills And Spells|ru|Умения и заклинания игроков|

    en|Below are some scripts demonstrating how you can work with skills and spells. You can test them by pasting the one you like into debug console.
    | ru|Здесь есть кое-какие скрипты, показывающие, как можно работать с умениями и заклинаниями. Скопируйте приглянувшийся скрипт в отладочную консоль, чтобы протестировать его.
    | -- learn all spells for _, pl in Party do for i in pl.Spells do pl.Spells[i] = true end end -- give Expert Perception for all players for _, pl in Party do local skill, mastery = SplitSkill(pl.Skills[const.Skills.Perception]) pl.Skills[const.Skills.Perception] = JoinSkill(math.max(skill, 4), math.max(mastery, const.Expert)) end -- get all skills at Master 12 for _, pl in Party do for i, val in pl.Skills do local skill, mastery = SplitSkill(val) pl.Skills[i] = JoinSkill(math.max(skill, 12), math.max(mastery, const.Master)) end end -- get all learned skills to Master 12 for _, pl in Party do for i, val in pl.Skills do if val ~= 0 then local skill, mastery = SplitSkill(val) pl.Skills[i] = JoinSkill(math.max(skill, 12), math.max(mastery, const.Master)) end end end -- learn all available skills at Expert for _, pl in Party do for i, learn in EnumAvailableSkills(pl.Class) do if learn >= const.Expert then local skill, mastery = SplitSkill(pl.Skills[i]) skill = math.max(skill, 4) -- learn at least level 4 mastery = math.max(mastery, const.Expert) -- learn the mastery pl.Skills[i] = JoinSkill(skill, mastery) end end end -- learn all available skills at their maximum level local LearnLevel = (Game.Version > 6 and {1, 4, 7, 10} or {1, 4, 12}) for _, pl in Party do for i, learn in EnumAvailableSkills(pl.Class) do local skill, mastery = SplitSkill(pl.Skills[i]) skill = math.max(skill, LearnLevel[learn]) -- learn at least the usual needed level mastery = math.max(mastery, learn) -- learn the mastery pl.Skills[i] = JoinSkill(skill, mastery) end end

    en|Artifact Bonuses|ru|Бонусы артефактов|

    en|Giving Hareck's Leather an 'Of Earth Magic' enhancement. (untested)| ru|Дать кожаным доспехам Харека бонус 'Магии Земли'. (не тестированный код)| function events.CalcStatBonusByItems(t) if t.Stat ~= const.Stats.EarthMagic or t.Player.Skills[const.Skills.EarthMagic] == 0 then return end for item, slot in t.Player:EnumActiveItems() do if item.Number == 516 then t:SetMagicBonus(SplitSkill(t.Player.Skills[const.Skill.EarthMagic]):div(2)) return end end end
    en|Giving Hareck's Leather 'Armsmaster + 8' bonus. (untested)| ru|Дать кожаным доспехам Харека бонус 'Оружейник + 8'. (не тестированный код)| function events.CalcStatBonusByItems(t) if t.Stat ~= const.Stats.Armsmaster or t.Player.Skills[const.Skills.Armsmaster] == 0 then return end for item, slot in t.Player:EnumActiveItems() do if item.Number == 516 then t:SetArtifactBonus(8) return end end end
    en|Q|ru|Вопрос|: en|Is there a way to change values of enchantments? For example, to make Of the Gods one give say +30 stats instead of +10?|ru|Возможно ли изменить силу усилений предметов? Например, чтобы предметы со свойством 'Богов' давали +30 к статистикам вместо +10?|
    en|A|ru|Ответ|: en|(untested)|ru|(не тестированный код)| function events.CalcStatBonusByItems(t) if t.Stat >= const.Stats.Might and t.Stat <= const.Stats.Luck then for it in t.Player:EnumActiveItems() do if it.Bonus2 == 2 then t.Result = t.Result + 20 end end end end

    en|Spells Damage|ru|Урон заклинания|

    en|Change damage of spell 2 - Flame Arrow. You can also make damage depend on <'>t.Mastery (skill mastery) and <'>t.HP (monster hit points).| ru|Изменить урон заклинания №2 - Стрела пламени. Также можно сделать его зависящим от мастерства <'>t.Mastery в магии и здоровья <'>t.HP монстра.| local function Randoms(min, max, count) local r = 0 for i = 1, count do r = r + math.random(min, max) end return r end function events.CalcSpellDamage(t) if t.Spell == 2 then -- Flame Arrow t.Result = 100 + Randoms(1, 100, t.Skill) -- 100 + (1-100) per skill level end end

    en|Controlling monsters aggression in MM6|ru|Управление агрессией монстров в MM6|

    en|Put this script into Scripts\Global folder. It would make archers in Free Haven friendly and female peasants aggressive. In other places archers would still be aggressive and peasants still friendly.| ru|Положите этот скрипт в папку Scripts\Global. Это делает лучников в Свободной гавани дружественными, а крестьян - агрессивными. В других местах лучники будут по-прежнему агрессивными, а крестьяне - дружелюбными.| function events.BeforeLoadMap() if Game.Map.Name == "outc2.odm" then LocalMonstersTxt() Game.MonstersTxt[1].HostileType = 0 -- ArcherA Game.MonstersTxt[2].HostileType = 0 -- ArcherB Game.MonstersTxt[3].HostileType = 0 -- ArcherC Game.MonstersTxt[121].HostileType = 4 -- PeasantF1A Game.MonstersTxt[122].HostileType = 4 -- PeasantF1B Game.MonstersTxt[123].HostileType = 4 -- PeasantF1C end end en|Instead of map name check you can put any condition. For example, after player completes a quest you can make some monsters friendly or aggressive.| ru|Вместо проверки имени карты вы можете поставить любое условие. Например, после того как игрок выполнит задание, вы можете сделать некоторых монстров дружественными или агрессивными.|

    en|Flowers you can pick up (MM8)|ru|Цветы, которые можно подобрать (MM8)|

    en|Put this script into Scripts\Global folder.| ru|Положите этот скрипт в папку Scripts\Global.| -- You can find such flowers in Ravenshore at X = 16921, Y = 4112 -- Flowers disappear when picked up, which makes life easier than in Barrels script -- The game remembers which sprites are hidden by itself local SpriteEvents = 20000 local TXT = Localize{ FlowerHint = "Flowers", } local function Flower(EvtId) local i = EvtId - SpriteEvents evt.Add("Inventory", 208) evt.SetSprite(i, false) -- alternatively, for the same effect: Map.Sprites[i].Invisible = true end local function InitFlower(i, a) a.Event = SpriteEvents + i evt.map[SpriteEvents + i] = Flower evt.hint[SpriteEvents + i] = TXT.FlowerHint end function events.LoadMap() for i, a in Map.Sprites do if a.DecName == "plant27" then InitFlower(i, a) end end end

    en|Unusually looking barrels (MM7)|ru|Необычно выглядящие бочки (MM7)|

    en|Put this script into Scripts\Global folder to turn trees into barrels.|ru|Положите этот скрипт в папку Scripts\Global, чтобы превратить деревья в бочки.| -- Turns trees into barrels local SpriteEvents = 20000 local TopicBase = 383 local TextBase = 582 local Reorder = {[0] = 0, 1, 4, 3, 5, 2, 6, 7} -- NPC topics order for barrels is messed up local AutonotesBase = 32 local function Barrel(EvtId) local i = EvtId - SpriteEvents local v = mapvars.Barrels[i] Game.ShowStatusText(Game.NPCText[TextBase + Reorder[v]]) if v > 0 then evt.Add(evt.VarNum.BaseStats[v - 1], 2) evt.Set("AutonotesBits", AutonotesBase + v) mapvars.Barrels[i] = 0 evt.hint[SpriteEvents + i] = Game.NPCTopic[TopicBase] end end local function InitBarrel(i, a) mapvars.Barrels = mapvars.Barrels or {} mapvars.Barrels[i] = mapvars.Barrels[i] or math.random(1, 7) a.Event = SpriteEvents + i evt.map[SpriteEvents + i] = Barrel evt.hint[SpriteEvents + i] = Game.NPCTopic[TopicBase + Reorder[mapvars.Barrels[i]]] end function events.LoadMap() for i, a in Map.Sprites do if a.DecName and a.DecName:match("^tree") then InitBarrel(i, a) end end end

    en|Other Examples|ru|Еще примеры|

    en|Summon monster (Peasant)|ru|Призвать монстра (крестьянина)|: local mon = SummonMonster(151, Party.X, Party.Y, Party.Z, true) mon.NPC_ID = 52 mon.Hostile = false
    en|See global event number (copy to console to quickly test what event is triggered by a dialog item):|ru|Посмотреть номер глобального события (скопируйте в консоль, чтобы легко увидеть, какое событие вызывает пункт диалога):| -- on: function events.EvtGlobal(evt) Message(evt) end -- off: events.EvtGlobal.clear()
    en|See current house (2DEvent) and NPC indeces (run it from console):|ru|Посмотреть номера текущего дома (2DEvent) и NPC (вызывайте из консоли):| Game.GetCurrentHouse(), GetCurrentNPC()
    en|Q|ru|Вопрос|: en|How to change the home position after the death of the team on current map?|ru|Как изменить место, где воскрешается команда после смерти?|
    en|A|ru|Ответ|: en|Specify desired coordinates in the call to|ru|Впишите нужные координаты в вызов функции| <#>XYZ en|and set desired|ru|и присвойте| <#>Direction:Party.Directionru| нужное направление|: function events.DeathMap(t) t.Name = "out05.odm" XYZ(Party, 0, 0, 0) Party.Direction = 0 Party.LookAngle = 0 end
    en|Changing starting map (similar to previous example):| ru|Изменение стартовой карты (аналогично предыдущему примеру):| Game.NewGameMap = "out05.odm" function events.NewGameMap() XYZ(Party, 0, 0, 0) Party.Direction = 0 Party.LookAngle = 0 end
    en|Q|ru|Вопрос|: en|How to run a method when a user presses a specific key?|ru|Как исполнить код при нажатии определённой кнопки?|
    en|Method 1:|ru|Способ 1:| function Keys.F1(t) Message("F1 pressed") end en|Method 2:|ru|Способ 2:| function events.KeyDown(t) if t.Key == const.Keys.F1 then Message("F1 pressed") end end en|Obsolete method provided for backward compatibility: (This is NOT the same as setting Keys.F1)| ru|Устаревший способ, оставленный для обратной совместимости: (Это НЕ то же самое, что установка Keys.F1)| Keys[const.Keys.F1] = function() Message("F1 pressed") end en|This method may ignore quick key presses.|ru|Этот способ может пропустить быстрое нажатие кнопки.|