Перейти к содержанию

Программирование на LUA для создания аддона


Yamenko

Рекомендуемые сообщения

Доброго времечка всем.

 

Хотел бы разобраться как написать самому аддон. (программирую на php+mysql, пишу простенькие программы для ардуинки)

Не совсем понимаю принцип работы ЛУА аддонов, где он берет данные и как их обратно вернуть чтобы игра их потом обрабатывала.

в настоящий момент хочу сделать маленький аддон который бы показывал звездочку или галочку напротив неизвестных рецептов и мотивов... (сторый аддон Craft перестал показывать галочки.)

Может кто объяснить принцип или может форум есть какой для начинающих?

 

Заранее благодарен.

 

Ссылка на комментарий
Поделиться на другие сайты

http://wiki.esoui.com/Main_Page

 

http://wiki.esoui.com/MyFirstAddon_tutorial/ru

 

Добавлено: 

Небольшой учебник по языку LUA

Lua за 60 минут

Изменено пользователем ForgottenLight
Ссылка на комментарий
Поделиться на другие сайты

Это я прочитал.

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

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

Ничего ни откуда не получаем и ничего никуда не возвращаем...

Может форум желательно русскоязычный.

Спасибо.

Изменено пользователем Yamenko
Ссылка на комментарий
Поделиться на другие сайты

Иногда у меня на работе бывает свободное время, т.ч. могу немного поделиться опытом. :)
Типа лекция 1-я: Сохраняемые переменные
Для сохранения аддонами своих данных ZOS'ы придумали специальный механизм. Настройки сохраняются локально на компьютере пользователя, а не на сервере, что добавляет гемороя людям играющим с нескольких компьютеров (например дома и на работе). Настройки сохраняются в папке "Мои документы\Elder Scrolls Online\live\SavedVariables\" причем имя файла совпадает с именем аддона, а расширение у файла - lua. По сути файл сохраненных переменных это и есть LUA скрипт, в котором определяется и инициализируется массив данных. Вообще то, по терминологии LUA массивы называются таблицами, собственно как и классы, это тоже таблицы. Т.ч. когда я употребляю термин массив вместо таблицы, то прошу меня простить. :)
Итак, в файле сохраненных переменных определяется массив. Имя этого массива должно быть декларировано в текстовом файле описания аддона строкой вида:
## SavedVariables: <НазваниеМассива>
Обычно в качестве имени массива используют имя аддона к которому добавляют SV, SavedVars или чего нибудь подобное. У меня, например, для аддона WPamA в файле описания WPamA.txt указана строка:
## SavedVariables: WPamASavedVars
В некоторых аддонах определяют несколько сохраняемых массивов (имена разделяются пробелами), как правило для разделения переменных сохраняемых для каждого персонажа и переменных общих на аккаунт. Но в эти дебри на первых порах лезть не стоит. :)
Где задается имя массива и где хранятся сохраненки определились. Теперь нужно считать эти переменные внутри аддона. Как правило, считывание организуется внутри секции инициализации аддона, которая выполняется один раз при входе персонажа в игру. Для считывания переменных ZOS'ы сделали две функции:
<переменная> = ZO_SavedVars:New("<НазваниеМассива>", <НомерВерсии>, <ПространствоИмен>, <ЗначенияПоУмолчанию>, <Профиль>)
<переменная> = ZO_SavedVars:NewAccountWide("<НазваниеМассива>", <НомерВерсии>, <ПространствоИмен>, <ЗначенияПоУмолчанию>, <Профиль>, <Акаунт>)

где:

  • <НазваниеМассива> - Название, которое задавали в текстовом файле, это важно! Если указать другое - работать не будет.
  • <НомерВерсии> - Теоретически, можно хранить настройки одновременно от нескольких версий аддона, или вернее от нескольких версий форматов сохранения своих переменных, ведь каждую версию формата может использовать несколько версий аддона. Таким образом если пользователь решит откатиться на предыдущую версию аддона у которой был другой формат сохраняемых переменных то аддон у него не сглючит. :) На практике используется редко, большинство авторов ставят сюда рекомендованную в качестве первой версии строку "1" и больше ее не меняют. :)
  • <ПространствоИмен> - Еще одна сомнительная фича. Теоретически настройки можно разбить на несколько групп пространствами имен, но когда я пробовал использовать одновременно два пространства, то второе просто перетирало первое, т.ч. для этих целей нужно определить в текстовом файле несколько разных массивов. В качестве значения обычно указывается nil, в этом случае в файле сохраненных переменных будет использована строка "Default".
  • <ЗначенияПоУмолчанию> - А вот это вещь замечательная! Сюда подается таблица значений по умолчанию, причем она может быть со структурой, то есть со вложенными таблицами. При считывании сохраненных переменных будет выполнена проверка, чтобы в них существовали все переменные и вложенные таблицы из таблицы <ЗначенияПоУмолчанию>. Все недостающие будут созданы и значения у них возьмутся из <ЗначенияПоУмолчанию>. Таким образом, после считывания переменных автор может быть уверен, что в них есть все структуры и все переменные которые он определил в дефолте и ошибка ненахождения переменной не вылезет. Не важно, установился ли аддон первый раз на компьютер или в новой версии добавились новые сохраненные переменные при считывании недостающие добавятся.
  • <Профиль> - Имя персонажа, для которого считываются настройки. Как правило не указывается (либо указывают nil) и функции используют значение по умолчанию - функция New использует имя текущего персонажа, а функция NewAccountWide - строку "$AccountWide".
  • <Акаунт> - Еще один параметр, который как правило не используют. Если он не указан или равен nil, то используется имя аккаунта. А вот если его указать, то можно сохранять в одном месте настройки для нескольких аккаунтов на компьютере. Я, например, в патче аддона DIKIFA использую сохранение всех аккаунтов в один массив, чтобы видеть какие рецепты/мотивы изучены чарами с двух аккаунтов, а не только с текущего. :)

Функции New и NewAccountWide в файле сохраненных переменных выстраивают следующую структуру:

