cJass 1.4.0.2

Оглавление

1  Об использовании

Самый простой способ начать пользоваться парсером cJass — это скачать и установить дистрибутив, для этого вам нужен Jass New Gen Pack. При установке, все необходимые для работы парсера изменения в файлах NewGen будут сделаны автоматически, а вам останется просто запустить редактор мира и пользоваться синтаксисом cJass.

2  Синтаксические нововведения

2.1  Чистота записи

Как известно, скриптовый язык JASS2 был создан Blizzard Entertainment для использования в Warcraft III. Синтаксически он больше всего напоминает Turing, поэтому не отличается лаконичностью. Для вызова функций используется ключевое слово call, для присвоения значения переменным — set, для объявления локальных переменных — local. Также необходимо выделять глобальные переменные в отдельный блок:

globals integer f = 12 endglobals function test takes nothing returns nothing local integer i call DoNothing() set i = f + 3 endfunction

При использовании cJass парсер самостоятельно определяет смысл выражения исходя из контекста его применения, что позволяет опускать указаные выше ключевые слова и добиваться большего удобства при записи кода:

integer f = 12 function test takes nothing returns nothing integer i DoNothing() i = f + 3 endfunction

2.2  Объявление переменных

В JASS2 все локальные переменные должны быть объявлены сразу после начала функции, в которой они используются. Используя cJass, вы можете объявлять локальные переменные в любом месте тела функции.

function onKill takes nothing returns nothing unit u = GetTriggerUnit() if (GetUnitTypeId(u) == 'hfoo') then ReviveUnit(u) else effect sfx = AddSpecialEffect("deatheffect.mdx", GetUnitX(u), GetUnitY(u)) Sleep(1) DestroyEffect(sfx) endif endfunction

Парсер перенесет в начало функции объявления всех локальных переменных. Так, как в JASS2 вместе с объявлением, переменная может быть инициализирована, cJass анализирует присваемое ей значение перед переносом в начало. Инициализация переменных явными значениями однозначно переносится вместе с соответствуюшими переменными. Остальные строки инициализации переменных остаются на местах объявления, например:

function test takes nothing returns nothing DoNothing() integer i = 0 // объявление будет перенесено целиком, location l = Location(0,0) // эта переменная будет инициализирована здесь endfunction

После трансляции приведенный выше фрагмент кода будет выглядеть так:

function test takes nothing returns nothing local integer i = 0 local location l call DoNothing() set l = Location(0,0) endfunction

Также синтаксис cJass позволяет объявлять несколько переменных одного типа на одной строке, разделяя их запятой. Переменные могут быть инициализированы тут же:

integer i = 7, j, k

2.3  Операторы

Синтаксис cJass вводит некоторые новые операторы для облегчения жизни программистам и повышения наглядности кода.

2.3.1  Операторы инкремента и декремента

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

function test takes nothing returns nothing integer a = 7 a++ // после этого выражения, a = 8 integer b; b = 3 + a-- // после этого выражения, a = 7, b = 11 b-- // после этого выражения, b = 10 endfunction

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

function test takes nothing returns nothing local integer a = 7 local integer b set a = a + 1 set b = 3 + a set a = a - 1 set b = b - 1 endfunction

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

function test takes nothing returns nothing integer a = 7 integer b = ++a // после этого выражения, a = 8, b = 8 integer c = a + b-- // после этого выражения, a = 8, b = 7, c = 16 endfunction

что после трансляции будет выглядеть так:

function test takes nothing returns nothing local integer a = 7 local integer b local integer c set a = a + 1 set b = a set c = a + b set b = b - 1 endfunction

2.3.2  Операторы присваивания

Вводимые синтаксисом cJass операторы присваивания позволяют сокращать запись присваиваемых выражений. Например, выражение

a = a + 2

может быть сокращено применением составной операции сложения +=:

a += 2

Оператор += прибавляет значение выражения, записанного справа, к выражению, записанному слева от него. Так же образуются и другие составные операторы: -=, *= и /=.

function test takes nothing returns nothing integer a = 3, b = 5, c = 4, d = 6 a += 7 // в результате a = 10 b -= 4 // в результате b = 1 c *= 5 // в результате c = 20 d /= 3 // в результате d = 2 endfunction

