[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 2
  • 1
  • 2
  • »
Модератор форума: ZLОY  
Форум карты Жизнь на Арене » Картостроение » Творцы миров » [Jass]Пособие к Jass (special for Цезарь)
[Jass]Пособие к Jass
perimetralДата: Суббота, 07.11.2009, 14:46 | Сообщение # 1
Знаток Jass
Группа: Тестеры
Сообщений: 332
Репутация: 14
Статус: Offline
Написана Глава 11 - Как это делает we. Еще не до конца, но все же.
Дописана Глава 11 - Как это делает we. Почти до конца
НЕ КОММЕНТИТЬ ПОКА Я НЕ НАПИШУ, ЧТО ЗАКОНЧИЛ!
Статья для тех, кто знает триггеры.
В общем, так. Я решил исходя из собственных знаний написать обучающее пособие по jass для всех обитателей этого форума. Карантин все таки продлили, так что времени - много и еще чуть-чуть. Зачем? Чтобы ЖнА развивалась еще быстрее и становилась еще лучше =) Итак, начнем.

Глава 1. Что такое jass?
JASS - скриптовый язык программирования, при помощи которого движок wc3 динамически управляет игрой. Все триггеры, которые вы напишете на GUI ("обычные" триггеры) в последствии все равно преобразуются в jass. Но, как я уже упоминал в соседнем топике, делается это убого. Так почему бы не сделать лучше? Преобразовать триггер в jass вы можете в РТ (редактор триггеров), выбрав в меню "Правка"/"Конвертировать в текст". Есть еще нестандартный код карты - он объявляется сразу после объявления глобальных переменных (об этом ниже), то есть его можно использовать в любом месте другого кода.

Глава 2. Типы.
В jass есть 6 типов данных. Из них 4 - скалярные (просто запомните это слово), а 2 - указатели. Скалярные:
integer - целое число (4, 18, 347834, -7, 0 и т.д.)
real - число с плавающей точкой (0.2, 58.61, -99.4 и т.д.)
string - строка произвольных символов ("лалала", "кагебуншин" и т.д.)
boolean - логическое. То есть "да" или "нет" (true или false)
Указатели:
code - указатель на другой код (функцию). То есть ссылка на эту функцию
handle - это супертип в прямом смысле. Все юниты, мультибоарды, точки, эффекты и остальное (то есть все, что не есть скалярным типом и не есть ссылкой на функцию) - это handle.
Теперь подробней:
integer - бывают восьмеричными (чтобы написать число в 8ричной СИ, добавь в начале 0: 025, 031, 07 и т.д.), десятеричными (ну это обычные числа, которые мы привыкли видеть), шестнадцатеричными (чтоб написать, добавь в начале числа 0x: 0x5d2, 0x3ac, 0xbb761 и т.д.) и 256ричными (заключи число в одинарные кавычки, чтоб написать его в 256ричной СИ: 'A00i', 'J72Z' и т.д.).
В восьмеричных числах переход в другой разряд происходит при достижении восьмой единицы (когда в десятеричных - при достижении десятой единицы). То есть в числах 8ричной СИ не должно быть восьмерки - вместо 028 надо писать 030. Надеюсь, ясно. В 16ричных числах цифры после девятки заменяются первыми шестью буками латинского алфавита (10==a, 11==b ... 15==f). То есть разряд увеличивается при достижении числа, равного (f+1). 256ричную СИ я еще не выучил, поэтому просто знайте, как записывать такие числа вот и все =).
real - они бывают только десятеричными (позже вы поймете, почему). Кстати, будьте осторожны, при преобразовании real в integer, WE (world editor) не округляет их в случае чего, а просто "отрезает" то, что справа от запятой. Это может помешать при точных расчетах.
string - во-первых, пишутся они только в двойных кавычках. Во-вторых, при преобразовании строки в integer или real, будет взято первое совпадение (то есть например у нас есть строка "24 мужика выпили 50 банок пива" - так вот при преобразовании получится именно 24, про полтинник все забыли). Как это обойти - см. ниже.
boolean - бывает только в двух состояниях - true и false. То есть как рычажок. Они служат для определения некоторых условий. Например: мертв ли тот или иной юнит, бежит ли в данный момент герой, или нет и т.д..
code - ну это, собственно, ссылка, как я уже говорил, на другую функцию. Как в интернете =). Главное, что функция, на которую ссылаются, должна быть выше по коду той, в которой ссылаются (логика).
handle - по моему, самый интересный тип. Это "батя" всех остальных нескалярных типов. Юнитов, декораций, рельефа, точек и т.д. и т.п.. Его работу мы еще не раз заденем ниже.

Глава 3. Триггер в jass.
Попробуйте создать пустой триггер в GUI (то есть как вы привыкли). Без событий, условий и действий. Назовите его mytrig Преобразуйте его в jass. Получится такое:

Code
function Trig_mytrig_Actions takes nothing returns nothing
endfunction

//==================================================
function InitTrig_mytrig takes nothing returns nothing
                     set gg_trg_mytrig = CreateTrigger(  )
                     call TriggerAddAction( gg_trg_mytrig, function Trig_mytrig_Actions )
endfunction

Разберем каждую строку здесь.
Code
function Trig_mytrig_Actions takes nothing returns nothing
- это объявление функции. Мы их изучим ниже, но пока усвоим основы: функция объявляется ключевым словом function. Далее через пробел идет название функции - оно может быть произвольным, содержать любые цифры, латинские (проще - английские) символы и знак подчеркивания "_". Не может начинаться с цифры или знака подчеркивания. Дальше через пробел идет ключевое слово takes и через еще один пробел список принимаемых ф-й параметров. В случае отсутствия таковых, стоит слово nothing. Дальше через пробел слово return и после него чз пробел тип возвращаемого ф-й значения. Тут тоже стоит nothing, если ф-я ничего не возвращает. Пока этого хватит.
Code
endfunction
Это слово обозначает конец ф-и. Запимните: нельзя объявлять одну ф-ю внутри другой. Первая обязательно должна закончиться прежде, чем начнется вторая.
Code
//=== и т.д.
Это комментарий. Он объявляется после двойного слеша "//" и до конца строки. Тут может что угодно - он не повлияет на ход действия кода. То есть вместо знаков "=" тут могут быть ваши пояснения к коду для самих себя (чтоб не забыть) или для других (например, вы делаете спелл для кого-то и т.д.). Комментарий можно объявлять в конце любой строки. Например:
Code
endfunction //конец функции
- тут нет ошибки, ведь комментарий объявлен в конце строки.
Code
function InitTrig_mytrig takes nothing returns nothing
Это объявление триггера. Эта ф-я должна быть в каждом триггере причем в его конце. Даже если в ней нет действий, она должна присутствовать.
Code
    set gg_trg_mytrig = CreateTrigger(  )