<НазваниеМассива> =
{
    [<ПространствоИмен>] = 
    {
        [<Акаунт>] = 
        {
            [<Профиль>] = 
            {
            <СобственноХранимыеПеременные со структурой как у ЗначенияПоУмолчанию>
            }
        }
    }
}

Функции New и NewAccountWide возвращают ссылку на таблицу со считанными переменными, которую нужно куда-то сохранить и через нее обращаться к сохраненным переменным.
ВАЖНО: Таблица, которую вернули функции, не совсем обычная. Ссылка на нее запомнилась где-то в системе и при выходе персонажа из игры всё, что есть в этой таблице будет сохранено в файл!
Если аддон небольшой и его код размещается весь в одном файле, то результат функций New и NewAccountWide можно поместить в локальную переменную. А вот если код аддона размещен в нескольких файлах, то рекомендуется в том файле, который грузится первым, создать глобальную таблицу для объектов аддона (переменных, функций и т.п.) и уже в этой таблице создавать переменную связанную с хранимыми. Тогда она будет доступна из всех файлов аддона.
ВАЖНО: Единицей сохранения для переменных является <НазваниеМассива>. Если связать две таблицы с одним <НазваниеМассива>, то сохранится только последняя. Поясню на примерах:

  • В файле сохранятся только переменные из MyAddon.SV_AccW т.к. именно он был привязан последним к НазваниюМассива "MyAddonSV".
    MyAddon.SV_Pers = ZO_SavedVars:New("MyAddonSV",1,nil)
    MyAddon.SV_AccW = ZO_SavedVars:NewAccountWide("MyAddonSV",1,nil)
    
  • В файле сохранятся только переменные из MyAddon.SV_NS2 т.к. именно он был привязан последним к НазваниюМассива "MyAddonSV".
    MyAddon.SV_NS1 = ZO_SavedVars:NewAccountWide("MyAddonSV",1,"NS1")
    MyAddon.SV_NS2 = ZO_SavedVars:NewAccountWide("MyAddonSV",1,"NS2")
    
  • В файле сохранятся только переменные из MyAddon.SV_AllA т.к. именно он был привязан последним к НазваниюМассива "MyAddonSV".
    MyAddon.SV_AccW = ZO_SavedVars:NewAccountWide("MyAddonSV",1,nil)
    MyAddon.SV_AllA = ZO_SavedVars:NewAccountWide("MyAddonSV",1,nil,default,nil,"AllAccaunts")
    
  • В файле сохранятся как переменные из MyAddon.SV_Pers так и из MyAddon.SV_AccW т.к. они привязаны к разным НазваниямМассивов - "MyAddonSVPers" и "MyAddonSVAccW".
    MyAddon.SV_Pers = ZO_SavedVars:New("MyAddonSVPers",1,nil)
    MyAddon.SV_AccW = ZO_SavedVars:NewAccountWide("MyAddonSVAccW",1,nil)
    
Изменено пользователем ForgottenLight
Ссылка на комментарий
Поделиться на другие сайты

Типа лекция 2-я: Шаблон для создания аддонов

Существует много вариантов как выстроить структуру аддона и многие из них будут правильные. Хотя, разумеется, есть и примеры криво написанных аддонов. :) Как правило, автор сам выбирает какой структурой пользоваться. Я приведу в качестве примера структуру, которую использую в своих аддонах (WPamA и DBGN). Эта структура для более сложных аддонов, чем пример таймера на сайте. Пользоваться этой структурой или создать свою - решать только тебе. :)

Для начала нужно определиться с полным именем аддона и его сокращенным названием. Например, для аддона DBGN полное имя "Daggerfall Bandits Guild Notes", а сокращенное "DBGN". Пусть мы создаем аддон с полным именем "My Very Useful Addon" и сокращенное имя возьмем по первым буквам "MVUA". Ббанально, но эффективно. :)

ВАЖНО: Сокращенное имя не должно содержать пробелов, т.к. в дальнейшем мы будем его использовать для имен переменных и т.п.

Понятное дело, что имя аддона, как полное, так и сокращенное не должно совпадать с именами других аддонов. Для этого достаточно поискать это имя на сайте ESOUI.COM. Это сайт откуда аддоны берет большинство пользователей. Ну а если кто-то берет с других сайтов, то и о твоем аддоне он скорее всего не узнает, т.ч. голову этим парить не стоит. :) Если, предположим, нашелся аддон с именем "MVUA" то малость его изменим, например на "MyVUAdd".

 

Первым делом в папке "Мои документы\Elder Scrolls Online\live\AddOns\" создаем папку с названием аддона - "MVUA". Все файлы аддона должны лежать в ней. Когда аддон передается другим людям или публикуется на ESOUI.COM, то эта папка сжимается в ZIP архив, который и передается.

 

Вторым делом в нашей папке создаем текстовый файл "MVUA.txt" - это будет файл описания аддона. Именно по нему игра определяет что именно входит в аддон.

Файл содержит приблизительно следующее:

