22:17 | 03.06.2026
Последние рипы от нас
Партнёры
Ответы на форуме
    Всего: 331
    Новых за месяц: 1
    Новых за неделю: 0
    Новых вчера: 0
    Новых сегодня: 0
У кого cпособность Invissible круче ?
Всего ответов: 276
Самое скачивание
Статистика
Онлайн всего: 4
Гостей: 4
Пользователей: 0
Пользователи
Гости сайта

Нас посетили:

Добро пожаловать на сайт www.plan-css.ru, на нашем сайте вы сможете скачать множество рипов файлов пренадлежащих системе uCoz: Рип шаблонов для uCoz, Рип скриптов, Рип графики. И ещё вы сможете скачать PSD работы для фотошопа, PSD баннеров, PSD аватарок, а также вы сможете заказать любой рип для своего сайта, рип графики | рип шаблонов и рип скриптов, заказать данные услуги вы сможете абсолютно бесплатно!
Главная » Статьи » Различные статьи » Создание карт

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

В статье мы рассмотрим самую распространённую область её применения в wc3: прикрепление данных к объекту, на простейшем примере. Статья предполагает, что читатель знаком с основами работы таймеров. Пример будет только на обычном Jass, для совместимости (да и не все умеют работать c v/cJass).

Если вы знакомы с кэшем в wc3, то принцип работы с ним схож, с принципом работы с хеш-таблицей. Только вместо строковых ключей, хеш-таблица использует целочисленные значения (integer).

Допустим, мы хотим создать спелл, в котором врагу на протяжении некоторого времени с малым периодом постоянно наносится урон (для которого wait не подходит).
Мы создали триггер (в редакторе триггеров, для простоты объяснения) с событием каста, дали ему условия и действия:
Code
function Spell takes nothing returns nothing  
  local unit caster = GetSpellAbilityUnit() //Кастер  
  local unit target = GetSpellTargetUnit() //Цель  
  endfunction  

  //Проверка спелла  
  function SpellCond takes nothing returns boolean  
  return GetSpellAbilityId()=='A000'  
  endfunction  

  //===========================================================================  
  function InitTrig_Spell takes nothing returns nothing  
  set gg_trg_Spell = CreateTrigger()  
  call TriggerRegisterPlayerUnitEvent(gg_trg_Spell,Player(0),EVENT_PLAYER_UNIT_SPELL_CAST,null)  
  call TriggerAddCondition(gg_trg_Spell,Condition(function SpellCond))  
  call TriggerAddAction(gg_trg_Spell,function Spell)  
  endfunction


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

Code
function SpellDamage takes nothing returns nothing  
  call UnitDamageTarget(...)  
  endfunction  

  function Spell takes nothing returns nothing  
  local unit caster = GetSpellAbilityUnit() //Кастер  
  local unit target = GetSpellTargetUnit() //Цель  
  local timer t = CreateTimer() //Создаём таймер  

  call TimerStart(t,0.04,true,function SpellDamage) //Стартуем таймер  
  endfunction


Но как передать в функцию, кто кому должен наносить урон, и сколько раз? Тут нам на помощь и приходит хеш-таблица. Перед работой нужно создать и инициализировать глобальную хеш-таблицу, желательно при инициализации карты.

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

Делать это нужно только один раз, например в этом триггере:
Code
function InitTrig_Spell takes nothing returns nothing  
  set gg_trg_Spell = CreateTrigger()  
  call TriggerRegisterPlayerUnitEvent(gg_trg_Spell,Player(0),EVENT_PLAYER_UNIT_SPELL_CAST,null)  
  call TriggerAddCondition(gg_trg_Spell,Condition(function SpellCond))  
  call TriggerAddAction(gg_trg_Spell,function Spell)  

  set udg_hash = InitHashtable() //Инициализируем хеш-таблицу  
  endfunction


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

Работает хеш-таблица так: [ключ|значение]. Только как ключ мы используем уникальный id объекта, а точнее - id нашего таймера.
На него и будем сохранять нужные нам данные:
Code
function Spell takes nothing returns nothing  
  local unit caster = GetSpellAbilityUnit() //Кастер  
  local unit target = GetSpellTargetUnit() //Цель  
  local timer t = CreateTimer() //Создаём таймер  
  local integer h = GetHandleId(t) //Узнаём id таймера  

  //Сохраняем объекты с ключом - id таймера  
  call SaveUnitHandle(udg_hash,h,1,caster) //Сохраняем кастера со значением 1  
  call SaveUnitHandle(udg_hash,h,2,target) //Сохраняем цель со значением 2  
  call SaveInteger(udg_hash,h,3,125) //Сохраняем количество ударов, из расчёта, что урон наносится в течение 5 секунд (5/0.04=125).  

  call TimerStart(t,0.04,true,function SpellDamage) //Стартуем таймер  

  //Не забываем устранять утечки  
  set caster = null  
  set target = null  
  set t = null  
  endfunction