после трансляции выглядит так:

function test takes nothing returns nothing local integer a = 3 local integer b = 5 local integer c = 4 local integer d = 6 set a = a + 7 set b = b - 4 set c = c * 5 set d = d / 3 endfunction

2.3.3  Логические операции

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

function test takes boolean a, boolean b returns nothing boolean c if (a && b) then // a and b c = !b // not b else c = b || a // b or a endif endfunction

2.4  Блоки

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

function test takes nothing returns nothing integer i = 0 loop { exitwhen i > 5 i++ } endfunction

Такая запись может быть использована для всех блоков, присутствующих в JASS2 ( loop, if, else и elseif), а также блоков, вводимых расширением vJass (library, scope, struct, interface и module). При использовании сокращенной записи в блоках if и elseif, последующее ключевое слово then может быть безнаказанно пропущено:

function test takes integer i returns integer if (i > 3) { return --i } else { BJDebugMsg("i <= 3") } return i endfunction

что после трансляции вполне предсказуемо выглядит так:

function test takes integer i returns integer if (i > 3) then set i = i - 1 return i else call BJDebugMsg("i <= 3") endif return i endfunction

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

2.5  Циклы

Синтаксис cJass вводит цикл whilenot как аналог часто используемого варианта цикла с предусловием. Способ использования этого цикла аналогичен стандартному: после оператора whilenot следует условие, при выполнении которого цикл завершается:

function test takes integer num, integer pow returns integer integer res = 1, i = 0 whilenot (i++ >= pow) { res *= num } return res endfunction

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

function test takes integer num, integer pow returns integer local integer res = 1 local integer i = 0 loop exitwhen (i >= pow) set i = i + 1 set res = res * num endloop return res endfunction

Также, существует запись этого цикла, при которой проверка условия происходит после выполнения свех действий тела:

function test takes integer num, integer pow returns integer integer res = 1, i = 0 do { res *= num } whilenot (i++ >= pow) return res endfunction

что ожидаемо выглядит на JASS2 как:

function test takes integer num, integer pow returns integer local integer res = 1 local integer i = 0 loop set res = res * num exitwhen (i >= pow) set i = i + 1 endloop return res endfunction

Кроме того, вы можете использовать ключевое слово do вместо loop — в cJass они взаимозаменяемы.

2.6  Упрощенная запись функций

Для большего удобства и повышения наглядности, синтаксис cJass допускает упрощенную запись функций (и методов vJass). В общем виде она выглядит так:

nothing test(integer x) { BJDebugMsg(I2S(x)) }

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

function test takes integer x returns nothing call BJDebugMsg(I2S(x)) endfunction

Таким же образом записываются и методы, применяемые в vJass. Парсер сам определит, чего от него ждут — объявления функции или метода.

2.7  Управление концом строки

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

nothing RandomUnit(integer pIndex) { Sleep(1) unit u; u = CreateUnit( Player(pIndex), \ GetRandomInt('H000', 'H009'),\ GetRandomReal(-100, 100), \ GetRandomReal(-100, 100), \ GetRandomReal(0, 360)) Sleep(2) RemoveUnit(u) }

Символ ; при трансляции заменяется на перенос строки, а символ сшивает строку с последующей.

3  Макросы

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

3.1  Общие понятия

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

define FOOTMAN = 'hfoo'

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

define FOOTMAN = 'hfoo' nothing test() { CreateUnit(GetLocalPlayer(), FOOTMAN, 0, 0, 0) }

будет транслировано в

function test takes nothing returns nothing CreateUnit(GetLocalPlayer(), 'hfoo', 0, 0, 0) endfunction

Вы также можете записывать несколько макросов за раз, просто используя define с последующим блоком:

define { FOOTMAN = 'hfoo' MAGE = 'Hblm' }

Для тех, кто предпочитает стандартную запись команд JASS2, имеется следующий вариант:

define FOOTMAN = 'hfoo' MAGE = 'Hblm' enddefine

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

define <GetPlayableMapRect()> = bj_mapInitialPlayableArea

