WarCraft 3: JASS: Курс молодого бойца

» Раздел: Триггеры и объекты

v1.01 от 26.06.2017

Предисловие

Приветствую на страницах первой из серии статей, посвящённых JASS - языку программирования, созданному студией Blizzard Entertainment для своих игр. Здесь речь пойдёт о JASS, используемом в WarCraft III: The Frozen Throne. Собранные в этой статье знания пригодятся как новичку, который делает свои первые шаги в увлекательный мир программирования, так и более искушённому в нём профессионалу.

Назначение статьи

Чтобы понять, насколько статья будет полезна и актуальна лично для Вас, ознакомьтесь со списком ниже. Он наглядно иллюстрирует основную идею статьи, что помогает составить общее представление о ней.
Статья научит Вас:
  • Понимать и использовать применяемую в программировании терминологию. Объяснение построено таким образом, что перед использованием каждого термина в статье дано его определение.
  • Программированию с использованием языка JASS. Внимание к деталям упрощает объяснение, делая его более доступным для понимания.
  • Разбору готового кода JASS. Вы сможете работать с кодом, который писал другой программист, анализировать и улучшать этот код, понимая, как он работает.
Статья не научит Вас:
  • Готовым решениям. Наработки и конкретные случаи применения JASS всегда можно найти как в других статьях, так и в специальном разделе для наработок.
  • Взлому (депротекту, открытию) карт. На самом деле, придумывать какую-либо новую концепцию или делать что-нибудь своё гораздо интереснее, чем копировать существующее, не говоря уже о моральной стороне взлома. Если карта закрыта, а самостоятельно Вы не можете разобраться, как сделать то, что в ней реализовано, рекомендую обратиться в раздел вопросов проекта WarCraft III на XGM - там Вам обязательно помогут разобраться, что к чему, а база, которую Вам даст эта статья, существенно облегчит понимание ответов.

Перед чтением

Два важных совета:
  • Если во время прочтения статьи возникают какие-либо вопросы по JASS, задавайте их в разделе "Вопросы" проекта WarCraft III на XGM.
  • Если во время прочтения статьи возникают вопросы по её содержанию и смысловой нагрузке, обращайтесь в местные комментарии.
Эти простые правила позволят Вам получать ответы на Ваши вопросы гораздо быстрее. Спасибо за то, что следуете ему.

Благодарю от всей души:
Благодарности расположены в порядке хронологии моего ознакомления.
  • Blizzard Entertainment. Студия, подарившая нам WarCraft III: The Frozen Throne и редактор World Editor.
  • Vexorian. Человек, внёсший неоценимый вклад в развитие и распространение JASS. Именно он объяснял, как и что в нём работает, когда документации не было практически никакой. Он широко известен как автор популярного диалекта vJASS и различных мелких программ для работы с WarCraft III, таких как WC3 Map Optimizer.
  • ADOLF. Создатель AdicHelper, парсер для диалекта cJASS, также автор многих полезных ремарок, пригодившихся мне при разборе механики работы JASS.
  • Nestharus. Автор интересных экспериментов и работ по программированию на JASS, разбор которых открыл этот язык для меня с иной, более неизведанной и интересной стороны.
  • Duosora. Автор и переводчик различных статей по World Editor и JASS; человек, собравший много материала для этого статейного цикла и передавший его мне для дополнения и публикации.
  • ScorpioT1000. Очень полезный материал о типизации и обработке данных, который лёг в основу описания типов JASS в этой статье.

Коротко о JASS

В этом статейном цикле Вы узнаете о том, что такое JASS и как с ним работать. Название JASS - это аббревиатура, расшифровывающаяся как Just Another Scripting Syntax, что в переводе означает "ещё один синтаксис скриптования". Вопреки популярному мнению, он используется не только в WarCraft III, но также и в играх серии StarCraft, так что это не один язык программирования, а целая их серия.
JASS - это событийно-ориентированный интерпретируемый сценарный язык программирования. Если Вы не особо разбираетесь в терминах программирования, откройте спойлер, данный ниже, чтобы узнать подробнее о том, что такое событийная ориентация и скриптование. Это поможет Вам вкратце понять принцип работы языка.
» Краткий ликбез о типах и переводах языков программирования
Изучив множество тематических сайтов, где описывались типы языков программирования в поисках такого объяснения, на которое можно было бы смело ссылаться и быть уверенным, что его с лёгкостью поймёт любой новичок, для которого большая часть терминов программирования - это большое открытие, в конечном итоге сделал вывод, что писать такое объяснение надо самому.
Существует такое понятие, как парадигма программирования - это определённый подход к написанию программ. Для тех, кто хочет ознакомиться с парадигмами подробнее, есть специальная статья. Для упрощения понимания можно отбросить большинство из них и сказать, что языки программирования делятся на две огромные группы:
  • Событийно-ориентированное программирование (СОП) - это такая парадигма программирования, где принцип работы программы строится вокруг работы с событиями и реакциями на них (то есть, произошло какое-либо событие (например, программа запустилась) - программа среагировала на него (этот процесс называют также "отловом" события) - выполнился код, связанный с событием). По факту - это обособившееся ответвление ООП.
    Примечательно, что аббревиатура СОП также используется и для субъектно-ориентированного программирования, но в общем случае под ней понимают именно событийно-ориентированное программирование.
  • Объектно-ориентированное программирование (ООП) - это такая парадигма программирования, где принцип работы программы строится вокруг работы с объектами (создаются объекты (программа "конструируется") - ожидается взаимодействие пользователя с объектом (щелчок на кнопку, обращение к объекту и т.п.) - выполняется необходимый код, связанный с этим объектом).

По сопутствующей классификации языки программирования делятся на четыре уровня - низкоуровневые, среднеуровневые, высокоуровневые и сверхвысокоуровневые. Чтобы чётко определить, к какому из уровней принадлежит тот или иной язык программирования, достаточно знать простое правило - чем выше уровень языка программирования, тем дальше он от понимания машиной и тем ближе он к пониманию человеком.
Люди в мире разговаривают на разных языках. У машины тоже есть свой язык и называется он машинным (бинарным) языком. Допустим, встречаются два человека - один разговаривает на английском языке, а другой - на вьетнамском. Для того, чтобы они поняли друг друга, им необходим переводчик, который знает оба языка и может помочь им в беседе. Такой переводчик необходим и машине для перевода инструкций языка программирования любого уровня в машинный код.
Напрямую в машинный код можно переводить только низкоуровневый язык программирования. Остальные необходимо переводить постепенно, понижая уровень вплоть до машинного кода, поэтому зачастую переводчиков требуется несколько. Например, чтобы перевести код со сверхвысокогоуровневого языка программирования в машинный код, сначала нужно перевести его в высокоуровневый, затем высокоуровневый перевести в среднеуровневый, затем среднеуровневый перевести в низкоуровневый, который переводится в машинный код.
Такие переводчики называются трансляторами. Процесс трансляции называется транслированием кода.
Вы могли встречать традиционное деление переводчиков на устных (interpreters - осуществляющих устный перевод) и письменных (translators - осуществляющих письменный перевод). У трансляторов тоже есть подобное деление, но немного по иному признаку.
Два основных типа трансляторов, о которых знают многие программисты - это компиляторы и интерпретаторы.
Интерпретаторы - такие трансляторы, принцип работы которых основывается на построчном переводе исходного кода в целевой язык и последующем, тоже построчном, его выполнении. Работают подобно устным переводчикам - они прослушивают предложение и воспроизводят его на другом языке.
Процесс такого транслирования называют интерпретацией кода.
Компиляторы - такие трансляторы, принцип работы которых основывается на переводе исходного кода в машинный язык и последующей его проверке на ошибки, таким образом "собирая" его в исполняемый файл (исполняемые файлы в системе Windows, например, часто называют "экзешниками" или "exe-файлами"). Такой файл целевая среда может выполнить самостоятельно при его запуске. Работают подобно письменным переводчикам, которые сначала выполняют черновой перевод на целевой язык, а затем читают его и корректируют, выявляя и исправляя ошибки. Вот только в случае с кодом компиляция не исправляет ошибки сама, а только выводит результаты проверки программисту, чтобы он внёс необходимые изменения.
Процесс такого транслирования называют компиляцией кода.

Теперь Вы лучше разбираетесь в основных терминах программирования и понимаете, что такое JASS. Если не совсем, откройте спойлер ниже - он прольёт Вам свет на ситуацию.
» Нажмите для просмотра объяснения определения JASS
JASS - это высокоуровневый язык программирования (не среднеуровневый из-за того, что это сценарный язык - все сценарные языки по классификации высокоуровневые из-за большей ориентированности на человека).
Исходный код WarCraft III написан на языке программирования С. Технически на С можно реализовывать конструкции с низким уровнем абстракции, приближенные к машинному коду, но С - компилируемый язык, то есть он также нуждается в переводе на уровень ниже (Assembler), что уже не даёт называть его языком низкого уровня. Среднеуровневым языком программирования его тоже не назовёшь, потому что Си никаким образом не работает с компьютерным железом непосредственно.
Итого Си - это классический высокоуровневый язык программирования, а JASS - список "руководств к действию" для функций, описанных в Си, но так как метод передачи данных не изменяется, JASS ровно такой же высокоуровневый язык (это помимо общего правила о том, что скриптовые или сценарные языки - все высокоуровневые). Нелишне будет сказать о том, что Си - процедурный язык программирования и JASS наследует аспект этой "процедурности", выполняя действия шаг за шагом, но строит свою структуру выполнения не вокруг процедур, а вокруг определённых событий. Поэтому JASS - событийно-ориентированный, а не процедурный язык программирования.
Несмотря на то, что многие картостроители употребляют термины "компиляция карты" и "скомпилировать JASS", язык на самом деле интерпретируемый, поскольку, во-первых, он сценарный и интерпретируемая природа JASS следует из его определения, а во-вторых - встроенное в World Editor средство работы с JASS при сохранении карты занимается только проверкой синтаксиса кода (обратите внимание, что в самой карте после сохранения исходный код в файле war3map.j остаётся неизменным), а его выполнением при запуске карты занимается встроенный в игру интерпретатор, поэтому иногда можно получить фатальную ошибку уже при запуске карты.
Надеюсь, что приведённое здесь пояснение облегчило Вам понимание определения.
О JASS можно говорить сутками напролёт, не теряя интереса. Несмотря на свою простоту, он очень интересен в своём изучении и использовании. Несмотря на то, что этот язык существует столько же, сколько и WarCraft III (с 2002 года; возможность использования JASS в World Editor напрямую была открыта только в 2003 году с выходом дополнения The Frozen Throne), он до конца не изучен. Кто знает, возможно, Вы, дорогой читатель, откроете что-нибудь новое в этом языке или сделаете интересную карту, в которую ещё долго будут играть, несмотря на формальный износ ядра WarCraft III.
Изучение JASS - времязатратный, но увлекательный процесс. Если после прочтения всего вышеизложенного Вы остаётесь тверды в решении изучить JASS, то этот язык Вам по силам. Если Вы сомневаетесь, изучите азы программирования и только затем приступайте к JASS.
» Это интересно
  • Увы, кроме одной маленькой строчки в самом начале статьи о JASS в Википедии, в ней ничего не сказано про тот JASS, который используется в StarCraft и StarCraft 2, а ведь он не менее интересен, хотя и имеет немного другой синтаксис, нежели тот, что используется в WarCraft III.
  • Технически, программировать на JASS можно было ещё в 2002 году, вместе с выпуском WarCraft III: Reign of Chaos. Для этого карту необходимо было вскрывать при помощи редактора MPQ-архивов и непосредственно менять код в файле war3map.j, используя Блокнот (Notepad). Говоря справедливости ради, об этом в то время знали единицы, да и списка функций JASS нигде не было, не говоря уже об объяснении их принципа действия.
  • В названии статьи о JASS в Википедии, аббревиатура написана как Jass, хотя это неправильное написание. Примечательно, что в других частях этой же статьи JASS пишется в правильном регистре.

Преимущества и недостатки

Как и во всём прочем, в JASS есть свои преимущества и свои недостатки.

Преимущества

+ Высокоуровневый язык программирования: высокий уровень восприятия кода человеком. Как и почти в любом другом языке программирования подобного уровня, в JASS действительно можно сделать хорошо структуризированный код, при этом понятный человеку.
+ Широкоплановый функционал: при помощи JASS можно выполнить огромный спектр поставленных задач вплоть до превращения карты в совершенно другую игру на базе движка WarCraft III. В качестве примера хочу привести TCX, переделавший привычный WarCraft III в нечто совсем иное.
+ "Низкоуровневость" по отношению к WarCraft III: владение JASS позволит Вам практически напрямую общаться с движком игры в отличии от надстройки GUI и получить максимально возможный в World Editor доступ к нему.

Недостатки

- Высокоуровневый язык программирования: здесь нет ошибки, это как плюс, так и существенный минус, ведь обработка команд языка движком игры - тайна за семью печатями.
- Нет встроенной поддержки пользовательской типизации: да, JASS - это такая штуковина, где система типов строго упорядочена и возможности создавать собственные типы нет. Эту проблему частично решают диалекты vJASS и cJASS, но встроенной поддержки как таковой нет.
- Низкий уровень интеграции с модулями World Editor: изучение JASS всё-таки не панацея при создании карт. JASS не даст Вам возможность получать и изменять значения большинства игровых констант или игрового интерфейса. То же самое можно сказать про другие модули, а в особенности про Редактор объектов. Именно из-за этого существенного недостатка JASS картостроителям приходится писать целые надстройки для изменения параметров объектов, таких как, к примеру максимальное здоровье и максимальная мана боевых единиц - получить их можно, изменить только средствами JASS - нельзя. Вот такие пироги.
- Низкий уровень поддержки функций работы с интерфейсом: здесь стоит уточнить - уровень не то, что низкий, считайте, что он вообще отсутствует: при помощи JASS Вы не сможете получить координаты мыши игрока ни на мониторе (где они измеряются от 0 до 1), ни на карте (точка на трёхмерном пространстве). Вам также не дадут узнать, наведена ли мышь на какой-либо объект (trackable - особый случай, но даже этот тип не позволяет изменять существующий игровой интерфейс, только добавлять что-то вроде полноэкранного выбора персонажа или инвентаря). Из-за отсутствия этих фундаментальных функций JASS не даст Вам нормально работать с интерфейсом, который поставляется с игрой.