Также можно делать без создания переменной h:
call SaveUnitHandle(udg_hash,GetHandleId(t),1,caster)
Утечек это не вызовет, но немного снизит производительность.

Аналогичными действиями сохраняются и другие объекты, например группа:
call SaveGroupHandle(udg_hash,h,1,some_group)
Думаю, нет смысла перечислять все функции, так как на это существуют function-листы.

Некоторые утверждают, что сохранение объектов под ключами 1,2,3... неудобно и неуниверсально, и предлагают сохранять через уникальное значение строки:
call SaveUnitHandle(udg_hash,h,StringHash("caster"),caster)
С ними можно согласиться, вам не придётся давать и запоминать цифры для значений. Вы можете использовать такой способ для удобства.

Готово, данные сохранены, теперь их можно будет достать в функции нанесения урона, на которую запущен таймер.
Доставать данные мы будем тоже по id таймера:
Code
function SpellDamage takes nothing returns nothing  
  local timer t = GetExpiredTimer() //Наш таймер - истёкший  
  local integer h = GetHandleId(t) //Узнаём id таймера  
  local unit caster = LoadUnitHandle(udg_hash,h,1) //Достаём кастера из значения 1  
  local unit target = LoadUnitHandle(udg_hash,h,2) //Достаём цель из значения 2  
  local integer counter = LoadInteger(udg_hash,h,3) //Достаём количество ударов  

  if counter>0 then //Если количество ударов больше 0  
  call UnitDamageTarget(caster,target,1.0,true,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,null) //Наносим урон цели  
  call SaveInteger(udg_hash,h,3,counter-1) //Сохраняем количество ударов, убавленное на 1  
  else //Иначе  
  call DestroyTimer(t) //Уничтожаем таймер  
  //Очищаем хеш-таблицу, чтобы избежать утечек и наложений  
  call FlushChildHashtable(udg_hash,h) //Очищаем ключ по id  
  endif  

  //Не забываем устранять утечки  
  set caster = null  
  set target = null  
  set t = null  
  endfunction


Очень часто встречается неправильная конструкция очистки, приводящая к утечкам:
call DestroyTimer(t)
call FlushChildHashtable(udg_hash,GetHandleId(t))
В данном случае, очистка произведена не будет, так как таймер уничтожается раньше получения его id, поэтому в функцию очистки будет подано неправильное значение (0).
Если вы используете конструкцию без переменной с id, то делать нужно так:
call FlushChildHashtable(udg_hash,GetHandleId(t))
call DestroyTimer(t)
То есть сначала очищать, а потом удалять таймер.
В случае с переменной, порядок этих действий значения не имеет.

Спелл готов, данные записываются, достаются и удаляются из хеш-таблицы.
Вот что у нас получилось в итоге:
Code
function SpellDamage takes nothing returns nothing  
  local timer t = GetExpiredTimer()  
  local integer h = GetHandleId(t)  
  local unit caster = LoadUnitHandle(udg_hash,h,1)  
  local unit target = LoadUnitHandle(udg_hash,h,2)  
  local integer counter = LoadInteger(udg_hash,h,3)  

  if counter>0 then  
  call UnitDamageTarget(caster,target,1.0,true,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,null)  
  call SaveInteger(udg_hash,h,3,counter-1)  
  else  
  call DestroyTimer(t)  
  call FlushChildHashtable(udg_hash,h)  
  endif  

  set caster = null  
  set target = null  
  set t = null  
  endfunction  

  function Spell takes nothing returns nothing  
  local unit caster = GetSpellAbilityUnit()  
  local unit target = GetSpellTargetUnit()  
  local timer t = CreateTimer()  
  local integer h = GetHandleId(t)  

  call SaveUnitHandle(udg_hash,h,1,caster)  
  call SaveUnitHandle(udg_hash,h,2,target)  
  call SaveInteger(udg_hash,h,3,125)  

  call TimerStart(t,0.04,true,function SpellDamage)  

  set caster = null  
  set target = null  
  set t = null  
  endfunction  

  function SpellCond takes nothing returns boolean  
  return GetSpellAbilityId()=='A000'  
  endfunction  

  //===========================================================================  
  function InitTrig_Spell takes nothing returns nothing  
  set gg_trg_Spell = CreateTrigger()  
  call TriggerRegisterPlayerUnitEvent(gg_trg_Spell,Player(0),EVENT_PLAYER_UNIT_SPELL_CAST,null)  
  call TriggerAddCondition(gg_trg_Spell,Condition(function SpellCond))  
  call TriggerAddAction(gg_trg_Spell,function Spell)  

  set udg_hash = InitHashtable()  
  endfunction
Категория: Создание карт | Добавил: Nevermore (05.02.2012)
Просмотров: 587 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]