Основы Lua

Материал из W3d.

Перейти к: навигация, поиск

Wikipedia гласит:

Lua ([лу́а], «луна») — интерпретируемый язык программирования, разработанный подразделением Tecgraf Католического университета Рио-де-Жанейро (Computer Graphics Technology Group of Pontifical Catholic University of Rio de Janeiro in Brazil). Является свободно распространяемым, с открытыми исходными текстами на языке Си.

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

Здесь будет рассказано о использовании Lua на примере Worms4 (для Worms3D срипты необходимо к тому же компилировать из *.lua в *.lub файл с помощью программы luac.exe)

Содержание

Image:Lua.gif


[править] Смысл скриптов

Итак, приступим! Сначала определимся что же такое скрипт? Понимать можно по-разному но мы будем называть это программкой, которая выполняется не операционной системой, а какой-то средой, в нашем случае это интерпретатор языка lua, встроенный в червяков.

Интерпретатор - это программа, которая способна выполнять определённые программы, которые она понимает. Если это интерпретатор lua, то она выполняет наш скрипт на языке lua.

Интерпретатор языка lua имеет чудесное свойство - с помощью него скрипту можно дать возможность управлять той программой, которая исполняет скрипт. То есть интерпретатор языка lua это лишь прослойка между нашим скриптом и червяками.

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

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

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

[править] В чём работать?

Всё о чём мы будем далее говорить требует попыток воспроизвести это всё и понять, как всё работает. Конечно lua - код можно тестировать непосредственно в червях(об этом будет рассказано позже), однако так совершенно неудобно изучать язык, его самые простые конструкции, поэтому нужно другое решение. Для тестирования рекомендуем использовать другой lua-интерпретатор с редактором SciTE.

[править] Установка и настройка SciTE с lua интерпретатором

  1. Скачайте редактор SciTE и интерпретатор lua
  2. Установите SciTE.
  3. Извлекаем содержимое второго архива в какое-либо надёжное место, желательно без пробелов в пути, например C:\lua
  4. На рабочем столе щёлкаем правой кнопкой на иконке "Мой компьютер", выбираем в меню "Свойства", переходим на вкладку "Дополнительно", жмём по кнопке "Переменные среды", ищем в списке системных переменных строчку Path, выбираем её и жмём кнопку изменить. В текстовом поле "Значение переменной" в конце добавляем текст ";C:\lua", жмём OK. В этом же окне под списком системных переменных жмём кнопку "Создать". В текстовом поле "Имя переменной" вводим "LUA_DIR", а в текстовом поле "Значение переменной" вводим "C:\lua", жмём OK. Аналогично добавляем ещё 2 переменные LUA_PATH = "?.lua;%LUA_DIR%\?.lua;%LUA_DIR%\L_DIR\?.lua" и LUA_CPATH = "?.dll;%LUA_DIR%\?.dll;%LUA_DIR%\C_DIR\?.dll".
  5. Скачайте файл настройки для редактора SciTE для языка lua тут и замените файл C:\Program Files\SciTE\languages\lua.properties на скаченный.
  6. Запустите SciTE. Наберите в редакторе текст:
print("Hello world!")


Сохраните файл как Hello.lua, а затем нажмите "Выполнить" в меню "Tools"(или кнопку F5). Внизу появится окошко консоли, в котором будет выведен результат работы вашего первого скрипта

>lua.exe "C:\Documents and Settings\ДИМА\Рабочий стол\1111.lua"

Hello world! >Exit code: 0 Time: 0.24


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

message = print


[править] Основы программирования

Для понимания работы скриптов в Worms крайне желательно иметь некоторые сведения о программировании, хотя реально и так понять. Расскажем, как пишутся скрипты(точнее как их правильно писать).

Программа - набор команд. Команды должны быть понятны для исполнителя(у нас это интерпретатор). Наш интерепретатор понимает только команды, записанные определённым образом.

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

Команды выполняются последовательно одна за другой. Порядок исполнения можно менять. Далее приведены стандартные команды(операторы):

Примечание: далее по тексту есть примеры с использованием псевдокода-под этим подразумевается, что он не должен работать в червях. Это просто абстрактный код. К примеру message(a) - некоторая функция, которая каким либо образом выводит свой параметр пользователю, не стоит воспринимать это так, как будто message есть в червях!

[править] Комментарии

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

В Lua комментарии записываются двумя способами

  • Однострочный комментарий. После двойного знака "минус" в конце строки, например:
 message(3) --пример однострочного комментария.
            --текст после двух минусов игнорируется


  • Многострочный комментарий. После двойного знака "минус" и двух открывающих квадратных скобок, до двух закрывающих двойных скобок, например:
 message(3)  --[[
                  пример многострочного комментария.
                  текст можно записывать в несколько строк,
                  и всё что внутри будет проигнорировано,
                  даже если написать какие-либо операторы
                  чтобы окончить комментарий надо поставить две скобки(см следующую строку)
               ]]


[править] Операторы присваивания, типы данных и массивы

Переменная - особая ячейка в памяти, где могут храниться данные. Например, мы можем хранить числа, текст(строки), массивы (или таблицы, как это принято называть в lua). Ячейки памяти (переменные) имеют имена, чтобы их можно было однозначно определить. Переменные можно использовать в различных выражениях - обычных математических(+ - * /), в функциях и т.д.