Типы данных и их наследование

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

Основные типы данных в JASS

НазваниеРодительский типДопустимые значенияЗначение по умолчаниюКомментарий
integer __int32 в C [ -2147483648 ; 2147483647 ] 0 Целочисленная. Хранит в себе любые целые числа в своей области допустимых значений (32-битные числа). Аналогична типу __int32 языка программирования С.
real float в C [ -3.4 * 10^38 ; 3.4 * 10^38 ] 0.0 Реальная. Хранит в себе любые целые числа в своей области допустимых значений. Аналогична типу float языка программирования С.
string Ссылка на массив символов char до 1023 байтов (до 1013 для совместимости с сохранением и загрузкой игры) null Строка, принимающая символы UTF-8. В памяти содержится как хеш-таблица строк, которые не удаляются из памяти до окончания игры.
boolean __int32 в C [ -2147483648 ; 2147483647 ], константы true и false false Логическая (булевая). Несмотря на то, что технически обрабатываются только значения 1 и 0 (истина и ложь), можно использовать весь доступный диапазон чисел. Аналогична типу bool языка программирования С++.
handle __int32 в C [ -2147483648 ; 2147483647 ] null Указатель, но не на ячейку памяти напрямую, а на адрес в хеш-таблице ссылок. По своей сути это обычное число, представленное в виде, похожем на pointer языка программирования С++, но со своими отличиями - об этом позже.
code handle Существующие функции 0 Указатель на функцию. Единственный тип в JASS, который не поддерживает массивы и формально не может быть задан локальной переменной, только напрямую в параметрах функции. Работает аналогично с указателем на функцию в языках С и С++.
» Доступное новичку объяснение того, что такое ссылки, указатели и объекты
В самом понятном определении, объекты - это таблицы с данными специфического содержания.
Адресацию объектов можно объяснить так: есть стол, который стоит в определённой квартире. Квартира - это объект в доме. Дом - это объект на улице. Улица - это объект в городе. Город - это объект в стране. Страна - это объект на материке. Материк - это объект на планете. Планета - это объект в системе. Система - это объект в галактике. Галактика - это объект во Вселенной.
Таким образом, чтобы обратиться к объекту "стол", сначала надо узнать галактику его нахождения, в галактике - систему, в системе - планету, на планете - материк, на материке - страну, в стране - город, в городе - улицу, на улице - дом, в доме - квартиру.
Чтобы избежать такой долгой адресации, каждому экземпляру объекта присваивается уникальный идентификатор во Вселенной (или же в программе). Этот идентификатор - номер ячейки памяти, в которой содержится определённый экземпляр объекта.
Присвоим этот идентификатор нашему столу. Теперь вместо того, чтобы говорить "стол в таком-то доме на такой-то улице такого-то города в такой-то стране такого-то материка на такой-то планете такой-то системы в такой-то галактике во Вселенной", мы можем сказать "стол с таким-то идентификатором". Обращение получается короче и выполняется быстрее. Такое обращение называется ссылкой, потому что указывает на определённый объект в определённой ячейке памяти.
Отличайте указатели от ссылок!
Указатель - это тип переменной, которая может принимать значение любого адреса в памяти, либо нулевое. Благодаря указателям можно получать объекты прямо из памяти и читать их с жёсткого диска без загрузки в оперативную память. В связи с этим указатели существенно экономят расход памяти.
Ссылка - это объект, который при обращении к себе "перенаправляет" на другой объект, на который он ссылается. Обращение к объекту по ссылке называют разыменовыванием ссылки. Ссылка - своеобразный посредник между пользователем и объектом, к которому он хочет обратиться и реализуется разными способами в каждом языке программирования.

Производные от handle типы JASS

