Структуры привносят в Jass элементы объектно-ориентированного программирования (ООП). Они являются удобными объектами для хранения каких либо данных, не затрагивая хеш-таблицу, которая является сама по себе довольно медленной.
Что ты такое?
Структура является сама по себе набором массивов на оригинальном Jass. Однако все сделано для очень удобного использования. С помощью них, можно хранить различные связки нужных нам данных, с которыми можно удобно работать. Хеш таблица работает медленнее структур, и обращаться к данным и хранить их более неудобно. Применение можно найти например в системах движения, в полностью триггерных системах урона, в системах, где необходимо запоминать связку нескольких данных к какому либо объекту.
Создание
Сначала, структуру нужно описать.
struct A
real a = 2.
real b = 1.
real c = 3.
endstruct
function SomeFunc takes nothing returns nothing
local A local_struct = A.create()
call BJDebugMsg(R2S(local_struct.a + local_struct.b + local_struct.c))
<выведет 6.0>
call local_struct.destroy()
endfunction
Обратите внимание на то как нужно взаимодействовать с данными внутри структуры. Что бы создать структуру, можно пользоваться встроенным методом <название объекта структуры>.create(). Для уничтожения, используется метод <название структуры>.destroy(). Все функции внутри структуры называются методами, и эти 2 являются встроенными в каждую. Вы можете создать одновременно лишь 8190 структур одного типа. Если вы попытаетесь создать больше при существующих 8190, <название объекта структуры>.create() вернет вам 0. В конце я расскажу об альтернативном создании структур. 0 так же используется для обозначения null структуры
if local_struct > 0 then
Важно: вы можете объявлять массивы внутри структур. Необходимо задать изначальный размер массива, однако при этом количество максимально возможных структур этого типа уменьшится на 8190/размер массива
struct A
real array a[10]
endstruct
Задавать значения массиву можно в методе .create(), таким образом создавая их при создании структуры. Об этом ниже.
Со структурами можно работать как с обычными переменными, ведь по сути наш local_struct является целочисленной(integer) переменной указывающий на индекс структуры
function SomeFunc takes nothing returns nothing
local A local_struct_a = A.create()
local A local_struct_b
if local_struct_a > 0 then
set local_struct_b = local_struct_a
endif
call local_struct_a.destroy()
call local_struct_b.destroy()
endfunction
Видимость
Можно делать как приватные, так и публичные, внутри Области либо Библиотеки
library SomeLibrary
private struct A
real x
real y
real z
endstruct
public struct B
unit owner
endstruct
endlibrary
Типы
Объявить структуру можно как локальную, как вы уже видели
function SomeFunc takes nothing returns nothing
local A local_struct = A.create()
endfunction
так и глобальную
globals
A global_struct
endglobals
Важно: создать структуру таким образом сразу нельзя, нужно делать это при инициализации
library SomeLibrary initializer SomeFunc
globals
A global_struct
endglobals
function SomeFunc takes nothing returns nothing
set global_struct = A.create()
endfunction
endlibrary
=== Методы ===
Я уже упоминал методы, это функции внутри структуры
struct A
unit some_unit
integer attack = 30
integer defence = 20
real attack_speed = 1.
method InitClassStates takes integer class returns nothing
if class == 1 then
this.attack = 20
this.defence = 40
elseif class == 2
this.attack = 30
this.defence = 35
endif
endmethod
endstruct
function SomeFunc takes nothing returns nothing
local A local_struct = A.create()
set local_struct.some_unit = CreateUnit(Player(0), 'hpea', 0., 0., 270.)
call local_struct.InitClassStates(2)
call BJDebugMsg(I2S(local_struct.attack))
<выведет 30>
call BJDebugMsg(I2S(local_struct.defence))
<выведет 35>
call local_struct.destroy()
endfunction
Внутри методов, используется ключевое слово this. Оно всегда указывает на текущую структуру в которой вызвалась функция. Впрочем, можно просто писать
if class == 1 {
.attack = 20
.defence = 40
}
Существуют так же такие стандартные методы как onInit, onDestroy
onInit является статичным методом, и запускается при запуске инициализации карты.
onDestroy запускается когда вызывается метод .destroy()
onDestroy запускается когда вызывается метод .destroy()
Альтернативное создание структуры
В обычном случае, как выше, мы указывали некоторые данные сразу в структуре, они задавались при создании структуры.
struct A
real x = 10.
real y = 15.
real z = 20.
endstruct
При создании через .create() у нас будут уже такие заданные значения в этих переменных. Однако можно сделать вот как, обходя стандартный метод создания, при этом, значения вам нужно будет задать самим
struct PhaserSlot
real durability
integer slot_id
static method new takes integer id returns thistype
thistype this = thistype.allocate()
set this.durability = PhaserMaxHp[id]
set this.slot_id = id
return this
endmethod
endstruct
- thistype всегда указывает на имя структуры, в данном случае PhaserSlot
- static методы отличаются тем, что они не будет работать с конкретной структурой, тоесть если вы захотите обратиться к данным через this, у вас ничего не выйдет.
- allocate() является встроенным методом который выделяет новый индекс структуре
По моим данным, такой способ создания является более чистым, ибо при стандартном создании создается много мусора.
Внутри структуры можно перегрузить конструктор create. Если структура не имеет определенный программистом метод create, парсер установит вызов метода allocate для выделения ячейки под структуру. Если метод create программистом определен, он сам должен вызвать метод allocate внутри конструктораstruct Point real x real y static method create takes nothing returns thistype local Point p = Point.allocate() set p.x = 0.0 set p.y = 0.0 return p endmethod endstruct
Метод create должен быть статичным.
Наследование
Структуры могут наследовать данные друг друга
struct A
real a = 5.
real b = 7.
endstruct
struct B extends A
real c
endstruct
function SomeFunc takes nothing returns nothing
B local_struct = B.create()
<выведет 5.0>
call BJDebugMsg(R2S(local_struct.a))
endfunction
Структура B будет иметь переменные a и b, для этого необходимо использовать ключевое слово extends <имя наследуемой структуры>
Это необходимо, если у вас должно быть много разных структур, но у всех у них будут какие либо одинаковые данные, например количество здоровья. Аналогом из Jass можно привести в пример тип Widget, который всегда имеет количество здоровья, и является основой таким типам как Item, Destructable и Unit
Использование с таймер эксплоитом
Существует эксплоит таймера, который был найден пользователем ScorpioT1000
немного cJass'а
define TimerStartEx(whichTimer, period, handlerFunc, userData) = {
TimerStart(whichTimer, I2R(userData), false, null) // timer exploit, xgm 2007, by Scorpio
PauseTimer(whichTimer)
TimerStart(whichTimer, period, false, handlerFunc )
}
define GetTimerAttach(h) = R2I(TimerGetRemaining(h)+0.5)
Используя его, можно прицеплять на таймер целочисленное значение, а структура как раз передает свой целочисленный индекс
struct A
string str = "млг360ноускопе"
endstruct
function Result takes nothing returns nothing
local timer t = GetExpiredTimer()
local A local_struct = GetTimerAttach(t)
call BJDebugMsg(local_struct.str)
<выведет "млг360ноускопе">
call DestroyTimer(t)
call local_struct.destroy()
set t = null
endfunction
function Start takes nothing returns nothing
local A local_struct = A.create()
local timer t = CreateTimer()
TimerStartEx(t, 1., function Result, local_struct)
set t = null
endfunction
Ред. GetLocalPlayer
Худо-бедно короче говоря. Многое сказано вскользь и с успехом опущены важные детали.
Ред. Clamp