Данные, хранимые в переменных могут иметь следующие типы:

  • Число

Запись чисел обычная (для обозначения дробных чисел используется символ .):

 number1 = 12412.124
 number2 = 7


  • Текст

Для записи текста (в программировании текст чаще называется строкой текста или просто строкой) используются кавычки, например так:

 text = "Привет!"


Соединение двух строк в одну делается с помощью оператора две точки (псевдокод):

 text1 = "Привет,"
 text2 = " мир!"
 text = text1 .. text2
 message(text)


Данный код выведет на экран надпись Привет, мир!

  • Булево значение (логическое, или истина или ложь)

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

Примеры(псевдокод):

 a = 2
 b = 4
 
                                   -- Сравнения
 message(a == b)                   --  равенство, выведет false
 message(a ~= b)                   --  неравенство, выведет true
 message(a > b)                    --  больше, выведет false
 message(a < b)                    --  меньше, выведет true
 message(a >= b)                   --  больше или равно, выведет false
 message(a <= b)                   --  меньше или равно, выведет true
 
                                   -- Логическое отрицание
 message(not true)                 --  выведет false
 message(not false)                --  выведет true
 
                                   -- Логическое И
 message(true and true)            --  выведет true
 message(false and true)           --  выведет false
 message(true and false)           --  выведет false
 message(false and false)          --  выведет false
 
                                   -- Логическое ИЛИ
 message(true or true)             --  выведет true
 message(false or true)            --  выведет true
 message(true or false)            --  выведет true
 message(false or false)           --  выведет false
 
                                   -- Сравнения и логические операторы можно комбинировать
 message((a > b) or (a + 2) == b)  --  выведет true, потому что хотя бы одно из условий выполнено
 message((a > b) and (a + 2) == b) --  выведет false, потому что одно из условий не выполнено


Как видно из примера, к булевым значениям можно применять логические операторы and, or и not. Они позволяют составлять сложные условия. Запомните, как они работают по этому примеру.

  • Таблица

Таблицу можно описать в виде

 sometable = { [индекс1] = значение1; [индекс2] = значение2; [индекс3] = значение3 <...> }


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

 sometable = { ["name1"] = значение1; ["name2"] = значение2; ["name3"] = значение3 <...> }


эквивалентна следующей:

 sometable = { name1 = значение1; name2 = значение2; name3 = значение3 <...> }


  • Функция

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

 local somefunc
 somefunc = function(a, b)
   return math.sin(a + b)   -- не пугайтесь, так записывается стандартная функция sin - синус
 end
 
 message(somefunc(1, 2))


Код примера выведет значение синуса 1 + 2. Обратите внимание, что somefunc - это переменная, а не функция. Сама функция является безымянной.

Переменные могут иметь разную область видимости-глобальные и локальные. Локальные действуют только в пределах определённого логического куска кода, например внутри функции, цикла и т.д. Глобальные действуют на самом высоком уровне. Если мы хотим воспользоваться(получить или присвоить) значение переменной с именем foo, то сначала переменная с именем foo ищется среди локальных, а потом среди глобальных.

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

 <имя переменной> = <значение>


Создание переменной с ограниченной областью видимости(к примеру только в функции)

 local <имя переменной> = <значение>


Примеры(псевдокод):

 a = 12
 b = 17
 c = a + b
 message(c)


При выполнении сначала переменная a примет значение 12, и b значение 17, а зачем c примет значение a+b, то есть 12+17, поэтому message(c) выведет 29

 d = 10
 g = 15
 message(d)
 message(g)
 
 function a()
   local d = 5
   g = 20
   message(d)
   message(g)
 end
 
 a()
 message(d)
 message(g)


При выполнении выведется сначала 10 и 15, потом 5 и 20, а потом 10 и 20, что говорит о том, что локальная и глобальная переменная не одно и то же. Локальная переменная пропадает, когда заканчивается работа функции.

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

 x = 10
 y = 20
 
 message(x, y)   -- выведет 10, а затем 20
 
 x, y = y, x     -- параллельное присваивание
 
 message(x, y)   -- выведет 20, а затем 1


Этот код аналогичен следующему:

 x = 10
 y = 20
 
 message(x,y)   -- выведет 10, а затем 20
 
 t = x          -- приходится использовать дополнительную переменную
 x = y
 y = t
 
 message(x,y)   -- выведет 20, а затем 1


Существует особый тип переменных - массивы или таблицы. Таблица - это набор из элементов с общим именем. Чтобы их отличать используется индекс. Таблицу в lua можно представить в виде таблицы с двумя столбцами. Пусть у нас есть таблица с именем foo

Индекс Значение Как обращаться
1 15 foo[1]
2 55 foo[2]

Создание переменной-массива, делается как и в случае обычной переменной, однако в конце надо ставить ={} что означает создание пустого массива. Его наполнение производится после этого.

Для создания массива foo, приведённого в таблице выше, можно написать следующее.

 local foo = {}
 foo[1] = 15
 foo[2] = 55


