Я больше чем на 70% уверен, что у меня код написан плохо. Так как взаимодействия с отрядами не очень сложилось у меня, прошу помочь оптимизировать код, и убрать лишнее. Наворотил всякого.
Код и карту приложил.
Код:
return GetSpellAbilityId() == 'A001'
endfunction
function FilterR takes nothing returns boolean
local unit z = LoadUnitHandle(udg_Hash,StringHash("x"),1)
local player p = GetOwningPlayer(z)
return  IsPlayerEnemy(p, GetOwningPlayer(GetFilterUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405 and not IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING)
endfunction
function Trig_R_Boom takes unit d returns nothing
local real x = GetUnitX(d)
local real y = GetUnitY(d)
local group g = CreateGroup()
local unit f
call GroupEnumUnitsInRange(g, x, y,500,Condition(function FilterR))
    loop
    set f = FirstOfGroup(g)
    exitwhen f == null
    call UnitDamageTarget(d,f,400, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
    call GroupRemoveUnit(g,f)
endloop
set g = null
set f = null
call DestroyGroup(g)
endfunction
function Trig_LOCAL_TimerR takes nothing returns nothing
local timer timerer = GetExpiredTimer()
local integer i = GetHandleId(timerer)
local unit Caster = LoadUnitHandle(udg_Hash,i,4)
local unit Dummy = LoadUnitHandle(udg_Hash,i,3)
local real xd = GetUnitX(Dummy)
local real yd = GetUnitY(Dummy)
if (GetWidgetLife(Dummy) < 0.405) then
call Trig_R_Boom (Dummy)
call FlushChildHashtable(udg_Hash,i)
call FlushChildHashtable(udg_Hash,StringHash("x"))
call DestroyTimer(timerer)
set Dummy = CreateUnit(GetOwningPlayer(Caster),'u001',xd,yd,0)
call UnitApplyTimedLife(Dummy,'BTLF', 2)
set Caster = null
set Dummy = null
else
set xd=GetUnitX(Dummy)+10*Cos(GetUnitFacing(Dummy)*bj_DEGTORAD)
set yd=GetUnitY(Dummy)+10*Sin(GetUnitFacing(Dummy)*bj_DEGTORAD)
call SetUnitPosition(Dummy,xd,yd)
endif
set Caster = null
set Dummy = null
endfunction

function Trig_R_Actions takes nothing returns nothing
local unit Caster = GetTriggerUnit()
local location Target = GetSpellTargetLoc()
local real xt = GetLocationX(Target)
local real yt = GetLocationY(Target)
local real xc = GetUnitX(Caster)
local real yc = GetUnitY(Caster)
local real Angle = bj_RADTODEG * Atan2(yt-yc,xt-xc)
local unit Dummy = CreateUnit(GetOwningPlayer(Caster),'u000',xc,yc,Angle)
local timer timerer = CreateTimer()
local integer i = GetHandleId(timerer)
call SetUnitAnimation( Dummy, "Birth" )
call UnitApplyTimedLife(Dummy,'BTLF', 1)
call SaveUnitHandle(udg_Hash,i,3,Dummy)
call SaveUnitHandle(udg_Hash,i,4,Caster)
call SaveUnitHandle(udg_Hash,StringHash("x"),1,Caster)
call TimerStart(timerer, 0.01, true, function Trig_LOCAL_TimerR)
call RemoveLocation(Target)
set Caster = null
set Dummy = null
endfunction
Карта:

Принятый ответ

call SaveInteger(udg_Hash,StringHash("x"),1,GetPlayerId(GetOwningPlayer(Caster)))

function Trig_R_Boom takes unit d, unit c returns nothing

call GroupEnumUnitsInRange(g, x, y,500,Condition(function FilterR))
loop
	set f = FirstOfGroup(g)
    exitwhen f == null
    call UnitDamageTarget(c,f,400, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
    call GroupRemoveUnit(g,f)
endloop

В конце функции Trig_R_Actions( )не обнулена переменная типа timer.
- Короче, вот:
function Trig_LOCAL_TimerR takes nothing returns nothing
    local  timer   timerer =  GetExpiredTimer( )
    local  integer i       =  GetHandleId( timerer )
    local  unit    Caster  =  LoadUnitHandle( udg_Hash, i, 4 )
    local  unit    Dummy   =  LoadUnitHandle( udg_Hash, i, 3 )
    local  real    xd      =  GetUnitX( Dummy )
    local  real    yd      =  GetUnitY( Dummy )
    local  group   g       =  null
    local  unit    f       =  null

    if ( GetWidgetLife( Dummy ) < 0.405 ) then

        set  g  =  CreateGroup( )

        call GroupEnumUnitsInRange( g, xd, yd, 500.0, null )
        loop
            set  f  =  FirstOfGroup( g )
            exitwhen ( f == null )
            call GroupRemoveUnit( g, f )

            if IsUnitEnemy( f, GetOwningPlayer( Caster ) ) and ( GetWidgetLife( f ) > 0.405 ) and ( not IsUnitType( f, UNIT_TYPE_STRUCTURE ) ) and ( not IsUnitType( f, UNIT_TYPE_FLYING ) ) then
                call UnitDamageTarget( Caster, f, 400.0, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null )
            endif

        endloop

        call DestroyGroup( g )
        set  g  =  null

        call FlushChildHashtable( udg_Hash, i )
        call DestroyTimer( timerer )
        set  Dummy  =  CreateUnit( GetOwningPlayer( Caster ), 'u001', xd, yd, 0.0 )
        call UnitApplyTimedLife( Dummy, 'BTLF', 2.0 )

    else
        set  xd = GetUnitX( Dummy ) + 10.0 * Cos( GetUnitFacing( Dummy ) * bj_DEGTORAD )
        set  yd = GetUnitY( Dummy ) + 10.0 * Sin( GetUnitFacing( Dummy ) * bj_DEGTORAD )
        call SetUnitPosition( Dummy, xd, yd )
    endif

    set  timerer  =  null
    set  Caster   =  null
    set  Dummy    =  null
endfunction


function Trig_R_Actions takes nothing returns nothing
    local  unit     Caster   =  GetTriggerUnit( )
    local  real     xt       =  GetSpellTargetX( )
    local  real     yt       =  GetSpellTargetY( )
    local  real     xc       =  GetUnitX( Caster )
    local  real     yc       =  GetUnitY( Caster )
    local  unit     Dummy    =  CreateUnit( GetOwningPlayer( Caster ), 'u000', xc, yc, bj_RADTODEG * Atan2( yt - yc, xt - xc ) )
    local  timer    timerer  =  CreateTimer( )
    local  integer  i        =  GetHandleId( timerer )

    call SetUnitAnimation( Dummy, "Birth" )
    call UnitApplyTimedLife( Dummy, 'BTLF', 1.0 )
    call SaveUnitHandle( udg_Hash, i, 3, Dummy )
    call SaveUnitHandle( udg_Hash, i, 4, Caster )
    call TimerStart( timerer, 0.01, true, function Trig_LOCAL_TimerR )

    set  Caster   =  null
    set  Dummy    =  null
    set  timerer  =  null
endfunction
0
32
6 лет назад
0
WoLLFeR, почему проще?
0
7
6 лет назад
Отредактирован WoLLFeR
0
quq_CCCP:
WoLLFeR, почему проще?
Потому что в фильтр я не могу обратиться, я не могу поставить туда условие связанное с кастером, потому что фильтр вызывается в таймере. Мне нужно, чтобы именно кастеру засчитывали нанесённый урон(он убивает(чтобы опыт зачислился), он своим не наносит урон). Либо я что-то не знаю, точнее как сделать иначе (внимательно на код в вопросе взгляните, мне приходится сохранять кастера, чтобы фильтр настроить, разве это удобно?). А как показал ScopteRectuS, используя всего лишь условие сразу видно, что не нужны лишние функции и могу непосредственно выбрать всё связанное с кастером, просто загрузив его хэндл привязанный к таймеру. Если не сложно, расскажите в чём преимущество фильтра, может я просто не знаю?
function FilterR takes nothing returns boolean

	local integer p = LoadInteger(udg_Hash,StringHash("x"),1)
	
return  IsPlayerEnemy(Player(p), GetOwningPlayer(GetFilterUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405 and not IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING)
endfunction
Чтобы не заниматься подобной ерундой (не приводящей к нужному результату), как на коде сверху, проще будет сделать так, без фильтра и всё сразу:
   if ( GetWidgetLife( Dummy ) < 0.405 ) then

        set  g  =  CreateGroup( )

        call GroupEnumUnitsInRange( g, xd, yd, 500.0, null )
        loop
            set  f  =  FirstOfGroup( g )
            exitwhen ( f == null )
            call GroupRemoveUnit( g, f )

            if IsUnitEnemy( f, GetOwningPlayer( Caster ) ) and ( GetWidgetLife( f ) > 0.405 ) and ( not IsUnitType( f, UNIT_TYPE_STRUCTURE ) ) and ( not IsUnitType( f, UNIT_TYPE_FLYING ) ) then
                call UnitDamageTarget( Caster, f, 400.0, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null )
            endif
Да, я понимаю, что с глобалкой я решу это, но что, если мне хотелось бы обойтись без неё? Предположим, если я юзаю спелл, он через таймер нанесёт урон через 1 секунду с помощью глобалки Кастера, но а если в этот момент, точнее где-то в 0.5 после моего юза, юзает кто-то еще, тогда глобалка запишет последнего кастера и урон в результате двух юзов сочтется для последнего кастера. Я так могу от своего же скилла подохнуть.
0
21
6 лет назад
0
Да вроде нет разницы какой-то существенной.. Разве что, через фильтр это официальный способ, который нам предоставили BLIZZARD.

Предположим, если я юзаю спелл, он через таймер нанесёт урон через 1 секунду с помощью глобалки Кастера, но а если в этот момент, точнее где-то в 0.5 после моего юза, юзает кто-то еще, тогда глобалка запишет последнего кастера и урон в результате двух юзов сочтется для последнего кастера. Я так могу от своего же скилла подохнуть.
В глобалки данные нужно записать прямо перед тем, как будет вызвана функция фильтра. И нужно уследить за тем, чтобы в функции фильтра не было функций, которые создают новые потоки, а если создают, то нужно смотреть, чтобы в новом потоке не использовалась это глобалка. Новые потоки могут вызываться при использовании следующих функции: TimerStart( ), ExecuteFunc( ), TriggerEvaluate( ), TriggerExecute( ), ForGroup( ) и другие, а также новые потоки могут создаваться при совершении события какого-либо триггера (например, приказ кастануть способность запустит новый поток, если имеется триггер на отлов каста способностей). Новые потоки имееют приоритет над уже работающими, поэтому сперва исполнится код в новом потоке, а после этого возобновится поток, который остановился из-за запуска нового потока.
0
32
6 лет назад
0
фильтр намного быстрее перебирает...
0
7
6 лет назад
Отредактирован WoLLFeR
0
ScopteRectuS, Уфф, морока, но в целом ясно почему так происходит.
quq_CCCP:
фильтр намного быстрее перебирает...
(Выбираем всех)Фильтр: проверяет, добавляет в группу, потом я произведу нужные действия через цикл.
(Выбираем всех в группу)Цикл: проверяет, производит действия.
В целом видно, что фильтр добавляет в группу после перебора, в отличии от цикла. Да, тут явно фильтр быстрее, так как цикл будет удалять из группы (лишние действия). Ещё что-нибудь?
Но цикл удобнее, имхо, кому как, в некоторых случаях мб фильтр будет намного эффективнее.
0
32
6 лет назад
0
WoLLFeR, нет, фильтр это обьект, который работает вне jass он намного быстрее, чем переборы цилками, это особая конструкция на с++ внутри движка, которая делает выборку обьектов. Ваши поделки медленее в разы.
Насчет обращений к фильтру - что не так? В нем наследуются параметры потока из которого он запушен + глобалки, юзайте фильтры а не перебирайте группы. для игрока - bj_groupEnumOwningPlayer - присваевете этой переменной нужного игрока перед фильтром, а в фильтре обращаетесь к этой переменной чтобы определить враг или не враг, так же с другими данными. Истекший таймер, стоп а нах он нам, нам нужно всго лишь его handle id и все, по хт ищим все нужные данные, но лучше предварительно занести их в глобалки, да и вполне удобно, можно не плодить фильтры как условия а сделать несколько на все случаи жизни.
0
7
6 лет назад
0
quq_CCCP, Намного быстрее? доли секунды что ли? Я бы заметил тогда. Речь наверно ведёте о нагрузке памяти. Да и в общем дело такое, я не умею пользоваться фильтрами так, как хотелось бы, мне удобнее циклом, я понимаю как сделать так, как мне нужно. Я с фильтрами уже замучился, найти бы статью или наработку, которая мне нормально объяснит что к чему, искал, видимо плохо, или не правильно.
0
32
6 лет назад
0
WoLLFeR, ну а что может быть непонятно с фильтром?
function EnemyFilter takes nothing returns boolean
    set bj_lastFilterUnit = GetFilterUnit( )
    return GetUnitState( bj_lastFilterUnit, UNIT_STATE_LIFE ) > 0.405 and IsUnitEnemy( bj_lastFilterUnit, bj_groupEnumOwningPlayer ) and not( IsUnitType( bj_lastFilterUnit, UNIT_TYPE_MAGIC_IMMUNE ) or IsUnitType( bj_lastFilterUnit, UNIT_TYPE_MECHANICAL ) or IsUnitInvulnerable( bj_lastFilterUnit ) or IsUnitWard( bj_lastFilterUnit ) )
endfunction
Вот пример фильтра, bj_lastFilterUnit - глобальная переменная типа юнит, записываем вначале функции фильтра - GetFilterUnit(), это юниты которых будет перебирать фильтр, далее в проверке IsUnitEnemy - еще одна глобальная переменная, типа player - bj_groupEnumOwningPlayer.
Юзается это вот так
set bj_groupEnumOwningPlayer = GetOwningPlayer( нужный юнит ) // ну устанавливаем нашего игрока.
call GroupEnumUnitsInRagne( grp, x,y, 500, Condition( function EnemyFilter ) )
В группе grp будут только живые враги для bj_groupEnumOwningPlayer, не здания, не имунные к магии и так далее (см. условия фильтра).
Все просто и вполне удобно, минимум писанины, что еще непонятно? и какие статьи?
0
7
6 лет назад
0
quq_CCCP, так бы сразу написали, я бы не мучился. Статьи о том как это устанавливать. Но теперь не нужно.
0
32
6 лет назад
0
WoLLFeR, ну хз, это есть в бж функциях, как бы фильтры примитив, никому до этого статей не требовалось...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.