А что, если вам необходимо определить макрос, который заменяется на выражение, имеющее несколько строк? Никаких проблем — просто используйте блок внутри макроса!

define msg = { BJDebugMsg("one!") BJDebugMsg("two!") }

3.2  Область видимости

Макросы могут быть определены внутри регионов, библиотек, структур и модулей как принадлежащие только этой области видимости с помощью ключевого слова private:

scope test { define private FOOT = 'HBlm' }

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

define msg = "X" nothing test () { BJDebugMsg("Global = " + msg) } library A { define private msg = "A" nothing test () { BJDebugMsg("Library A = " + msg) } } scope B { define private msg = "B" nothing test () { BJDebugMsg("Scope B = " + msg) } }

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

function test takes nothing returns nothing call BJDebugMsg("Global = " + "X") endfunction library A function test takes nothing returns nothing call BJDebugMsg("Library A = " + "A") endfunction endlibrary scope B function test takes nothing returns nothing call BJDebugMsg("Scope B = " + "B") endfunction endscope

3.3  Директивы setdef и undef

При повторном определении макроса в пределах одной области видимости cJass выдаст ошибку о том, что данный макрос уже определен (это не касается приватных макросов во вложенных элементах). Если же вы хотите изменить значение ранее определенного макроса, то для этого стоит использовать директиву setdef. Для отмены назначения макроса используется директива undef, после которой вызов макроса приведет к ошибке во время трансляции.

