Предисловие - для кого эта статья
Статья носит скорее более теоретический характер, хотя и дает достаточно
пищи для размышлений о том, как код можно сделать намного проще и качественней.
Предполагается что читатель достаточно хорошо знает JASS.
Теория
Тип integer в Jass это long signed int (в C) или попросту DWORD, это значит
что он имеет 32 бита: 31 бит что бы обозначить само число и один знаковый бит. В
JASS коде он может быть представлен в 8 (восьмеричная или octimal), 10
(десятеричная или decimal, самая привычная для большинства людей), 16
(шестнадцатеричная или hexadecimal) и 256(ASCII) нотации (системе
счисления).
Чем одна система отличается от другой? (если Вы знакомы с этими понятиями
можете пропустить этот абзац) В десятеричной системе переход в следующий разряд
происходит когда число достигает 10 (9+1=10). Точно также и в других системах
счисления: в восьмеричной системе 7+1=10 (причем 10 в ней реально равно 8 в
десятеричной системе). В шестнадцатеричной системе что бы обозначить цифры (не
путайте число и цифру, цифра - обозначение числа, поэтому разные цифры - в
разных нотациях могут указывать на одно число), так вот, что бы обозначить в
шестнадцатеричной системе цифру больше 9 мы будем использовать первые буквы
английского алфавита: A=10, B=11, ... F=15. С 256 системой счисления дело
обстоит также, ниже я выложу таблицу символов и значений (взято из WEU
документации):
Хорошо, теперь вернемся к JASS. Что бы написать цифру в восьмеричной
системе счисления мы должны приставить к ней 0
local integer i=012 // 10 в десятеричной си
Цифры без префиксов считаются десятичными, hex обозначается через
0x
local integer i=0x0f // 15 в десятеричной си
256 цифры указываются обрамленными в апострофы
local integer i='A'
Если Вы хотите перевести цифру в другую си - используйте WinCalc (есть
такая программа в меню "пуск") переставленный в инженерный вид. Перевод числа из
ASCII делается тоже достаточно просто: переведите каждый чар (ну т.е. символ) в
hex, и вы получите 16ричное число. К примеру: 'A0a1', 'A' = 0x41, '0' = 0x30,
'a' = 0x61, '1' = 0x31, 'A0a1' равно 0x41306131 или 1093689649.
Помните, что большинство равкодов (кроме lightning, ubersplat) в JASS
обычные integer, поэтому с ними возможны любые математические действия, что и
будет описано дальше.
В JASS невозможно указать integer в двоичной (binary) системе счисления, но
она очень важна для понимания всего материала. Как Вы могли догадаться, в ней
переход в следующий разряд происходит при достижение 2: 1bin+1bin == 10bin
(2dec). Теперь вернемся к нашему 32 битному integer
0x 7 a 9 8 0 1 f 0
0111 1010 1001 1000 0000 0001 1111 0000
Один разряд в двоичной системе называется бит, восемь разрядов или двух
разрядное шестнадцатеричное число - байт.
Напомню, что первый бит числа - знаковый бит, и он определяет будет ли оно
положительным (0) или отрицательным (1). Положительные числа идут от 0x00000000
(0dec) до 0x7fffffff (2147483647dec), отрицательные от 0xffffffff (-1dec) до
0x80000000 (-2147483648dec). Что бы изменить знак числа надо инвертировать (где
было 1 написать 0, где был 0 написать один) все биты числа и потом к нему
прибавить 1.
// в примере я использую однобайтовое число что бы было легче понять
0000 0001 // +1dec
1111 1110 // inv
1111 1111 // -1dec
Теперь, хоть в JASS и нету побитовых операций над числами, можно
имитировать их с помощью умножения и деления
1000 1000 // *10bin (<<)
1 0001 0000 // /100bin (>>)
0000 0100
Биты, которые не помещаются в результате будут считаться потерянными. Также
с помощью нескольких сдвигов мы сможем получить любые биты числа, имитировав тем
самым логические побитовые операции, к примеру на нужно узнать значение 3 и 4
бита
0110 **11**01 // *1000
0**11**0 1000 // /100000
0000 00**11**
Отлично! Но у нас могут возникнуть некоторые проблемы с знаковым битом при
таких операциях
0111 1100 // *1000
1110 0000 // 1110 0000bin = -20dec
1110 0000 // но мы можем изменить знаковый бит
+1000 0000
_____ _____
1 0110 0000
Так что мы смело можем прибавить 0x80000000, но есть еще более простой
способ использовать бит "сепавратор", который всегда будет равен 0, и если он
займет место знакового то результат все равно будет верным
0vv! vvvv
v - наши данные
! - бит сепаратор
Остается добавить, что деление или умножение на 10bin, 100bin, 1000bin это
сдвиг влево или вправо на 1, 2, 3 бита (2^1 == 2 dec == 10 bin; 2^2 == 4 dec ==
100 bin; 2^3 == 8 dec == 1000 bin; 2^n == сдвиг на n бит).
Хорошо, теперь попробуем применить это все на практике.
Практика
Тут я выложу несколько простых задач, в решение которых стоит применить
полученные знания.
Золото за убийство
Мы должны добавить владельцу убившего юнита золота в зависимости от какой
либо характеристики (к примеру у юнита есть способность или предмет +50% золота
от крипов). На карте есть много всяких крипов, и от каждого должно даваться
случайное количество золота, но пересчитанное по какой либо формуле. Функция,
создающая texttag и добавляющая золота у нас есть, в нее просто надо сообщить
его количество. Также сделайте два варианта, в первом 1 <=minGold
<=maxGold <=80000000, во втором 1 <=minGold <=MaxGold
<=75.
Добавление предмета
На карте существует ~60 типов различных героев. Когда какой либо герой
достигнет 50 уровня надо создать уникальный для это типа героя артефакт возле
него.
Данные юнита
Необходимо сохранить для каждого юнита в игре сколько героев и крипов он
убил, и на какой стороне он сражается (к примеру свет и тьма). Предположительно
юнит не может убить больше 2000 других юнитов, не может убить больше чем 500
героев, и он либо темный, либо светлый. Возможное количество таких юнитов,
которым надо сопоставить эти данные неограниченно. Также добавлю условие что
нельзя использовать глобальные переменные, массивы или кешь.