Можно создавать массив, сразу заполняя его:

 local foo = {[1] = 15; [2] = 55 }


Для того, чтобы получить значение какого-либо элемента можно написать так (псевдокод):

 message(foo[1])


Иногда бывает нужно узнать длину массива, для этого надо написать # перед именем массива, например:

 local foo = {}
 foo[1] = 15
 foo[2] = 55
 message(#foo) -- выведет 



Чудесная возможность, которую даёт lua - это создавать так называемые ассоциативные массивы, то есть такие, где в качестве индекса выступает текстовая строка(на самом деле в качестве индекса может быть что угодно, даже другой массив, но это редко применяется). Рассмотрим пример массива car.

Индекс Значение Как обращаться
"model" "BMW" car["model"] или car.model
"speed" 350 car["speed"] или car.speed
"price" 1000000000 car["price"] или car.price

Для создания такого массива можно написать следующее:

 car = {}
 car["model"] = "BMW"
 car["speed"] = 350
 car["price"] = 1000000000


или так:

 car = {}
 car.model="BMW"
 car.speed=350
 car.price=1000000000


или даже так:

 car = { model = "BMW"; speed = 350; price = 1000000000 }


Как видно, третья запись намного удобнее. При этом на массив можно смотреть, как на некоторое описание реального объекта, в нашем случае-машины с маркой BMW и большущей ценой. Для того, чтобы получить значение какого-либо элемента, например скорости можно написать так (псевдокод):

 message(car["speed"])


ну и намного проще так:

 message(car.speed)

[править] Условные операторы

Эти операторы позволяют исполнять часть команд по условию

 if <условие> then
   <последовательность команд, которая будет выполнена при выполнении условия>
 end
 if <условие> then
   <последовательность команд, которая будет выполнена при выполнении условия>
 else
   <последовательность команд, которая будет выполнена, если условие не выполнено>
 end
 if <условие 1> then
   <последовательность команд, которая будет выполнена при выполнении условия 1>
 elseif <условие 2> then
   <последовательность команд, которая будет выполнена, если прошлые условия не выполнены, и  при выполнении условия 2>
   ...
 elseif <условие n> then
   <последовательность команд, которая будет выполнена, если прошлые условия не выполнены, при выполнении условия n>
 else
   <последовательность команд, которая будет выполнена, если ни одно условие не выполнено>
 end



Пример (псевдокод):

 number = InputNumber()
 if number > 10 then
   message("Число больше 10")
 elseif number == 3 then
   message("Число равно 3")
 else
   message("Число меньше 11 и не равно 3")
 end

[править] Операторы цикла

Цикл - цепочка действий, которая может повторяться несколько раз. Житейский пример: вам сказали подпрыгнуть 10 раз - это цикл, в котором повторяется одно и тоже действие - прыжок - ровно 10 раз.

В языках программирования аналогично можно повторять одно и тоже действие. В качестве оператора цикла в Lua используются операторы for, while и repeat.

Цикл for-do-end позволяет повторять действие определённое количество раз, он называется цикл со счётчиком (то есть в нём задана переменная-счётчик, которая хранит номер повтора). Цикл for устроен просто:

 for <переменная-счётчик> = <начало цикла>, <конец цикла>, <шаг> do
   <цепочка действий, повторяемых для всех значений переменной-счётчика из указанного диапазона> 
 end


Цепочка действий, выполняемая циклом также называется телом цикла. Например (псевдокод):

 for i = 1, 10 do
   message(i)      -- Выведет числа 1, 2, 3, 4, 5, 6, 7, 8, 9, 10         
 end


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

Можно задать шаг, как в следующем примере(псевдокод):

 for i = 1, 10, 2 do
   message(i)         -- Выведет все нечётные числа 1, 3, 5, 7, 9
                      -- 11 выведено не будет, так как не попадает в заданные пределы         
 end


Существует другая разновидность циклов - циклы с условием (repeat–until и while–do-end). Данный вид цикла может повторяться пока будет выполняться или не выполняться какое-либо условие.

Цикл repeat–until устроен следующим образом:

 repeat
   <цепочка действий, которая будет выполнена хотя бы 1 раз, а если выполнится условие после until, то будет повторено> 
 until <условие продолжения>

Цикл repeat–until оканчивается условием, идущим следом за until, поэтому в условии можно ссылаться на локальные переменные, описанные внутри цикла. Цикл обязательно выполняется хотя бы 1 раз, а затем проверяется условие, необходимо ли продолжать повтор цикла.

Пример для repeat (псевдокод):

 a = 0
 repeat
   message("Введите число > 0")
   a = input()                         -- Пользователь вводит число
 until (a < 1)                         -- Повторяем, если введено не положительное число


Цикл while-do-end позволяет делать циклы, в котором тело может быть никогда не выполнено. Основное отличие от прошлого цикла в том, что условие стоит в начале цикла, на входе в него. То есть сначала проверяется, нужно ли выполнять цикл, а затем происходит выполнение и, если условие всё ещё выполняется, цикл повторяется.

Цикл while-do-end устроен следующим образом:

 while <условие> do
   <цепочка действий, которая может быть не выполнена ни разу, выполняется, только если выполнено условие выше> 
 end


В качестве примера для while рассмотрим, как сделать аналогию цикла for:

 local <переменная-счётчик>=<начало цикла>
 while <переменная-счётчик> < <конец цикла> do
   <тело цикла>
   <переменная-счётчик> = <переменная-счётчик> + 1
 end


Для управления циклами, надо уметь выходить из них по необходимости. Выйти из цикла можно с помощью оператора break.

Пример для break (псевдокод):

 a = 0
 repeat
   message("Введите число")
   a = input()                         -- Пользователь вводит число
   if a == 0 then
     break                             -- Если введён 0, то выходим
   end
 
   message(a)
 until true                            -- Это означает правду, то есть цикл будет выполняться всегда, если его не остановить

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

[править] Функции

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

  1. желания сократить код
  2. сделать его более понятным
  3. лучше контролировать работу программы, искать ошибки

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

В Lua функция имеет следующий формат:

 function <имя функции>(<параметр 1>, <параметр 2>, <...>)
   <тело функции>
   return <результат>
 end


Оператор return в функции необязателен. Его задача выйти из функции и вернуть результат. Результат может быть любым типом, который мы описали раньше. Можно писать return не указывая результата, при этом функция не будет возвращать результат. Размещать return можно где угодно внутри функции (не только в конце). Оператор return немедленно выходит из всех циклов, условий и других конструкций и завершает работу этой функции.

Вызов функции происходит в следующем виде:

<имя функции>(<значение параметра 1>, <значение параметра 2>, <...>)


Вызов функции возвращает её значение, то есть можно использовать в различных выражениях, например:

<переменная> = <имя функции>(<значение параметра 1>, <значение параметра 2>, <...>)


В самом lua уже есть несколько предопределённых функций. Они, как правило разделены на библиотеки (то есть при вызове функции sin надо указывать её библиотеку, то есть math.sin() ) и конкретная версия интерпретатора может не включать в себя некоторые из них. Список функций можно узнать на этой странице

В прошлых примерах мы рассматривали псевдо-функцию message. В worms её можно реализовать так:

 function message(text)
   SetData("Text.TestComment", text)	
   SetData("CommentaryPanel.Comment", "Text.TestComment")
   SendMessage("CommentaryPanel.ScriptText")
 end


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

Функция становится доступной после её определения, то есть нет смысла писать строку:

 message("Привет")


если функция ещё не определена. 

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

 function factorial(number)
   if (number > 1) then
     return number * factorial(number - 1)
   else
     return 1
   end
 end
 
 message(factorial(6))


Выведет 720. Работа этого кода довольно проста. Сначала мы вызываем factorial(6), она возвращает 6 * factorial(5), factorial(5) возвращает 5 * factorial(4), и так далее до factorial(1), который вернёт 1. Получим, что factorial(6) = 6 * 5 * 4 * 3 * 2 * 1 = 720.

Функции могут возвращать не одно, а несколько значений. В этом случае значения перечисляются через запятую:

 function add_mul(a, b)
   return a + b, a * b
 end
 
 x = 10
 y = 20
 
 s, m = add_mul(x, y)
 
 message("Сумма x и y = " .. s)
 message("Произведение x и y = " .. m)

[править] Программируем червяков

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

[править] Работа интерпретатора в червяках

Напомним, интерпретатор - это программа, которая способна выполнять определённые программы, которые она понимает. Он даёт нам некоторые возможности, но и требует от нас определённого порядка работы.

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

Рассмотрим, в чём же проявляются особенности интерпретатора lua в worms

[править] Функции интерпретатора

Интерпретатор в червях предоставляет несколько функций. Рассмотрим их.

[править] SetData(container_name, value)

Позволяет изменять содержимое xml(или xom в Worms3D) контейнера с помощью скрипта. Чтобы понять смысл этого, откройте любой xml файл из папки W4/data/tweak - там вы увидите данные в xml-разметке, которая очень похожа на html. В этих файлах хранится игровая информация: все параметры червя, настройки оружия и так далее. К тому же там есть контейнеры, которые на первый взгляд не имеют смысла, к примеру CommenteryPanel.Comment. Вот он (часть содержимого файла Local.xml):

 <XStringResourceDetails id='CommentaryPanel.Comment'>
   <Value></Value>
   <Name>CommentaryPanel.Comment</Name>
   <Flags>96</Flags>
 </XStringResourceDetails>

Странно, зачем в настройках хранить текст на панели комментариев, тем более что он постоянно меняется, а к тому же поле Value пусто?
Оказывается xml файлы предназначены не только для хранения данных, но и для определения "общих рабочих ячеек" для совместной работы скрипта и хоста. Внутренние функции червяков работают с различными данными, находящимися в xml, а скрипт может их менять с помощью SetData. Обычно в червяках для совершения какого-либо действия надо сначала установить все необходимые контейнеры, а затем уже совершить действие. То есть SetData сама по себе ничего не делает. Не ждите мгновенного проявления результата после вызова SetData. Обратите внимание, SetData не изменяет xml файл на диске. Эта функция работает только с версией файла в памяти! Пример использования SetData:

 SetData("CommentaryPanel.Comment", "Hello!")

Для вывода этого текста к тому же надо добавить строчку

  SendMessage("CommentaryPanel.ScriptText")
[править] SendMessage(message)

Данная функция "просит" червей выполнить какое-либо действие. К примеру можно вызвать так:

 SendMessage("CommentaryPanel.ScriptText")

Это заставит червей посмотреть, что в данный момент записано в контейнере CommentaryPanel.Comment и вывести его содержимое или содержимое контейнера с таким именем на экран в панель комментариев. Поэтому обычно для вывода текста сначала необходимо в какой-либо текстовый контейнер записать сам комментарий, затем в контейнер CommentaryPanel.Comment записать имя 1-го контейнера, а затем послать сообщение CommentaryPanel.ScriptText

 SetData("Text.TestComment", Text)
 SetData("CommentaryPanel.Comment", "Text.TestComment")
 SendMessage("CommentaryPanel.ScriptText") 

SendMessage после вызова может изменять значения различных контейнеров. Это свойство используется функцией GetData. Приведём список всех известных сообщений (все они найдены в стандартных скриптах).

Для Worms 4:

 AI.ExecuteActions
 AI.PerformDefaultAITurn
 Camera.ShakeStart
 Comment.SuddenDeath
 Commentary.Clear
 Commentary.EnableDefault
 Commentary.NoDefault
 CommentaryPanel.ScriptText
 CommentaryPanel.TimedText
 DoubleDamage.Activated
 EFMV.End
 EFMV.Play
 Earthquake.End
 Explosion.Construct
 Game.PlaceObjects
 GameLogic.AboutToApplyDamage
 GameLogic.AboutToWaterRise
 GameLogic.ActivateNextWorm
 GameLogic.AddInventory
 GameLogic.ApplyDamage
 GameLogic.Challenge.Failure
 GameLogic.Challenge.Success
 GameLogic.ClearInventories
 GameLogic.CrateShower
 GameLogic.CreateAirstrike
 GameLogic.CreateCrate
 GameLogic.CreateNuclearEffect
 GameLogic.CreateRandMineFactory
 GameLogic.CreateRandomMine
 GameLogic.CreateRandomOildrum
 GameLogic.CreateRandomTelepad
 GameLogic.CreateSentryGun
 GameLogic.CreateTelepad
 GameLogic.CreateTrigger
 GameLogic.Draw
 GameLogic.DropRandomCrate
 GameLogic.EndTurn
 GameLogic.GetAllTeamsHadTurn
 GameLogic.IncrementInventory
 GameLogic.Mission.Failure
 GameLogic.Mission.Success
 GameLogic.PlaceObjects
 GameLogic.PlaceTelepads
 GameLogic.ResetCrateParameters
 GameLogic.ResetTriggerParams
 GameLogic.SetNoFallDamage
 GameLogic.SetSpecialWeapon
 GameLogic.StartMineFactory
 GameLogic.Turn.Ended
 GameLogic.Turn.Started
 GameLogic.Tutorial.Failure
 Jetpack.UpdateFuel
 Land.Clear
 Net.DisableAllInput
 Particle.NewEmitter
 RandomNumber.Get
 SentryGun.FireAt
 SentryGun.LookAt
 String.Substitute
 String.SubstituteIndirect
 Team.Surrender
 Timer.EndRetreatTimer
 Timer.EndTurn
 Timer.StartGame
 Timer.StartHotSeatTimer
 Timer.StartPostActivity
 Timer.StartTurn
 Utility.Delete
 Weapon.Create
 Weapon.Delete
 Weapon.DisableWeaponChange
 Worm.ApplyPoison
 WormManager.GetActiveAlliances
 WormManager.GetSurvivingTeam
 WormManager.Reinitialise
 WormManager.TeleportIn
 WormSelect.OptionSelected
 Wormpot.SetupModes

Для Worms3D:

 AI.ClearActions
 AI.ExecuteActions
 AI.PerformDefaultAITurn
 AI.PerformFireAtTargetAction
 AI.PerformMoveAction
 AI.PerformSetWeaponAction
 Camera.Path.Start
 Camera.SetDefault
 Camera.ShakeEnd
 Camera.ShakeStart
 Comment.SuddenDeath
 Commentary.Clear
 Commentary.NoDefault
 CommentaryPanel.DebugText
 CommentaryPanel.ScriptText
 CommentaryPanel.TimedText
 EFMV.End
 EFMV.Start
 Earthquake.End
 Earthquake.Start
 Explosion.Construct
 FCS.QuitAttractMode
 FE.InGameDialogText
 GameLogic.AboutToApplyDamage
 GameLogic.AddInventory
 GameLogic.ApplyDamage
 GameLogic.Challenge.Failure
 GameLogic.Challenge.Result
 GameLogic.ClearInventories
 GameLogic.CrateShower
 GameLogic.CreateAirstrike
 GameLogic.CreateCrate
 GameLogic.CreateNuclearEffect
 GameLogic.CreateRandomMine
 GameLogic.CreateRandomOildrum
 GameLogic.CreateTrigger
 GameLogic.Draw
 GameLogic.DropRandomCrate
 GameLogic.EndGame
 GameLogic.EndTurn
 GameLogic.IncrementInventory
 GameLogic.Mission.Failure
 GameLogic.Mission.Success
 GameLogic.PauseGame
 GameLogic.PlaceObjects
 GameLogic.QuitGame
 GameLogic.RequestWeaponIndex
 GameLogic.ResetCrateParameters
 GameLogic.ResetTriggerParams
 GameLogic.ResumeGame
 GameLogic.RoundTime.Pause
 GameLogic.RoundTime.Resume
 GameLogic.SetSpecialWeapon
 GameLogic.Turn.Ended
 GameLogic.Turn.Started
 GameLogic.TurnTime.Pause
 GameLogic.TurnTime.Resume
 GameLogic.Tutorial.End
 GameLogic.Tutorial.Failure
 GameLogic.Tutorial.Success
 GameStats.AddStatText
 GameStats.Print
 HUD.HideTurnTime
 Jetpack.Kill
 NinjaRope.Kill
 Particle.NewEmitter
 Particle.StartEvent
 RandomNumber.Get
 Stats.IgnoreWormDeath
 Stats.UpdateAtEndOfTurn
 String.Substitute
 String.SubstituteIndirect
 Timer.EndRetreatTimer
 Timer.EndTurn
 Timer.StartGame
 Timer.StartHotSeatTimer
 Timer.StartPostActivity
 Timer.StartRetreatTimer
 Timer.StartTurn
 Weapon.Create
 Weapon.CreateGraphic
 Weapon.Delete
 Weapon.DisableWeaponChange
 Weapon.Selected
 Worm.ApplyPoison
 Worm.PendPoison
 WormManager.GetActiveAlliances
 WormManager.GetActiveWormCount
 WormManager.GetSurvivingTeam
 WormManager.Reinitialise
 WormManager.TeleportIn
 WormSelect.OptionSelected

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

 Comment
 Comment.10Kill
 Comment.10Mins
 Comment.11Kill
 Comment.12Kill
 Comment.13Kill
 Comment.14Kill
 Comment.15Kill
 Comment.1Min
 Comment.2Kill
 Comment.3Kill
 Comment.4Kill
 Comment.5Kill
 Comment.5Mins
 Comment.6Kill
 Comment.7Kill
 Comment.8Kill
 Comment.9Kill
 Comment.Airstrike
 Comment.Baseball
 Comment.Construction
 Comment.Crate
 Comment.Demolition
 Comment.Donkey.drop
 Comment.Donkey.explode
 Comment.Donkey.wait
 Comment.Draw
 Comment.Health
 Comment.Kill
 Comment.LandDeath
 Comment.LowTime
 Comment.Mystery
 Comment.Radiation
 Comment.Reinforce
 Comment.Scales
 Comment.Sdeath
 Comment.Sickness
 Comment.StartTurn
 Comment.SuddenDeath
 Comment.TeamDeath
 Comment.TeamWin
 Comment.Utility
 Comment.WaterDeath
[править] SendIntMessage(message, number)

Эта функция - аналог SendMessage, однако позволяет передавать числовой параметр. Список допустимых сообщений:

Имя сообщения message Параметр number
Crate.Delete индекс ящика или триггера, который удаляем
Crate.RadarHide индекс ящика, который надо скрыть
GameLogic.CreateTeamStatues индекс команды, для которой надо создать статуи
GameLogic.DestroyTrigger индекс триггера, который надо уничтожить
GameLogic.Win индекс команды, которая объявляется победителем
Particle.DelGraphicalEmitter индекс эмиттера
SpawnVolume.Remap индекс команды
WXWormManager.UnspawnWorm индекс червя, которого убрать
Worm.DieQuietly индекс червя, которого "по-тихому" убиваем =)
Worm.Poison индекс червя, которого заражаем
Worm.QueueAnim индекс червя
Worm.ResetAnim индекс червя
Worm.Respawn индекс червя, которого оживляем
[править] SendStringMessage(message,text)