В JASS кроме шести основных типов существует 89 типов, производных от handle.
Запомните, что тип handle и все производные от него типы содержат не сам объект, а ссылку на адрес этого объекта в памяти, но при этом одни из производных от handle типы содержат таблицы данных (т.е. провоцируют утечки памяти), а другие просто содержат численные значения (т.е. не провоцируют утечек памяти). Этим обосновано наличие или отсутствие функций для очистки памяти от объектов и ссылок на них (есть и исключения, например, объекты trackable нельзя уничтожать).
Условно договоримся, что ссылки на объекты провоцируют утечки памяти, если в таблице нет специального пояснения. Так Вам будет проще разобраться в таблице, спрятанной под спойлером ниже.
» Осторожно, внутри живёт большая таблица!
НазваниеРодительский типКомментарийАналог в триггерах (GUI)
agenthandleСсылка на объект, ссылку на который можно записать в хэш-таблицу. При этом ссылку на хэш-таблицу можно также записать как в другую, так и в ту же самую хэш-таблицу, так как она также является частью типа agent. -
eventagentСсылка на объект события триггера. Провоцирует утечку памяти, однако, прямой функции для его чистки из памяти в JASS нет. Можно предположить, что если при регистрации экземпляра объекта типа event он привязывается к определённому триггеру (один экземпляр event не может использоваться для нескольких триггеров), то при уничтожении триггера должна уничтожаться и вся таблица зарегистрированных на него событий, условий и действий. То есть, объектов типов event, triggercondition и triggeraction. -
playeragentСсылка на идентификатор игрока. Речь, конечно, идёт не о человеке, играющем в Вашу карту, а об экземпляре игры, запущенном на его компьютере. Игрок (Player)
widgetagentСсылка на интерактивный объект, обладающий ресурсом "здоровье" (Health). Это объекты, с которыми игрок (человек) может взаимодействовать при помощи игрового интерфейса. -
unitwidgetСсылка на объект боевой единицы. В Редакторе объектов боевые единицы создаются и изменяются на вкладке "Войска" (Units).Боевая единица (Unit)
destructablewidgetСсылка на разрушаемый объект. В Редакторе объектов разрушаемые объекты создаются и изменяются на вкладке "Разрушаемые объекты" (Destructables). Не путать с декорациями - у декораций (Doodads) нет ограниченного запаса здоровья, что уже исключает их из списка интерактивных объектов.Разрушаемый объект (Destructable)
itemwidgetСсылка на объект предмета. В Редакторе объектов предметы создаются и изменяются на вкладке "Предметы" (Items).Предмет (Item)
abilityagentСейчас этот тип не используется, потому что Blizzard отправили его на покой за ненадобностью. Сейчас для ссылок на способности используются не объекты, а их идентификаторы - числа. Функцию GetSpellAbility(), судя по всему, поленились вырезать из движка, как и сам тип, а поддержку записи ссылок этого типа в хеш-таблицу добавили просто для полноты картины. -
buffabilityЕщё один неиспользуемый тип, ранее он предназначался для хранения ссылок на заклинания, теперь на заклинания можно также ссылаться при помощи их идентификаторов. -
forceagentСсылка на объект группы игроков. Эти объекты - это таблицы, в которые можно помещать идентификаторы объектов типа player.Группа игроков (Force)
groupagentСсылка на объект группы боевых единиц. Как и объекты, на которые ссылается тип force, они представляют собой таблицы, только в них хранятся идентификаторы объектов типа unit.Отряд (Unit Group)
triggeragentСсылка на объект триггера. Для тех, кто с GUI не знаком, триггерами называют подобия файлов, на которые условно делится весь код в Редакторе триггеров. В GUI они могут содержать события, условия и действия. Преобразованные в текст триггеры содержат в себе функции, из которых обязательной для классической версии JASS является только функция инициализации.Триггер (Trigger)
triggerconditionagentСсылка на объект условия триггера. Не может быть удалена из памяти напрямую. Подробнее об этом читайте в описании типа event. -
triggeractionhandleСсылка на объект действия триггера. Не может быть удалена из памяти напрямую. Подробнее об этом читайте в описании типа event. -
timeragentСсылка на объект таймера. Такие объекты позволяют осуществлять обратный отсчёт и управлять им.Таймер (Timer)
locationagentСсылка на объект-точку. Точкой может быть любая отдельно взятая на карте точка по координатам X и Y. При помощи точки также можно определить высоту рельефа Z над уровнем карты в точке по координатам X и Y. Точка (Point)
regionagentСсылка на объект-регион. Не стоит путать регионы с тем, что называется областями в Редакторе триггеров. Регион - это группа квадратных областей на карте. Если Вы добавите точку, то в регион добавится квадрат 32х32 с центром в этой точке. Самая близкая аналогия - это group, только вместо идентификаторов боевых единиц в регионе содержатся идентификаторы областей. Очень удобно объединять много областей в регион, чтобы потом проверять только вход-выход в регион, а не добавлять в триггер по событию на каждую область. -
rectagentСсылка на объект квадратной области, которая строится по двум точкам, образующим их диагональ. Слово Rect, которое использовано для наименования типа - это сокращение от слова Rectangle (в переводе означающего "прямоугольник").Область (Region)
boolexpragentУказатель на функцию, которая возвращает данные типа boolean. Создаётся функциями Filter и Condition для дальнейшей передачи в условия триггера либо условия фильтрации игроков / боевых единиц (для добавления их в группы). Обычно условия, которые передают в boolexpr, абстрактны. -
soundagentСсылка на звуковые объекты. Такие объекты могут быть предсозданы в Редакторе звуков, а могут быть созданы JASS-программистом после добавления звука в Менеджер импорта либо взятого из стандартной библиотеки. Звук (Sound)
conditionfuncboolexprСсылка на объект-условное выражение, предназначенное для использования в условиях триггеров. Конструируется функцией Condition. -
filterfuncboolexprСсылка на объект-условное выражение, предназначенное для использования в фильтрующих функциях, запускающихся при добавлении идентификаторов боевых единиц или идентификаторов игроков в группы. Конструируется функцией Filter. -
unitpoolhandleСсылка на объект-таблицу типов боевых единиц. Такая таблица предназначена для генерации случайного типа боевой единицы с равнораспределённой или неравнораспределённой вероятностью (поддерживается система весов). -
itempoolhandleСсылка на объект-таблицу типов предметов. Такая таблица предназначена для генерации случайного типа предмета с равнораспределённой или неравнораспределённой вероятностью (поддерживается система весов). -
racehandleСсылка на идентификатор расы.Раса (Race)
alliancetypehandleСсылка на объект-идентификатор аспекта союзничества. Под аспектами союзничества подразумеваются факторы, в отношении которых один игрок считается союзником по отношению к другому (благодаря наличию этих аспектов, можно сделать так, чтобы войска, подконтрольные одному игроку, не нападали на войска другого игрока, но при этом общего обзора у них не было). -
racepreferencehandleСсылка на идентификаторы предустановленных в настройках карты рас. Благодаря наличию этих объектов можно программно узнать, какая раса была задана в настройках карты для каждого игрока (вне зависимости от того, какую расу он на самом деле выбрал). -
gamestatehandleСсылка на объект-игровой параметр. В качестве примера игрового параметра могу привести внутриигровое время суток. -
igamestategamestateСсылка на объект-игровой параметр, представленный целочисленной. Пример такого параметра - выход игрока из игры (дисконнект). -
fgamestategamestateСсылка на объект-игровой параметр, представленный числом с плавающей запятой. Пример такого параметра - внутриигровое время суток. -
playerstatehandleСсылка на объект-параметр игрока. Такими параметрами могут являться количество его ресурсов (золото, древесина, пища), а также некоторые другие параметры, например, является ли игрок наблюдателем. -
playerscorehandleСсылка на объект-параметр игрока, который обновляется в процессе игры и выводится на финальный экран (счёт игрока). -
playergameresulthandleСсылка на идентификатор, описывающий исход игры для игрока. Возможно всего четыре различных исхода - победа, поражение, ничья и нейтралитет. -
unitstatehandleСсылка на идентификатор параметра боевой единицы. Примечательно, но таких параметра всего четыре - здоровье, мана, максимальное здоровье и максимальная мана, причём максимальные параметры получить при помощи функций, связанных с unitstate можно, а задать нельзя. -
aidifficultyhandleСсылка на идентификатор уровня сложности искусственного интеллекта (для тех игроков, которыми управляет компьютер). Всего можно задать три уровня сложности компьютерных игроков - лёгкий, средний и сложный. -
eventidhandleСсылка на идентификаторы событий. Эти идентификаторы содержат ссылки на все возможные события, отлавливаемые триггерами, что позволяет добавлять в триггер несколько событий и путём сравнения узнавать, какое из событий сработало. -
gameeventeventidСсылка на идентификаторы событий, связанных с игровым процессом. Сюда входят загрузка игры из файла сохранения и сохранение игры, истечение таймера, открытие подменю "построить" и многое другое. -
playereventeventidСсылка на идентификаторы событий, связанных с игроками. В эту категорию попадают нажатия игроком клавиш-стрелок на клавиатуре, выход игрока из игры и т.п. -
playeruniteventeventidСсылка на идентификаторы событий, связанных с боевыми единицами, подконтрольными определённому игроку. Среди них смерть боевой единицы, её разложение, обнаружение, повышение уровня у героев и т.п. -
uniteventeventidСсылка на идентификаторы событий, связанных с боевыми единицами. Содержат тот же арсенал событий, что и playerunitevent, но с той разницей, что не уточняет владельца боевой единицы. -
limitopeventidСсылка на идентификатор вида оператора, который может быть использован в событиях сравнения величины какого-либо параметра игры, боевой единицы или игрока. Какой-то уж слишком нелепый хардкод, если Вам интересно моё мнение. Сам без понятия, зачем так нужно было делать. Однако, там действительно таблица ссылок. -
widgeteventeventidСсылка на идентификаторы событий, связанных с типом widget (напоминаю, что это интерактивные объекты, обладающие параметром "здоровье"). -
dialogeventeventidСсылка на идентификаторы событий, связанных с игровыми диалоговыми окнами. Подробнее Вы можете узнать в описании типов button и dialog. -
unittypehandleСсылка на идентификаторы разновидностей боевых единиц. Не путайте с триггерным типом, который называется Тип объекта (Unit Type). В триггерах он представляет собой целое число, которое является идентификатором типа боевой единицы, пригодном (в отличие от unittype в JASS) для использования в unitpool. -
gamespeedhandleСсылка на идентификаторы, определяющие возможные варианты скорости игры (очень медленная, медленная, нормальная, быстрая и очень быстрая). Скорее всего, таблицы в себе не содержит, как и тип player, но не знаю наверняка. Чтобы я не повторял одно и то же на протяжении всей статьи, давайте договоримся так - тип, который хранит в себе идентификаторы, таблицу в себе не содержит и является на самом деле числом, представленным как псевдообъект. Скорость игры (Game Speed)
gamedifficultyhandleСсылка на идентификаторы, определяющие возможные уровни сложности игры (сложность регулирует скорость и рациональность принимаемых игроком-компьютером решений, а также в некоторых случаях регулирует ресурсы, находящиеся в распоряжении игрока-человека. Таким образом, игрок-человек находится в прямо пропорциональной зависимости от сложности игры, а игрок-компьютер - в обратно пропорциональной, потому что с рост уровня сложности игры для игрока-человека означает облегчение задачи игроку-компьютеру и наоборот. -
gametypehandleСсылка на идентификаторы, описывающие возможные правила игры. Среди них есть классический режим сражения, режим арены ("каждый сам за себя", Free For All или FFA) и прочие. -
mapflaghandleСсылка на идентификаторы, описывающие параметры карты. В их числе наличие на карте тумана войны, разрешены ли наблюдатели/судьи, разрешение или запрет на изменение параметров союза и т.д. -
mapvisibilityhandleНеиспользуемый тип, который просто не вырезали из игры (такой как ability или buff). Ранее содержал параметры видимости карты, но потом их перенесли в mapflag'и, посчитав это чрезмерным делением на типы. Здесь соглашусь. -
mapsettinghandleТакже неиспользуемый тип, ранее содержавший настройки карты, которые ныне также входят в тип mapflag. -
mapdensityhandleСсылка на идентификаторы, описывающие интенсивность погодных эффектов на карте. Таких идентификатора интенсивности четыре: отсутствует, малая, средняя, большая. -
mapcontrolhandleСсылка на идентификаторы, описывающие возможные варианты того, что может управлять игроком, то есть человек, обычный компьютер или специально запрограммированный компьютер (т.е. "спасаемые" игроки или нейтрально-враждебное поведение). -
playerslotstatehandleСсылка на идентификаторы, описывающие состояние слота (зарезервированного картой места) игрока. Их немного - пустой, активный или оставленный игроком (т.е. игрок вышел из игры). -
volumegrouphandleСсылка на идентификаторы, описывающие звуковые группы, взаимодействующие со звуковым движком игры. В WarCraft III существует 8 различных звуковых групп, на которые разбиты все игровые звуки, в том числе и музыка. Собственные группы создавать нельзя: в JASS нет функции для инициализации новых экземпляров volumegroup. Группировка звуков позволяет экономить потребляемую память. Стоит заметить, что одновременное воспроизведение более одного звука одной и той же группы невозможно, то есть наслаиваться друг на друга могут только звуки из разных звуковых групп. -
camerafieldhandleСсылка на идентификатор параметра объекта камеры (camerasetup), такой как угол, высота, удалённость и т.д. -
camerasetuphandleСсылка на объект камеры. Тип назван camerasetup, так как по факту это не что иное, как таблица настроек (setup) для игровой камеры (camera). Все возможные параметры, которые можно изменить у камеры, описаны типом camerafield. Камера (Camera Object)
playercolorhandleСсылка на идентификатор цвета игрока. Как и в случае со звуковыми группами, новые цвета разработчики игры создавать не дают. Цвет игрока (Player Color)
placementhandleСсылка на идентификатор параметра расположения игроков на карте. При сохранении карты World Editor обязывает создавать стартовые позиции для каждого активного слота игрока. Так вот, благодаря этому типу такими позициями можно манипулировать. Можно "разбросать" игроков в случайном порядке по всем стартовым позициям, можно поставить строгую зависимость (красного игрока на красную стартовую позицию, синего - на синюю и т.п.), можно использовать настройки карты, а можно расположить союзников рядом друг с другом (в остальном порядок расположения при этом случаен). -
startlocpriohandleСсылка на идентификатор уровеня приоритетности стартовой позиции для обработки (низкий, обычный или высокий). Если Вы не понимаете, что только что прочли, просто запомните, что чем выше приоритет, тем больше шанс на то, что эта стартовая локация будет обработана раньше других. -
raritycontrolhandleСсылка на идентификатор частоты проигрывания анимации у боевых единиц. Возможны только два варианта частоты - частая и редкая. Дело в том, что у боевых единиц может быть несколько вариантов анимации для некоторых состояний (например, stand, когда боевая единица просто стоит на месте, ожидая приказов), проигрывающихся в случайном порядке. При помощи этого типа можно сделать так, чтобы некоторые из них появлялись чаще других. -
blendmodehandleСсылка на идентификатор режима наложения изображений, которые относятся к группе кинематографических фильтров (это изображения, применяемые в основном для придания большей атмосферы или эффектности внутриигровым роликам). Это не совсем те режимы наложения, которые встречаются в графических редакторах, таких как GIMP. Это уже тема другой статьи. -
texmapflagshandleСсылка на идентификатор серии инструкций для обработки текстурных карт объектов при наложении кинематографического фильтра. Чтобы получить более детальное представление о текстурных картах, прочтите статью о UV-развёртке, именно с ней взаимодействуют инструкции этого типа. -
effectagentСсылка на объект-спецэффект. В WarCraft III объекты спецэффектов - это обычные модельки, с которыми в игре никак нельзя взаимодействовать, но можно их видеть. Могут быть прикреплены к определённым точкам модели боевой единицы либо привязаны к точке на карте. Спецэффект (Special Effect)
effecttypehandleСсылка на идентификатор типа спецэффектов. Это та небольшая часть поддержки Редактора объектов, которую разработчики заботливо интегрировали в JASS. Типы спецэффектов - это такие поля у любой способности в Редакторе объектов, где хранятся пути к спецэффектам для различных назначений. О назначении этого типа узнал из материала Дуосоры о функциях и ссылки Николаса сообщением ниже на статью о нём. -
weathereffecthandleСсылка на объект-погодный эффект. Погодные эффекты - это специальные модели, создающие погоду в игре. К ним относятся дожди, туманы и т.п. Погодный эффект (Weather Effect)
terraindeformationhandleСсылка на объект-деформацию рельефа. Благодаря деформированию рельефа средствами JASS и этому типу можно изменять рельеф прямо в игре. Деформация рельефа (Terrain Deformation)
fogstatehandleСсылка на идентификатор функции тумана войны. Функций немного: спрятать с сокрытием (в этом состоянии туман войны закрашивает область чёрным цветом без возможности её просмотра, даже если игрок уже разведал её), спрятать без сокрытия (стандартный туман войны: данная область карты "сереет" для игрока и не обновляет её для него, если там нет его активных боевых единиц), показать (область видна и предоставляет актуальную информацию). -
fogmodifieragentСсылка на объект-модификатор видимости. Такой модификатор изменяет в определённом регионе стандартные правила тумана войны, раскрывая его, маскируя его или отключая туман войны в его пределах. Модификатор видимости (Visibility Modifier)
dialogagentСсылка на объект-игровое диалоговое окно. В WarCraft III эти диалоги очень близки к модальным на уровне окна за тем исключением, что в многопользовательской игре они не приостанавливают игру автоматически. В диалоги можно добавлять кнопки. Диалог (Dialog)
buttonagentСсылка на объект-кнопку определённого игрового диалогового окна. Кнопка (Dialog Button)
questagentСсылка на объект-квест. Такие объекты располагаются в меню заданий. По классификации задания делятся на основные и дополнительные. Технически, это ссылка на таблицу (квестов), в которой хранятся таблицы (условия, например), а в каждой - ссылка на таблицу (параметров) , которая в тёмном чулане хранится...Задание (Quest)
questitemagentСсылка на объект-требование для успешного выполнения задания, то есть условие задания. Это просто ряд строчек, которые пишутся в специально отведённой колонке задания в соответствующем меню. Оформлены они, всё же, как объекты. Их можно пометить выполненными или проваленными. Условия задания (Quest Requirement)
defeatconditionagentСсылка на объект-условие победы или поражения. При этом в файле common.j мало функций, связанных с этим типом, в основном - создание его экземпляра, уничтожение его экземпляра и задание описания его экземпляру. Условие поражения (Defeat Condition)
timerdialogagentСсылка на объект-окно таймера. Такие окна показываются в правом верхнем углу экрана и могут отображать название и оставшееся время до истечения таймера. Эти окна всегда отображаются выше объектов multiboard. Таких окон может быть несколько, они выстраиваются по горизонтали с выравниванием вправо. Окно таймера (Timer Window)
leaderboardagentСсылка на объект-таблицу рекордов. Конечно, это название не ограничивает спектр их применения только как таблиц рекордов. В таких таблицах может содержаться название (наверху), под которым содержатся две колонки - ник игрока и целочисленная (условно назовём её "счётом" игрока). Эти таблицы всегда отображаются ниже объектов timerdialog и multiboard. Больше одного экземпляра leaderboard вывести на экран нельзя, но создавать и менять один экземпляр на другой можно. Таблица рекордов (Leaderboard)
multiboardagentСсылка на объект-расширенную таблицу. Иногда эти объекты называют мультидосками (от англ. multi board), иногда их зовут меню паузы (по названию из русской официальной локализации игры), но правда в том, что multiboard - это не что иное, как таблица универсального назначения (multi-purpose board). Такие таблицы всегда отображаются ниже объектов timerdialog и выше объектов leaderboard. Как и с leaderboard, больше одного multiboard одновременно вывести на экран нельзя. Меню паузы (Multiboard)
multiboarditemagentСсылка на объект-ячейку расширенной таблицы. Ячейки могут содержать в себе текст и иконку перед ним; их ширину менять можно, а высоту - нет. Можно также отображать или прятать как иконку, так и текст. -
trackableagentСсылка на специальный объект, способный отслеживать события указателя мыши (наведение и нажатие на него). К сожалению, всю карту для отслеживания координат указателя ими заполнить нельзя (да и от существующих будет много неприятностей - упадёт производительность карты в игре). Их также нельзя изменять, перемещать и удалять. -
gamecacheagentСсылка на объект-игровой кэш. В WarCraft III это двумерный массив со строковыми ключами, который может сохранять в себе различные игровые данные и хранится в отдельном специально сгенерированном файле. Большинство функций этого типа было ликвидировано в обновлении 1.24, но до этого игровой кэш имел широкое применение. Буфер игры (Game Cache)
versionhandleСсылка на идентификатор версии игры. Здесь речь идёт не о конкретном числе (1.24 или 1.27), а о том, оригинал это или оригинал с дополнением (Reign of Chaos или The Frozen Throne). Он добавлен для увеличения совместимости дополнения с оригиналом. -
itemtypehandleСсылка на идентификатор класса предмета. Не путайте классы предметов с типами предметов, которые являются целочисленными! В Редакторе объектов на вкладке Предметы (Items) есть папки, по которым они классифицируются (имеющие заряды, артефакты, прочие и т.п.). Этот тип позволяет узнать, в какой из этих папок находится предмет. Класс предмета (Item Class)
texttaghandleСсылка на объект-всплывающий текст. Разработчики также установили ограничение на предельно допустимое количество одновременно созданных экземпляров объектов всплывающего текста - 99. Такие тексты можно анимировать, показывать, скрывать, окрашивать в определённые цвета, менять им позицию и т.п. Плавающий текст (Floating Text)
attacktypehandleСсылка на идентификатор типа атаки. Используется в функциях нанесения урона. -
damagetypehandleСсылка на идентификатор типа урона. Используется в функциях нанесения урона. -
weapontypehandleСсылка на идентификатор типа оружия. Используется в функциях нанесения урона. -
soundtypehandleСсылка на идентификатор типа звука способности. Их всего два - зацикленный и незацикленный. Это тоже значения, получаемые из звукового поля любой способности в Редакторе объектов. -
lightninghandleСсылка на объект-молнию. На самом деле, это не молния в погодном смысле слова, работает такой объект иначе. Молнии в WarCraft III - это прямые линии, заполненные определённой текстурой (её называют типом молнии). Можно менять их размер, высоту, положение на карте. Такие объекты используются в том числе в стандартных способностях (Цепь молний орочьего Говорящего с духами, Похищение маны эльфийского Чародея крови и т.д.). Можно создавать и импортировать собственные типы молний, но это уже тема для отдельной статьи. Молния (Lightning)
pathingtypehandleСсылка на идентификатор типа проходимости. Тип проходимости определяет, что может ходить, плыть или летать в конкретном квадрате. -
imagehandleСсылка на объект изображения. Эти изображения могут быть наложены на поверхность карты и будут располагаться по заданным координатам x, y и z при их создании. В основном этот тип используют для дополнительного украшения карты (например, в карте TCX для отображения логотипа в центре арены использован именно этот тип). Изображение (Image)
ubersplathandleСсылка на объект-уберсплэт. В терминологии WarCraft III уберсплэты - это специальная графика на земле, которая по умолчанию есть у зданий (как у действующих, так и у разрушенных), а также у некоторых спецэффектов, оставляющих следы на земле (например, Огненный столб Чародея крови оставляет подобие "выжженной" земли, которое затем постепенно исчезает). -
hashtableagentСсылка на объект-хэш-таблицу. Она представляет собой классический двумерные массив, оформленные в функциональную оболочку и поддерживающие идентификаторы в диапазоне, который позволяет хранить integer. Хэш-таблица (Hashtable)

Операции с типами данных

Стоит также привести таблицу операторов, которые могут быть использованы при работе с различными данными. Она поможет Вам понять, что можно при помощи JASS сделать с ними.
Договоримся, что a и b - это переменные одного и того же типа.
Об условных операторах речь пойдёт в разделе об условиях.
Под колонкой "Типы данных" подразумевается перечень типов, с которыми такой оператор можно использовать. Если нарушить это правило, при сохранении карты на экран будет выведена ошибка "Bad types for a binary operator".
Оператор Типы данных Использование Принцип работы
Сложение integer, real, string a + b Для типов integer и real - суммирует значения a и b. В случае выхода суммы за пределы допустимых значений этих типов, то результат вписывается в ОДЗ по специальной формуле (если нарушена нижняя граница: [Mx-(b+(Mn-a))]; если нарушена верхняя граница: [Mn+(b-(Mx-a))], где Mn - нижняя граница, а Mx - верхняя граница). Для типа string - это конкатенация строк, то есть их склеивание в одну путём приписывания строки b непосредственно после строки a.
Вычитание integer, real a - b Отнимает значение b от значения a. В случае выхода разности за пределы допустимых значений результат также вписывается в ОДЗ по тем же формулам, что и после сложения.
Умножение integer, real a * b Умножает значение a на значение b. В случае выхода произведения за пределы допустимых значений результат также вписывается в ОДЗ по тем же формулам, что и после сложения.
Деление integer, real a / b Делит значение a на значение b. В случае выхода частного за пределы допустимых значений результат также вписывается в ОДЗ по тем же формулам, что и после сложения.

Переменные и массивы

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

Теория

Начнём знакомство с определений, чтобы Вы понимали, что такое переменная, её область видимости и чётко понимали принцип её работы.
Переменная - это область памяти, выделяемая для хранения данных, которой присваивается идентификатор, называемый именем переменной. С его помощью можно получать значение переменной, то есть хранимые в ней данные.
Процесс выделения памяти под переменную называется её аллокацией, а процесс освобождения выделенной под неё памяти - деаллокацией. Процесс присвоения переменной имени называется её именованием.
Проще всего сравнить переменную с ящиком в столе. Стол - это вся память, ящик стола - часть этой памяти. Предположим, ящики расположены по вертикали и пронумерованы сверху вниз. Если говорить о пятом ящике как о ящике номер пять, тогда это указатель на ящик (номер ячейки памяти). Есть и другой вариант - хранить в ящике что-нибудь определённое и называть ящик не по номеру, а по названию. Например, ящик для тетрадей. Это обращение к ящику подобно обращению к нему не через указатель, а через переменную - ящик имеет не адрес, а название и выделен под определённый тип данных (тетради).
Переменные в JASS - это не сами области памяти, а ссылки на них. Похоже, разработчики серьёзно задумывались о безопасности, так как через ссылки можно реализовать ограничение доступа к памяти.
Объявление переменной - это процесс её аллокации и присвоения ей имени. Присвоение начального значения переменной называется её инициализацией.
Удаление переменной - это процесс её деаллокации и освобождения её имени для дальнейшего использования другими переменными.
Присваивание (задание) переменной - помещение в переменную разрешённых её типом данных для обращения к ним по её имени.
» Как технически устроено присваивание
На самом деле, здесь всё устроено намного сложнее, чем кажется на первый взгляд. Машина технически работает только с числами. Поэтому если, к примеру, взять тип integer, то в переменную записывается указанное число. Если же взять тип string, то записывается адрес её значения в строковой таблице.
Таким образом, запись объектов-таблиц в переменную (в JASS за исключением типов integer, boolean и real все типы являются табличными) производится не путём их переноса из одной области памяти в другую, не путём их копирования, а путём указания в переменной числа-идентификатора этих таблиц в памяти или других таблицах.
Иначе возникает нелепая ситуация - присвоение одной переменной бы либо "клонировало" таблицу, оставляя её в одной переменной и создавая точную её копию (на момент присваивания) в другой, либо удаляла бы её из одной области памяти, перенося в другую, соответствующую адресу целевой переменной. Это сделало бы одновременное хранение объекта в нескольких переменных в актуальном состоянии невозможным.
Именно поэтому переменные обращаются к положению объекта в таблице, а не записывают его напрямую. В некоторых языках программирования, впрочем, действительно существует реализация функций клонирования и переноса объектов.
Обнуление переменной - это присвоение переменной нулевого значения её типа. Оно необходимо, чтобы показать обработчику кода, что в переменной значения не содержится. Сама переменная при этом не удаляется из памяти.
Запомните! Средств для ручной деаллокации в JASS не предоставляется. Обнуление деаллокацией не является, потому что переменная всё ещё определена в памяти!
Переменные типов integer, real, string и boolean в обнулении не нуждаются, так как наследуются напрямую от соответстувующих типов языка программирования C, а это говорит о том, что на вышеперечисленные типы работает его встроенная сборка мусора. Это делает их обнуление бессмысленным.
До обнуления переменной во многих случаях также нужно удалять объект, на который она ссылается, потому что после обнуления объект "потеряется" в памяти и к нему уже нельзя будет никак обратиться. Впрочем, если ссылка на объект, хранящийся в одной переменной, передаётся в другую, этот объект можно удалить и позже (на него можно будет ссылаться через другую переменную).

Область видимости переменной - все места кода, где к переменной можно обращаться по её имени.
По области видимости переменные разделяют на локальные и глобальные.
Локальная переменная - переменная, область видимости которой ограничена определённым блоком кода. Память под локальную переменную аллоцируется при её объявлении и деаллоцируется после окончания выполнения блока кода, видящего её, но при том условии, что эта переменная содержит нулевое значение.
Глобальная переменная - переменная, область видимости которой ограничивается пределами программы. То есть, её видит весь код программы.
В JASS глобальными переменными считаются:
  • Переменные, созданные в Редакторе переменных Редактора триггеров. При обращении к ним в коде перед их именем пишется приставка udg_, означающая Universally Defined Global (универсально объявленная глобальная переменная).
  • Переменные, объявленные внутри блока globals в файле war3map.j - в обычном (недиалектовом) JASS объявить этот блок непосредственно в коде не представляется возможным, но объявлять в нём переменные в уже сохранённой карте и потом работать с ними в World Editor вполне возможно.

По принципу хранения данных переменные делятся на немассивные и массивные.
Немассивная переменная (нуль-мерный массив, скаляр) - переменная, под которую отводится памяти ровно столько, сколько необходимо для хранения одной единицы данных её типа или для хранения ссылки на неё.
При объявлении скаляра ему не присваивается начальное значение его типа. Инициализировать его нужно отдельно, при этом, если его не инициализировать, выполнение программы остановится при чтении неинициализированного скаляра.
Массивная переменная - несколько скаляров, расположенных в памяти друг за другом. Эти скаляры называются элементами массива, а их количество называется размером массива.
Каждому элементу массива присваивается начальное значение его типа, то есть элементы массива в отличие от скаляров инициализируются при объявлении массива.
Каждому элементу массива не нужно присваивать уникальное имя. Вместо этого достаточно лишь присвоить такое имя самой массивной переменной, а к её элементам обращаться через специальным образом приписываемое после имени число, уникальное для каждого элемента. Такое число называется индексом массива.
Далее в статье под словом "переменная" будет подразумеваться немассивная переменная, а под словом "массив" - массивная.

По размеру массивы бывают статические (имеют строго отведённое количество элементов) и динамические ("подстраиваются" под наличие данных в массиве).
В JASS размер массива всегда статический - индексы могут принимать значения от 0 до 8191. Так как значения под индексом 8191 время от времени дают непредсказуемые сбои, безопасно использовать только индексы от 0 до 8190.
Индексов у массива может быть несколько. Их количество называется размерностью массива. Стоит различать размерность массива и его размер. Если под размером понимается диапазон индексов, то под размерностью понимается их количество. Таким образом:
  • Массивы без индекса называют скалярами. Графически обозначаются как точки.
  • Массивы с одним индексом называют одномерными или векторами. Графически обозначаются как отрезки. Это самая распространённая размерность массивов.
  • Массивы с двумя индексами называют двумерными или матрицами. Графически обозначаются как квадраты.
  • Массивы с тремя индексами называют трёхмерными или кубическими. Графически обозначаются как кубы.
  • Массивы более, чем с одним индексом называют многомерными.
  • Массивы более, чем с тремя индексами, имеют общее название - n-мерные массивы.
В обычном JASS поддерживаются только скаляры и векторы. Средствами языка возможно реализовать многомерность массивов, но синтаксис их использования будет желать лучшего.

Стоит отдельно рассказать о правилах, согласно которым переменной присваивается имя.
» Немного об именовании переменных
Чтобы указать имя правильно, запомните следующее:
  • Разрешается использование латинского алфавита верхнего и нижнего регистров.
  • Разрешается использование цифр, символа нижнего подчёркивания.
  • Имя не может начинаться с цифры.
  • Имя не может быть используемым в JASS ключевым словом.
  • Имя не может совпадать с названием любого из типов данных.
Переменные в ряде случаев могут иметь одинаковые имена. Чтобы не запутаться, ниже приведён список того, в каких случаях к чему обращается код.
  • Название именованного блока кода (функции) может совпадать с именем глобальной переменной, потому что синтаксис при работе с блоком кода и при работе с переменной существенно отличается.
  • Название именованного блока кода не может совпадать с именем локальной переменной внутри этого блока.
  • Имя глобальной переменной может совпадать с именем локальной переменной. При этом в области видимости локальной переменной при обращении по этому имени будет происходить обращение к этой локальной переменной, в остальных случаях обращение по этому имени будет считаться обращением к глобальной переменной.

На этом краткий ликбез о переменных завершён.

Практика

Приступим к написанию Вашего первого кода. Разберёмся, что такое объявление и присвоение переменных не в теории, а на практике.
Научимся объявлять переменные.

Объявление целочисленной локальной переменной

local integer myInteger
Так выглядит код, который объявляет локальную переменную типа integer.
Как объявить переменную:
  • Строка, в которой объявляется локальная переменная, начинается с ключевого слова local и хотя бы одним знаком пробела после него. Оно означает, что переменная локальная. Глобальные переменные могут быть объявлены в коде только внутри специального блока globals. В случае с ними ключевое слово вовсе не требуется, но если Вы никогда не будете менять значение глобальной переменной, напишите ключевое слово constant, чтобы сэкономить память и дополнительно защитить переменную от изменения. С локальными переменными так делать нельзя - их ключевое слово - local и никакое другое.
  • После ключевого слова пишется название типа и хотя бы один знак пробела после него. В данном случае это тип integer (целое число). Если Вы сомневаетесь в том, какой тип Вам необходим или забыли его название, воспользуйтесь таблицей типов из предыдущих разделов.
  • После названия типа пишется имя переменной. В данном примере именем является myInteger.

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

Присвоение переменной нового значения

set myInteger = 2
Разбор вышеуказанной строки:
  • Строка, задающая переменной значение, начинается с ключевого слова set и хотя бы одним знаком пробела после него.
  • После ключевого слова set пишется название (имя) переменной, заданное при инициализации.
  • После названия переменной в той же строке нужно поставить знак = с любым количеством знаков пробелов до и после него.
  • После знака равенства прописывается значение переменной из области допустимых значений её типа.

Переменной можно присваивать начальное значение непосредственно после её объявления. Это делается следующим образом:

Присвоение переменной начального значения

local integer myInteger = 2
Как присвоить переменной значение сразу после её объявления:
  • Объявить переменную.
  • После названия переменной в той же строке нужно поставить знак = с любым количеством знаков пробелов до и после него.
  • После знака равенства прописывается значение переменной из области допустимых значений её типа (иначе карта откажется сохраняться, пожаловавшись на то, что Вы присваиваете переменной недопустимое значение).

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

Как объявить локальную переменную-массив? Точно так же, как и обычную, только между типом и названием добавляйте ключевое слово array (в переводе с английского означает "массив"). Значение при этом сразу задать нельзя.
Значение переменной-массива задаётся так же, как и обычной переменной, только после её имени ставятся квадратные скобки с индексом ячейки, в которую требуется записать (или из которой нужно получить) данные ([i], где i - индекс массива).

Больше о переменных сказать нечего. Теперь Вы знаете, как обращаться с ними.

Функции

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

Теория

Проще всего понять, что такое функции, можно, изучив базовые определения.
Функция - это часть кода, которую можно подключать к другим частям программы. Так как в JASS нет частей программы (программных юнитов) и программа фактически является одним сплошным списком функций, в JASS функции можно определить рекуррентным определением - как части кода, которые могут использоваться другими частями кода.
Функция состоят из аргументов и фактов тела.
Вызов функции - это подключение функции в другой части кода. Функция может вызывать сама себя, такой вызов называется рекурсией.
» Хотите понять, почему рекурсия возможна и чем полезна? Загляните сюда.
Для начинающих программистов это может звучать несколько дико - то есть, как так допускается, что функция может вызывать сама себя? Разве она не будет постоянно "гонять" себя по кругу, не давая никакой пользы? Если да, то сколько на такой "прогон" нужно ресурсов?
Стоп.
Да, стоп.
Задавая такие вопросы, Вы не понимаете природы рекурсии. Функция не может "гонять" себя бесконечно: рекурсия имеет предельный уровень вложенности (т.е. количество раз, которые функция может вызывать сама себя, ограничено), а кроме того, можно ограничить вложенность до определённого уровня при помощи условных ветвлений (о них чуть позже). Это делает рекурсию очень удобным способом реализации перевода чисел, например, из десятичной в двоичную систему (делим число на два и сохраняем остаток, пока число не станет меньше двух).
У функций тоже есть область видимости. По правилам JASS функция может вызывать любые функции, помещённые выше, чем она сама, и себя саму, потому что функций, находящихся ниже, она "не видит". Это значит, что если Вы сначала объявляете функцию А, ниже неё объявляете функцию Б, а ниже этой - функцию В, то:
  • Функция В сможет вызывать функции А, Б и саму себя;
  • Функция Б сможет вызывать функцию А и саму себя, но не сможет вызывать функцию В, потому что функция В стоит ниже функции Б;
  • Функция А сможет вызывать только саму себя, потому что выше неё нет никаких функций.
Аргумент (параметр) функции - это технически локальная переменная функции, значение которой задаётся при вызове функции. Используется в пределах функции как обычная переменная - её значение можно менять в процессе выполнения функции. Одна функция может принимать один или более аргументов (при этом она не обязана их принимать, то есть может и не принимать их вообще), также может возвращать аргумент любого типа (или не возвращать ничего). Достаточно гибкая штука в обращении.
Тело функции - это перечень совершаемых функцией действий, которые выполнятся при её вызове. Говоря короче, это то, что функция делает. Тела у функции может и не быть вообще (только какой тогда смысл в такой функции?)
Кроме того, по правилам синтаксиса JASS на одной строке может располагаться только одно конкретное действие.
Анонимные функции - это функции без названия. Увы, JASS требует именовать все функции, поэтому поддержки этого типа функций в JASS нет.
В JASS поддерживаются три типа функций - общие, постоянные и скрытые.
  • Общая функция - обыкновенная, ничем не примечательная функция, которая обладает всеми характеристиками функций без изменений.
  • Постоянная функция - такая функция, которая может возвращать только постоянные значения, например, чётко указанные числа (такие как 8, 10, 14 и т.п.). Работает быстрее, чем общая функция, но не имеет широкого спектра применения, так как ограничивает работу с переменными.
  • Скрытая функция - это функция, которая находится в числе перечисленных в файле common.j. Реализация скрытых функций (или нативных, как их ещё называют) осуществлена на более низком уровне абстракции (находится в составе ядра программы), а в JASS они объявлены на правах базовых.

Практика

Разобравшись в теории, можно перейти к практике, то есть к непосредственному объявлению функции в JASS.
Структура функции достаточно проста:

Простая функция без аргументов и тела

function MyFunction takes nothing returns nothing

endfunction
Как объявить функцию:
  • Строка, в которой объявляется функция, начинается с приставки, которая определяет тип функции и хотя бы одним знаком пробела после неё. Таким образом:
    • Скрытые функции начинаются с приставки native. В переводе с английского приставка native означает "родная". По крайней мере, в классическом JASS их нельзя объявить напрямую, но есть статьи, в которых объясняется, как "вшить" в ядро что-нибудь ещё.
    • Постоянные функции начинаются с приставки constant.
    • Общие функции не имеют приставок.
  • После приставки (или же сразу - для общих функций) для всех типов функций указывается ключевое слово function, которое означает, что здесь объявляется функция. После ключевого слова нужно поставить хотя бы один знак пробела.
  • После ключевого слова function указывается имя функции (в примере выше это MyFunction) с хотя бы одним знаком пробела после него. Требования к имени функции такие же, как и к имени локальной переменной - уникальное, состоит из латиницы любого регистра, знака нижнего подчёркивания и цифр, причём имя не может начинаться с цифры.
  • После имени функции указывается ключевое слово takes, которое означает, что далее будут указаны аргументы функции (ставьте это слово, даже если аргументов не будет, это Ваша обязанность). В переводе с английского языка это слово означает "принимает". После этого ключевого слова необходимо поставить хотя бы один знак пробела.
  • После ключевого слова takes перечисляются через запятую аргументы функции в формате тип имя - как при объявлении переменных, причём на именование аргументов распространяются те же правила (например: integer i, unit u, timer t). Тип и имя разделяются как минимум одним знаком пробела. После последнего указанного аргумента запятая не ставится. Также запятая не ставится, если аргумент один. Если функция не принимает аргументов, то замените их на ключевое слово nothing, которое в переводе с английского означает "ничего". Разумеется, запятая после него тоже не ставится.
  • После списка аргументов указывается ключевое слово returns и хотя бы один знак пробела после него. В переводе с английского языка это слово означает "возвращает".
  • После ключевого слова returns укажите тип значения, которое функция возвращает после выполнения (например, если функция возвращает булевый тип, укажите boolean). Если функция ничего не возвращает, укажите ключевое слово nothing.
  • Строки ниже той, которая только что была объяснена - это тело функции.
  • На строке, следующей за последней строкой тела функции, напишите только ключевое слово endfunction. Оно означает, что функция закончена. Всё, что расположено ниже, функция не "видит" и своим телом не считает.
Что может находиться в теле функции?
  • Локальные переменные. Они указываются по одной на строчку, причём до всего остального содержимого тела функции. О них уже много рассказывалось выше, так что заострять на них внимание здесь нет смысла.
  • Комментарии. Обычно выделяются серым цветом. Комментарии - это такие части кода, которые никак не обрабатываются и не запускаются. Начало комментария обозначается двойным слэшем (символы // называют открывающими символами комментариев). Комментарием считается вся остальная часть строки после двойного слэша. Там можно писать всевозможные пояснения к коду. Увы, многострочные комментарии в классическом JASS не поддерживаются, но поддерживаются в его диалектах (vJASS, cJASS). Именно в силу того, что они не обрабатываются, комментарии можно писать в любой части кода, даже вне функций.
  • Работа с переменными. Изменение значений локальных и глобальных переменных.
  • Вызовы функций. Обращение к другим функциям - самый распространённый вид операции, которая помещается в тело функции. Как говорилось в разделе теории, JASS состоит примерно на 90% из функций и обращений к ним.
  • Условные ветвления. О них речь пойдёт в следующем разделе статьи.
  • Циклы. Эта полезная конструкция также будет рассмотрена далее в рамках этой статьи.
  • Возврат значения. Прерывает исполнение операций в теле функции, возвращая значение указанного при создании функции типа.

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

Функция и её вызов

function A takes integer i returns nothing
// Тело функции A
endfunction

function B takes nothing returns nothing
call A(23)
endfunction
Как вызвать функцию:
  • Строка, которая начинается с вызова функции, должна начинаться с ключевого слова call (оно в переводе с английского означает "вызвать") и хотя бы одного знака пробела после него.
  • После ключевого слова call указывается имя функции, которую нужно вызвать.
  • После имени функции ставится открывающая скобка. Она означает, что далее будут перечисляться значения для параметров, которые она принимает.
  • После открывающей скобки через запятую перечисляются значения для параметров, принимаемых вызываемой функцией. Порядок перечисления тот же, что и порядок, в котором вызываемая функция их требует. Если она просит параметры (integer i, real r, unit u), то сначала нужно указать целое число (которое запишется в локальную переменную i вызываемой функции), потом число с плавающей запятой (оно запишется в локальную переменную r вызываемой функции), потом ссылку на объект-боевую единицу (она запишется в переменную u вызываемой функции). Порядок менять нельзя. Так требует синтаксис языка. После последнего значения параметра запятая не ставится. Если функция параметров не принимает, Вы ничего не указываете.
  • После передачи значений для параметров ставьте закрывающую скобку.
В примере выше принимаемому функцией А параметру i (целое число) при её вызове функцией B было присвоено значение 23.
Теперь Вы умеете вызывать функции.

Зная, как функции создаются и вызываются, Вы можете перейти к изучению возврата функцией значения.

Функция, возвращающая значение

function A takes integer i returns integer
    // Функция вернёт число i, умноженное на два. Если i = 2, то будет возвращено число 4.
    return i*2
endfunction
Для того, чтобы вернуть из функции значение:
  • Строка, которая возвращает значение, должна начинаться ключевым словом return. Оно означает "вернуть". После него необходимо поставить хотя бы один знак пробела.
  • После ключевого слова return напишите возвращаемое значение. Оно может быть вызовом другой функции, арифметической операцией, постоянным значением и т.д. Если функция ничего не возвращает, просто ничего больше не пишите.
Помните, что после возврата значения исполнение функции прерывается (не останавливается, а именно прерывается, поскольку функция, вернувшая значение, дальнейшее выполнение не осуществляет).

Поздравляю, Вы прошли ликбез о функциях! Перейдём к изучению условий.

Условия

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

Теория

Как и до этого, для начала - немного теории. Вам нужно понимать, что Вы делаете, поэтому не ленитесь читать и вникать в смысл определений.
Условие (конструкция ветвления) - оператор, который выполняет одну или несколько команд, указанных внутри него, но при условии того, что логическое значение в его составе, расположенное до этих команд, возвращает значение истины.
» О конструкциях ветвления для новичков в программировании
Прочитав это определение, Вы могли впасть в недоумение, не понимая, что Вы только что прочли. Вы пришли за разъяснением. Хорошо, читайте.
Когда выполняется определённая функция, некоторые её действия иногда необходимо выполнять не всегда, а только при определённом условии. Допустим, если Вы проверяете количество цифр в каком-либо числе, Вы не сможете возвращать одинаковое значение для каждого числа (не в каждом же числе только одна цифра, верно?). В таких случаях на помощь приходит конструкция ветвления.
Она позволяет выстроить необходимую логику. В нашем примере:
  • Если число меньше нуля, то условия ниже будут сравнивать не переданное в функцию число, а его модуль (просто чтобы убрать знак минуса).
  • Если число меньше десяти, то в нём одна цифра.
  • Если число больше девяти и меньше ста, то в нём две цифры.
  • Если число больше девяносто девяти и меньше тысячи, то в нём три цифры.
...
  • И так далее до максимально возможного в типе integer количества цифр.
Конечно, проверку цифр в числе можно сделать намного проще и ресурсоэкономнее, но цель данного примера - показать, на что способны условные конструкции.
Видите? Такая конструкция позволяет с лёгкостью сделать то, что нам надо. Проверка диапазона значений числа (меньше десяти, больше девяти и меньше ста и т.д.) и есть то самое "логическое значение в составе оператора", о котором шла речь.
Это действительно один огромный оператор. Не пугайтесь.

Для того, чтобы работать с условиями, необходимо понять, как принцип работы с логическим типом boolean. Ранее о нём говорилось, но вот о работе с ним - нет. Логический параметр может быть переменной, а может быть и функцией, возвращающей булевое значение (true или false, т.е. "истина" или "ложь").
При помощи логических параметров можно сравнивать, что угодно, проверяя истинность или ложность выражения. Всё, что происходит при сравнении логического типа - это сведение выражения к одному определённому результату - истине (true) или лжи (false). Для этого есть приставка not ("не"), связки and ("и") и or ("или").
Также для сравнений разных параметров, логических и не логических, можно использовать операторы, которые для удобства сведены в одну таблицу ниже.
Любой оператор сравнивает два одинаковых типа или два неявно приводимых к одинаковым.
Договоримся условно, что a и b в таблице ниже - это переменные любого типа. Вместо переменных можно также подставлять вызов функции, возвращающей соответствующий тип, либо постоянное значение (т.е. готовый результат).
НазваниеРеализация в JASSТипы, с которыми работает операторПринцип работы
Равноa == bЛюбой типЕсли значение переменной а соответствует значению переменной b, то операция вернёт результат true (истина), иначе она вернёт результат false (ложь).
Не равноa != bЛюбой типЕсли значение переменной а не соответствует значению переменной b, то операция вернёт результат true (истина), иначе она вернёт результат false (ложь).
Большеa > binteger, real, stringЕсли значение переменной а больше, чем значение переменной b, то операция вернёт результат true (истина), иначе она вернёт результат false (ложь).
Меньшеa < binteger, real, stringЕсли значение переменной а меньше, чем значение переменной b, то операция вернёт результат true (истина), иначе она вернёт результат false (ложь).
Больше или равноa >= binteger, real, stringЕсли значение переменной а больше, чем значение переменной b, либо соответствует её значению, то операция вернёт результат true (истина), иначе она вернёт результат false (ложь).
Меньше или равноa <= binteger, real, stringЕсли значение переменной а меньше, чем значение переменной b, либо соответствует её значению, то операция вернёт результат true (истина), иначе она вернёт результат false (ложь).
Помимо операторов существуют логические операции, которые заметно упрощают построение действий, где нужно получить определённый результат работы на выходе. Если Вы - несведущий в логике человек или же хотите посмотреть, как базовые логические функции реализовываются в JASS, откройте спойлер, расположенный ниже.
» Краткий курс логики, необходимый программисту
Чтобы упростить понимание, все логические функции сведены в удобную таблицу.
Договоримся условно, что a и b в таблице ниже - это логические переменные (типа boolean).
НазваниеСинтаксис в JASSПринцип работы
Отрицаниеnot a"Меняет знак" логического выражения. Если в переменной а получается значение true (истина), то выражение not a даст в результате false (ложь) и наоборот.
Конъюнкцияa and bЕсли переменные a и b обе истинны, то конъюнкция даст в результате значение true (истина), иначе она даст в результате значение false (ложь).
Дизъюнкцияa or bЕсли хотя бы одна из переменных a и b содержит значение true, то дизъюнкция в результате даст значение true (истина), если же обе содержат значение false, то и дизъюнкция даст в результате значение false (ложь).
Импликация(not a) or bУсловная конструкция формы "посылка - следствие". Возвращает true (истина) во всех случаях, кроме того, когда переменная а содержит значение true (истина), а переменная b содержит значение false (ложь).
Эквиваленция((not a) or b) and (a or (not b))Не путайте эквиваленцию с конъюнкцией - они похожи, но это на самом деле разные операции. Если конъюнкция для возврата значения true (истина) требует, чтобы обе переменные a и b содержали значение true (истина), то эквиваленции для возврата значения true (истина) достаточно того, чтобы a и b содержали одинаковое значение, то есть либо обе были истинны, либо обе были ложны.
Строгая дизъюнкцияnot (a and b) and (a or b)Забавная вещь. Более известна под названиями "сложение по модулю 2", "исключающее <или>", а также XOR. Так как оператора xor (или ^) в JASS нет, обходимся без него, используя правило "одно или другое, но не оба". Возвращает значение true (истина), если у переменных a и b значения разные (одна из них истинна, а другая - ложна) и значение false (ложь) в других случаях.
Штрих Шеффераnot (a and b)Эта операция также известна под названием NAND. Если значения переменных a и b оба истинны, то эта операция вернёт в результате значение false (ложь). В любом другом случае она вернёт значение true (истина).
Стрелка Пирсаnot (a or b)Эта операция также известна под названием NOR. Фактически, это отрицание штриха Шеффера, то есть, эта операция вернёт значение true (истина) только в одном случае - если значения переменных a и b ложны.
Это все основные логические функции. Существуют ещё основные логические аксиомы и тождества, которые придут Вам на помощь при упрощении логических выражений. О них Вы сможете узнать здесь.

Практика

В JASS синтаксис простого условного блока достаточно нехитрый:

Простой условный блок

if b then
    // Действия, если b возвращает true.
endif
Как правильно объявить условный блок:
  • Строка, где объявляется условный блок, начинается с ключевого слова if и хотя бы одного знака пробела после него. Это слово в переводе с английского означает "если".
  • После ключевого слова if можно поместить переменную типа boolean, логическое значение (true или false напрямую), функцию, которая такое значение возвращает, сравнительную операцию (например, a == b) или логическую операцию (например, not b). Скобки разрешаются, но не обязательны. Когда этот параметр указан, поставьте один или более знак пробела после него.
  • Закончите строку ключевым словом then, которое в переводе с английского означает "то".
  • С новой строки начинается та часть тела условия, которая выполняется, если условное выражение b вернуло значение true (их ещё называют действиями после успеха проверки).
  • После того, как нужные действия перечислены (внутри условия тоже может быть условие), на новой строке поставьте ключевое слово endif, которое в дословном переводе с английского означает "закончить "если"".
Поздравляю, Вы создали своё первое условие. Но не будем останавливаться на достигнутом.

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

Условный блок с обработко й любого результата b

if b then
    // Действия, если b возвращает true.
else
    // Действия, если b возвращает false.
endif
» Как бы Вы делали, если бы такой конструкции не существовало

Вот так...

if b then
    // Действия, если b возвращает true.
endif

if not b then
    // Действия, если b возвращает false.
endif
Всё отличие этого варианта условного блока от предыдущего в том, что вместо объявления отрицающего условия not b после действий в случае прохождения проверки на пустой строке до ключевого слова endif ставится ключевое слово else (означает "иначе" в переводе с английского), после чего с новой строки до того, как указывать ключевое слово endif, перечисляются действия в противном случае (если проверка НЕ прошла).

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

Конструкция с использованием elseif

if b then
    // Действия, если b возвращает true.
elseif c then
    // Действия, если b возвращает false, но c возвращает true.
elseif d then
    // Действия, если b и с возвращают false, а d возвращает true.
else
    // Действия, если ни одно из условий выше не даёт true в результате.
endif
» Как бы Вы обходились без неё

Таким образом

if b then
    // Действия, если b возвращает true.
else
    if c then
        // Действия, если b возвращает false, но c возвращает true.
    else
        if d then
            // Действия, если b и с возвращают false, а d возвращает true.
        else
            // Действия, если ни одно из условий выше не даёт true в результате.
        endif
    endif
endif
Выглядит громоздко, неправда ли?
Вся разница с предыдущими условными блоками в том, что вместо многовложенного условия на новой строке ставится ключевое слово elseif, которое в переводе с английского означает "иначе, если". Оно как бы склеивает эту вложенность. По механике elseif, если одно из указанных условий срабатывает, те, что ниже, не проверяются.
Поздравляю Вас с успешным изучением условных конструкций! На горизонте - циклы.

Циклы

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

Теория

В программировании бывают такие случаи, когда определённую часть кода необходимо повторить определённое количество раз и при этом не всегда одинаковое. Рекуррентное исполнение отдельной функции специально для этого далеко не всегда является оптимальным вариантом, поэтому в программировании существует такая удобная вещь, как циклы.
Цикл - это такая конструкция, которая используется для многократного повторения определённого набора действий. Начало повторения называется входом в цикл. Окончание повторения называется выходом из цикла. Для выхода из цикла обязательно наличие условия, при котором цикл закончится.
Виток цикла - каждое успешное выполнение его набора действий.
Циклы бывают следующих видов:
  • Безусловные (бесконечные) - такие циклы, которые из-за отсутствия условия (либо неправильной условной конструкции, которая всегда даёт один и тот же результат) повторяются неопределённое количество раз. Являются типичной ошибкой начинающих программистов.
  • Предусловные (циклы с предусловием) - такие циклы, которые содержат условие перед выполнением набора действий. Набор действий в цикле будет продолжать выполняться, пока условие будет возвращать значение true. При этом набор действий не будет выполнен ни разу, если условие изначально вернёт значение false - цикл при этом будет пропущен полностью. Их также называют while-циклами.
  • Постусловные (циклы с постусловием) - такие циклы, которые содержат условие после выполнения набора действий. Набор действий в цикле выполнится как минимум один раз и будет продолжать выполняться до тех пор, пока условие не вернёт значение true. Их также называют циклами "do...while", циклами "до" или циклами "repeat...until".
  • Общие (циклы с выходом из середины) - такие циклы, где условие может находиться в любом месте набора действий, таким образом прерывая цикл после себя, если условие возвращает значение true. Их также называют exitwhen-циклами. В JASS реализован только этот вид, но из него можно построить любой другой, об этом речь пойдёт в практической части.
  • Циклы со счётчиком (циклы "для") - разновидность циклов, где количество повторений строго привязывается к увеличению определённой целочисленной переменной до определённого значения (обычно с шагом 1, но можно с каждым витком цикла прибавлять к счётчику и больше, чем единицу). Счётчик также называют итератором (отсюда его традиционное имя i), а виток цикла - его итерацией. Также известны под названием циклов for.
  • Совместные (циклы просмотра) - такие циклы, предназначение которых - проведение набора действий с каждым элементом определённого множества (массива, хэш-таблицы или данных с подобной структурой организации). Их также называют циклами foreach.
Рассмотрим принцип работы циклов в JASS.

Практика

Самый простой цикл в JASS выглядит так:

Структура цикла

loop
    // Действия до проверки выхода из цикла.
    exitwhen b
    // Действия после проверки выхода из цикла.
endloop
Это цикл с выходом из середины. Как уже было оговорено, это - единственный тип циклов, который присутствует в инструментарии JASS.
Как построить общий цикл в JASS:
  • Строка, с которой начинается цикл, должна содержать только ключевое слово loop (в переводе с английского означает "цикл").
  • На строках ниже той, где содержится ключевое слово loop, описываются действия, которые нужно сделать хотя бы один раз (до проверки условия выхода).
  • После этих действий на новой строке указывается ключевое слово exitwhen (в переводе с английского означает "выйти, когда") с хотя бы одним знаком пробела после него.
  • Сразу после ключевого слова exitwhen ставится условное выражение или значение типа boolean. Это и есть то условие, которое окончит цикл, если его результат будет значением true (эту строку иногда также называют точкой останова, но не всегда).
  • На строках ниже условного выражения описываются действия, которые ещё нужно совершить после проверки условия цикла до начала следующего витка. Эти действия могут не выполниться ни разу.
  • После указания всех нужных действий на новой строке укажите ключевое слово endloop. Оно будет означать окончание цикла.

В качестве факультативного материала приведу все остальные типы циклов, построенных на базе общего (чтобы Вы сразу узнавали тип цикла в коде JASS).
Вместо b подставляется условие цикла (данные типа boolean). Под переменной i подразумевается целочисленный итератор. Под переменной m - максимальное количество итераций.
» Безусловный цикл
loop
    // Действия внутри такого цикла будут повторяться бесконечно.
endloop
» Цикл с предусловием
loop
    exitwhen b
    // Эти действия могут не выполниться ни разу.
endloop
» Цикл с постусловием
loop
    // Эти действия выполнятся как минимум один раз.
    exitwhen b
endloop
» Цикл со счётчиком с шагом 1
Это тот самый цикл, который изначально реализован в GUI (вспомните действие For each (Integer A) do (Actions)). Теперь Вы будете знать, как он работает.

Реализация через предусловный цикл

loop
    exitwhen i > m
    // Эти действия могут не совершиться ни разу.
    set i = i + 1
endloop

Реализация через постусловный цикл

loop
    // Эти действия выполнятся как минимум один раз.
    exitwhen i == m
    set i = i + 1
endloop
» Цикл просмотра

Цикл просмотра переменной-массива arr

loop
    // Эти действия выполнятся как минимум один раз.
    // К элементу массива обращаться по индексу i, то есть arr[i].
    exitwhen i == 8190 // Максимальный индекс, конечно, может быть и меньше, но в данном случае мы смотрим весь массив вне зависимости от того, сохранены ли под его индексами значения. 
    set i = i + 1
endloop
Теперь Вы знаете, как работать с циклами.

Послесловие

Эта статья дала Вам всю необходимую базу для того, чтобы программировать на языке JASS.
Что дальше?
Улучшайте свои навыки. Читайте другие статьи, объясняющие, как работают функции ядра WarCraft III и как их правильно использовать. Благодаря той базе, которую Вы узнали здесь, Вы сможете легко понять тот код и те слова, которые там присутствуют.
Если у Вас есть претензии, предложения и дополнения для данной статьи, напишите их в комментариях. Помните: настоящий опыт и настоящие знания никогда не даются легко.
Используйте модуль вопросов и ответов - в этом нет ничего зазорного. Каждый ошибается и чему-то учится у других или у себя.
» Интересные факты о разработке статьи
  • На написание статьи ушло 7 дней, 9 чашек кофе, 2 чашки чая, 43 печеньки, 5 булочек, 1 рогалик и 14 прогулок на свежем воздухе.
  • Статья была написана на ноутбуке модели Samsung Q45, работающем на базе операционной системы Debian 8.
  • Статья (включая этот раздел) содержит 82 ссылки на Википедию, 10 ссылок на XGM и 6 ссылок на сторонние ресурсы. Таким образом, ссылочный объём статьи - 97 ссылок.
  • Статья изначально предназначалась для инновационного ресурса "Полезная Информация" (musitup.com), однако, так как открытие ресурса затянулось, была также опубликована на XGM в качестве ресурса-зеркала, потому что может быть полезна.
  • Во время написания статьи ни WarCraft III, ни какого-либо интерпретатора JASS под рукой не было. Вся информация получалась логическим путём при помощи перепроверки суждений в соседних статьях.
  • Статья использует около 80% возможностей разметки txt2, используемой в пределах XGM.
  • Для написания статьи использовался текстовый редактор Kate.
» Список изменений статьи
v1.01 от 26.06.2017
За критику, которая улучшила статью, огромное спасибо пользователям Clamp и DracoL1ch.
  • Переработаны разделы вступления и назначения статьи для достижения большей претенциозности.
  • Тщательно откорректирован ликбез об уровнях и типах языков программирования, в частности определения Си, транслятора, компилятора и интерпретатора. Также указано, что JASS - интерпретируемый язык программирования.
  • Абзац про Return Bug убран за ненадобностью.
  • В преимуществах JASS откорректирован текст, описывающий, почему высокий уровень языка - это преимущество.
  • Абзац о работе с памятью как недостатке языка убран.
  • Дано правильное пояснение про то, как trackable соотносится с интерфейсом игры.
  • Прямо определены родительские типы базовых типов JASS.
  • Убрана неуверенность в определении типов JASS как ссылок.
  • Введено понятие задания переменных в рамках понятия присваивания их значения, а также принципа его работы.
  • Введено понятие именования переменных.
  • Улучшено и облегчено для понимания определение обнуления переменных.
  • Переработано определение локальных переменных, убран прямой ввод понятия функции.
  • Переработано определение глобальных переменных, добавлено объяснение работы с ними.
  • Переработано описание массивов, выставлена строгая иерархия понятий.
  • Введено понятие размерности массивов, даны развёрнутые сопутствующие определения.
  • В практикуме по переменным пример с локальной переменной разбит на два - объявление и присвоение.
  • В практикум по переменным добавлены пояснения для объявления глобальных переменных.
  • Строковому типу добавлено пояснение о лимите в 1013 символов.
  • Улучшено определение скрытых функций, устранены разночтения.
  • Дано уточнение, что функция после возврата значения не останавливается, а прерывается.
  • Добавлен раздел "операции с типами".
  • Улучшено определение типа boolexpr в соответствии с найденным материалом.
v1.00 от 23.06.2017
  • Первая редакция статьи
Спасибо за внимание.
Всегда Ваш,
Singularity, 23.06.2017

Просмотров: 1 615

» Лучшие комментарии


Clamp #1 - 5 месяцев назад (отредактировано ) 2
Немного позанимался вычиткой без особого пристрастия, результаты ниже. Обращал внимание только на фактические ошибки, неоднозначные формулировки и некоторые стилистические моменты, грамотность не вычитывал.

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

» Собственно, фидбэк
WarCraft III написан на языке программирования С - это язык низкого уровня.
Си - это всё-таки язык высокого уровня, если придерживаться строгой классификации. На нём действительно можно писать на низком уровне абстракции, но, тем не менее, Си был создан как компилируемый язык, что по факту исключает возможность называть его языком низкого уровня.

Так как он основывается на работе с событиями, это событийно-ориентированный язык программирования.
Предложение сформулировано таким образом, что читателю остаётся непонятно, про Си идёт речь, или про JASS. Если первое, то замечу, что Си - процедурный язык программирования, и нативного функционала работы с событиями (event subscription) в нём нет вовсе. Ближайший родственный язык, в котором в нативно присутствует функционал СОП - C#.

Эти переводчики называют трансляторами. Если они при переводе проверяют, может ли выполниться каждый отдельно взятый кусочек программы, их называют интерпретаторами (очень напоминает процесс синхронного устного перевода с одного языка на другой). Если они осуществляют сборку без проверки её работоспособности, их называют компиляторами.
Интерпретатор считывает исходный код программы и выполняет его, причём преобразование исходного кода в бинарный и выполенение выполняется построчно (исполняют полученный скрипт, не изменяя его). Компиляторы же полностью переобразовывают исходный код программы в бинарный, который целевая система может выполнять самостоятельно (создают исполняемый файл). Подавляющее большинство компиляторов при работе производит поиск ошибок в коде, выдавая их списком после завершения компиляции.
Если говорить в отношении "чистого" JASS, то это скриптовый язык, и, следовательно, он именно интерпретируемый. Для избежания разночтений поясняю: в архиве карты файл скрипта хранится в неизменном виде, выполняется целевой системой (JASS VM) дословно и построчно, и, само собой, ни в какой машинный код не преобразуется. Существует способ реализации в JASS чего-то вроде ассемблерных вставок (принудительного исполнения определённым образом сохранённого в скрипте байт кода), но это совсем другая история.
Советую в соответствии с этим замечанием пересмотреть некоторые формулировки в статье, чтобы не сбивать читателей с толку. В отношении JASS, возможно, это значения не имеет, но сам по себе момент достаточно принципиальный, и неверное его понимание в будущем может выстрелить читателю в колено.

Встроенное средство в World Editor занимается компиляцией JASS.
Исключительно проверкой кода, причём, насколько мне известно, посредством запуска внутриигрового интерпретатора. Остальное разобрано выше.
На такие ошибки, как бесконечные циклы или десинхронизация код не проверяется.
Не рекомендовал бы ставить подобные примеры в один ряд: причиной появления бесконечные циклов почти всегда является ошибка в коде или алгоритме, а причиной появления десинхронизации служит исключительно неосведомлённость картодела о механике синхронизации игровых данных ввиду некоторых неочвидных особенностей её работы.

Подобные недочёты компилятора World Editor широко использовались взломщиками.
Не указано, какие "подобные недочёты" имеются в виду. Если это про ошибки из предыдущего абзаца, то замечу, что RB работал из-за совершенно иного недочёта разработчиков игры и был весьма топорно заткнут в патче 1.24. Популярный сейчас Memory Hack технически эксплуатирует ту же самую ошибку.
RB в далёком прошлом, не вижу смысла о нём вообще упоминать. Кроме того, не вполне понятно, как это упоминание соотносится с названием спойлера, в котором находится: "Краткий ликбез о типах и переводах языков программирования".

Структуру кода на JASS под силу понять даже новичку в программировании.
Не согласен, понятность кода зависит только от того, как он написан, независимо от ЯП (разумеется, если это не ASM).

Обработка команд языка движком игры - тайна за семью печатями.
Абсолютно нет, всё давно разобрано по винтикам, доходило даже до абсурда: та самая ошибка в работе JASS VM, которая позволяла исполнять произвольный код на машине игрока, была окончательно исправлена только после того, как парень с хайва написал им письмо с указанием, где именно и каким образом её стоит исправлять, с огромным описанием технических деталей.

Недостатки
Отсутствие поддержки работы с памятью
  1. Она условно есть, просто появилась случайно
  2. Реальная необходимость работать напрямую с памятью в WarCraft не для хака функционала, который "забыли" добавить в нативки отсутствует => это сложно считать недостатком, скорее просто характеристикой

trackable - особый случай, но он не касается игрового интерфейса
Практически единственное адекватное применение trackable - как раз-таки создание альтернативного внутриигрового UI.

После небольшого опыта работы с JASS у меня появилось такое подозрение, что в JASS переменные - это не сами области памяти, а ссылки на эти области
Так и есть, поскольку JASS-скрипт исполняется через VM.

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

В общем, массив - это несколько переменных, объединённых в таблицу в одной переменной для удобства.
ЕМНИП, там принципиально различается подход к использованию памяти, это не только удобство.

Это действие также называется инициализацией переменной.
Крайне советую разбить пример кода на
local integer myInteger
и
set myInteger = 0
чтобы пояснить разницу между объявлением переменной и её инициализацией. Технически, в JASS это практически одно и то же, но концептуально это разные вещи. Кроме того, кейворд local имеет смысл только после введения в контекст понятия "функция", так как вне её оно не может быть использовано.

Также имя переменной должно быть уникальным <...> переменная и функция не может иметь одно и то же имя
Уточнение - только в одном неймспейсе, в противном случае будет использоваться переменная с наименьшей областью видимости. Тут могу ошибаться, так как не использую "чистый" редактор. В моей сборке код из-под ката прекрасно сохраняется.
» код
globals
integer Test=0
endglobals

function TestFunc takes nothing returns nothing	// Если поменять название на "Test", то возникает конфликт с локалкой, но не с глобалкой.
local integer Test=12
endfunction
То есть, глобальная переменная может иметь одинаковое имя с функцией. Ну и да, опять-таки, слово "функция" используется раньше пояснения, что это такое. Читатель-то не компилирует, а интерпретирует текст, построчно. =)

Такие функции часто называют "нативками" в честь приставки native, используемой для таких функций.
Native - родной, функции называют так в силу того, что они предоставляются вместе с языком на правах базовых, и их фактическая реализация находится на более низком уровне абстракции.
И серьёзно, 11я ссылка на одну и ту-же статью в википедии. Мне как читателю стало понятно личное отношение автора к проприетарному софту ещё на таблице, и даже там повторения было избыточными.

Постоянная функция
ЕМНИП, константы в JASS - это именно константы, а не константные функции. Могу ошибаться.

Помните, что после возврата значения исполнение функции останавливается.
Лучше написать "прерывается", так как остановка может подразумевать продолжение исполнения в дальнейшем.

Сравнивает два значения одинаковых типов.
Можно вынести перед таблицей и не повторять для каждого элемента.


В целом - весьма здорово написано, видно, что автор вложил довольно-таки много труда, надеюсь, что мой фидбек поможет сделать статью ещё лучше.
nvc123 #2 - 5 месяцев назад 4
слишкам сложна и многа букаф
имхо кмб нужен чтобы научить основам полного нуба
здесь же текст рассчитан на человека знакомого с программированием и терминологией
причём ничего нового для таких людей эта статья не рассказывает
DracoL1ch #3 - 5 месяцев назад 4
красиво, полно, но для молодых бойцов - это все равно что "духов" кинуть в горячую точку. куча излишних уточнений. Кстати, начального значения у переменных нет - они считаются неинициализированными и завалят выполнение при доступе. У массивов это решается тем, что они генерируются при объявлении, поэтому хотя бы в заполенные нулями адреса ты всегда попадешь.
ну и про утечки памяти вновь криво сказано. Не страшно, если ты хендл не освободишь, забыл зануллить. Страшно, если ты не уничтожишь объект, который не планируешь использовать.
Singularity #4 - 5 месяцев назад 0
Clamp,
Приятно видеть серьёзный подход человека к критике.
Моё предложение - спрятать это критическое резюме под кат, потому что, как по мне, не у каждого пользователя есть желание скроллировать вниз критику, которая и касается-то только автора статьи и интересна (кроме автора, разумеется) лишь узкому кругу лиц.
» Мой разбор Вашего резюме
Из персонального фидбэка могу назвать только лирические вставки не по теме (например, про "такой подход - путь к деградации") со ссылками лурк-стайл (в таблице далее - ещё и многократно повторяющиеся), что не очень вяжется с претенциозностью (в самом лучшем смысле) остальной статьи.
Эти вставки предназначались только для того, чтобы немножко поднять настроение автору и этим дать ему мотивацию к продолжению работы над статьёй, а вот почистить их по готовности статьи автор забыл. Прошу прощения.
Си - это всё-таки язык высокого уровня, если придерживаться строгой классификации. На нём действительно можно писать на низком уровне абстракции, но, тем не менее, Си был создан как компилируемый язык, что по факту исключает возможность называть его языком низкого уровня.
Мой явный промах. Свою базу по уровням языков программирования я брал из этой статьи на сайте Tproger. Не понимал до неё, куда конкретно изначально нужно было классифицировать Си (инструментов из разных уровней в нём ну слишком уж много) и решил руководствоваться этим источником.
После Вашего замечания переделал абзац. Теперь мне благодаря Вам окончательно ясно, как чётко структурировать ЯП-ы по уровням.
Предложение сформулировано таким образом, что читателю остаётся непонятно, про Си идёт речь, или про JASS. Если первое, то замечу, что Си - процедурный язык программирования, и нативного функционала работы с событиями (event subscription) в нём нет вовсе.
Разве этот функционал традиционно называют не event handling, а функции не event handler'ами? В любом случае, формулировка уточнена.
Интерпретатор считывает исходный код программы и выполняет его <...> Компиляторы же полностью переобразовывают исходный код программы в бинарный, который целевая система может выполнять самостоятельно (создают исполняемый файл). <...>
Если говорить в отношении "чистого" JASS, то это скриптовый язык, и, следовательно, он именно интерпретируемый. <...>
Советую в соответствии с этим замечанием пересмотреть некоторые формулировки в статье, чтобы не сбивать читателей с толку. В отношении JASS, возможно, это значения не имеет, но сам по себе момент достаточно принципиальный, и неверное его понимание в будущем может выстрелить читателю в колено.
Соглашусь. Конкретизация понятий компилятора и интерпретатора сделала статью более простой для понимания. Надеюсь, что теперь, после редактуры, эти две разновидности трансляторов объяснены правильно.
Не указано, какие "подобные недочёты" имеются в виду. Если это про ошибки из предыдущего абзаца, то замечу, что RB работал из-за совершенно иного недочёта разработчиков игры и был весьма топорно заткнут в патче 1.24. Популярный сейчас Memory Hack технически эксплуатирует ту же самую ошибку.
В новой формулировке этот абзац не нужен вовсе. Так что я его убрал.
Не согласен, понятность кода зависит только от того, как он написан, независимо от ЯП (разумеется, если это не ASM).
Полемично, но логично. Здесь речь шла не о коде, а о его структуре. Но в целях того, чтобы читателя не путать и не дезинформировать, переработал предложение.
  1. Она условно есть, просто появилась случайно
  2. Реальная необходимость работать напрямую с памятью в WarCraft не для хака функционала, который "забыли" добавить в нативки отсутствует => это сложно считать недостатком, скорее просто характеристикой
Убедительно. Убрал это из списка недостатков.
Практически единственное адекватное применение trackable - как раз-таки создание альтернативного внутриигрового UI.
Ну... да, этой конкретизации в абзаце действительно не хватало.
Так и есть, поскольку JASS-скрипт исполняется через VM.
Спасибо огромное за уточнение! Теперь можно убрать эту неопределённость.
Освобождается при смерти потока, но только при условии, что переменной не задано значение. Базовые типы прямо наследуются от типов из Си, поэтому с ними работает его сборщик мусора, и обнулять их не требуется (в статье Скорпа были уточнения на этот счёт).
Очень не хотелось нагружать новобранцев такими понятиями, как потоки (однопоточность и многопоточность), синхронное и асинхронное программирование. Старался упростить.
ЕМНИП, там принципиально различается подход к использованию памяти, это не только удобство.
После внимательного перечитывания организации памяти массивных и немассивных переменных могу только согласиться. Да и объяснение того, что это такое, благодаря Вашему замечанию, теперь в разы понятнее, чем было до редактуры.
Крайне советую разбить пример кода <...> чтобы пояснить разницу между объявлением переменной и её инициализацией. Технически, в JASS это практически одно и то же, но концептуально это разные вещи. Кроме того, кейворд local имеет смысл только после введения в контекст понятия "функция", так как вне её оно не может быть использовано.
Видите ли, здесь такое дело. Получается бесконечный цикл между переменными и функциями. Понимаю, что понятие функции, введённое до его определения - это структурная ошибка в статье, но как её исправить, когда только писал статью, был без понятия.
Помимо этого, в классическом недиалектовом JASS прямого объявления глобальных переменных без приставки udg_ через Редактор нет. Их можно только "дописать" в файл war3map.j в уже сохранённой карте. Хотя, указать в статье наличие такого способа имеет смысл.
Уточнение - только в одном неймспейсе, в противном случае будет использоваться переменная с наименьшей областью видимости. <...> То есть, глобальная переменная может иметь одинаковое имя с функцией. Ну и да, опять-таки, слово "функция" используется раньше пояснения, что это такое. Читатель-то не компилирует, а интерпретирует текст, построчно. =)
Проверил, переработал этот пункт. Весьма нелепый промах с моей стороны. Насчёт использования понятия функции до определения уже упомянуто выше. Весьма красивая аналогия о компиляции и интерпретации, кстати.
Native - родной, функции называют так в силу того, что они предоставляются вместе с языком на правах базовых, и их фактическая реализация находится на более низком уровне абстракции.
И серьёзно, 11я ссылка на одну и ту-же статью в википедии. Мне как читателю стало понятно личное отношение автора к проприетарному софту ещё на таблице, и даже там повторения было избыточными.
Конструктивное замечание о native. Исправлено.
Ссылки на статью о проприетарном ПО в Википедии почищены практически все. На тему проприетарного программного обеспечения я ещё напишу в своём дневнике, когда дойдёт дело до того, чтобы его завести.
ЕМНИП, константы в JASS - это именно константы, а не константные функции. Могу ошибаться.
На самом деле, ключевое слово constant в JASS можно применять как к функциям, так и к переменным. Понимая, что в статье не было уделено достаточно внимания объяснению того, как работает это ключевое слово в одном и в другом случае, исправился.
Лучше написать "прерывается", так как остановка может подразумевать продолжение исполнения в дальнейшем.
Очень конструктивное замечание. Стыдно признаться, но до этого сам называл прерывания остановками.
Можно вынести перед таблицей и не повторять для каждого элемента.
Хотелось дать на это акцент, так как читатель в силу своей невнимательности обычно смотрит только в таблицу, а пояснения над ней читает реже. В данном же случае Ваш вариант имеет место быть.
В целом - весьма здорово написано, видно, что автор вложил довольно-таки много труда, надеюсь, что мой фидбек поможет сделать статью ещё лучше.
Он очень помог. Спасибо Вам огромное за то время, которое Вы уделили этому труду!

