Оптимальна ли реализация?
Написал простенькую систему разброса урона, чисто jass без диалектов и мемхака.
Собственно, вопрос в том, оптимально ли это или есть какие-то неочевидные минусы, способные как-то навредить?
//attack -> add damage event -> function


function damaging takes nothing returns nothing
    local integer i = 0
    local integer h = GetHandleId(GetExpiredTimer())
    local unit damager = LoadUnitHandle(udg_data, h, 0)
    local unit damaged = LoadUnitHandle(udg_data, h, 1)
    local real radius = GetRandomReal(udg_minradius, udg_maxradius) 
    local location tmp = PolarProjectionBJ(GetUnitLoc(damaged), radius, GetRandomReal(0, 360))      
    local unit u = CreateUnit(GetOwningPlayer(damager), 'h000', GetLocationX(tmp), GetLocationY(tmp), 0 )  
    call SetUnitInvulnerable( damaged, false )
    call DestroyEffect(AddSpecialEffectLoc( udg_effect_name, tmp ))
    call UnitDamagePointLoc( u, 0, udg_damage_radius, tmp, udg_damage, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_NORMAL ) 
    call RemoveUnit(u)
    set u = null
endfunction

function get_damage takes nothing returns nothing
    local timer t = CreateTimer()
    local integer h = GetHandleId(GetTriggeringTrigger())
    local unit damager = LoadUnitHandle(udg_data, h, 0)
    local unit damaged = LoadUnitHandle(udg_data, h, 1)
    local integer h = GetHandleId(t)                                     
    call SaveUnitHandle(udg_data, h, 0, damager)
    call SaveUnitHandle(udg_data, h, 1, damaged)
    call SetUnitInvulnerable( GetTriggerUnit(), true )
    call TimerStart(t, 0.0, false, function damaging)
    call DestroyTrigger(GetTriggeringTrigger())
endfunction

function Trig_attacking_Conditions takes nothing returns boolean
    return GetUnitTypeId(GetAttacker()) == 'hrif'
endfunction

function Trig_attacking_Actions takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer h = GetHandleId(t)
    call TriggerRegisterUnitEvent( t, GetTriggerUnit(), EVENT_UNIT_DAMAGED )            
    call TriggerAddAction(t, function get_damage)
    call SaveUnitHandle(udg_data, h, 0, GetAttacker())
    call SaveUnitHandle(udg_data, h, 1, GetTriggerUnit())
endfunction

function InitTrig_attacking takes nothing returns nothing
    set gg_trg_attacking = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_attacking, EVENT_PLAYER_UNIT_ATTACKED )
    call TriggerAddCondition( gg_trg_attacking, Condition( function Trig_attacking_Conditions ) )
    call TriggerAddAction( gg_trg_attacking, function Trig_attacking_Actions )
endfunction



Views: 205

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


XGM Bot #1 - 2 months ago 0
Голосов: +0 / -0
Похожие вопросы:

» ответ
Всё зависит от мелочей... если ты хочешь запаузить всех кто в данный момент находится на карте - это одно, если вообще ВСЕХ и ВСЁ - это другое...
Стомп застанит только тех, по кому попадёт... те кто появятся после - не будут застанены
Молотбурь застанит только тех, кого укажешь... кого не укажешь - не будут застанены
Пауза работать будет так же как молот, но так паузить баффы и прочее...
Можно собрать стан, который будет станить на 0.00 сек (вечно), а далее его снимать вручную... но тут надо учесть то что все станы должны будут это учитывать (или быть такими же - время таймерное).
. . .
По факту, когда я планировал перевести баффы на триггерную основу (тайминг), пришёл к выводу что мои баффы "обездвиживание" (сетка), "безмолвие" (сало), "обезоруживание" (дизарм - склад) и дополнительный бафф на "сковывание" (скорость поворота на 0), давали в сумме следующее:
  • нельзя двигаться и поворачиваться
  • нельзя кастовать
  • нельзя атаковать
В итоге это практически тот же стан... но с набором баффов в статусе (сетка и сало минимум)
» ответ
Есть одна наработка - Dota helper, это dll которая подключается мемхаком к карте, дает много чего (в основном для доты), но там есть дабл клик, который реализован так: когда игрок нажал способность и пытается её кликнуть в область инетфейса, код из библиотеки мгновенно переносят курсор в позицию портрета, так как определение координат виджетов на экране уже есть, думаю автонаведение сделать вполне можно.
Но это уже C++ и поддержки нету, думайте и решайте все сами.