Эта функция - аналог SendMessage, однако позволяет передавать текстовый параметр. Список сообщений:

WXMsg.EasterEggFound строка вида "Lock.EasterEgg.номер"
Land.GetLandRemaining код куска земли
Land.EnablePointLight имя эмиттера света
GameLogic.PlaceMine имя эмиттера мины
GameLogic.CreateBriefingBox тип ящика
[править] GetData(container_name)

Обратное действие к SetData. Позволяет считать содержимое xml(или xom в Worms3D) контейнера с помощью скрипта. Чаще всего вызов GetData производится при каком-то событии, чтобы узнать что именно произошло. Понятие события вы поймёте дальше, а сейчас надо понять такой пример:

 function Worm_Died() 
   DeadWorm = GetData("DeadWorm.Id")   --узнаём, кто умер
 end

При смерти червя интерпретатор вызовет событие - Worm_Died(). При этом мы можем узнать номер умершего червя. В другом, менее распространённом случае, мы сначала с помощью SendMessage "просим" червей вернуть нам какое-либо значение, а потом мы его считываем из контейнера, например:

 SendMessage("RandomNumber.Get")                -- запрашиваем случайное число
 local RawRand = GetData("RandomNumber.Uint")   -- "забираем" число из контейнера
[править] QueryContainer(container_name)