nvc123,
слишкам сложна и многа букаф
имхо кмб нужен чтобы научить основам полного нуба
Программирование на самом деле по своей природе в разы объёмнее, чем то, что указано в статье, поэтому фактически она и есть полный курс молодого бойца для того, кто хочет освоить JASS.
Эта статья как раз рассчитана на новичка. Чтобы убедиться в этом, изучите её структуру.
здесь же текст рассчитан на человека знакомого с программированием и терминологией
причём ничего нового для таких людей эта статья не рассказывает
У Вас могло сложиться такое мнение, потому что Вы, наверное, читали статью не с самого начала или не вдумчиво, а обзорно. Её концепция состоит в строгой иерархической последовательности ввода терминов, так, чтобы каждый последующий термин исходил из предыдущего. Это и делает освоение JASS простым для тех новичков, которые действительно пришли его изучить.

DracoL1ch,
красиво, полно, но для молодых бойцов - это все равно что "духов" кинуть в горячую точку. куча излишних уточнений.
Приятно видеть то, что Вы действительно вчитывались в детали. Программирование, на самом деле, несколько более интересная штука - она становится горячей точкой всякий раз, когда в порядке терминов пропадает объяснение одного из них. Это ломает всю структуру.
Как её упростить, чтобы статья больше подходила под молодых бойцов? Об этом я ещё думаю и кое-какие мысли уже есть, но их уже буду реализовывать в новой редакции этого материала.
Кстати, начального значения у переменных нет - они считаются неинициализированными и завалят выполнение при доступе. У массивов это решается тем, что они генерируются при объявлении, поэтому хотя бы в заполенные нулями адреса ты всегда попадешь.
Хорошее, конструктивное замечание. Оно попало в список исправлений. У типов, впрочем, значение по умолчанию всегда имеется, а у скаляров - действительно нет.
ну и про утечки памяти вновь криво сказано. Не страшно, если ты хендл не освободишь, забыл зануллить. Страшно, если ты не уничтожишь объект, который не планируешь использовать.
Формулировку утечек исправил.