Здесь мы присваиваем переменной gg_trg_mytrig значение. Это делается с помощью ключевого слова set. После него чз пробел идет название переменной, которой присваивается значение, еще чз пробел знак "=" и еще чз пробел само значение. Здесь значение берется из ф-и CreateTrigger(). То есть эта ф-я возвращает значение. И мы используем это, чтоб присвоить это значение нашей переменной. Если значение берется из ф-и, в конце ее названия должны стоять скобки с параметрами, передаваемыми ф-и. Если их нет, скобки все равно должны стоять - даже пустые.
Code
    call TriggerAddAction( gg_trg_mytrig, function Trig_mytrig_Actions )
А вот тут мы непосредственно вызываем ф-ю. То есть приказываем движку игры выполнить то, что записано в теле вызываемой ф-и. Для этого служит ключевое слово call, а чз пробел - имя вызываемой ф-и с передаваемыми ей параметрами в скобках. Здесь параметрами служат переменная gg_trg_mytrig типа trigger (хотя на самом деле это тип handle) и ссылка на ф-ю типа code. Заметьте, что в переменной типа trigger уже есть значение - мы его ей присвоили строкой выше. Это вшитая в движок wc3 ф-я, добавляющая триггеру(в данном случае - триггеру gg_trg_mytrig) действия, перечисленные в ф-и, которая передается в качестве второго параметра. А ведь в качестве второго параметра передается та самая первая пустая ф-я в самом начале нашего триггера. Надеюсь, теперь вы поняли, куда надо совать действия, которые выполнит этот триггер? Правильно, в ф-ю Trig_mytrig_Actions. Осторожно: при передаче ф-и в качестве параметра другой ф-и, после ее названия не надо ставить скобки - мы же ее не вызываем.
Думаю, второй endfunction уже не надо описывать - он "закрывает" ф-ю InitTrig_mytrig.

Глава 4. Переменные.
Сразу разделю для вас два понятия: глобальные переменные (глобалки) и локальные переменные (локалки). Глобалки - это те переменные, которые вы привыкли создавать ранее - в редакторе переменных. К глобалкам можно получить доступ из любой части кода, т.к. они объявляются самыми первыми. Но у них есть и недостатки: они медленнее локалок в 2-3 раза (кому надо доказательства - ищите на xgm), и при использовании глобалок у вас наверняка появятся конфликты несовместимости (см. ниже). Локалки - это такие переменные, которые объявляются в самом начале ф-и с помощью ключевого слова local. Далее чз пробел следует тип переменной и еще чз пробел ее имя. Подчиняется тем же законам именования, что и ф-и. Дальше по усмотрению можно сразу присвоить ей значение знаком "=" и, собственно, значением. Как присваивать переменным значения, мы уже рассмотрели. Например:

Code
local integer i = (6 * 2)

Code
local integer i
set i = (6 * 2)

Эти два куска кода сделают одно и то же действие: создадут переменную i типа integer и присвоят ей значение (6 * 2). Заметьте, тут можно писать любые арифметические действия, но они должны быть в скобках.
Область видимости любой локалки (те места кода, откуда можно узнать ее значение, присвоить ей новое и т.д.) - ее ф-я. То есть ф-я, в которой она объявлена. Также можно передать локалку в качестве параметра при вызове другой ф-и, но тогда вы передатите на саму локалку, а ее значение, а в принимающей ф-и неявно будет объявлена одноименная локалка с таким же значением. То есть локалка не выйдет за пределы своей области видимости, а просто будет "клонирована".
Зачем нужны локалки? Суть их в том, чтобы упростить написание кода и ускорить его работу. Например вот код:
Code
call ModifyHeroStat( bj_HEROSTAT_STR, GetKillingUnitBJ(), bj_MODIFYMETHOD_ADD, 10 )
call ModifyHeroStat( bj_HEROSTAT_AGI, GetKillingUnitBJ(), bj_MODIFYMETHOD_ADD, 10 )
call ModifyHeroStat( bj_HEROSTAT_INT, GetKillingUnitBJ(), bj_MODIFYMETHOD_ADD, 10 )
Ф-я ModifyHeroStat() увеличивает/уменьшает любой параметр (сила/ловкость/разум) указанного героя. Первый параметр - собственно, изменяемая характеристика (bj_HEROSTAT_STR - сила, например), второй параметр - герой, характеристики которого подлежат изменению, третий параметр - будем ли мы добавлять/отнимать/явно устанавливать значение характеристики, четвертый параметр - собственно, значение (сколько?). Так вот тут трижды вызывается ф-я GetKillingUnitBJ(), возвращающая убившего юнита. А одноразовый вызов ф-и в 5-7 раз медленнее (истоник - xgm) обращения к переменной. Так вот лучше сначала занести значение, возвращаемое этой ф-й в локалку типа unit (не забывайте, что на самом деле это handle), а потом не вызывать ф-ю повторно, а обращаться к локалке. Смотрите:
Code
local unit u = GetKillingUnitBJ()
call ModifyHeroStat( bj_HEROSTAT_STR, u, bj_MODIFYMETHOD_ADD, 10 )
call ModifyHeroStat( bj_HEROSTAT_AGI, u, bj_MODIFYMETHOD_ADD, 10 )
call ModifyHeroStat( bj_HEROSTAT_INT, u, bj_MODIFYMETHOD_ADD, 10 )
set u = null
Код вырос на 2 строки, а производительность - в 10-14 раз.
Что мы сделали? Во-первых, объявили локалку типа unit с именем u и значением, которое нам вернула ф-я GetKillingUnitBJ(). Дальше мы заменили в коде все вызовы этой ф-и на переменную u. В конце мы обнулили локалку u, присвоив ей значение null, что значит "пусто". Обнуление освободит память, занимаемую переменной для дальнейшего использования. Зачем нам значение в переменной u, которое будет занимать память, когда переменной уже нет? А ведь при каждом вызове ф-и создается свой набор локалок, объявленных в этой ф-и и при ее завершении он удаляется. Но значения нам приходится удалять самим - недоработка Blizz'ов, или, возможно так и надо. В любом случае сразу скажу, что обнулять надо только указатели. То есть нет никакой необходимости обнулять integer, real, string и boolean. Это все-таки делается движком игры. А вот code и handle (а следовательно и unit и все остальное) нам надо обнулять вручную - присвоением таким локалкам пустых значений.
Именование глобалок. Все глобалки в jass именуются по другому: в начале заданного имени добавляется приставка "udg_". Это сделано, чтобы можно было различить глобалки и локалки в ф-ях, а также, чтоб не создавались конфликты имен. Например: у вас есть глобалка Caster и вот вы написали какую-то ф-ю, в которой объявляется локалка Caster. Так вот если б к глобалкам не добавлялся префикс "udg_", возникла бы синтаксическая ошибка. Запомните это правило, ведь при обращении к глобалкам в jass вам придется явно дописывать префикс.