Эта функция похожа на GetData. Она принимает на вход имя контенера из xml (или xom) файла и возвращает значение, однако QueryContainer используется немного для других целей, а именно для извлечения сложных контенерных типов данных. Вот пример таких данных:

 <XContainerResourceDetails id='Worm.Data00'>
 <Value href='Worm.Data00-0'/>
 <Name>Worm.Data00</Name>
 <Flags>369</Flags>
 </XContainerResourceDetails>
 <WormDataContainer id='Worm.Data00-0'>
 <Name></Name>
 <Active>false</Active>
 <PlayedInGame>false</PlayedInGame>
 <Position x='0' y='0' z='0' />
 <ForcedCameraOffset x='0' y='0' z='0' />
 <Velocity x='0' y='0' z='0' />
 <Aftertouch x='0' y='0' z='0' />
 <InputImpulse x='0' y='0' z='0' />
 <Acceleration x='0' y='0' z='0' />
 <SupportNormal x='0' y='0' z='0' />
 <Orientation x='0' y='0' z='0' />
 <AngularVelocity x='0' y='0' z='0' />
 <ControlX>0</ControlX>
 <ControlY>0</ControlY>
 <LastLogicalUpdate>0</LastLogicalUpdate>
 <SupportFrame>0</SupportFrame>
 <SupportVoxel>0</SupportVoxel>
 <WeaponAngle>0.6</WeaponAngle>
 <WeaponFuse>3</WeaponFuse>
 <WeaponIsBounceMax>false</WeaponIsBounceMax>
 <WeaponHerd>3</WeaponHerd>
 <TeamIndex>0</TeamIndex>
 <PositionInTeam>0</PositionInTeam>
 <PhysicsOverride>0</PhysicsOverride>
 <Flags>0</Flags>
 <PhysicsState>8</PhysicsState>
 <WeaponIndex>0</WeaponIndex>
 <InitialEnergy>0</InitialEnergy>
 <Energy>0</Energy>
 <CPUFixedWeapon>0</CPUFixedWeapon>
 <CPUActionRadius>0</CPUActionRadius>
 <ArtilleryMode>false</ArtilleryMode>
 <PoisonRate>0</PoisonRate>
 <PendingPoison>0</PendingPoison>
 <PlaceWormAtPosition>false</PlaceWormAtPosition>
 <SfxBankName></SfxBankName>
 <Spawn></Spawn>
 <IsParachuteSpawn>false</IsParachuteSpawn>
 <IsAllowedToTakeTurn>true</IsAllowedToTakeTurn>
 <GunWobblePitch>0</GunWobblePitch>
 <GunWobbleYaw>0</GunWobbleYaw>
 <LipSynchBank>255</LipSynchBank>
 <ATT_Hat></ATT_Hat>
 <ATT_Glasses></ATT_Glasses>
 <ATT_Gloves></ATT_Gloves>
 <ATT_Tash></ATT_Tash>
 <MovedByImpulse>true</MovedByImpulse>
 <GraphicalOrientation x='0' y='0' z='0' />
 <Scale x='0' y='0' z='0' />
 <LastCollisionNormal x='0' y='0' z='0' />
 <LogicAnimState>0</LogicAnimState>
 <SlopeAngle>0</SlopeAngle>
 <DamagePending>0</DamagePending>
 <CurrentEnergy>0</CurrentEnergy>
 <IsAfterTouching>false</IsAfterTouching>
 <AfterTouchVector x='0' y='0' z='0' />
 <IsHatWearer>false</IsHatWearer>
 <IsQuickWalking>false</IsQuickWalking>
 <AllowBazooka>1</AllowBazooka>
 <AllowGrenade>1</AllowGrenade>
 <AllowClusterGrenade>1</AllowClusterGrenade>
 <AllowAirstrike>1</AllowAirstrike>
 <AllowDynamite>1</AllowDynamite>
 <AllowHolyHandGrenade>1</AllowHolyHandGrenade>
 <AllowBananaBomb>1</AllowBananaBomb>
 <AllowLandmine>1</AllowLandmine>
 <AllowShotgun>1</AllowShotgun>
 <AllowBaseballBat>1</AllowBaseballBat>
 <AllowProd>1</AllowProd>
 <AllowFirePunch>1</AllowFirePunch>
 <AllowHomingMissile>1</AllowHomingMissile>
 <AllowFlood>1</AllowFlood>
 <AllowSheep>1</AllowSheep>
 <AllowGasCanister>1</AllowGasCanister>
 <AllowOldWoman>1</AllowOldWoman>
 <AllowConcreteDonkey>1</AllowConcreteDonkey>
 <AllowSuperSheep>1</AllowSuperSheep>
 <AllowGirder>1</AllowGirder>
 <AllowBridgeKit>1</AllowBridgeKit>
 <AllowNinjaRope>1</AllowNinjaRope>
 <AllowParachute>1</AllowParachute>
 <AllowLowGravity>1</AllowLowGravity>
 <AllowTeleport>1</AllowTeleport>
 <AllowJetpack>1</AllowJetpack>
 <AllowSkipGo>1</AllowSkipGo>
 <AllowSurrender>1</AllowSurrender>
 <AllowChangeWorm>1</AllowChangeWorm>
 <AllowRedbull>1</AllowRedbull>
 <AllowArmour>1</AllowArmour>
 <AllowWeaponFactoryWeapon>1</AllowWeaponFactoryWeapon>
 <AllowStarburst>1</AllowStarburst>
 <AllowAlienAbduction>1</AllowAlienAbduction>
 <AllowFatkins>1</AllowFatkins>
 <AllowScouser>1</AllowScouser>
 <AllowNoMoreNails>1</AllowNoMoreNails>
 <AllowPipe>1</AllowPipe>
 <AllowPoisonArrow>1</AllowPoisonArrow>
 <AllowSentryGun>1</AllowSentryGun>
 <AllowSniperRifle>1</AllowSniperRifle>
 <AllowSuperAirstrike>1</AllowSuperAirstrike>
 <AllowBubbleTrouble>1</AllowBubbleTrouble>
 <TeleportIn>false</TeleportIn>
 <IsEmotional>true</IsEmotional>
 <HasDrunkRedbull>false</HasDrunkRedbull>
 <Armoured>false</Armoured>
 </WormDataContainer>

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

 local worm = QueryContainer("Worm.Data00")      -- worm - таблица
 message(worm.Energy)                            -- выведет оставшуюсь жизнь червяка
 message(worm.Armoured)                          -- есть ли броня?
 
                                                 -- поскольку worm-таблица, то можно и так:
 message(worm["Energy"])