makkad #2 - 2 months ago 0
Голосов: +0 / -0
Могу сказать, что TriggerAddAction вызывает утечку памяти, если отдельно его не сохранять и позже удалять. Лучше через TriggerAddCondition вызывать действия триггера.
rsfghd #3 - 2 months ago (изм. ) 0
Голосов: +0 / -0
Нет, не оптимально, юзаешь точки вместо координат и даже не обнуляешь их, переменные юнитов тоже не обнулил, таймеры не обнуляешь, да и локальные триггеры тоже в null можно

local location tmp = PolarProjectionBJ(GetUnitLoc(damaged), radius, GetRandomReal(0, 360)) 
-->
local real x = GetUnitX(damaged)+radius*Cos(GetRandomReal(0,360)*bj_DEGTORAD))
local real y = GetUnitY(damaged)+radius*Sin(GetRandomReal(0,360)*bj_DEGTORAD))
local unit u = CreateUnit(GetOwningPlayer(damager), 'h000',x, y, 0 ) 
Координаты обнулять не нужно

call UnitDamagePointLoc( u, 0, udg_damage_radius, tmp, udg_damage, ATTACK_TYPE_PIERCE, DAMAGE_TYPE_NORMAL )
Функция дамажит всех без исключения если не ошибаюсь, лучше самим юнитом через цикл/группу дамажить юнитов

Ой посмотрел на это всё, вообще желательно всё переписать, зачем сохранять что-то в хэш-таблицу если ты уже создал триггер для этого
map_maiker #4 - 2 months ago 0
Голосов: +0 / -0
call TriggerAddCondition( gg_trg_attacking, Condition( function Trig_attacking_Conditions ) )
То есть, можно написать нечто в духе
call TriggerAddCondition( gg_trg_attacking, function Trig_attacking_Actions )
и оно будет работать?
Нет, не оптимально, юзаешь точки вместо координат и даже не обнуляешь их, переменные юнитов тоже не обнулил, таймеры не обнуляешь, да и локальные триггеры тоже в null можно
Хм, ясно. А хеш-таблицу по ключу не надо очищать?
А сама идея норм или есть лучше варианты? С тз алгоритма.

Функция дамажит всех без исключения если не ошибаюсь, лучше самим юнитом через цикл/группу дамажить юнитов
В данном случае так и задумывалось...
rsfghd:
Ой посмотрел на это всё, вообще желательно всё переписать, зачем сохранять что-то в хэш-таблицу если ты уже создал триггер для этого
Значения передавать...
rsfghd #5 - 2 months ago (изм. ) 0
Голосов: +0 / -0
Не нужно значения передавать, у тебя триггер реагирует на дамаг по этому юниту, ты уже можешь получить нужные тебе переменные с самого триггера

Как ты вообще сохранил карту без ошибок, если у тебя пересоздание переменной идёт?

закинул счётчик хэндлов, можешь посмотреть что происходит
Прикрепленные файлы
makkad #6 - 2 months ago 0
Голосов: +0 / -0
local code c=function Trig_attacking_Actions
call TriggerAddCondition( gg_trg_attacking, Filter(c) )
rsfghd #7 - 2 months ago 0
Голосов: +0 / -0
Сейчас попробую реализовать твою задумку, если никто не преуспеет)
map_maiker #8 - 2 months ago 0
Голосов: +0 / -0
Не нужно значения передавать, у тебя триггер реагирует на дамаг по этому юниту, ты уже можешь получить нужные тебе переменные с самого триггера
Хм, логично. Как-то не подумал.
rsfghd:
Как ты вообще сохранил карту без ошибок, если у тебя пересоздание переменной идёт?
Забавно. Ну как-то так получилось. Мб вар не считает это ошибкой?
makkad:
local code c=function Trig_attacking_Actions
call TriggerAddCondition( gg_trg_attacking, Filter(c) )
Хм, а что Filter делает?

закинул счётчик хэндлов, можешь посмотреть что происходит
Вылет в главное меню...
rsfghd #9 - 2 months ago 0
Голосов: +0 / -0
map_maiker, сохрани карту перед запуском

И возможно стоит проверить эти галочки, раз уж вар не выдал ошибку с пересозданием переменной
Прикрепленные файлы
map_maiker #10 - 2 months ago 0
Голосов: +0 / -0
map_maiker, сохрани карту перед запуском
У меня обычный редактор, если это имеет значение. Как-то не дошли пока руки поставить jngp.
makkad #11 - 2 months ago 0
Голосов: +0 / -0
map_maiker, Filter() - в твоём случае позволит подставить вместо функции, которая должна возвратить boolean, подставить функцию, которая возвращает nothing
rsfghd #12 - 2 months ago 0
Голосов: +0 / -0
map_maiker, лол, как ты вообще работаешь с кодом в обычном редакторе?
Многое теряешь без джнгп, и скорее всего, мою версию не заценишь
map_maiker #13 - 2 months ago 0
Голосов: +0 / -0
makkad:
map_maiker, Filter() - в твоём случае позволит подставить вместо функции, которая должна возвратить boolean, подставить функцию, которая возвращает nothing
Хм, любопытно.