## Title: MVUA (My Very Useful Addon)
## APIVersion: 100021
## Version: 0.0.0.1
## Author: |c779cff@MyAccount|r [EU]
## SavedVariables: MVUASavedVars
## OptionalDependsOn: RuESO
MVUA.xml
MVUAConsts.lua
i18n\en.lua
i18n\$(language).lua
MVUASettings.lua
MVUA.lua
bindings.xml
Разберем, что здесь имеется. Строки начинающиеся с ## это директивы, указывающие разные параметры аддона. Все что без ## - это список файлов аддона. Для тех файлов, которые лежат в корневой папке аддона путь не указывается, а для тех которые лежат внутри других папок указывается. В данном случае в папке аддона есть папка "i18n", в которой хранятся файлы языковых настроек.

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

  • ## Title: - Название аддона, обязательная директива.
  • ## APIVersion: - Поддерживаемые версии API клиента игры, обязательная директива. Можно указать несколько разделив их пробелом. Если версии текущего клиента нет в этом списке аддон будет считаться устаревшим или более правильно в английском варианте outdated
  • ## Version: - Версия аддона, не обязательная директива. Является текстовой строкой, в которую можно написать чего угодно. Но все же, рекомендую, не страдать фигней, выдумывая названия версий типа линуксовых "здравомыслящий сурикат" и т.п., а использовать какую либо числовую систему. Я, например, использую четыре числа разделенных точками: major.minor.release.betta. Они означают: major-старший номер в версии, меняется при глобальных изменениях в аддоне, minor-младший номер версии, меняется при значительных изменениях, release-порядковый номер публикуемых версий, betta-версия, находящаяся в процессе разработки или отладки. Причем, публикую я только трехзначные версии major.minor.release. Бетта - это чисто для внутреннего использования, например когда передаешь версию бетта-тестерам.
  • ## Author: - Собственно автор аддона, по идее необязательная деректива. Но кто-же откажется себя похвалить? ;-) Помимо ЧСВ, указание своего игрового акка и сервера имеет еще и практическое применение. Мне несколько раз на игровую почту приходили багрепорты, когда аддон переставал работать правильно потому, что ZOS'ы в очередном патче поправили ID некоторых зон. Не у всех игроков есть аккаунты на сайте ESOUI.COM чтобы писать багрепорты. :)
  • ## SavedVariables: - Имена массивов сохраняемых переменных, деректива обязательна только если аддон сохраняет переменные. Можно указать несколько массивов разделив их пробелами.
  • ## DependsOn: - Зависимости от других аддонов, не обязательная директива. Через пробел перечисляются имена аддонов без которых ваш работать не сможет. Все аддоны из списка будут загружены раньше вашего. Если какого-то аддона из списка нет - ваш аддон загружен не будет!
  • ## OptionalDependsOn: - Опциональная зависимость от других аддонов, не обязательная директива. Похожа на предыдущую, также все аддоны из списка будут грузиться раньше вашего, но отличие в том, что если какой-то или все аддоны из списка отсутствуют ваш все равно загрузится.
Теперь разберем назначение файлов аддона. Хотя минимальный состав аддона это всего два файла - текстовый файл описания и один файл с LUA кодом - на практике аддоны довольно быстро разрастаются и размещать все в одном файле неудобно. Если правильно разделить код по разным файлам, то работать будет гораздо удобнее.

ВАЖНО: Клиент игры загружает и обрабатывает файлы аддона именно в том порядке, в котором они указаны в текстовом файле!

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

Если какого либо файла не находится в папке, то строчка игнорируется, никаких ошибок не выдается и загружаются файлы из следующих строчек.

Файлы:

  • MVUA.xml - Файл в формате XML содержащий описания графических объектов - окошек, кнопочек, картинок и т.п. Файл не обязательный, т.к. графические элементы интерфейса пользователя можно создавать и коммандами из LUA кода. Использовать его или нет - по желанию автора.
  • MVUAConsts.lua - В этом файле определяется основная глобальная переменная аддона, а так-же разные константы не зависящие от языков.
  • en.lua и i18n\$(language).lua - Эта хитрая конструкция рекомендована ZOS'ами для поддержки нескольких языков в аддонах. Здесь вначале загружается файл с английскими текстами, а после него уже файл с текстами текущего языка, установленного в клиенте игры. Макрос $(language) возвращает строку из двух букв - текущий язык, например ru. Такая конструкция позволяет избежать ошибок, если в языковых файлах аддона нет файла для текущего языка. На данный момент есть три официальных языка поддерживаемых ZOS'ами - en,de,fr, один официальный поддерживаемый сторонним разработчиком, партнером ZOS - jp. Так же есть несколько неофициальных языков, которыми занимаются команды энтузиастов. Один из крупнейших неофициальных - наш ru от команды RuESO.
  • MVUASettings.lua - Если у аддона предполагаются какие либо настройки, то для этого очень удобно использовать библиотеку LibAddonMenu-2.0, а всю работу с ней вынести в отдельный файл. После этого в "Главном меню\Настройки\Дополнения" появится меню для этого аддона.
  • MVUA.lua - Собственно основной код аддона.
  • bindings.xml - Если в аддоне подразумевается использование назначенных клавиш для управления аддоном, то нужен специальный файл в формате XML, который добавит ваш аддон в меню назначения клавиш. Ну и после назначения клавиш скажет клиенту игры, какие процедуры аддона вызывать для того или иного действия. Если в аддоне не планируется использовать назначенные клавиши то файл не нужен. Как правило файл идет последним в списке, чтобы все указываемые в нем функции уже были загружены и работали.
Примечание: У меня папка в которой расположены языковые файлы называется i18n, ее можно назвать по другому, например Lang. Подробнее о названиях можно посмотреть тут.

По содержимому файлов, если интересно, напишу отдельные посты. :)

Изменено пользователем ForgottenLight
Ссылка на комментарий
Поделиться на другие сайты

Игорь жжешь! Человек просто не внимателен. По ссылкам что ты дал куча примеров, все исходники и даже детально расписанные константы которых тьма. А он октрыл-закрыл и ноет. Не люблю таких кто не готов поработать. Программистов без усердия попросту не бывает. Выложи свои лекции в ру-разделе лекций на esoui! Их там хоть читать будут. А Яменко врядли вникать станет.

 

Статья про сохраняемые переменные, кстати, на esoui есть. Можно было по новой не писать.

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

 