nothing test1 () { define msg = "text" BJDebugMsg(msg) // выведет строку "text" setdef msg = "other text" BJDebugMsg(msg) // выведет строку "other text" undef msg BJDebugMsg(msg) // не будет заменено }

3.4  Макросы с аргументами

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

define msg(playerid,text) = DisplayTextToPlayer(Player(playerid), text, 0, 0)

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

3.5  Перегрузка макросов

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

define { msg(text) = DisplayTextToPlayer(GetLocalPlayer(), text, 0, 0) msg(text,playerid) = DisplayTextToPlayer(Player(playerid), text, 0, 0) msg(text,playerid,x,y) = DisplayTextToPlayer(Player(playerid), text, x, y) } nothing test() { msg("test 1") msg("test 2", 1) msg("test 3", 2, 0.1, 0.1) }

будет транслировано в следующее:

function test takes nothing returns nothing call DisplayTextToPlayer(GetLocalPlayer(), "test 1", 0, 0) call DisplayTextToPlayer(Player(1), "test 2", 0, 0) call DisplayTextToPlayer(Player(2), "test 3", 0.1, 0.1) endfunction

Внимание! Если в перегруженной группе есть макрос, не принимающий аргументов, его все равно необходимо записывать с указанием пустых скобок после него.

3.6  Конструкции, используемые в макросах

Иногда возникает потребность вывести аргумент макроса в текстовом виде (обратите внимание: не значение аргумента, а аргумент). Для этого существует инструкция ``, которая представляет переданный ей аргумент в виде строки. Также в макросах можно использовать оператор конкатенации (склеивания) ##, который склеивает в одно слово выражения, находящиеся с двух сторон от него (этот оператор можно использовать в любом месте кода, а не только внутри макросов).

define register_func(type) = { nothing func_##type (type t) { BJDebugMsg(`type`) } } register_func(real)

что после трансляции будет выглядеть так:

function func_real takes real t returns nothing call BJDebugMsg("real") endfunction

3.7  Предопределённые макросы

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

DATE — возвращает текущую дату в виде гггг.мм.дд

TIME — возвращает текущее время в виде чч:мм:сс

COUNTER — возвращает целое число начиная с 0, при каждом использовании число увеличивается на 1. Пример использования таков:

define unique_name = func_##COUNTER void unique_name () {} // это станет func_0 void unique_name () {} // а это - func_1 void unique_name () {} // соответственно тут func_2

DEBUG — возвращает 1 если включен флажок "Debug mode", иначе возвращает ноль. Используется в условной трансляции (см. 4.1) для обозначения действий, выполняемых только в режиме отладки.

FUNCNAME — возврашает имя функции, в которой использован.

WAR3VER — возвращает WAR3VER_23 или WAR3VER_24 в зависимости от положения переключателя в меню cJass. Может использоваться в блоках условной компиляции (см. 4.1) для легкой поддержки двух версий карты: для игры до 1.24 и после. Например:

#define H2I(h) = GetHandleId(h) #if WAR3VER == WAR3VER_23 undef H2I integer H2I (handle h) { return h; return 0 } #endif

Все предопределённые макросы возвращают не строковое значение. Для представления их в виде строки используйте оператор преобразования в строку или форматированный вывод (см. 6.2)

3.8  Приёмы использования макросов

В данном разделе приведено несколько примеров использования макросов для решения нетривиальных задач.

3.8.1  Перехват функций

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

define { SetUnitPosition = SetUnitPosition_hook RemoveUnit(u) = { BJDebugMsg("A unit is being removed!") Remove##Unit(u) } } boolean SetUnitPosition_hook (unit u, real x, real y) { BJDebugMsg("We're moving the unit to (" + R2S(x) + "," + R2S(y) + ")") SetUnit##Position(u, x, y) return (GetUnitX(u) == x) && (GetUnitY(u) == y) } nothing test() { unit u = CreateUnit(Player(0), 'hfoo', 0, 0, 0) if (SetUnitPosition(u, 100, 300)) { BJDebugMsg("Landed successfully!") } RemoveUnit(u) }

что после трансляции выглядит так:

function SetUnitPosition_hook takes unit u, real x, real y returns boolean call BJDebugMsg("We're moving the unit to (" + R2S(x) + "," + R2S(y) + ")") call SetUnitPosition(u, x, y) return (GetUnitX(u) == x) and (GetUnitY(u) == y) endfunction function test takes nothing returns nothing local unit u = CreateUnit(Player(0), 'hfoo', 0, 0, 0) if (SetUnitPosition_hook(u, 100, 300)) then call BJDebugMsg("Landed successfully!") endif call BJDebugMsg("A unit is being removed!") call RemoveUnit(u) endfunction

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

3.8.2  Эмуляция аргументов функций по умолчанию

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

define { CreateUnit(p, id) = Create##Unit(p, id, 0, 0, 0) CreateUnit(p, id, x, y) = Create##Unit(p, id, x, y, 0) CreateUnit(p, id, x, y, f) = Create##Unit(p, id, x, y, f) } nothing test() { CreateUnit(Player(0), 'hfoo') CreateUnit(Player(1), 'Hblm', 100, 231) CreateUnit(Player(2), 'Ewar', 382, 16, 42) }

что вполне ожидаемо транслируется в

function test takes nothing returns nothing call CreateUnit(Player(0), 'hfoo', 0, 0, 0) call CreateUnit(Player(1), 'Hblm', 100, 231, 0) call CreateUnit(Player(2), 'Ewar', 382, 16, 42) endfunction

4  Препроцессор

Директивы препроцессора являются первым, что выполняет парсер при обработке кода карты. Из-за этой особенности они позволяют делать некоторые интересные вещи.

4.1  Подключение внешних файлов

Иногда удобно держать отдельные части кода во внешних файлах — часто используемые библиотеки, системы и заклинания, созданные другими, и прочее. Для включения в карту кода из внешних файлов в синтаксисе cJass существует директива include, после которой должна следовать строка с именем подключаемого файла. Файлы с кодом, подключаемые этой директивой, могут находиться в подпапке "lib" папки с парсером AdicHelper или в папке с картой — тогда можно передавать путь относительно этих папок. Также можно подключать файлы из любых других папок, но при этом нужно указывать полный путь к ним.

include "myClasses.j" // будет подключен файл "AdicHelper\lib\myClasses.j" include "D:\\dev\\Warcraft 3\\my_mega_system.j"

Код, подключаемый с помощью этой директивы, может быть написан с использованием синтаксиса cJass и vJass. Внимание! Не забывайте, что обратный слэш в строках нужно экранировать: \\

Иногда возникает ситуация, при которой один и тот же внешний файл включается в карту несколько раз, например, разными библиотеками. В таком случае возникнут неизбежные ошибки из-за повторения кода, чего хотелось бы избежать. Для этого в cJass существует директива #guard идентификатор, которая предотвращает повторное подключение внешних файлов с одинаковыми идентификаторами. В качестве идентификаторов может выступать любое слово, но принято использовать для этого имя файла, в котором все недопустимые символы (например, точки) заменены на знаки подчёркивания. К примеру, во внешнем файле "my-system.j" идентификатор может быть таким: #guard my_system_j.

Внимание! Директива #guard должна стоять в самом начале подключаемого файла.

4.2  Условная трансляция

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

define CONTROL = 3 #if CONTROL == 3 // code #elseif CONTROL == 1 // alternative code #else // another code #endif

На данный момент в командах #if и #elseif возможно использование только операций сравнения == и !=

В блоках условной трансляции можно писать любой код с одним ограничением: в них не должны определяться макросы с одинаковыми именами:

#if 1 != 0 define msg = "hello!" #else define msg = "good bye!" #endif

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

define msg #if 1 != 0 setdef msg = "hello!" #else setdef msg = "good bye!" #endif

Директивы условной трансляции также могут срабатывать при определенном состоянии флага — им выступает особого вида макроопределение.

define FLAG = true #if FLAG // code #else // alternative code #endif

Директива #if value сработает только если сравниваемое макроопределение имеет значение true или 1.

4.3  Другие директивы

В некоторых случаях возникает необходимость остановить компиляцию и выдать пользователю ошибку: например, если не выполнено какое-то требование библиотеки. Тогда на помощь придёт директива #error "MSG", которая остановит компиляцию с этим сообщением. Выгодно использовать внутри блоков условной компиляции.

5  Другие элементы

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

5.1  Перечисления

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

enum { RED, GREEN, BLUE }

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

define { RED = 0 GREEN = 1 BLUE = 2 }

Перечисление также может быть именованным:

enum (color) { RED, GREEN, BLUE }

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

enum { FOOTMAN, MAGE } // FOOTMAN == 0, MAGE == 1 enum (color) { RED, GREEN, BLUE } // RED == 0, GREEN == 1, BLUE == 2 enum { WARDEN, ARCHER} // WARDEN == 2, ARCHER == 3

Перечисления поддерживают JASS2-подобный синтаксис записи:

enum (color) RED GREEN BLUE endenum

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

library SomeLib { enum (LIB_SPACE) { STD_SPACE, ALT_SPACE, DEBUG_SPACE } // choose your namespace ! define private SOMELIB_SPACE = STD_SPACE // what space to use #if SOMELIB_SPACE == STD_SPACE void SomeFunc(int a) { // base } #elseif SOMELIB_SPACE == ALT_SPACE void SomeFunc(int a) { // alt } #elseif SOMELIB_SPACE == DEBUG_SPACE void SomeFunc(int a) { // debug } #endif }

Или вместо именованных констант как маркеры для выполнения некоторых действий:

enum (moves) { MOVING_ANIM_STATE_WALK, MOVING_ANIM_STATE_WALKBACK, MOVING_ANIM_STATE_STOP } void MovingAnimationControl (unit u, integer state) { if (state == MOVING_ANIM_STATE_WALK) { SetUnitTimeScale(u, 1) SetUnitAnimationByIndex(u, "Walk" ) } elseif (state == MOVING_ANIM_STATE_WALKBACK) { SetUnitTimeScale(u, -1) SetUnitAnimationByIndex(u, "Walk" ) } elseif (state == MOVING_ANIM_STATE_STOP) { SetUnitAnimation(u,"Stand") SetUnitTimeScale(u, 1) } }

6  Стандартная библиотека

В поставку cJass, кроме парсера, входят также файлы стандартной библиотеки, найти которые вы можете в подпапке "lib" папки "AdicHelper". Эти файлы были созданы специально для облегчения написания кода, а также простой оптимизации некоторых его аспектов. Для использования их при разработке, вы просто подключаете нужные директивой include. Все стандартные подключаемые файлы имеют префикс "cj_" расширение ".j".

6.1  cj_types и cj_typesEx

include "cj_types.j" include "cj_typesEx.j" include "cj_types_priv.j" include "cj_typesEx_priv.j"

Данные подключаемые файлы содержат макросы, приближающие работу с типами в JASS2 к таковой в C++. В первом из них вводятся псевдонимы для элементарных типов, соответствующие наименованиям этих типов в языке C:

define { int = integer bool = boolean void = nothing float = real }

Во втором файле определяются конструкции вида new <type> для всех основных типов, например:

define <new timer> = CreateTimer()

Полный список этих конструкций вы можете посмотреть, открыв в текстовом редакторе файл "cj_typesEx.j".

Для использования при создании своих библиотек, созданы версии этих двух файлов, в которых все макросы определяются как приватные. Данные файлы имеют постфикс "_priv" в названии файла.

6.2  cj_order и cj_antibj_base

include "cj_order.j" include "cj_antibj_base.j"

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

Первый из них заменяет все вызовы функций вида OrderId("smart") на соответствующие им целочисленные значения приказов, а также вводит макросы вида order_smart для целочисленных значений всех существующих приказов.

Второй же выполняет легкую оптимизацию при использовании в карте функций и констант из файла "Blizzard.j". Используемые константы заменяются на их значения, а многие функции заменяются на свои аналоги из "common.j". Хочу заметить, что эта оптимизация работает даже на ГУИ-триггерах в вашей карте.

6.3  cj_print

include "cj_print.j"

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

6.3.1  Список функций

В описаниях функций строка формата записана как string format, а аргументы для форматирования показаны в виде ...

Функция sprintf форматирует принимаемую строку и возращает ее в качестве результата. После трансляции не является вызовом функции, а полностью заменяется на результирующую строку.

Функция printf выводит форматированную строку на экран локальному игроку. Для вывода используется DisplayTimedTextToPlayer.

Приведенные ниже функции делают то же самое, что их стандартные аналоги, но в качестве текстового аргумента принимают форматную строку и список аргументов для форматирования.

void sBJDebugMsg (string format, ...) void sDisplayTextToPlayer (player p, real x, real y, string format, ...) void sDisplayTimedTextToPlayer (player p, real x, real y, real time, string format, ...) void sDisplayTimedTextFromPlayer (player p, real x, real y, real time, string format, ...) void sSetTextTagText (texttag t, string format, ..., real h) void sQuestSetTitle (quest q, string format, ...) void sQuestSetDescription (quest q, string format, ...) void sQuestItemSetDescription (questitem q, string format, ...) void sMultiboardSetTitleText (multiboard m, string format, ...) void sMultiboardSetItemsValue (multiboard m, string format, ...) void sMultiboardSetItemValue (multiboarditem m, string format, ...) void sDialogSetMessage (dialog d, string format, ...) button sDialogAddButton (dialog d, string format, ..., int hotkey) button sDialogAddQuitButton (dialog d, bool b, string format, ..., int hotkey) void sLeaderboardAddItem (leaderboard l, string format, ..., player p)

6.3.2  Строка формата

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

%p - имя игрока, принимает player %pc - цветное имя игрока, принимает player %i - десятичное число, принимает int %igold - десятичное число, золото, принимает int %ilumb - десятичное число, древесина, принимает int %ip - имя игрока, принимает int (номер игрока) %ipc - цветное имя игрока, принимает int (номер игрока) %b - bool, выводит "true" или "false" %r - вещественное число, без форматирования, принимает real %s - строка, в случае передачи immed значение "склеится" с источником %v - var, прямая подстановка аргумента в строку %h - десятичное число, ид дескриптора, принимает соответсвенно дескриптор (handle)

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

// сначала переопределяем макрос setdef cj_sprintf_argTyp_User = /*разметка*/ // здесь мы форматируем строки, используя нашу разметку // затем откатываем нашу разметку, чтобы другие библиотеки могли назначить ее заново setdef cj_sprintf_argTyp_User =

Разберем синтаксис определения обработчиков на примере %i:

Внимание! В секции нестандартного кода (в дереве триггеров имеет иконку карты) символ % ведет себя странным образом при сохранении карты. Это — ошибка редактора мира, но она до сих пор не исправлена. Чтобы использовать в секции нестандартного кода функции форматирования, приведенные в данном разделе, в качестве символа начала управляющей последовательности стоит использовать ^

7  Благодарности


This document was translated from LATEX by HEVEA.