Примечательно, что никто не заметил отсутствия раздела о возможных действиях с типами данных. Свёл их в одну таблицу и добавил к основному содержанию статьи.
Naadir #5 - 5 месяцев назад 1
Очень годно, дружище.
Статейка хорошая, полноценная.
Но, присоединяюсь к форумчанам из верхних комментариев: она не для молодых, т.к. умы, не закалённые программированием, не будут читать такую здоровенную уйму текста.
Steal nerves #6 - 5 месяцев назад 3
Очень классно, пригодится =))
Я не программист, но неплохо освоил jass, и меня некоторые вещи смущают. Например
Постоянная функция - такая функция, которая может возвращать только постоянные значения, например, чётко указанные числа (такие как 8, 10, 14 и т.п.). Работает быстрее, чем общая функция, но не имеет широкого спектра применения, так как ограничивает работу с переменными.
зачем она тогда нужна, не проще взять переменную?

Пиши еще, можешь, знаешь например про Vjass =)) про него не много написано, даже я мало знаю
nvc123 #7 - 5 месяцев назад 0
Singularity, ты даёшь много информации которая сложна/непонятна для новичков
например раздел родительский тип в таблице
ты и правда думаешь что вася из 5б знает что такое _int32?
вообще в статье много отсылок к c/c++ (видимо ты изучаешь этот язык, скорее всего ты студент либо джуниор т.к. люди постарше в основном акцентируют внимание на алгоритмах и проектировании)
так же термины программирования которые ты дал будут бесполезны для большинства людей т.к. нубы просто хотят запилить спел как в доте и срать они хотели на программирование
а "про" либо забросили джасс давным давно либо работают программистами и знают всю терминологию
Steal nerves, про vjass мануал есть
а про использование vjass в основном в комментариях сказано + несколько статей наподобие этой
а вообще если дорос до vjass то готовые наработки + доки самый лучший способ обучения
alexprey #8 - 5 месяцев назад 0
Не согласен, понятность кода зависит только от того, как он написан, независимо от ЯП (разумеется, если это не ASM).
как человек, который начинал програмировать именно с jass, то он действительно легко переваривается человеком, кто первый раз изучает программирование, потому что каждые конструкции явно прописаны. Да и асм в умелых руках можно оформить так, что читать его будет одно удовольствие :3
DracoL1ch #9 - 5 месяцев назад 1
по долгу мемхака читаю асм и заявляю - это разрыв мозга, особенно когда доходит до фич компиляторов
nvc123 #10 - 5 месяцев назад (отредактировано ) 0
DracoL1ch, фичи компиляторов это всегда разрыв мозга
alexprey, асм легко переваривается если это чтото простое
а если чтото сложное, да ещё отдельные наборы команд не разделены пустой строкой то понять что делает кусок кода довольно сложно (нередко сидишь с бумагой и ручкой и по шагам выполняешь инструкции чтобы понять что делает этот кусок)
хотя сами команды довольно понятны
ну или на любое более менее сложное действие есть подпрограмма (как и должно быть) но этого к сожалению почти никто не делает(
Singularity #11 - 5 месяцев назад (отредактировано ) 2
Naadir:
Очень годно, дружище.
Статейка хорошая, полноценная.
Но, присоединяюсь к форумчанам из верхних комментариев: она не для молодых, т.к. умы, не закалённые программированием, не будут читать такую здоровенную уйму текста.
Дело здесь не в закалке. Задача статьи - дать материал, по которому каждый читатель сможет разобраться, на каких китах JASS стоит и как его использовать. Причём если в данной статье главное - это желание, то во многих других "базовых" статьях о JASS (даже в пределах этого сайта) основных определений не дано вовсе, что в дальнейшем затрудняет общение с воспитанными на них программистами, потому что они "плавают" в терминах.
Скорость обучения в них жертвует его качеством.
» Насчёт уймы текста
Увы, но JASS нельзя полно объяснить более коротко. Как и было написано в статье...
Singularity:
Изучение JASS - времязатратный, но увлекательный процесс.
И помните:
Для изучения языка гораздо важнее свободная любознательность, чем грозная необходимость.
- бл. Августин

Steal nerves:
Очень классно, пригодится =))
Вы только что доказали, что некоторые из комментаторов выше неправы в том, что развивающимся программистам (пусть даже и в WarCraft III) статья не нужно. Огромное Вам спасибо!
Я не программист, но неплохо освоил jass, и меня некоторые вещи смущают. Например <...>постоянная функция -<...>
зачем она тогда нужна, не проще взять переменную?
Проще и лучше взять переменную. Вы правы.
Константные функции почти не используют, но, например, многие native-функции константны, так как получают значения из таблиц.
Один из примеров применения пользовательских константных функций - стандарт для написания заклинаний с открытым исходным кодом (JESP). О нём я расскажу, как найду время, в отдельной статье.
Пиши еще, можешь, знаешь например про Vjass =)) про него не много написано, даже я мало знаю
Планирую сделать такие статейки по наиболее распространённым диалектам: vJASS, cJASS, ZINC, WurstScript. Для плавающих в терминах людей руководство по vJASS, например, написано слишком сложным языком.

