Novik16

Heroes of Might&Magic: 2. Немножко про LUA.

Справочник по скриптовому API HoMM V, версия 1.3
  1. Немножко про LUA.
С полным руководством по языку рекомендуется ознакомиться тут. От начала и до пункта 2.6 включительно - дальнейшее не имеет отношения к нашему случаю. Ниже приведенная информация прочтение полного руководства не заменяет.
Тезисно изложу некоторые моменты, которые для меня показались интересными.
LUA - встроенный (embedded) язык. В связи с чем, речи о чем-либо вроде сишной функции main() не идет.
Понятие идентификатора в LUA совпадает с таковым в прочих алгоритмических языках. Это последовательность латинских букв, подчеркиваний и цифр, начинающаяся с буквы.
Регистр букв в идентификаторах имеет значение. Т.е. переменные Dummy и dummy - разные переменные.
Типизация в языке динамическая. Переменные не нужно описывать. Тип переменной определяется в зависимости от контекста ее использования. Порой в связи с этой особенностью приходится предпринимать определенные "финты ушами", например:
interval = GetGameVar("interval")
interval = interval + 0
sleep(interval)
Функция GetGameVar возвращает строку, а sleep принимает в качестве параметра число. Строчка с прибавлением нуля нужна только для того, чтобы показать интерпретатору, что в дальнейшем мы будем использовать переменную interval как число. Если данную строчку убрать - на вызове sleep получим ругань о несовпадении типов.
Впрочем, подобный подход имеет и массу преимуществ, например, можно написать что-то вроде
local currentSign = 2

local lastSign = "sign"..(CurrentSign-1)
local newSign = "sign"..(CurrentSign+1)
local sign = "sign"..CurrentSign
SetObjectEnabled(newSign, false)
SetObjectEnabled(lastSign, true)
ChangeHeroStat(HERO_NAME, STAT_EXPERIENCE, 500*currentSign)
В данном примере для формирования имени объектов используется операция конкатенации - ".."
Простых типов имеется три - число (обычный double), строка (последовательность октетов в одинарных либо двойных кавычках) и логический (false\true). Есть выделенное значение nil (привет Паскалю). Собственно, значений false и true как таковых в данном интерпретаторе языка нет. Они определены константами в файле /scripts/advmap-startup.lua
true = not nil
false = nil
Данный файл «работает» только на стратегической карте, а для тактических скриптов эти константы определить забыли. Это следует учитывать.
Из сложных типов представлены таблицы - нечто среднее между структурами и массивами. В общем, таблицы очень похожи на привычные массивы, за исключением того, что 1) они могут включать разнородные элементы 2) могут индексироваться не только числами. Допустима, например, такая запись:
cam_switch =
{
{ cam = 1, from = 0, to = 3000 },
{ cam = 2, from = 3000, to = 5000 },
{ cam = 1, from = 5000, to = 10000 },
{ cam = 3, from = 10000, to = 16000 },
{ cam = 1, from = 16000, to = 25000 },
}
и, соответственно, обращение из кода
cam_switch[2].cam (или cam_switch[2]["cam"], такая вот забавная форма)

Отдельным интересным вопросом применительно к игре является нижняя граница индексации таблиц в случае числового индекса. С одной стороны, таблицы, заданные непосредственно в теле скрипта (как выше) по умолчанию индексируются от единицы, с другой – большинство таблиц, возвращаемых функциями наподобие GetPlayerHeroes, имеют нижней границей ноль. «Умом это не понять, следует просто запомнить» (с).
Другим сложным типом является функция. Или указатель на функцию – зависит от восприятия. В игровых скриптах он практически не используется, вместо этого задействованы имена функций (типа строка) с последующим их вычислением посредством функции parse. Впрочем, я забегаю вперед.
Полная форма оператора присваивания весьма забавна. Например, выражение
x, y = y, x
приведет к обмену значений переменных x и y. Рекомендовал бы использовать простую форму - незачем путать себя и окружающих. Вместо
local state, new_state = GetGameVar( stname ), -1
можно (и IMHO нужно) написать
local state = GetGameVar( stname )
local new_state = -1
Имеется стандартный набор операторов контроля выполнения кода. Т.е. if, while, repeat, и две формы цикла for. Синтаксис таков:
while exp do block end
Цикл while - выполнять блок block пока условие exp истинно.
Пример:
while IsTutorialMessageBoxOpen() do
sleep(1)
end
Цикл repeat - выполнять блок block пока условие exp ложно. Всегда выполняется хотя бы один раз.
repeat block until exp
Пример:
repeat
Sleep(1)
until (GetCurrentPlayer() ~= PLAYER_2)
Условный оператор.
if exp then block {elseif exp then block} [else block] end
Пример:
if sID == "n1male" then
nUnitObjectID = 1
elseif sID == "n1female" then
nUnitObjectID = 2
else
nUnitObjectID = 3
end
Замечу - выражение exp в выше приведенных примерах может быть любого типа. Но только значения false и nil трактуются как false. Все прочее будет рассматриваться, как true. Посему товарищам, практикующим С и С++ следует быть осторожнее - выражения типа
if(0) then