И. Похоже нам с тобой заняться нефиг ((

Изменено пользователем Hoft
Ссылка на комментарий
Поделиться на другие сайты

Статья про сохраняемые переменные, кстати, на esoui есть. Можно было по новой не писать.

Статья то есть, но там либо на английском, чего я например, читать не очень люблю, только когда других вариантов нет. И некоторых моментов описанных мной там точно нет. Я в свое время долго искал и экспериментировал.

 

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

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

 

И. Похоже нам с тобой заняться нефиг ((

У меня работа такая - то густо то пусто. :)

Ссылка на комментарий
Поделиться на другие сайты

Типа лекция 3-я: Содержимое файла констант и языковых файлов.

Начнем рассмотрение содержимого с файла MVUAConsts.lua.

MVUA = {
	Name = "MVUA",
	Version = "0.0.0.1",
	Const {
		aaa = "aaa",
		ColCount = 5,
	},
}
MVUA.bbb = "bbb"
В начале создаем глобальную переменную с именем MVUA и инициализируем ее как таблицу. Внутри этой таблицы мы будем создавать все переменные, константы и функции аддона. По сути, это реализация объектно ориентированного стиля программирования, а таблица MVUA у нас будет корневым объектом аддона. В языке LUA переменные и функции могут быть глобальными или локальными. Нужно стараться, чтобы в коде аддона было минимум глобальных переменных и функций, т.к. такую переменную могут перетереть или переписать другие аддоны. Или наоборот, наш аддон перепишет переменную или функцию другого аддона или вообще пользовательского интерфейса клиента игры. Такие случаи приводят к глюкам в игре, которые очень трудно отследить и устранить. Если аддон маленький и весь содержится в одном файле, то все функции и переменные можно объявить как локальные. В нашем случае файлов несколько, и для того чтобы функции видели переменные и функции из других файлов без глобальных переменных не обойтись. При описываемом подходе глобальная переменная будет всего одна - MVUA.

В таблице MVUA создаем переменную Name и присваиваем ей имя аддона. Переменную Name мы будем использовать для регистрации событий, обрабатываемых аддоном. Переменная Version нужна для отображения версии в окне аддона или в окне настроек аддона. Ей нужно присвоить такое же значение как и в файле MVUA.txt в директиве Version. Соответственно при изменении версии аддона, она меняется в двух местах - в файле описания и в файле констант. Создавать переменную Version приходится потому, что через API получить значение директивы не получается. После этих двух переменных описываются и инициализируются другие переменные и константы. Описание констант лучше вынести в отдельную таблицу, в данном случае Const. Переменные можно описывать как внутри инициализации таблицы MVUA (пример переменная Const) так и после (пример переменная bbb). Если констант много, то будет удобно разделить их по смыслу на несколько групп и для разных групп использовать разные таблицы внутри таблицы Const.

Постарайтесь вынести в файл констант все, не зависящие от языка, константы из текста аддона, чтобы в LUA коде основного модуля не было явных констант. Поясню мысль на примере. Пусть аддон рисует в своем окне таблицу из 5 колонок. И в основном коде есть несколько функций изменяющих информацию в этих колонках. Указание в этих функциях в цикле явного количества колонок, например

for i = 1, 5 do
	MVUA.ColCtrl[i]:SetText(MVUA.i18n.ColName[i])
end	
плохая идея. Если в будущем понадобится увеличить количество колонок, то выискивать по коду все места, где было упоминание количества колонок, накладно. Правильнее будет в файле MVUAConsts.lua определить константу MVUA.Const.ColCount и присвоить ей значение 5, а в коде уже писать:

local c = MVUA.Const
local l = MVUA.i18n
local r = MVUA.ColCtrl
for i = 1, c.ColCount do
	r[i]:SetText(l.ColName[i])
end	
В данном случае применена еще одна хитрость. Дело в том, что доступ к локальным переменным идет в разы быстрее чем к глобальным, по этому до цикла мы определяем три ссылки на таблицы - c, l и r. По скольку ссылки являются локальными переменными, доступ к ним будет быстрее, а более долгий доступ к глобальной переменной MVUA будет выполнен один раз для каждой из ссылок. Более того, использование ссылок позволило нам сократить количество операций поиска индекса (это когда внутри таблицы ищется переменная или функция). В первом примере глобальные переменные индексировались 10 раз, а локальные 20, во втором примере 3 и 16 раз соответственно. В дальнейшем опишу используемый для подобных целей макрос ":" и переменную self.

 

Далее рассмотрим содержимое языковых файлов en.lua и остальных.

Есть несколько способов организовать поддержку нескольких языков в аддоне. Можно, как рекомендуют ZOS'ы, использовать глобальную переменную EsoStrings и для работы с ней стандартные функции ZO_CreateStringId и SafeAddString, но мне этот способ не нравится, т.к. в таблица EsoStrings глобальная и в ней уже содержится очень много строк клиента игры, да и некоторые аддоны туда тоже строк добавляют. Правда у ZOS'го способа есть и приимущество - добавленную строку можно использовать из нескольких аддонов. Да и некоторые функции ZOS'ов требуют, чтобы им параметры передавались таким образом.

Я же использую другой подход - определяю текстовые константы. Например, файл en.lua:

MVUA.i18n = {
	TabName = "Table",
	ColName = {"Column 1","Column 2","Column 3","Column 4","Column 5"},
}
и ему соответствующий ru.lua:

MVUA.i18n = {
	TabName = "Таблица",
	ColName = {"Колонка 1","Колонка 2","Колонка 3","Колонка 4","Колонка 5"},
}
Все зависящие от языка константы лучше всего разместить в одной таблице, в данном примере это MVUA.i18n, чтобы при загрузке языковых файлов отличных от en эта таблица полностью перетерлась. При таком подходе, когда добавляется новая текстовая константа ее обязательно нужно добавить во все языковые файлы. Можно воспользоваться другим подходом, при котором все строки обязательно должны быть только в en.lua, а в остальных языковых файлах только те, для которых есть перевод. Например, файл en.lua:

MVUA.i18n = {}
local l = MVUA.i18n
l.TabName = "Table"
l.ColName = {"Column 1","Column 2","Column 3","Column 4","Column 5"}
и ему соответствующий ru.lua:

local l = MVUA.i18n
l.TabName = "Таблица"
l.ColName = {"Колонка 1","Колонка 2","Колонка 3","Колонка 4","Колонка 5"}
Обращаю внимание, что при таком подходе таблица MVUA.i18n инициализируется только в английском файле, а в остальных переписываются только значения переменных. Изменено пользователем ForgottenLight
Ссылка на комментарий
Поделиться на другие сайты

Спасибо за очень подробное описание. Узнал много нового.

Парочка вопросов по уже написанному.

 

1. По поводу сохраненных параметров их файла переменных:

MyAddon.SV_Pers = ZO_SavedVars:New("MyAddonSVPers",1,nil)
MyAddon.SV_AccW = ZO_SavedVars:NewAccountWide("MyAddonSVAccW",1,nil)

Вопрос: это мы получили данные, а как записать первый раз их (ручками default создать?) и как потом обновить их при изменении в игре.

 

2. Ковыряя уже готовые аддоны столкнулся с  ":" и переменно self, я так понимаю это тоже фишка не LUA, а ZOS? Совсем не понимаю принципа работы с таким стилем.

Выше было написано, что опишите принцип работы с этим синтаксисом, жду-недождусь.

 

3. Опишу то, что я хочу сделать:

     - Главное: Поставить любой значёк на против или возле неизвестных мотивов и рецептов (возможно уже есть готовые решения, но я хочу разобраться на таком как мне кажется простом, но более информативном уроке)

     - Как я это вижу:

            1. получить список изученных рецептов (Храним в SaveVar)

            2. при открытии вендора/банка/инвентаря/гильд-банка/гильд-стора получаем значения об имеющихся рецептах (id, название, и т.д.).

            3. функция сравнения того, что есть в инвентаре или банке с тем, что знаем.

            3. добавить любой значёк, записываем обратно.

            4. вернуть обратно в игру новые данные.

 

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

 

почитав esoui для себя нашел пока только концовку:

 

В конце основного файла аддона дописать когда включать функцию:

EVENT_MANAGER:RegisterForEvent(ТУТ ИМЯ АДДОНА, EVENT_OPEN_STORE, ТУТ ФУНКЦИЯ КОТОРАЯ БУДЕТ СРАВНИВАТЬ)
EVENT_MANAGER:RegisterForEvent(ТУТ ИМЯ АДДОНА, EVENT_OPEN_GUILD_BANK, ТУТ ФУНКЦИЯ КОТОРАЯ БУДЕТ СРАВНИВАТЬ)

Как я понимаю для каждого ивента нужно будет писать отдельную строку. Так же не нашел событие которое описывает открытие инвентаря...

А еще нужно ли выгружать аддон? Типо "EVENT_MANAGER:UnRegisterForEvent(ТУТ ИМЯ АДДОНА, EVENT_CLOSE_GUILD_BANK, ТУТ ФУНКЦИЯ КОТОРАЯ БУДЕТ СРАВНИВАТЬ)"?

 

Столько вопросов..... :sorry:

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

 

Заранее благодарен.

Ссылка на комментарий
Поделиться на другие сайты

@Hoft, Ех... вот хорошо когда разбираешься в чем-то и думаешь, что это так просто.... А вот если это не твой профиль? что тогда?

Еще раз напишу: wiki.esoui я почитал как мог. даже урок делал с таймером... но он не описывает взаимодействие с игрой... он добавляет просто отдельную функцию в своем окошке и все...

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

ZOS грубо говоря создали свой язык программирования (условно), и дали просто набор функций.

тем более англ описания не в счет.

Еще читаю (опять же как могу) про создание стамина бара, там более близко к тому, что надо, но опять же англ не сильно помогает пониманию.

Изменено пользователем Yamenko
Ссылка на комментарий
Поделиться на другие сайты

1. По поводу сохраненных параметров их файла переменных:

MyAddon.SV_Pers = ZO_SavedVars:New("MyAddonSVPers",1,nil)
MyAddon.SV_AccW = ZO_SavedVars:NewAccountWide("MyAddonSVAccW",1,nil)
Вопрос: это мы получили данные, а как записать первый раз их (ручками default создать?) и как потом обновить их при изменении в игре.

 

Никаких функций по сохранению нет. Переменные сохраняются АВТОМАТИЧЕСКИ при выходе персонажа из игры. А как же игра узнает какие именно переменные нужно сохранить? Все просто - те которые получены функцией ZO_SavedVars:New или ZO_SavedVars:NewAccountWide. Функция возвращает переменную типа таблица. Внутри этой таблицы ты можешь создавать новые переменные и таблицы. Все что ты создашь внутри этой таблицы сохранится!

ВАЖНО: Таблица, которую вернули функции, не совсем обычная. Ссылка на нее запомнилась где-то в системе и при выходе персонажа из игры всё, что есть в этой таблице будет сохранено в файл!

 

2. Ковыряя уже готовые аддоны столкнулся с  "

:" и переменно self, я так понимаю это тоже фишка не LUA, а ZOS?

Как раз наоборот, это фишка LUA. Следующий пост как раз собирался посвятить этому. Также я давал ссылку Lua за 60 минут, там вроде тоже про это есть. LUA это скриптовый язык который существенно отличается от привычных языков типа бейсика и паскаля, хотя на первый взгляд эти отличия и не видно. :)

 

 

3. Опишу то, что я хочу сделать:

     - Главное: Поставить любой значёк на против или возле неизвестных мотивов и рецептов (возможно уже есть готовые решения, но я хочу разобраться на таком как мне кажется простом, но более информативном уроке)

     - Как я это вижу:

            1. получить список изученных рецептов (Храним в SaveVar)

            2. при открытии вендора/банка/инвентаря/гильд-банка/гильд-стора получаем значения об имеющихся рецептах (id, название, и т.д.).

            3. функция сравнения того, что есть в инвентаре или банке с тем, что знаем.

            3. добавить любой значёк, записываем обратно.

            4. вернуть обратно в игру новые данные.

 

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

Для получения значений данных о каких либо характеристиках/знаниях/и т.п. персонажа есть два пути. Первый относительно простой - через функции API. Например функции IsUnitGrouped("player") и IsUnitGroupLeader("player") возвращают логические значения. Первая вернет находится ли твой персонаж в группе, а вторая является ли он лидером группы. Второй путь более сложный - перехват стандартных функций ZOS. Его я рассотрю позже.

 

почитав esoui для себя нашел пока только концовку:

 

В конце основного файла аддона дописать когда включать функцию:

EVENT_MANAGER:RegisterForEvent(ТУТ ИМЯ АДДОНА, EVENT_OPEN_STORE, ТУТ ФУНКЦИЯ КОТОРАЯ БУДЕТ СРАВНИВАТЬ)
EVENT_MANAGER:RegisterForEvent(ТУТ ИМЯ АДДОНА, EVENT_OPEN_GUILD_BANK, ТУТ ФУНКЦИЯ КОТОРАЯ БУДЕТ СРАВНИВАТЬ)
Как я понимаю для каждого ивента нужно будет писать отдельную строку. Так же не нашел событие которое описывает открытие инвентаря...

А еще нужно ли выгружать аддон? Типо "EVENT_MANAGER:UnRegisterForEvent(ТУТ ИМЯ АДДОНА, EVENT_CLOSE_GUILD_BANK, ТУТ ФУНКЦИЯ КОТОРАЯ БУДЕТ СРАВНИВАТЬ)"?

 

Столько вопросов..... :sorry:

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

 

Заранее благодарен.

 

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

Типа лекция 4-я: Использования макроса ":" и переменной self.
Перед рассмотрением макроса кратенько пройдемся по сведениям о таблицах, т.к. макрос ":" применяется именно на таблицах. Одной из главных особенностей языка LUA, отличающей его от других, являются таблицы. В других языках есть похожие конструкции - массивы, классы, объекты, записи, структуры и т.п. Но они имеют одно важное отличие - у них есть описание типа и на основе этого типа создается один или несколько экземпляров. В LUA нет описания типов и таблица всегда является экземпляром, существующим в единственном числе. Правда, специальными функциями можно сделать копию таблицы, т.е. по сути второй экземпляр.
В отличие от массивов в других языках, таблицы в LUA могут содержать данные разных типов, в том числе и таблицы. Т.е. может быть создана структура объектов. Для получения значения переменной внутри таблицы (операция индексирования) есть всего два равнозначных способа - через оператор "." и через оператор "[ ]". Например

a = {}
a.b = 4
a["b"] = 4

Первой строкой мы создаем таблицу a, второй строкой мы внутри таблицы a создаем переменную b и присваиваем ей значение 4. Третья строка делает точно то же, что и вторая. Хотя вторая и третья строка внешне отличаются, но с точки зрения языка LUA они абсолютно одинаковы. Т.е. когда LUA выполняет эти строки он делает одно и то же: ищет внутри таблицы a переменную b, если не находит то создает и присваивает ей значение 4.
Как уже говорилось, элементами таблицы могут быть переменные разных типов в том числе и функции. Эта особенность позволяет таблицам LUA являться заменой классам/объектам в других языках. Присвоить переменной таблицы функцию тоже можно несколькими способами. Хоть эти способы разные по написанию, по сути делают они одно и то же. Пример:

a = {}
a.b = 4
a["с"] = 5
-- Первый способ
a.f = function() return a.b * a.c end
-- Второй способ
a["f"] = function() return a.b * a.c end
-- Третий способ
function a.f() return a.b * a.c end

В примере создается таблица a, внутри нее создаются две переменные b и c и им присваиваются значения 4 и 5 соответственно. Далее, тремя способами определяется функция f, возвращающая произведение двух переменных из таблицы a.
Нужно отметить, что таблица a является глобальной переменной и когда будет выполняться функция, будет выполнено две операции индексирования глобальной переменной - получение значений a.b и a.c. Как уже говорилось в предыдущих лекциях, получение доступа к глобальной переменной более накладная операция, чем получения доступа к локальной. Если в данном коротком примере доступ затребовался всего 2 раза, то в реальности этих операций может быть гораздо больше. Для улучшения быстродействия можно применить прием с локальной переменной-ссылкой:

function a.f()
  local a = a
  return a.b * a.c
end

Здесь мы создаем локальную переменную a и присваиваем ей ссылку на глобальную переменную a. Таким образом доступ к глобальной переменной будет получен всего один раз, а индексирование будет производиться уже на локальной переменной. Чтобы каждый раз не писать в коде функции создание локальной переменной в LUA была придумана специальная макро подстановка ":". Когда мы описываем функцию с ее помощью:

function a:f()
  return self.b * self.c
end

язык LUA видит вместо этого такое описание:

function a.f(self)
  return self.b * self.c
end

Т.е. когда LUA разбирает описание функции он заменяет доеточие на точку и добавляет к списку параметров функции, параметр self. Если у функции есть какие-то параметры, то они располагаются уже после self. Пример:

-- Функция описанная через макрос ":"
function a:w(z)
  return (self.b - z) * self.c
end
-- Идентичная предыдущей, функция, написанная через обычный оператор индексирования
function a.w(self, z)
  return (self.b - z) * self.c
end

С описанием функций разобрались. Теперь рассмотрим как интерпритирует LUA макрос ":" при вызове функции:

local n = 0
-- Вызов функции через макрос ":"
n = a:w(1)
-- Идентичен такому вызову через точку.
n = a.w(a, 1)

Т.е. при вызове через ":" первым параметром передается сама таблица a, а затем уже все остальные параметры. Хотя функцию описанную через ":" вполне можно вызвать в написании через ".", подав первым параметром саму таблицу, лучше этого не делать, исключительно для читаемости кода. Код будет легче читать и соответственно ошибок будет меньше, если воспользоваться простым правилом - функции описанные через "." вызывай через ".", а функции описанные через ":" вызывай через ":". :)

Изменено пользователем ForgottenLight
Ссылка на комментарий
Поделиться на другие сайты

Это какой-то треш...

Могли бы вы сделать простой пример для наглядности работа с данными игры?

 

Пробую сделать вывод в чат сообщения (пока вообще без получения данных).

вот то, что я написал:

local function Initialize()
    local msg = "ПРОБУЕМ ТАК" --Задали переменную
CHAT_SYSTEM:AddMessage(msg) -- Пробуем вывести переменную в чат
CHAT_SYSTEM:AddMessage("переменная: "..msg) -- Пробуем вывести текст + переменная 
CHAT_SYSTEM:AddMessage("А тут закончили!") -- Пробуем вывести просто текст
  EVENT_MANAGER:UnregisterForEvent("MyAddon", EVENT_ADD_ON_LOADED)
end

EVENT_MANAGER:RegisterForEvent("MyAddon", EVENT_ADD_ON_LOADED, Initialize) 

Собственно ничего не происходит...

Что не так?

Спасибо.

Изменено пользователем Yamenko
Ссылка на комментарий
Поделиться на другие сайты

Это какой-то треш...

Могли бы вы сделать простой пример для наглядности работа с данными игры?

 

Пробую сделать вывод в чат сообщения (пока вообще без получения данных).

 

Собственно ничего не происходит...

Что не так?

Спасибо.

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

    Я использую функцию написанную на базе кода, подсмотренного давным давно, в каком-то аддоне:

    local function msg(txt, method)
      txt = "|c88aaff" .. MVUA.Name .. ":|r " .. txt
      if method == true or method == nil then
        CHAT_SYSTEM.primaryContainer.currentBuffer:AddMessage(txt) 
      else
        d(txt)
      end
    end
    
    В функции к подаваемой текстовой строке доклеивается название аддона, выделенное цветом. При обычном вызове функции например так:

    msg("Тест")

    В чат выведется примерно следующее:

    MVUA: Тест

    При этом будет использован первый метод - CHAT_SYSTEM.primaryContainer.currentBuffer:AddMessage(). На сколько я понимаю, он добавляет текст на первую закладку чата. Второй метод, который d(), выводит информацию на текущую закладку, но у него есть ограничения - в некоторые моменты он может не работать (то-ли при загрузке аддона, то-ли при подгрузке локации, уже точно не помню).

  • Если нужно отправить текст другим игрокам. Для этого используется функция:

    CHAT_SYSTEM:StartTextEntry(txt)
    
    Она поместит текст в поле редактирования окна чата и игроку останется нажать Enter чтобы текст отправился другим игрокам.

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

ЗЫ: На работе малость пригрузили, пока некогда "типа лекции" писать. Как будет посвободнее - продолжу. :) Изменено пользователем ForgottenLight
Ссылка на комментарий
Поделиться на другие сайты