nvc123:
ты даёшь много информации которая сложна/непонятна для новичков
например раздел родительский тип в таблице
ты и правда думаешь что вася из 5б знает что такое _int32?
Узко мыслите. Вася из 5-го "Б", желающий узнать, что такое __int32 (два знака нижнего подчёркивания, не один) , всегда справится с этим. Тем более, на каждый из основных типов дана ссылка на Википедию, что существенно упрощает понимание того, что это на самом деле такое.
вообще в статье много отсылок к c/c++ (видимо ты изучаешь этот язык, скорее всего ты студент либо джуниор т.к. люди постарше в основном акцентируют внимание на алгоритмах и проектировании)
В статье много отсылок к C и C++ по одной простой причине - на этих языках (даже больше на C, чем на C++) написан код движка WarCraft III.
Скажу честно - не студент, не джуниор, а просто человек, желающий докопаться и донести результаты личного исследования до других, потому что другие также могут извлечь пользу из него.
Голубчик... Если бы люди постарше действительно делали акцент на алгоритмах и проектировании, ошибок и недоработок в программах было бы намного меньше, чем есть сейчас.
так же термины программирования которые ты дал будут бесполезны для большинства людей т.к. нубы просто хотят запилить спел как в доте и срать они хотели на программирование
Нет и ещё раз нет. Статья таргетирована на вполне определённый диапазон аудитории, так что всё написанное в ней полезно и пригодится. Яркий пример - пользователь, оставивший комментарий до Вашего.
а "про" либо забросили джасс давным давно либо работают программистами и знают всю терминологию
Каждый судит со своей колокольни. Возможно, у "про", о которых Вы ведёте сейчас речь, просто нет времени и они ещё вернутся. Возможно, они просто сделали шаг к саморазвитию, а в этом нет ничего плохого. Возможно, они просто почувствовали, что принесут больше пользы в другой сфере. А Вы говорите, "забросили".
nvc123 #12 - 5 месяцев назад 0
Если бы люди постарше действительно делали акцент на алгоритмах и проектировании, ошибок и недоработок в программах было бы намного меньше, чем есть сейчас.
и с чего это вдруг такие выводы?
AlexSan #13 - 5 месяцев назад 0
Узко мыслите. Вася из 5-го "Б", желающий узнать, что такое __int32 (два знака нижнего подчёркивания, не один) , всегда справится с этим
О чем ты? Посмотри в создающиеся вопросы. Люди не могут простой триггер на ГУИ наклепать, куда им до понимания int32. Я согласен с мнением выше о том что статью толком поймет человек знакомый с программированием. Но для полного новичка это слишком сложно.
Clamp #14 - 5 месяцев назад 0
Можно добавить, что использование кейворда return допустимо также в функциях, которые ничего не возвращают, при таком использовании исполнение функции будет прервано при достижении строки с return. Как правило, используется совместно с условным оператором в теле цикла.
Singularity #15 - 5 месяцев назад 0
О чем ты? Посмотри в создающиеся вопросы. Люди не могут простой триггер на ГУИ наклепать, куда им до понимания int32. Я согласен с мнением выше о том что статью толком поймет человек знакомый с программированием. Но для полного новичка это слишком сложно.
Вот в такой формулировке - да, согласен.
С другой стороны, в JASS нечего делать тем людям, которым сложно даже в триггерах. Полагаю, стоит написать пояснение по этому поводу в начале статьи.
В качестве альтернативы - можно вдаться в то, чем по факту является память и из этого вывести более чёткое определение.