rsfghd:
map_maiker, лол, как ты вообще работаешь с кодом в обычном редакторе?
Многое теряешь без джнгп, и скорее всего, мою версию не заценишь
jasscraft есть. Так-то, я раньше особо не писал на jass, недавно вот решил что гуи сковывает. Изначально я хотел на гуи написать эту систему, но вышла хрень. Неработающая как надо, что важно.
На счёт твоей версии - мб и заценю...
rsfghd #14 - 2 months ago (изм. ) 0
Голосов: +0 / -0
Ну в таком случае вот
» раскрыть
Я не говорю, что это идеально, но чутка получше чем у тебя
library mylib initializer init
private group TempG = CreateGroup()
private trigger trg1 = CreateTrigger()

private function IsUnitDead takes unit u returns boolean
    return IsUnitType(u,UNIT_TYPE_DEAD) or GetUnitTypeId(u) < 1
endfunction

private function enemy takes nothing returns boolean
    set bj_lastReplacedUnit = GetFilterUnit()
    return not IsUnitDead(bj_lastReplacedUnit) and IsUnitEnemy(bj_lastReplacedUnit,GetOwningPlayer(bj_lastCreatedUnit))
endfunction

private function damage takes nothing returns nothing
    call UnitDamageTarget(bj_lastCreatedUnit,GetEnumUnit(),udg_damage,false,false,ATTACK_TYPE_PIERCE,DAMAGE_T​YPE_NORMAL,null)
endfunction

private function damage1 takes nothing returns nothing
    local unit attacker = GetEventDamageSource()
    local unit damaged = GetTriggerUnit()
    local real rad = GetRandomReal(udg_minradius,udg_maxradius)
    local real a = GetRandomReal(0,360)*bj_DEGTORAD
    local real x = GetUnitX(damaged)+rad*Cos(a)
    local real y = GetUnitY(damaged)+rad*Sin(a)
    local boolexpr b = Condition(function enemy)
    
    call KillUnit(CreateUnit(Player(15),'u000',x,y,0))
    set bj_lastCreatedUnit = attacker
    call GroupEnumUnitsInRange(TempG,x,y,udg_damage_radius,b)
    call DisableTrigger(trg1)
    call ForGroup(TempG,function damage)
    call EnableTrigger(trg1)
    call GroupClear(TempG)
    set bj_lastCreatedUnit = null
    
    set attacker = null
    set damaged = null
    set b = null
endfunction

//===========================================================================
private function myfunc takes nothing returns nothing
    call TriggerRegisterUnitEvent(trg1,GetEnumUnit(),EVENT_UNIT_DAMAGED)
endfunction
private function Trig_d1_Actions takes nothing returns nothing
    call TriggerRegisterUnitEvent(trg1,GetTriggerUnit(),EVENT_UNIT_DAMAGED)
endfunction
//===========================================================================
private function mycond1 takes nothing returns boolean
    return GetUnitTypeId(GetTriggerUnit()) != 'u000'
endfunction
private function mycond takes nothing returns boolean
    return GetEventDamage() > 0. and GetUnitTypeId(GetEventDamageSource()) == 'hrif'
endfunction
private function init takes nothing returns nothing
    set gg_trg_d1 = CreateTrigger(  )
    call GroupEnumUnitsInRect(TempG,bj_mapInitialPlayableArea,null)
    call ForGroup(TempG,function myfunc)
    call GroupClear(TempG)
    call TriggerRegisterEnterRectSimple( gg_trg_d1, bj_mapInitialPlayableArea )
    call TriggerAddCondition(gg_trg_d1,Condition(function mycond1))
    call TriggerAddAction( gg_trg_d1, function Trig_d1_Actions )
    call TriggerAddCondition(trg1,Condition(function mycond))
    call TriggerAddAction(trg1,function damage1)
endfunction
endlibrary

Кстати, ты можешь делать намного интереснее вещи с триггерной атакой, вплоть до кривой цепной молнии скакающей по юнитам накладывающая дебаффы с отталкиванием и всей ерундой что только можешь придумать