Спасибо.

Будем пробовать.

Просто я пробовал вместо 

CHAT_SYSTEM:AddMessage(msg) (такой метод написан в уроке на англ для вывода)

писать

d(msg). (такой метод написан в BugEater)

результат один и тот же - ничего в чат не выводится.

 

в скобках функции указывается ведь то что она принимает из вне... правильно же? (а не то что она потом отдает... а отдает то что в return ?)

поэтому если не надо ничего в нее специально запихивать то можно оставить скобки пустыми "()".?

Ссылка на комментарий
Поделиться на другие сайты

...результат один и тот же - ничего в чат не выводится.

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

Для примера, перехвати например событие взятие квеста и выведи его название в чат

MVUA = {
  Name = "MVUA",
}

function MVUA.OnQuestAdded(eventCode, journalIndex, questName, objectiveName, questId)
  d("MVUA, D(): " .. questName)
  CHAT_SYSTEM.primaryContainer.currentBuffer:AddMessage("MVUA, AddM(): " .. questName) 
end

function MVUA.OnAddOnLoaded(event, addonName)
  if addonName == MVUA.Name then
    EVENT_MANAGER:RegisterForEvent(MVUA.Name, EVENT_QUEST_ADDED, MVUA.OnQuestAdded)
    EVENT_MANAGER:UnregisterForEvent(MVUA.Name, EVENT_ADD_ON_LOADED)
  end