Глава 5. Функции.
Пора подробней рассмотреть устройство ф-и как таковой. Как ее объявлять, вы уже знаете, но все же внесу некоторые коррективы. Для начала - принимаемые параметры. Они перечисляются после ключевого слова takes чз запятую с указанием типа и имени параметра. Возвращаемое значение указывается после ключевого слова returns, а точнее указывается его тип. Например:

Code
function MyFunc takes integer i1, integer i2, real r1 returns real
local integer sq = (i1 * i2)
local real ee = (I2R(sq) - r1)
return ee
endfunction
Эта ф-я создаст локалку sq, равную перемноженным между собой первым принимаемым параметром и вторым. Также будет создана локалка ee, равная разнице локалки sq и третьего принимаемого параметра. Локалка ee и будет возвращена с помощью ключевого слова return.
Подробности: типы принимаемых параметров должны соответствовать в ф-и с ними и при вызове этой ф-и. Тип возвращаемого параметра должен соответствовать типу принимающей переменной (см. ниже). Дальше - мы использовали "родную" (вшитую в движок) ф-ю I2R(), чтобы преобразовать тип integer в тип real. Ф-я I2R() берет 1 параметр - число integer - и возвращает получившееся число real. Заметьте, что вызов ф-и произведен внутри арифметического д-я, что вполне допустимо. Как использовать нашу ф-ю MyFunc? Вот пример (допустим, что в этом примере уже есть наша ф-я MyFunc):
Code
function Result takes nothing returns nothing
local real res = MyFunc(5, 8, 21.5)
endfunction

Здесь мы создаем локалку res, которой присваивается значение, возвращаемое ф-й MyFunc при параметрах 5, 8, 21.5. Заметьте, что если в MyFunc первый принимаемый параметр - integer, то и передаваться ей при вызове должен integer, если третий параметр - real, то и передаваться должен real. Локалка res имеет тип real, т.к. ф-я MyFunc возвращает real. В итоге в данном случае в локалке res будет 18.5. То есть ф-я MyFunc перемножила 5 и 8 (первые 2 параметра), потом привела результат к типу real (с помощью ф-и I2R) и нашла разницу результата и 21.5, что равно 18.5. Вы можете при вызове ф-и MyFunc подставить любые другие числа и проверить результат. Главное, чтобы типы чисел совпадали с типами, указанными в MyFunc. Вы можете поменять тип третьего принимаемого значения на integer - тогда вам надо будет немножко по-другому оформить арифметические д-я и переместить вызов ф-и I2R в другое место. Попробуйте сами найти выход из этого положения. Это д/з =)



Таур
Оберокодаро


Сообщение отредактировал perimetral - Понедельник, 09.11.2009, 03:26
 
perimetralДата: Суббота, 07.11.2009, 15:42 | Сообщение # 2
Знаток Jass
Группа: Тестеры
Сообщений: 332
Репутация: 14
Статус: Offline
Глава 6. Циклы.
Циклы используются для повторения одного и того же д-я в заданном интервале значений. Более понятно: цикл выполняется по кругу несколько раз, пока правдиво условие цикла. Лирический пример: нам надо создать для каждого игрока по юниту в заданной точке. Допустим, у нас всего 10 игроков. Тогда условием цикла будет (i<=10), где 10 - количество игроков, i - переменная типа integer, которая изначально равна 1. Она также является счетчиком цикла - то есть при каждом выполнении она изменяется алгоритмически. В нашем случае она просто будет увеличиваться на 1. То есть при первом выполнении цикла она будет равна 1, при втором - 2 и т.д.. В данном случае изменять счетчик цикла надо в его конце (чтоб все зависящие от счетчика д-я выполнились с его исходным значением). Более понятно: если i изначально равна 1 и мы будем изменять ее в начале цикла, то всю оставшуюся итерацию (один "проход" цикла по своим д-ям) она будет равна 2м. То есть на первой итерации она будет равна 2м, а должна быть равна 1. В нашем случае i - номер игрока, для которого создается юнит. То есть на первой итерации юнит будет создан для первого игрока, при второй - для второго и т.д.. Если установить условие цикла (i<=5), то будут созданы только 5 юнитов (подумайте, почему). Если при условии цикла (i<=5) увеличивать счетчик цикла на 2, то будет создано 5 юнитов для игроков 1, 3, 5, 7 и 9 (и тут подумайте, почему).
В jass цикл объявляется так:
Code
loop
exitwhen (i>10)
endloop
Тут loop - ключевое слово, объявляющее начало цикла; exitwhen - ключевое слово, объявляющее обратное (см. ниже) условие цикла в скобках после пробела; endloop - думаю, сами догадаетесь, что это значит =). На счет обратного условия: все дело в том, что в jass цикл выполняется не до тех пор, пока правдиво какое-то условие, а до тех пор, пока не станет правдивым указанное после exitwhen. То есть, в данном случае цикл прекратит свою работу, когда i станет больше 10. Вместо знака ">" (больше) можно также использовать знаки "<" (меньше), "<=" (меньше или равно), ">=" (больше или равно), "==" (равно) и еще "!=" (не равно). Приведенный выше цикл никогда не начнется - мы не объявили локалку i (ведь их сначала надо объявлять - не забывайте, что не объявлять перед использованием можно только принимаемые параметры ф-и - они автообъявляются). Вот почти правильно:
Code
local integer i = 0
loop
exitwhen (i>10)
endloop
Однако, этот цикл никогда не завершит свою работу - в нем нет операции изменения счетчика цикла (i всегда будет == 0, а значит, никогда не станет больше 10, а значит, цикл никогда не остановится). Вот как правильно:
Code
local integer i = 0
loop
exitwhen (i>10)
set i = i + 1
endloop
Вот теперь цикл отработает ровно 11 раз. Почему 11? Почему не 10? Потому что i изначально == 0, а не 1, поэтому итерация, в которой i==0, тоже отработает - она и будет одиннадцатой. Пора что-то сделать полезное. Вспомним нашу ф-ю MyFunc.
Теперь давайте пройдемся циклом, в каждой итерации вызывая ее с разными параметрами. Например (допустим, что здесь уже есть ф-я MyFunc - не переписываю ее заново для экономии места):
Code
function Result2 takes nothing returns nothing
local integer i = 1
local real array res
loop
exitwhen (i>3)
set res[i] = MyFunc(i, i+3, I2R(i*2))
set i = i + 1
endloop
endfunction
Во-первых, посмотрите на объявление локалки res - добавилось новое ключевое слово array, означающее, что это массив (рассмотрим их подробно в следующей главе, пока только запомните, как их объявлять). Это ключевое слово ставится между указанием типа и указанием имени. Что такое массивы, думаю, вы знаете.
Дальше - что мы делаем? Во-первых, в цикле пройдет 3 итерации (т.к. счетчик i изначально равен 1, каждый раз увеличивается на 1 и условие цикла (i>3) - сколько единиц надо добавить к единице, чтобы полуилось число, которое больше 3х (а не три)? =) ) Уже в самом цикле мы обращаемся к ячейке массива res под номером i (1 на первой итерации, 2 на второй и 3 на третей). Мы заносим в эту ячейку значение, которое вернет ф-я MyFunc на каждой итерации. Т.к. параметры, передаваемые в ф-ю MyFunc тоже зависят от итерации, каждый раз эти значения будут разными. На первой итерации в res[1] будет 2, т.к. первый параметр - i (на первой итерации == 1), второй параметр - (i+3) что на 1й итерации == 3, третий параметр - I2R(i*2) - мы пробразуем integer в real, т.к. ф-я MyFunc принимает в качестве 3го параметра real, а нам надо передать integer (тип локалки i) - в итоге третий параметр на первой итерации будет == 2. В ф-и MyFunc произойдут необходимые арифметические расчеты и в итоге она вернет 2. Эта двойка и будет записана в res[1] на первой итерации. Так, на второй итерации в res[2] будет записано 6, а на третей итерации в res[3] будет записано 21. Проследите сами, почему именно 6 и 21.
Подробней об условиях цикла. Вообще то в обратном условии цикла может быть любое другое. Например, сейчас цикл будет выполняться до тех пор, пока юнит u не умрет:
Code
loop
exitwhen (IsUnitDeadBJ(u) == true)
//тут будут выполнятся действия цикла
endloop
Посмотрите, мы совсем не использовали счетчик цикла - мы его даже не объявляли. А все потому что тут он и не нужен. Здесь условие цикла не проверяет, равна ли чему-то переменная i (или больше ли она чего-то, или меньше и т.д.), здесь условие проверяет, не мертв ли юнит u - с помощью родной ф-и IsUnitDeadBJ(), которая принимает параметр типа юнит (не забывайте, что на самом деле это handle) и возвращает boolean. Как вы помните, boolean может быть в двух состояниях: true или false. Ф-я IsUnitDeadBJ() возвратит true, если юнит, передаваемый ей в качестве параметра, мертв, и false, если жив. То есть если юнит u мертв, то условие цикла примет вид (true==true), что по простой логике вещей остановит цикл.