Так-то, я раньше особо не писал на jass, недавно вот решил что гуи сковывает.
Чистый гуи да, но у тебя есть кастом скрипт, где можно обнулить точки и это всё, что в принципе нужно, чтобы эта система работала без утечек
Прикрепленные файлы
PT153 #16 - 2 months ago (изм. ) 2
Голосов: +2 / -0
makkad:
Могу сказать, что TriggerAddAction вызывает утечку памяти, если отдельно его не сохранять и позже удалять. Лучше через TriggerAddCondition вызывать действия триггера.
А в чём разница? Это всё одно и тоже.

А по теме - динамическое создание триггеров тут не нужно.

Filter() - в твоём случае позволит подставить вместо функции, которая должна возвратить boolean, подставить функцию, которая возвращает nothing
Фильтр берёт коде, и возвращает фильтр, что подтип булеэкспры. А TriggerAddCondition принимает только булекспры.
makkad #17 - 2 months ago (изм. ) 0
Голосов: +0 / -0
PT153, Разница в утечке памяти. Вот тестовая карта. А удалять - это больше строчек на код тратить, конкретно в этом случае. Если sleep не нужен.
Прикрепленные файлы
PT153 #18 - 2 months ago (изм. ) 0
Голосов: +0 / -0
makkad, убедил. TriggerClearActions и ResetTrigger не нужно, это ничего не удаляет.
makkad #19 - 2 months ago 0
Голосов: +0 / -0
PT153:
makkad, убедил. TriggerClearActions и ResetTrigger не нужно, это ничего не удаляет.
Да. И это тоже проверялось.
DracoL1ch #20 - 2 months ago 0
Голосов: +0 / -0
AddAction создает уникальный объект при каждом обращении, а AddCondition кэширует объекты. поэтому в динамических триггерах, где всё создается и разрушается множество раз за игру, нужно использовать именно Condition
Filter() и Condition() одно и то же
quq_CCCP #21 - 2 months ago (изм. ) 3
Голосов: +3 / -0
Детект урона что ты описал, уже есть на сайте и готовый, широко юзается в доте фрога - и это вызывает баги, не столь критичные но тем не менее, ибо замахиваясь ты провоцирует событие триггера, который создаёт еше 1 триггер, который ждёт любого урона по цели, что неправильно, во первых время существования триггера нужно, вдруг промах, так же событие смерти, если жертва умерла - то закругляемся.
Про кондишены правильно написал лич, ибо триггер акшин это объект, который сам по себе не удаляется.
map_maiker #23 - 2 months ago 0
Голосов: +0 / -0
Кстати, ты можешь делать намного интереснее вещи с триггерной атакой, вплоть до кривой цепной молнии скакающей по юнитам накладывающая дебаффы с отталкиванием и всей ерундой что только можешь придумать
Да, я знаю. Дамми-касты и всё такое. rsfghd:
Чистый гуи да, но у тебя есть кастом скрипт, где можно обнулить точки и это всё, что в принципе нужно, чтобы эта система работала без утечек
Я же не про утечки. Например, как в гуи прицепить функцию к таймеру?
PT153:
А по теме - динамическое создание триггеров тут не нужно.
А можно подробнее?
quq_CCCP:
во первых время существования триггера нужно, вдруг промах,
Да, в случае промаха урон просто откладывается. Но я не придумал как можно прицепить удаление триггера по времени. Самый правильный вариант - добавить событие в триггер, но... В общем, как это сделать чтобы работало - я не придумал.
PT153 #24 - 2 months ago 0
Голосов: +0 / -0
map_maiker, юнит входит на карту, на него и добавляем событие на получение урона. Всё. А все эти костыли с событием на атаку бред. Тем более, что это замах, а не сама атака. И на каждый замах по новому триггеру - бред.
quq_CCCP #25 - 2 months ago 0
Голосов: +0 / -0
map_maiker, с мемхаком куда веселее, просто смотришь тип урона и атаки.
map_maiker #26 - 2 months ago 0
Голосов: +0 / -0
PT153:
map_maiker, юнит входит на карту, на него и добавляем событие на получение урона. Всё. А все эти костыли с событием на атаку бред. Тем более, что это замах, а не сама атака. И на каждый замах по новому триггеру - бред.
Звучит как создание одного громоздкого триггера...
quq_CCCP:
map_maiker, с мемхаком куда веселее, просто смотришь тип урона и атаки.
К мемхаку я только присматриваюсь. Издалека. Мб и до него руки дойдут.
PT153 #27 - 2 months ago 2
Голосов: +2 / -0
Звучит как создание одного громоздкого триггера...
Что лучше, чем бред с постоянным созданием и удалением триггеров. Сколько писал на варе, ни разу такой дичи не потребовалось.
quq_CCCP #28 - 2 months ago 0
Голосов: +0 / -0
map_maiker, проблема в другом - что такая реализация очень хромает и хоть как то еще годится для мили юнитов, для ренжей никак, можно стопя атаку провоцировать создание пачки триггеров, все яды, или другие виды урона от лица героя тоже заставят триггер сработать. Ибо нужно ставить еще событие время вышло (секунды полторы), события - юнит отдал приказ - цель точка, цель обьект, без указания цели, чтобы удалять триггер не дожидаясь урона по цели, но это годится для милишников, ренжам - только собтие время вышло (примерное время за сколько долетит снаряд до цели +- секунда). И все. Есть вариация еще с отрецательным маг уроном, это когда всем на карте вручена пассивка рунных браслетов, если урон выше 0 - он физический или чистый, иначе магический (ну условно, не будем вдаватся в подробности типов урона и атаки).
Ну и естественно компенсировать лечение от отрицательного маг урона, забирать пассивку браслетов и еще раз наносить урон от лица того кто до этого нанес урон, при этом отключив триггеры реагирующие на урон, после вернуть все наместо. Вот такая реализация кроме ресурсоемкости уже почти идеальна и как то заобузить или забаговать её не получится.

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