end
 
EVENT_MANAGER:RegisterForEvent(MVUA.Name, EVENT_ADD_ON_LOADED, MVUA.OnAddOnLoaded)

в скобках функции указывается ведь то что она принимает из вне... правильно же? (а не то что она потом отдает... а отдает то что в return ?)

поэтому если не надо ничего в нее специально запихивать то можно оставить скобки пустыми "()".?

Да, в скобках указываются входящие параметры функции. Если функции параметры не нужны то указываются "()", например:

local function test()
  d("Test")
end
При этом функция вызывается также с пустыми скобками: test()

Если функцию описанную с параметрами вызвать с пустыми скобками то все параметрам будут переданы значения nil.

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

Ссылка на комментарий
Поделиться на другие сайты

Спасибо, будем пробовать.

Я дико извиняюсь, но где вы берете события и какие значения они принимают.

вот пример:

MVUA.OnQuestAdded(eventCode, journalIndex, questName, objectiveName, questId)

"MVUA" - название таблицы (придумываем сами, это понятно) 

"OnQuestAdded" - это функция или событие? (придумываем сами или есть где-то есть список где можно посмотреть?)

"eventCode, journalIndex, questName, objectiveName, questId" - вот эти переменные нигде не используются внутри созданной функции значит у них где-то есть уже присвоенные значения.

 