и с чего это вдруг такие выводы?
Качество современных программ, разумеется.

Можно добавить, что использование кейворда return допустимо также в функциях, которые ничего не возвращают, при таком использовании исполнение функции будет прервано при достижении строки с return. Как правило, используется совместно с условным оператором в теле цикла.
И не только это. :)
Есть уже целый крупный список задач по статье, который позволит сделать её лучше. Об этом подробнее напишу позднее.
Как правило, написание ключевого слова return без ничего будет просто сокращённой формой записи return nothing (вроде бы, запись return nothing заставит синтаксический анализатор ругаться).
nvc123 #16 - 5 месяцев назад (отредактировано ) 0
Качество современных программ, разумеется.
я имел ввиду с чего ты взял что правильное проектирование устраняет все баги
в проектировании (практическом а не теоретическом) есть такое понятие как стоимость решения
которое представляет собой соотношение + этого решения к затратам на реализацию
в теоретическом проектировании можно разработать идеальную систему без багов
но на практике архитектура и качество алгоритмов зависят от сроков дедлайна
и основная задача при проектировании это сделать так чтобы:
  1. было как можно меньше багов
  2. код был как можно более модульным и легко расширяемым
  3. можно было написать по за отведённое время
причём 3 пункт является самым важным
avuremybe #17 - 2 месяца назад (отредактировано ) 0
Прочитал здесь что у булина значение по умолчанию == false.
Изменил в своей системе
local boolean b = false
на
local boolean b
и теперь ничего не работает. Вернул назначение false при объявлении - все снова работает.
У него точно значение по умолчанию false, а не null?
IceFog #18 - 2 месяца назад 2
avuremybe, при обращении к неинициализированной переменной, поток выполнения обрывается.
Исключение — массивы, элементы которых имеют значения по-умолчанию.
avuremybe #19 - 2 месяца назад 0
IceFog, то есть он приобретет false по умолчанию только в случае, если это элемент массива, а не самостоятельная переменная?
IceFog #20 - 2 месяца назад (отредактировано ) 0
то есть он приобретет false по умолчанию только в случае, если это элемент массива, а не самостоятельная переменная?
Да.
Что касается обычной переменной, думаю, в памяти игры у неё значение 0 (false), но это не важно, так как игра не даст его прочитать.
Поэтому, необходимо явно задавать их значение перед использованием.
avuremybe #21 - 2 месяца назад 0
IceFog, спасибо.
Хорошо бы внести эти пояснения в статью. Все таки для новичков статья.
IceFog #22 - 2 месяца назад 0
Хорошо бы внести эти пояснения в статью.
Там об этом написано.
При объявлении скаляра ему не присваивается начальное значение его типа. Инициализировать его нужно отдельно, при этом, если его не инициализировать, выполнение программы остановится при чтении неинициализированного скаляра.
Массивная переменная - несколько скаляров, расположенных в памяти друг за другом. Эти скаляры называются элементами массива, а их количество называется размером массива.
Каждому элементу массива присваивается начальное значение его типа, то есть элементы массива в отличие от скаляров инициализируются при объявлении массива.