[править] EditContainer(container_name)

Как и прошлая функция, работает с контейнерами в xml или xom, но она позволяет менять их содержимое. Эта функция возвращает два значения. Первое - "блокировка" - необходима для того, чтобы после изменения контейнера освободить его. Второе - сама таблица с контейнером, которую можно изменять. EditContainer всегда используется вместе с CloseContainer. Пример (запрет на использование оружия наводнение):

 lock, scheme = EditContainer("GM.SchemeData")       -- блокируем контейнер и начинаем редактирование
 scheme.FloodMystery.Crate = 0
 scheme.Flood.Crate = 0
 scheme.Flood.Ammo = 0
 CloseContainer(lock)                                -- разблокируем и даём червям сигнал применить изменения

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

[править] CloseContainer(lock)

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

[править] CopyContainer(from_container_name, to_container_name)

Полностью копирует содержимое одного контейнера в другой. При этом типы контейнеров должны совпадать, то есть нельзя копировать контейнер типа WormDataContainer в контейнер типа SchemeColective. Пример применения:

 CopyContainer("Worm.Data05", "Worm.Data00")

Этот код скопирует содержимое червя Worm.Data05 в Worm.Data00

[править] StartTimer(timer_function, delay)

Данная функция позволяет выполнять действие после некоторого времени. При этом в функцию передаётся имя функции, которую надо вызвать, и задержка в миллисекундах (1 секунда = 1000 миллисекунд). Функция возвращает идентификатор таймера, который потом можно использовать для отмены действия.

 function SomeFunction()
   message("Timer: SomeFunction")
 end
 
 StartTimer("SomeFunction", 3000)                 -- Вызовет функцию SomeFunction через 3 секунды