end
тут не проходят. Блок внутри оператора if в вышеприведенном примере будет выполняться всегда.
С циклами for ситуация немного посложнее. Имеются две формы данного цикла. Первая форма:
for var = value, limit, step do block end
Выполнять block до тех пор, пока значение var не достигнет limit. На каждой итерации значение var увеличивается на step. Если step отсутствует, то принимается, что он равен единице. Понятно дело, явно изменять значение var внутри цикла нельзя.
Пример:
for i = 1, 11, 1 do
RemoveObject( 'inferno'..i )
end
в связи с последним замечанием, можно, впрочем написать и попроще -
for i = 1, 11 do
RemoveObject( 'inferno'..i )
end
Вторая форма оператора предназначена для работы с таблицами.
for index, var in explist1 do block end
Пример:
local heroes = GetPlayerHeroes( PLAYER_1 );
for i, hero in heroes do
if GetTownHero( 'Bobruisk') ~= hero then
print( i, hero );
end
end
На каждой итерации цикла в index имеем счетчик (кстати, не обязательно числовой), в var - очередное значение из таблицы. Цикл крутится по разу для каждого элемента таблицы
Внутри циклов Вы можете пользовать оператор break и return. Занятный момент - данные операторы должны быть последними в блоке. Если хочется обойти это условие (обычно такая необходимость возникает при при отладке) необходимо пользовать конструкции do break end и do return end.
Замечу - оператора goto как и понятия меток в языке нет. "И это правильно, товарищи".
Операции сравнения ничем не отличаются от таковых в прочих языках. Разве что не совсем обычной формой записи операции "не равно".

~= < > <= >

Результатом всегда является true или false. Если тип сравниваемых переменных не совпадает, то результатом сравнения всегда будет false. Т.е. блок внутри данного оператора
If("0"==0) then

end
не выполнится никогда.
Логические операторы -
and or not
Операторы как операторы. Ничего особенного. Единственное, что хотелось бы заметить - не следует полагаться на порядок вычисления операторов. Ставьте скобки. И вам уверенности больше, и другим понятнее. Иначе в один прекрасный момент Вы можете обнаружить, что конструкция
if objectname == "HIREPERS1" or objectname == "HIREPERS2" and NextBlockOrderID == 2 or NextBlockOrderID == 3 then

end
на самом деле вовсе не эквивалентна конструкции
if (objectname == "HIREPERS1" or objectname == "HIREPERS2") and (NextBlockOrderID == 2 or NextBlockOrderID == 3) then

end
что, конечно, не согласуется с мануалом. Но нам же ехать, а не шашечки, не правда ли?
Функции определяются так:
function funcname( [parlist1] ) block end
Функции могут возвращать несколько значений. Например, допустима следующая конструкция:
function f()
return 1,2,3
end
a,b,c = f()
Небольшая особенность, связанная с функциями. Лучше всего ее иллюстрирует следующий пример. Рассмотрим два фрагмента:
function GetCreatures(side)
return(GetUnits(side, CREATURE))
end
и
function GetCreatures(side)
local temp = GetUnits(side, CREATURE)
return(temp)
end
Казалось бы, функции идентичны, однако результатом работы первой всегда будет nil. Это ошибка (или особенность, кому как) реализации интерпретатора. Посему общее правило – функция должна возвращать результат через локальную переменную.
На этом с описанием особенностей языка закончу. Если написанного для Вас недостаточно - читайте оригинальный мануал.

Просмотров: 6 140

Комментарии пока отсутcтвуют