На вики есть ссылки только на API OnQuestAdded

  • ZO_FocusedQuestTracker:OnQuestAdded(questIndex)
  • ZO_Tracker:OnQuestAdded(questIndex)

но я так понимаю это относится вообще к другому.

 

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

Изменено пользователем Yamenko
Ссылка на комментарий
Поделиться на другие сайты

"OnQuestAdded" - это функция или событие? (придумываем сами или есть где-то есть список где можно посмотреть?)

"eventCode, journalIndex, questName, objectiveName, questId" - вот эти переменные нигде не используются внутри созданной функции значит у них где-то есть уже присвоенные значения.

Все события перехватываются так:

EVENT_MANAGER:RegisterForEvent(<УникальноеИмя>, <ПерехватываемоеСобытие>, <ФункцияОбработчик>)
Здесь:
  • <УникальноеИмя> - Уникальное имя события. Для всех аддонов и интерфейса игры комбинация <УникальноеИмя> + <ПерехватываемоеСобытие> должна быть уникальна. Обычно, в аддоне каждое событие перехватывается одни раз, по этому в качестве уникального имени применяют имя аддона. Но иногда бывает нужно перехватить одно и тоже событие два раза разными обработчиками, тогда к имени аддона добавляют чего нибудь.
  • <ПерехватываемоеСобытие> - Собственно событие, какое хочешь перехватить. События можно глянуть на сайте ESOUI или в других аддонах. События пишутся заглавными буквами и начинаются с EVENT_
  • <ФункцияОбработчик> - Функция внутри твоего аддона, которая будет обрабатывать события. Может называться как угодно, но обычно им дают названия начинающиеся на On после которого идет название эвента.
  • ZO_FocusedQuestTracker:OnQuestAdded(questIndex)
  • ZO_Tracker:OnQuestAdded(questIndex)