[править] CancelTimer(timer_handle)

Позволяет отменить выполнение функции, которая раньше была назначена с помощью StartTimer. Происходит это так:

 function SomeFunction()
   message("Timer: SomeFunction")
 end 
 
 timer = StartTimer("SomeFunction", 3000) -- Назначаем выполнение функции SomeFunction через 3 секунды ... 
 
           -- Вдруг передумали
 
 CancelTimer(timer) -- Отменяем. Если успели раньше 3 сек, то SomeFunction вызвана не будет.
[править] События

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

 function Worm_Died()
   deadworm = GetData("DeadWorm.Id")
   message(deadworm)
 end

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

Приведём список событий(таблица не завершена, если знаете событие, добавляйте):

Имя события Описание/назначение Worms4 Worms3D
Worm_Died() Обрабатывает смерть червя да да
Initialise() Инициализация, начало игры, расстановка объектов и червей да да
EFMV_Terminated() Законечен мультик да нет
Payload_Deleted() Уничтожен какой-либо объект(к примеру мина) да да
TurnEnded() Закончен ход да да
Weapon_PoweringUpStart() Вызывается, когда сила выстрела начинает рости. нет да
Weapon_Fired_Start() Вызывается, когда сила выстрела достигла придела или или нажат огонь. нет да
Weapon_Fired_End() Вызывается, после анимации выстрела, спустя задержку. нет да
Input_SomeInput() Нажатие какой либо кнопки нет да
PlayerCheck()  ? нет да
CheckTurns() Проверка хода, переменная turns, содержит номер хода нет да
Worm_Damaged() Ранение червя да  ?
Worm_Damaged_Current() Ранение текущего червя да да
[править] Дополнительные функции

[править] Примеры Lua скриптов

Wormpot2.lua


Личные инструменты
на других языках
ссылки

sl
דומיין בעברית  דומיין  דומין  תוכנה לניהול  קשרי לקוחות  CRM, ניהול קשרי לקוחות  דומין בעברית  פורומים  ספרדית  גיבוי