Глава 7. Массивы.
Что это такое, я уже написал. Теперь рассмотрим их устройство более подробно. Массив - это набор однотипных переменных, обозначенных одним именем. Массивами могут быть как глобалки, так и локалки. Цель массивов - упростить упорядочивание данных для дальнейшей обработки. Например: можно создать глобалку-массив pname типа string и с помощью цикла забить туда имена игроков с 1го до 10го. Вот:

Code
local integer i = 1
loop
exitwhen (i>10)
set udg_pname[i] = GetPlayerName(Player(i-1))
set i = i+1
endloop

Таким образом, можно будет не вызывать лишний раз ф-ю, находящую игрока по его номеру и ф-ю, извлекающую его имя, т.к. мы сделали это лишь раз и теперь легче обратиться к ячейке массива с индексом, равным номеру игрока, чье имя мы узнаём.
Что нового появилось в нашем коде? Во-первых, заметьте, что вместо pname написано udg_pname, ведь это глобалка, а в jass к ним надо обращаться с префиксом "udg_". Во-вторых, новая ф-я GetPlayerName() - она принимает параметр типа player (не забывайте, что на самом деле это handle) и возвращает значение типа string. Как мы преобразовали счетчик цикла i в параметр типа player? С помощью ф-и Player(), которая, принимая параметр типа integer, возвращает нам значение типа player. Не путайте тип player (с маленькой буквы) и ф-ю Player() (с большой буквы). Почему мы передали ф-и Player() не i, а (i-1)? А потому что вообще-то в jass нумерация большинства объектов идет с нуля, и ф-я Player(), приняв ноль в качестве аргумента (так называют параметры - см. ниже), вернет игрока #1, а приняв 7 в качестве аргумента, вернет игрока #8. Надеюсь, вы поняли. Как узнать, что надо вставлять, i, или (i-1) в какой-нибудь другой ф-и? Экспериментируйте! =)
Таким образом можно упорядочить большинство имеющихся на вашей карте данных. И связка массивы+циклы поможет вам эффективно ими (данными) управлять.
На счет параметров и аргументов: теоретически, аргумент - это тот момент, что ф-я принимает какое-то значение, а параметр - это уже само значение. Я еще не до конца понял смысл этих слов, поэтому использую их как синонимы happy

Глава 8. Условия.
Как я уже говорил, есть скалярный тип boolean, имеющий два состояния: true и false. Так вот его можно использовать очень эффективно при помощи блока if. Посмотрите на этот код:

Code
function MyFunc2 takes unit u returns nothing
local integer a = GetUnitUserData(u)
if (a<5) then
call KillUnit(u)
elseif (a==5) then
call SetUnitUserDara(GetRandomInt(1, 9))
else
call DisableTrigger(gg_trg_mytrig)
endif
endfunction