кстати там какраз выше производительность.
map_maiker #29 - 2 months ago 0
Голосов: +0 / -0
Что лучше, чем бред с постоянным созданием и удалением триггеров.
Как я понимаю, правильное удаление триггера не оставляет утечек. Каждый такой триггер живёт непродолжительное время, если добавить соответствующее событие.
Я не особо смотрел на производительность, но четыре сотни стрелков с задержкой между атаками 0.4 тормозили не больше чем любые другие четыре сотни сражающихся юнитов.
Так почему это бред?
события - юнит отдал приказ - цель точка, цель обьект, без указания цели,
А почему это не подойдёт к ренжам? В контексте отслеживания обычной атаки, не способностями.
quq_CCCP:
ренжам - только собтие время вышло (примерное время за сколько долетит снаряд до цели +- секунда)
А зачем +- секунда?
С наработкой детекта урона из мемхака у тебя появляется возможность проверять типы урона и прочие параметры в потоке любого триггера, сработавшего на событие - юнит получает урон.
кстати там какраз выше производительность.
Хм, ясно.
quq_CCCP #30 - 2 months ago 0
Голосов: +0 / -0
map_maiker, потому что нужно думать, ибо пока летит снаряд, юнит уже завершил атаку и может быть отдан новый приказ, что само собой все сломает.
Зачем секунда, другая - а чтобы исключить возможность промаха. чтобы у снаряда было время долететь и ударить, и если за отведенное время это го не получилось - то значит промах и отключаем и удаляем триггер. Как удалять триггеры правильно можно глянуть в доте айсфрога или в наработках на сайте (код баратрума и урсы кто то выкладывал).
Еще раз - подобные способы очень ограничены и их старается никто не юзать, кроме совсем простых вещей, плодить триггеры для сотен юнитов очень хреновая идея, потому что есть способы куда проще и оптимальнее.
PT153 #31 - 2 months ago 2
Голосов: +2 / -0
Так почему это бред?
Конкретно это реализация - потому что замах произойдёт, а атака нет, триггер будет создан, после чего атакованный получит урон от другого юнита, и созданный триггер сработает. И как я уже сказал, для данной задачи плодить триггеры не нужно, достаточно одного.
rsfghd #32 - 2 months ago (изм. ) 0
Голосов: +0 / -0
Если я не ошибаюсь, при удалении триггера событие же всё равно остаётся в памяти?

Звучит как создание одного громоздкого триггера...
Мой пример по такому принципу и работает, один триггер с регистрацией для всех юнитов на карте и те, кто входят в её область

Да, я знаю. Дамми-касты и всё такое
Ну вообще даммикаст только касательно дебаффов, всё остальное триггерно)
Да и на примере молний, разноцветную даммикастом фиг сделаешь)

Я же не про утечки. Например, как в гуи прицепить функцию к таймеру?
Что мешает другой триггер создать?)
Тебе в любом случае тут таймер не нужен был и я об этом и говорю, что твою затею спокойно на гуи можно было сделать
PT153 #33 - 2 months ago 2
Голосов: +2 / -0
Если я не ошибаюсь, при удалении триггера событие же всё равно остаётся в памяти?
Не остаётся.