но я так понимаю это относится вообще к другому.

 

Это стандартные обработчики событий от ЗОСовского интерфейса пользователя.

Ссылка на комментарий
Поделиться на другие сайты

Ок, спасибо.

Нашел, что присылает событие. )))

EVENT_QUEST_ADDED (number eventCode, number journalIndex, string questName, string objectiveName)

теперь будем копать в нужном направлении))))

Изменено пользователем Yamenko
Ссылка на комментарий
Поделиться на другие сайты

А есть ли какая нибудь функция для проверки того что присвоено функции, на примере php (функция var_dump() ;)?

 

Спасибо.

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

Из справочника по LUA:

-- Возвращает строку, содержащую двоичное представление данной функции function
-- если после этого вызвать функцию loadstring на эту строку, мы получим копию функции
string.dump(function)
Т.е. можно получить строку, которая является двоичным представлением функции и сравнить совпадают ли они по коду. Поясню примером:

-- Определяем переменные
local a = function(x,y) return x+y end
local b = function(x,y) return x+y end
local c = a
local s_a = string.dump(a)
local s_b = string.dump(b)
-- Производим сравнение
-- 1. Здесь выведется a <> b т.к. хоть по коду функции и совпадают, но это разные объекты и соответственно ссылки на них разные
if a == b then d("a = b") else d("a <> b") end
-- 2. Здесь выведется a = c т.к. ссылки идут на один и тот же объект
if a == c then d("a = c") else d("a <> c") end
-- 3. Здесь, по идее, выведется s_a = s_b т.к. по коду функции совпадают. Правда я это не проверял, т.ч. это чисто теоретически.  
if s_a == s_b then d("s_a = s_b") else d("s_a <> s_b") end
Проверка на равенство функций нужна, например, в случаях когда перехватываешь функцию другого аддона или пользовательского интерфейса и есть вероятность, что процедура, где идет перехват, может вызваться несколько раз. Пример из моего аддона DBGN:

function DBGN:Initialize()
-- Кусок кода пропущен
  zo_callLater(function()
    if GUILD_ROSTER_KEYBOARD.GuildRosterRow_OnMouseUp ~= self.GuildRosterRow_OnMouseUp then
      self.HoldGuildRosterRow_OnMouseUp = GUILD_ROSTER_KEYBOARD.GuildRosterRow_OnMouseUp
      GUILD_ROSTER_KEYBOARD.GuildRosterRow_OnMouseUp = self.GuildRosterRow_OnMouseUp
    end
-- Кусок кода пропущен
  end, 2000); 
-- Кусок кода пропущен
end

function DBGN.GuildRosterRow_OnMouseUp(self, control, button, upInside)
  if DBGN.HoldGuildRosterRow_OnMouseUp then
    DBGN.HoldGuildRosterRow_OnMouseUp(self, control, button, upInside)
  end
-- Кусок кода пропущен
end
Здесь идет перехват стандартного обработчика событий интерфейса пользователя. В функции DBGN:Initialize мы проверяем, а не ссылается ли уже стандартный обработчик на нашу функцию, приготовленную для замены. Если не ссылается, то запоминаем ссылку на стандартный обработчик в переменной DBGN.HoldGuildRosterRow_OnMouseUp и заменяем обработчик на свой. В своем обработчике проверяем, а есть ли уже сохраненный старый обработчик? Если есть, то вызываем вначале его, а затем уже делаем свои действия. В данном случае проверки излишни и являются перестраховкой, т.к. функция DBGN:Initialize вызывается всего 1 раз, а функция DBGN.GuildRosterRow_OnMouseUp вызывается только после инициализации и старый обработчик по идее уже должен быть сохранен по любому.

 

Если вопрос был не о сравнении функций, а о получении списка аргументов функции, то тут есть свои хитрые конструкции. Пример из Lua за 60 минут

-- в прототипе переменное число аргументов записывается как троеточие
function sum(...) 
   s = 0
   for _, n in pairs(arg) do -- в функции обращаются к ним, как к таблице "arg"
      s = s + n
   end
   return a
end
 
sum(1, 2, 3) -- вернет 6
sum(1, 2, 3, 4) -- вернет 10
В данном случае подразумевается, что аргументы все числовые, но они вполне могут быть и разнотипными, а в функции можно уже проверять их тип.
Ссылка на комментарий
Поделиться на другие сайты

В php функция var_dump($arg); вернет примерно такое значение для таблицы (массива):

array(3) {[name]=>string("Название")[qwer]=>int(2)[default]=>array(3) {[0]=>string(1) "a" [1]=>string(1) "b" [2]=>string(1) "c" }}

 

указывает тип и что есть в переменной $arg в данном случае внутри массива лежит строка, численное значение и вложенный массив с 3мя значениями...

 

думал может в LUA есть что-то похожее...

 

через перебор таблицы есть некоторые сложности, если есть вложенные таблицы их тоже нужно смотреть отдельно, а если переменная ссылается на функцию, то вообще не понятно, что там может быть :-)

 

Пока приходится так смотреть:

for key, msg in pairs(DNSE) do
	if msg ~= string then
		msg = tostring(msg)
	end
	CHAT_SYSTEM.primaryContainer.currentBuffer:AddMessage("Позиция "..i..", ключ "..key..", значение: "..msg)
	i = i + 1
end

 

Пример из Lua за 60 минут

это я в первую очередь прочитал :-) Спасибо.

 

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

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

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

Ссылка на комментарий
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...