Что тут нового? Во-первых, неизвестное (возможно) нам слово if. Сразу скажу, что все находящееся между словами if и endif в нашем коде - условный блок. Просто запомните это. Теперь подробней: условный блок - это блок кода, который проверяет какое-то условие (пусть, условие #1), и, если оно верно, выполняет определенный код. Если условие #1 не верно, то дальше по усмотрению проверяются другие условия, при верности которых выполняется второй код. И если ни одно из них не верно, выполняется третий код. Технически: условный блок начинается с ключевого слова if. Далее чз пробел в скобках идет проверяющееся условие (например (a<5) - меньше ли переменная a числа 5?) - вспомните циклы. Далее чз пробел идет ключевое слово then после которого и перечислены д-я, выполняемые при правдивости условия. Потом может встретиться ключевое слово elseif, а после него новое условие и новый набор действий. Это новое условие будет проверяться, если ложно предыдущее. И в конце по усмотрению стоит ключевое слово else, перечисляющее д-я, выполняющиеся если все предыдущие условия ложны. Теперь разберем наш блок if: сначала проверяется, не меньше ли локалка a числа 5. Если меньше, то юнит u триггерно убивается (см. ниже). Если а не меньше 5ти, то блоком elseif проверяется, не равна ли она пяти. Если равна, то вызывается ф-я SetUnitUserData() (см. ниже). И в конце концов, если а не меньше 5ти и не равна 5ти, то выполняется блок else, выключающий триггер gg_trg_mytrig. Дальше стоит ключевое слово endif, говорящее нам и движку wc3, что условный блок закончился.
Теперь на счет других новинок: ф-я GetUnitUserData() берет в качестве аргумента параметр типа unit и возвращает integer. Этот integer - CustomValue юнита, переданного в параметре. CustomValue (далее - CV) - т.с. "пришитое" к определенному юниту, предмету или декорации число integer. Зачем это надо, в следующей главе. Пока просто запомните что это такое. Далее - ф-я KillUnit(). Эта ф-я триггерно убивает юнита (при проверке условия, кто же был убийцей, выдаст владельца погибающего). Принимает параметр типа unit, ничего не возвращает. Далее - ф-я SetUnitUserData(). Она принимает параметр типа unit, ничего не возвращает. В отличие от своей "сестры", не извлекает CV юнита, а, наоборот, его устанавливает. Далее - ф-я DisableTrigger(). Принимает параметр типа trigger, ничего не возвращает. Отключает триггер, переданный в качестве параметра. Не прекращает его действие в данный момент, а просто делает его неспособным реагировать на события, в нем указанные.
Теперь, почему я вспомнил boolean'ы. А потому, что например, вот такой код не даст ошибку:
Code
local boolean b = ((a<7) and ((b==6) or (c<b)))
Тут сначала проверится, не равно ли b шести (пусть, условие1), потом проверится, не меньше ли c, чем b (пусть, условие2), и не меньше ли а семи (пусть, условие3). С помощью ключевого слова or достаточно правдивости только одного условия слева от or и справа от or (условие1 и условие2). Далее с помощью ключевого слова and нам необходима правдивость условий по обе стороны от and (то есть и чтобы хоть одно условие среди (условие1;условие2) было правдивым - ключевое слово or находится справа от and, и еще чтобы условие 3 тоже было правдивым). Тогда в переменную b будет установлено значение true
Теперь подробней: в переменную boolean можно записать только true или false. А, например, условие (a<4) вернет true, если а меньше 4х, и false, если равно, или больше. Чувствуете связь? Рассмотрите этот код:
Code
function Demo takes integer i1, integer i2 returns integer
local integer result
local boolean b = (i1>1)
if (b) then
set result = (10 * i2)
elseif (!b) then
set result = (10 / i2)
endif
endfunction
Что мы делаем: создаем локалку result типа integer, создаем локалку b типа boolean. В b проверяется, не больше ли первый передаваемый ф-и параметр (i1) единицы: если больше - в b будет записано true, если равно или меньше - false. Потом в блоке if мы проверяем переменную b. То есть, если b==true, то блок выполнится и переменной result присваивается значение, равное i2(второй передаваемый ф-и параметр) умножить на 10. Далее, на случай, если b==false, мы проверяем это в блоке elseif (восклицательный знак перед b значит, что возвращено будет обратное значение, то есть в блоке elseif в данный момент на самом деле написано (b!=true) - это то же самое). Если b==false (а это значит, что b!=true), то выполнится блок elseif - переменной result будет присвоено значение, равное делению десяти на i2.
Ключевое слово and. При использовании оного, должны быть правдивы оба условия по бокам от and. Например:
Code
local boolean b = ((a<9) and (z>a))
Переменной b будет присвоено значение true только если и а меньше 9, и z больше a. Используйте это.
Ключевое слово or. При использовании оного, достаточно правдивости хотя бы одного из условий по бокам от or. Например:
Code
local boolean b = ((a==x) or (n>=6))
b будет true, если хотя бы a будет равна x, несмотря на (n>=6), или если хотя бы n будет болше или равно 6ти, несмотря на (a==x). То есть достаточно правдивости лишь одного условия. Вот вам д/з:
Выясните, чему в следующем коде будет равно b, не используя we, метода научного тыка и т.д.. Попробуйте понять это.
Code
local integer a = 3
local integer z = (2 * a)
local integer j = (10 - z)
local boolean b = (((a>j) or (z<=5)) or (((a-z)>(10-j)) and ((j*z)==(a*8))))



Таур
Оберокодаро


Сообщение отредактировал perimetral - Суббота, 07.11.2009, 21:52
 
perimetralДата: Среда, 11.11.2009, 10:44 | Сообщение # 3
Знаток Jass
Группа: Тестеры
Сообщений: 332
Репутация: 14
Статус: Offline
Глава 9. Custom Value.
Итак, как вы уже знаете, CV - так сказать, "именное" число integer для какого-то определенного юнита/предмета/декорации. Так зачем же нужен CV? Вот вам лирический пример: у вас на карте при определенном событии (допустим, смерть юнита) создается предмет (допустим, руна). И вам надо, чтобы этот предмет пролежал на земле не больше 30ти секунд. То есть не просто каждые 30 секунд удалять все предметы типа "такая-то руна" на карте, а именно чтобы каждая руна пролежала 30 секунд. Не больше и не меньше. И тут-то на помощь приходит CV! Кстати, хочу заметить, что скорость обращения к CV примерно равна скорости обращения к локале, то есть быстрее, чем к глобалке. Поэтому, в нашей задаче мы не будем использовать глобалки. Что бы сделали вы? Подумайте. А я бы сделал так: при создании руны в триггере с событием смерти юнита, сразу устанавливаем CV этой руны == 30. Потом в отдельном триггере, событие которого - каждые X (в нашем случае X == 1) секунд делать то-то, я бы выбирал все предметы типа "руна" на карте и уменьшал бы их CV на единицу. В этом же триггере после уменьшения CV, проверял бы его, и если CV какой-то руны уже подойдет к нулю, удалял бы ее с карты. Таким образом, каждую секунду CV каждой руны уменьшался бы на 1, а при достижении нуля (то есть чз 30 секунд после создания руны), руна удалялась бы. Задача решена. Без глобалок, массивов и циклов (ну почти без циклов - см. послеслед. главу). Этот пример не так ярок, но все же. Так можно было бы засаживать в CV всех героев в игре, допустим, количество убийств, или время, оставшееся до возрождения при смерти героя. Главное - найти достойное применение =)

Глава 10. JassCraft.
На самом деле для работы с jass есть множество удобных инструментов. Один из них - программа-редактор JassCraft. Это очень мощная программа, не требующая установки. JC - текстовый редактор jass. В нем есть подсветка синтаксиса, проверка на ошибки и огромный набор "родных" (родных для wc3) ф-й. Без него ИМХО просто невозможно далее изучать jass и все последующие главы будут без JC бессмысленным бредом =) Вот ссылка на архив. Вирусов нет - проверено каспером(с). Распакуйте архив в удобную для вас папку, и запускайте "JassCraft.exe". При первом запуске слева откроется два лишних окошка ("Document Explorer" и "MPQ Viewer") - закройте их, ибо вряд ли когда-то ими воспользуетесь. В крайнем случае, в меню "View" можно будет заново их открыть.

Глава 11. Как это делает WE.
Вспомните, я говорил, что WE крайне убого делает из GUI то, что нам надо. Сейчас я вам это покажу, докажу и научу решать. Создайте пустой триггер mytrig. Добавьте д-е "if-then-else multiple actions". засуньте любое условие (не играет роли) в это д-е. Вот, у меня с условием, мертв ли TriggeringUnit, получилось так:

Теперь конвертируйте триггер в jass. Получится такое:
Code
function Trig_mytrig_Func001C takes nothing returns boolean
           if ( not ( IsUnitDeadBJ(GetTriggerUnit()) == true ) ) then
               return false
           endif
           return true
endfunction
function Trig_mytrig_Actions takes nothing returns nothing
           if ( Trig_mytrig_Func001C() ) then
           else
           endif
endfunction
function InitTrig_mytrig takes nothing returns nothing
           set gg_trg_mytrig = CreateTrigger(  )
           call TriggerAddAction( gg_trg_mytrig, function Trig_mytrig_Actions )
endfunction
Если у вас вдруг возникло чувство говнокода, то можете смело пропускать главу. Если нет, то поясняю. Для начала запустите JassCraft (JC). Создайте новый документ и вставьте весь вышеприведенный код в него. Теперь посмотрите на ф-ю Trig_mytrig_Actions. Что в ней делается? В ней просто проверяется условие, но какое? Условие, мертв ли данный юнит? Нет! Эта ф-я вызывает другую ф-ю, Trig_mytrig_Func001C(), которая возвратит boolean (у вас могут немного отличаться последние цифры). А вот в этой "другой" ф-и уже проверяется, мертв ли данный юнит. Теперь подумайте, зачем условие проверяется в одной ф-и, а сравнение результатов - в другой. И я не знаю. Это называется "we". Теперь посмотрите на этот код:
Code
function Trig_mytrig_Actions takes nothing returns nothing
           local unit u = GetTriggerUnit()
           local real r = GetUnitState(u, UNIT_STATE_LIFE)
           if (r <= 0) then
           //тут мы что-то делаем если юнит мертв
           else
           //а тут - если жив
           endif
           set u = null
endfunction
function InitTrig_mytrig takes nothing returns nothing
           set gg_trg_mytrig = CreateTrigger(  )
           call TriggerAddAction( gg_trg_mytrig, function Trig_mytrig_Actions )
endfunction
Что вы увидели нового по сравнению с первым кодом? Во-первых, нет ф-и Trig_mytrig_Func001C(). Во-вторых, нет проверки условия, мертв ли юнит (но так ли это?). В-третьих, мы занесли GetTriggerUnit() в локалу u. К тому же появилась локалка r типа real. Что в ней хранится? В ней хранится значение, которое вернет ф-я GetUnitState() с параметрами (u, UNIT_STATE_LIFE). Что делает ф-я GetUnitState()? Она, принимая два параметра, вернет нам текущее/максимальное количество hp/mp переданного в первом параметре юнита. Второй параметр указывает, что же возвращать (UNIT_STATE_LIFE - текущее hp; UNIT_STATE_MAX_LIFE - макс. запас hp; UNIT_STATE_MANA - текущее mp; UNIT_STATE_MAX_MANA - макс. запас mp). Таким образом, в локалке r будет храниться текущее значение hp юнита u. Смотрим ниже - условный блок. Что он проверяет? Он проверяет, ни ниже ли локалка r нуля. То есть он проверяет, не ниже ли текущее hp юнита u нуля. По логике вещей, если ниже, то юнит мертв, если нет - жив. Теперь ясно, почему нет условия IsUnitDeadBJ()? В JC в списке справа - под списком - есть серенькая надпись "Show options". Нажмите на нее. Теперь проставьте галочки везде, кроме последних 2х пунктов. Там, где было "Show options" сейчас "Hide options" - нажмите. Теперь найдите условие IsUnitDeadBJ() (там все рассортировано по алфавиту) в этом списке. Нажав один раз на него, в нижней области окна JC высветится код этого условия. Просмотрите этот код. Что проверяет это условие? Правильно, не ниже ли hp юнита, чем ноль. А сейчас вспомните, что я говорил про лишние вызовы ф-й? Правильно, они в 5-7 раз медленней обращения к локалкам. Надеюсь, теперь всем ясно, почему мы в своем коде явно сравниваем hp юнита с нулем, а не передаем это дело другой ф-и. В качестве д/з: создайте новый триггер mytrig2 и тем же путем проверьте, принадлежит ли убивающий юнит (Killing Unit) игроку #2. Далее конвертируйте триггер в jass и приведите его в нормальный вид, избавившись от утечек и лишних вызовов ф-й. Получившийся код положите в соседний топик "Пособие: д/з и вопросы".
Удалите два предыдущих триггера mytrig и mytrig2. Создайте пустой триггер mytrig3. В д-ях создайте цикл "For Each Integer A, Do Multiple Actions" с 1 до 10. В цикл вставьте д-е "Turn off trigger(This trigger)". То есть в цикле вы 10 раз выключите этот триггер. Конвертируйте триггер в jass. Получится такое:
Code
function Trig_mytrig3_Actions takes nothing returns nothing
         set bj_forLoopAIndex = 1
         set bj_forLoopAIndexEnd = 10
         loop
             exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
             call DisableTrigger( GetTriggeringTrigger() )
             set bj_forLoopAIndex = bj_forLoopAIndex + 1
         endloop
endfunction
function InitTrig_mytrig3 takes nothing returns nothing
         set gg_trg_mytrig3 = CreateTrigger(  )
         call TriggerAddAction( gg_trg_mytrig3, function Trig_mytrig3_Actions )
endfunction

Для начала: переменные bj_forLoopAIndex и bj_forLoopAIndexEnd - глобалки по умолчанию. 1я - это счетчик цикла. 2я - это граница цикла (то есть то число, при достижении которого счетика, цикл завершается). Во-первых, они медленны, во вторых, при одновременной работе двух циклов с использованием этих переменных, работа может сбиться (подумайте, почему). А теперь скажите, что мешает нам использовать локалки вместо них? Ничего! Посмотрите на этот код:
Code
function Trig_mytrig3_Actions takes nothing returns nothing
         local integer L = 1
         loop
             exitwhen (L > 10)
             call DisableTrigger( GetTriggeringTrigger() )
             set L = (L + 1)
         endloop
endfunction
function InitTrig_mytrig3 takes nothing returns nothing
         set gg_trg_mytrig3 = CreateTrigger(  )
         call TriggerAddAction( gg_trg_mytrig3, function Trig_mytrig3_Actions )
endfunction

Что мы сделали? Во-первых, избавились от двух глобалок. Теперь нам не грозит сбой работы цикла при одновременной работе нескольких таких. Во-вторых, у нас только одна локалка - мы указали границу цикла явно. Кстати, ф-я GetTriggeringTrigger() не принимает параметров и возвращает триггер, из которого вызвана. Теперь попробуйте избавиться от остальных утечек (подсказка - она всего одна). Должно получиться так:
[spoiler]
Code
function Trig_mytrig3_Actions takes nothing returns nothing
         local integer L = 1
         loop
             exitwhen (L > 10)
             call DisableTrigger(gg_trg_mytrig3)
             set L = (L + 1)
         endloop
endfunction
function InitTrig_mytrig3 takes nothing returns nothing
         set gg_trg_mytrig3 = CreateTrigger(  )
         call TriggerAddAction( gg_trg_mytrig3, function Trig_mytrig3_Actions )
endfunction
[spoiler] Только что мы привели цикл в человеческий вид. Вы научитесь автоматически делать это, даже не задумываясь. Тут больше сказать нечего, так что вот д/з (будет связано с условиями для закрепления материала): создайте в GUI триггер с циклом с 0 по 11. В нем создайте второй (вложенный) цикл с 0 по 11. Во втором цикле проверяйте, не равен ли его счетчик счетчику первого цикла. Если нет, делайте игрока[X] врагом игрока [Y], где X - счетчик первого цикла, а Y - второго. Так, вы сделаете всех игроков врагами. Триггерное д-е для этого: "Игрок"/"Set Alliance". Избавьтесь от всех лишних вызовов ф-й (JC вам в помощь) и утечек. Выложите работу в соседнем топике "Пособник: д/з и вопросы".
Удалите mytrig3 и создайте mytrig4 =) В GUI добавьте в него д-е Pick every unit in Unit group and Do Actions. Группа - Units in Playable Map Area. В д-я над группой - пусть, убьем их =). Конвертируйте триггер в jass. Получится такое (ф-ю InitTrig_mytrig3 не привожу):
Code
function Trig_mytrig3_Func001A takes nothing returns nothing
       call KillUnit( GetEnumUnit() )
endfunction
function Trig_mytrig3_Actions takes nothing returns nothing
       call ForGroupBJ( GetUnitsInRectAll(GetPlayableMapRect()), function Trig_mytrig3_Func001A )
endfunction
Что мы видим? В ф-и д-й вызывается ф-я ForGroupBJ(). Она берет 2 параметра: group и code. Ничего не возвращает. Эта ф-я выполняет ф-ю, переданную во втором параметре для каждого юнита в группе, переданной в 1м параметре. В ф-и Trig_mytrig3_Func001A мы убиваем юнита GetEnumUnit(). Ф-я GetEnumUnit() вернет нам юнита, по которому идет в данный момент ForGroup. В ForGroupBJ() в качестве первого параметра передается группа всех юнитов в пределах карты. Ф-я GetUnitsInRectAll() вернет нам всех юнитов в ректе (область), переданном ей в качестве параметра. Ф-я GetPlayableMapRect() вернет нам рект==всей играбельной карте (то есть куда можно попасть юнитом). Теперь взгляните на этот код:
Code
function Trig_mytrig3_Func001A takes nothing returns nothing
       local unit u = GetEnumUnit()
       call KillUnit(u)
       set u = null
endfunction
function Trig_mytrig3_Actions takes nothing returns nothing
       local rect pma = bj_mapInitialPlayableArea
       local group g = CreateGroup()
       call GroupEnumUnitsInRect(g, pma, null)
       call ForGroup(g, function Trig_mytrig3_Func001A)
       call DestroyGroup(g)
       call RemoveRect(pma)
       set g = null
       set pma = null
endfunction
Что мы сделали? Сначала рассмотрим ф-ю Trig_mytrig3_Func001A(). В ней мы только занесли GetEnumUnit() в локалку и очистили ее после убийства юнита, чтоб не было утечек. Теперь рассмотрим ф-ю Trig_mytrig3_Actions(). Во-первых, мы занесли рект, возвращаемый ф-й GetPlayableMapRect(), в локалку (причем явно - ведь этот рект есть в константах сразу - bj_mapInitialPlayableArea). Далее, мы занесли в локалку g типа group группу, возвращаемую ф-й CreateGroup() - эта ф-я просто создаст какую-то группу. То есть, инициализирует ее. Дальше мы заменили ф-ю GetUnitsInRectAll() на ф-ю GroupEnumUnitsInRect() - она берет в качестве первого параметра группу (которая должна быть обязательно инициализирована), в качестве второго - рект, а в качестве третьего будем указывать null ибо я не знаю, что он значит =). Эта ф-я ничего не возвращает, она просто делает GetEnumUnit() тем, кто нам и надо. То есть она собирает юнитов в ректе pma в группу g. Дальше мы использовали ForGroup() вместо ForGroupBJ(). Она делает то же самое, но без лишних вызовов ф-й. Дальше мы вызываем ф-и DestroyGroup() и RemoveRect(), о которых вы узнаете в следующей главе. Ну, обнуление переменных я пояснять не буду =). Так, мы избавились от нескольких лишних вызовов ф-й и утечек. Как обычно, д/з =) Проделайте все то же самое с группой декораций (Pick every destructable ...). Избавьтесь от лишних вызовов ф-й и утечек. Решение прошу в "Пособие: д/з и вопросы".

Глава 12. Оптимизация.
Многие из вас наверно заметили в GUI д-е "Боевая единица"/"Remove unit". На jass звучит так: RemoveUnit(). Берет один параметр типа unit и ничего не возвращает. Что делает эта ф-я? Она удаляет юнита из игры. Убитый юнит (кто не знал) будет храниться в памяти игры до воскрешения или же до конца игры. И движку плевать, что он мертв, движок считает, что у него просто (<0) hp. Вот лирический пример: у вас на карте (2Цезарь) создаются юниты. Много юнитов. Решающей роли их смерть не играет (то есть капитально ничего в игре ни изменится, если один из них погибнет), а значит, после исчезновения трупа они не должны висеть в памяти. И тут мы вернемся к ф-и RemoveUnit() - теперь вы должны были понять, когда ее используют. Вот простой триггер:

Code
function Trig_mytrig5_Actions takes nothing returns nothing
       local unit u = GetDyingUnit()
       call TriggerSleepAction(10.00)
       call RemoveUnit(u)
       set u = null
endfunction
function InitTrig_mytrig5 takes nothing returns nothing
       set gg_trg_mytrig5 = CreateTrigger(  )
       call TriggerRegisterAnyUnitEventBJ( gg_trg_mytrig5, EVENT_PLAYER_UNIT_DEATH )
       call TriggerAddAction( gg_trg_mytrig5, function Trig_mytrig5_Actions )
endfunction
Что он делает? У юнита (любого) есть очень много параметров, которые занимают немало места в памяти. Мы отреагировали на смерть юнита ожиданием в 10 секунд (чтоб труп исчез) и его удалением. То есть теперь он не будет занимать место в памяти. Его вообще не будет (юнита). Надеюсь, ясно, что юнитов, которые будут использоваться после смерти (герои - возрождаться, например), не стоит удалять. Однако не играющих роли юнитов - стоит. Так вы избавитесь от многих утечек. Точно также надо удалять и динамические точки (которые используются только для какого-то определенного д-я), и ректы, и эффекты и даже условия. Вот полный список:
DestroyCondition() - условие триггера (тип code)
DestroyEffect() - эффект (тип effect)
DestroyForce() - группа игроков (тип force)
DestroyGroup() - группа юнитов (тип group)
DestroyImage() - изображение (тип image)
DestroyLeaderboard() - таблица рекордов (не мультибоард) (тип leaderboard)
DestroyLightning() - молния (тип lightning)
DestroyMultiboard() - мультибоард (тип multiboard)
DestroyQuest() - квест (тип quest)
DestroyTextTag() - плавающий текст (тип texttag)
DestroyTimer() - таймер (тип timer)
DestroyTimerDialog() - окно таймера (тип timerdialog)
DestroyTrigger() - триггер (тип trigger)
RemoveDestructable() - декорация (тип destructable)
RemoveItem() - итем (тип item)
RemoveLocation() - точка (тип location)
RemoveRect() - рект (тип rect)
RemoveUnit() - юнит (тип unit)
RemoveWeatherEffect() - погодный эффект (тип weathereffect)
Но будьте осторожны! Удалять следует только те объекты, которые уже не будут использоваться. Юниты и декорации также сначала следует убить (декорациям - установить нулевую прочность). Триггерам сначала надо очистить д-я и условия (в след. главе). Таймер сначала надо остановить. В таблице рекордов и мультибоарде сначала надо очистить все элементы (все ячейки). Молнию надо остановить. Глава короткая, но без понимания этого материала будет тяжело уйти от утечек. Вот и д/з: В триггере с событием "Каждые 5 секунд" создавайте в одной точке 2 одинаковых юнита за 2х разных игроков (каждые 5 секунд - разных) - то есть, первый раз - 1й и 2й игроки; 2й раз - 3й и 4й - и т.д.. В РО расчитайте х-ки юнита так, чтобы один такой убивал другого за ~5 секунд. Вспомните циклы и CV. Удаляйте и точки, в которых создавали юнитов, и самих юнитов чз некоторое время. Создание должно прекратиться и триггер должен быть остановлен (не удален), когда за каждого игрока уже будет создано и убито по 2м юнитам. Не используйте глобалки. Избавьтесь от утечек. Можете создать одного "левого" юнита (зачем - подумайте). Сразу скажу - самое трудное д/з из всех предыдущих. Ответ в соседний топик - "Пособие: д/з и вопросы".
ДЛЯ ТЕХ КТО В ТАНКЕ: НЕ КОММЕНТИТЬ ПОКА Я НЕ НАПИШУ, ЧТО ЗАКОНЧИЛ!



Таур
Оберокодаро


Сообщение отредактировал perimetral - Среда, 11.11.2009, 10:46
 
GetredДата: Четверг, 26.11.2009, 01:25 | Сообщение # 4
Берсерк
Группа: Проверенные
Сообщений: 47
Репутация: 0
Статус: Offline
Quote (perimetral)
Вот ссылка на архив. Вирусов нет - проверено каспером(с)

Что это??



 
maxve11Дата: Четверг, 06.05.2010, 22:46 | Сообщение # 5
Бугай
Группа: Пользователи
Сообщений: 34
Репутация: 4
Статус: Offline
Длинновато )
 
[Nobody]Дата: Пятница, 07.05.2010, 17:26 | Сообщение # 6
.
Группа: Модераторы
Сообщений: 3535
Репутация: 15
Статус: Offline
Quote (maxve11)
Длинновато )

тут не разобрано ещё много всего...
 
L@nceRДата: Пятница, 07.05.2010, 17:29 | Сообщение # 7
Фап
Группа: Проверенные
Сообщений: 4495
Статус: Offline
Quote (perimetral)
ДЛЯ ТЕХ КТО В ТАНКЕ: НЕ КОММЕНТИТЬ ПОКА Я НЕ НАПИШУ, ЧТО ЗАКОНЧИЛ!

Quote (Getred)
Что это??

Quote (maxve11)
Длинновато )

Quote (|Nobody|)
Quote (maxve11)
Длинновато )

тут не разобрано ещё много всего...


=)))) люди не понимают слов)) хотя это уже не важно... Перим исчез и ему некогда продолжать писать....


 
[Nobody]Дата: Пятница, 07.05.2010, 17:31 | Сообщение # 8
.
Группа: Модераторы
Сообщений: 3535
Репутация: 15
Статус: Offline
Quote (L@nceR)
=)))) люди не понимают слов)) хотя это уже не важно... Перим исчез и ему некогда продолжать писать....

я и не это имел ввиду, а то, что тут не так длинно, как кажется.
 
perimetralДата: Суббота, 08.05.2010, 13:34 | Сообщение # 9
Знаток Jass
Группа: Тестеры
Сообщений: 332
Репутация: 14
Статус: Offline
L@nceR, да, продолжать я не буду, мне влом
[Nobody], тут рассмотрено ~20% того, что можно знать о джассе. Основные 20%, так сказать.



Таур
Оберокодаро
 
L@nceRДата: Суббота, 08.05.2010, 13:39 | Сообщение # 10
Фап
Группа: Проверенные
Сообщений: 4495
Статус: Offline
Quote (perimetral)
L@nceR, да, продолжать я не буду, мне влом

а мне плевать.... я сказал продолжил biggrin biggrin biggrin


 
FLESHNIKДата: Суббота, 08.05.2010, 13:47 | Сообщение # 11
xeno != Bloody// :B
Группа: Проверенные
Сообщений: 3638
Репутация: 30
Статус: Offline
Quote (L@nceR)
а мне плевать.... я сказал продолжил

L@nceR, а давай ты продолжишь))



FLESHNIK, PWFresh, JIoMuK


Сообщение отредактировал FLESHNIK - Суббота, 08.05.2010, 13:48
 
L@nceRДата: Воскресенье, 09.05.2010, 20:25 | Сообщение # 12
Фап
Группа: Проверенные
Сообщений: 4495
Статус: Offline
[off]
Quote (FLESHNIK)
L@nceR, а давай ты продолжишь))

Оо боже упаси)) [/off]


 
perimetralДата: Понедельник, 10.05.2010, 19:19 | Сообщение # 13
Знаток Jass
Группа: Тестеры
Сообщений: 332
Репутация: 14
Статус: Offline
L@nceR, FLESHNIK, в эпическом сообщении о моем уходе (где-то в Таверне) есть ссылка на сайт XGM, c которого можно почерпнуть абсолютно любую информацию о jass'е м всем с ним связанном. Дерзайте.


Таур
Оберокодаро
 
FLESHNIKДата: Пятница, 04.03.2011, 19:44 | Сообщение # 14
xeno != Bloody// :B
Группа: Проверенные
Сообщений: 3638
Репутация: 30
Статус: Offline
я смотрел там и чет неоч понял (может не хотел понять)

Добавлено (04.03.2011, 19:44)
---------------------------------------------

Quote (FLESHNIK)
я смотрел там и чет неоч понял (может не хотел понять)

эх...ностальгия...



FLESHNIK, PWFresh, JIoMuK
 
(D)e[X]e(L)Дата: Пятница, 04.03.2011, 19:54 | Сообщение # 15
Правило форума: Ты не очень. Мой причесон пиздатей
Группа: Тестеры
Сообщений: 1159
Репутация: 16
Статус: Offline
Quote (FLESHNIK)
эх...ностальгия...

Некр чтоле?
 
Форум карты Жизнь на Арене » Картостроение » Творцы миров » [Jass]Пособие к Jass (special for Цезарь)
  • Страница 1 из 2
  • 1
  • 2
  • »
Поиск: