Имеется код для одной способности включающая в себя, как активный режим, так и пассивный.
Задача состоит в оптимизации кода, преимущественно для активного режима, где герой перемещается (SetUnitPosition) с мелкой периодичностью.
Использую JNGP.
Функции, что используются часто (в нестандартном коде карты)
globals
group FilterGroup=null
player FilterPlayer=null
boolean FilterVisible=false
boolean RegisterDamage=true
endglobals

function UnitDamageTargetEx takes unit u, unit t, real d, boolean a, boolean r, attacktype at, damagetype dt, weapontype wt returns nothing
set RegisterDamage=false
call UnitDamageTarget(u,t,d,a,r,at,dt,wt)
set RegisterDamage=true
endfunction

function GroupFilter takes nothing returns boolean
local boolean b=((not IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD)) and GetUnitAbilityLevel(GetFilterUnit(),'Aloc')==0)
if FilterGroup!=null then
    set b=(b and not IsUnitInGroup(GetFilterUnit(),FilterGroup))
endif
if FilterPlayer!=null then
    set b=(b and IsUnitEnemy(GetFilterUnit(),FilterPlayer))
    if FilterVisible then
        set b=(b and IsUnitVisible(GetFilterUnit(),FilterPlayer) and not IsUnitFogged(GetFilterUnit(),FilterPlayer))
    endif
endif
return b
endfunction

function TextTag takes string s, real sz, unit u, real ho, integer r, integer g, integer b, real xv, real yv, real f, real l returns nothing
local texttag tt=CreateTextTag()
call SetTextTagText(tt,s,sz)
call SetTextTagPos(tt,GetUnitX(u),GetUnitY(u),ho)
call SetTextTagColor(tt,r,g,b,255)
call SetTextTagVelocity(tt,xv,yv)
call SetTextTagVisibility(tt,true)
call SetTextTagFadepoint(tt,f)
call SetTextTagLifespan(tt,l)
call SetTextTagPermanent(tt,false)
call SetTextTagSuspended(tt,false)
set tt=null
set u=null
set s=null
endfunction
Сам триггер способности...
globals
constant integer Custom_SkillW_Ability='A001'
constant integer Custom_SkillW_Ability_B='A006'
constant integer Custom_SkillW_Buff_B='B000'
constant integer Custom_SkillW_Ability_C='A00O'
constant integer Custom_SkillW_Ability_D='A008
constant integer Custom_SkillW_Ability_E='A007'
constant integer Custom_SkillW_HeroID_A='N000'
constant integer Custom_SkillW_HeroID_B='N001'

constant real Custom_SkillW_A_Bonus_Base=.7
constant real Custom_SkillW_A_Bonus_PerLvl=.3

constant real Custom_SkillW_C_Chance_Evade_Base=10.
constant real Custom_SkillW_C_Chance_Evade_PerLvl=.0
constant real Custom_SkillW_C_Chance_Evade_Stat_Base=.01
constant real Custom_SkillW_C_Chance_Evade_Stat_PerLvl=.01
constant real Custom_SkillW_C_Chance_Crit_Base=10.
constant real Custom_SkillW_C_Chance_Crit_PerLvl=.0
constant real Custom_SkillW_C_Chance_Crit_Stat_Base=.01
constant real Custom_SkillW_C_Chance_Crit_Stat_PerLvl=.01
constant real Custom_SkillW_C_Multi_Crit_Min_Base=1.5
constant real Custom_SkillW_C_Multi_Crit_Min_PerLvl=.0
constant real Custom_SkillW_C_Multi_Crit_Max_Base=3.
constant real Custom_SkillW_C_Multi_Crit_Max_PerLvl=.0

integer Custom_SkillW_C_StringMaxer
string array Custom_SkillW_C_Strings

constant real Custom_SkillW_E_Interval=.0125
constant real Custom_SkillW_E_Time_A=3.
constant real Custom_SkillW_E_Time_B=.5
constant real Custom_SkillW_E_AoE_A=600.+25.
constant real Custom_SkillW_E_AoE_B=.0
constant real Custom_SkillW_E_Bonus_Dmg_Base=.0
constant real Custom_SkillW_E_Bonus_Dmg_PerLvl=.0
constant real Custom_SkillW_E_Bonus_Stat_Dmg_Base=1.
constant real Custom_SkillW_E_Bonus_Stat_Dmg_PerLvl=.0

trigger SkillW_A=CreateTrigger()
trigger SkillW_B=CreateTrigger()
trigger SkillW_C=CreateTrigger()
trigger SkillW_D=CreateTrigger()
trigger SkillW_E=CreateTrigger()

integer SkillW_A__MAX=0
unit array SkillW_A__Unit
real array SkillW_A__Bonus

integer SkillW_D__MAX=0
unit array SkillW_D__Source
unit array SkillW_D__Target
effect array SkillW_D__Effect
trigger array SkillW_D__Trigger

timer SkillW_E__Timer=CreateTimer()
integer SkillW_E__MAX=0
unit array SkillW_E__Unit
unit array SkillW_E__Target
real array SkillW_E__Time
real array SkillW_E__Area
trigger array SkillW_E__Trigger
real array SkillW_E__Bonus
endglobals

//===========================================================================
function SkillW_A_Func_GetUnitIndex takes unit u returns integer
local integer index=0
loop
    exitwhen index==SkillW_A__MAX
    if u==SkillW_A__Unit[index] then
        return index
    endif
    set index=index+1
endloop
set u=null
return -1
endfunction

function SkillW_A_Func_Adder takes unit u, integer index returns nothing
local real level=I2R(GetUnitAbilityLevel(u,Custom_SkillW_Ability))
local real r=I2R(GetHeroLevel(u))*(Custom_SkillW_A_Bonus_Base+Custom_SkillW_A_Bonus_PerLvl*(level-1))
call SetHeroAgi(u,GetHeroAgi(u,false)-R2I(SkillW_A__Bonus[index]),true)
call SetHeroAgi(u,GetHeroAgi(u,false)+R2I(r),true)
if GetLocalPlayer()==GetOwningPlayer(u) then
    call TextTag("+"+R2SW(r-SkillW_A__Bonus[index],1,1),.023,u,250.,0,255,0,-0.04,.0,2,3)
endif
set SkillW_A__Bonus[index]=r
set u=null
endfunction

function SkillW_A_Func_Condition takes nothing returns boolean
local integer index=SkillW_A_Func_GetUnitIndex(GetTriggerUnit())
if GetLearnedSkill()==Custom_SkillW_Ability and ( not IsUnitIllusion(GetTriggerUnit()) ) then
    if index==-1 then
        set index=SkillW_A__MAX
        set SkillW_A__Unit[SkillW_A__MAX]=GetTriggerUnit()
        set SkillW_A__Bonus[SkillW_A__MAX]=.0
        call TriggerRegisterUnitEvent(SkillW_B, GetTriggerUnit(), EVENT_UNIT_HERO_LEVEL)
        call TriggerRegisterUnitEvent(SkillW_C, GetTriggerUnit(), EVENT_UNIT_DAMAGED)
        call TriggerRegisterUnitEvent(SkillW_E, GetTriggerUnit(), EVENT_UNIT_SPELL_EFFECT)
        set SkillW_A__MAX=SkillW_A__MAX+1
    endif
    call SkillW_A_Func_Adder(GetTriggerUnit(),index)
    return true
endif
return false
endfunction
//===========================================================================
function SkillW_B_Func_Condition takes nothing returns boolean
local integer index=SkillW_A_Func_GetUnitIndex(GetTriggerUnit())
if GetUnitAbilityLevel(GetTriggerUnit(),Custom_SkillW_Ability)!=0 then
    call SkillW_A_Func_Adder(GetTriggerUnit(),index)
    return true
endif
return false
endfunction

//===========================================================================
function SkillW_C_Func_Condition takes nothing returns boolean
local real level=I2R(GetUnitAbilityLevel(GetTriggerUnit(),Custom_SkillW_Ability))
local real stat=I2R(GetHeroAgi(GetTriggerUnit(),true))
local real r=(Custom_SkillW_C_Chance_Evade_Base+Custom_SkillW_C_Chance_Evade_PerLvl*(level-1))+stat*(Custom_SkillW_C_Chance_Evade_Stat_Base+Custom_SkillW_C_Chance_Evade_Stat_PerLvl*(level-1))
if GetUnitAbilityLevel(GetTriggerUnit(),Custom_SkillW_Ability)!=0 and GetRandomReal(0.,100.)<=r and GetEventDamage()>1. and GetEventDamageSource()!=GetTriggerUnit() then
    call TextTag(Custom_SkillW_C_Strings[GetRandomInt(0,Custom_SkillW_C_StringMaxer)],0.022,GetTriggerUnit(),100.,255,0,0,.0,.06,1,2)
    call SetWidgetLife(GetTriggerUnit(),GetWidgetLife(GetTriggerUnit())+GetEventDamage())
    return true
endif
return false
endfunction

//===========================================================================
function SkillW_D_Func_GetTriggerIndex takes trigger t returns integer
    local integer index=0
    loop
        exitwhen index==SkillW_D__MAX
        if t==SkillW_D__Trigger[index] then
            set t=null
            return index
        endif
        set index=index+1
    endloop
    set t=null
    return -1
endfunction

function SkillW_D_Func_GetSourceIndex takes unit u returns integer
    local integer index=0
    loop
        exitwhen index==SkillW_D__MAX
        if u==SkillW_D__Source[index] then
            set u=null
            return index
        endif
        set index=index+1
    endloop
    set u=null
    return -1
endfunction

function SkillW_D_Func_Second takes nothing returns boolean
    local trigger tr=GetTriggeringTrigger()
    local unit u=GetEventDamageSource()
    local unit t=GetTriggerUnit()
    local real level=I2R(GetUnitAbilityLevel(u,Custom_SkillW_Ability))
    local real min=Custom_SkillW_C_Multi_Crit_Min_Base+Custom_SkillW_C_Multi_Crit_Min_PerLvl*(level-1)
    local real max=Custom_SkillW_C_Multi_Crit_Max_Base+Custom_SkillW_C_Multi_Crit_Max_PerLvl*(level-1)
    local real dmg=GetEventDamage()*GetRandomReal(min,max)
    local integer index=SkillW_D_Func_GetTriggerIndex(tr)
    local boolean b=false
    if GetTriggerEventId()==EVENT_UNIT_DAMAGED then
        if GetEventDamage()>1. and u==SkillW_D__Source[index] and t==SkillW_D__Target[index] and RegisterDamage then
            set b=true
            call DisableTrigger(tr)
            call QueueUnitAnimation( u, "stand, ready" )
            call TextTag(I2S(R2I(dmg))+"!",.023,u,0.,255,0,0,.0,.04,2,3)
            call UnitDamageTargetEx(u,t,dmg-GetEventDamage(),true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,null)
            call DestroyEffect(AddSpecialEffectTarget( GetAbilityEffectById(Custom_SkillW_Ability,EFFECT_TYPE_MISSILE,1), t, "chest"))
        endif
    else
        set b=true
        call DisableTrigger(tr)
    endif
    if b then
        call DestroyTrigger(tr)
        call DestroyEffect(SkillW_D__Effect[index])
        set SkillW_D__MAX=SkillW_D__MAX-1
        set SkillW_D__Source[index]=SkillW_D__Source[SkillW_D__MAX]
        set SkillW_D__Target[index]=SkillW_D__Target[SkillW_D__MAX]
        set SkillW_D__Effect[index]=SkillW_D__Effect[SkillW_D__MAX]
        set SkillW_D__Trigger[index]=SkillW_D__Trigger[SkillW_D__MAX]
        set SkillW_D__Source[SkillW_D__MAX]=null
        set SkillW_D__Target[SkillW_D__MAX]=null
        set SkillW_D__Effect[SkillW_D__MAX]=null
        set SkillW_D__Trigger[SkillW_D__MAX]=null
    endif
    set t=null
    set u=null
    set tr=null
    return false
endfunction

function SkillW_D_Func_Condition takes nothing returns boolean
    local unit u=GetAttacker()
    local unit t=GetTriggerUnit()
    local integer index=SkillW_D_Func_GetSourceIndex(u)
    local real level=I2R(GetUnitAbilityLevel(u,Custom_SkillW_Ability))
    local real stat=I2R(GetHeroAgi(u,true))
    local real r=(Custom_SkillW_C_Chance_Crit_Base+Custom_SkillW_C_Chance_Crit_PerLvl*(level-1))+stat*(Custom_SkillW_C_Chance_Crit_Stat_Base+Custom_SkillW_C_Chance_Crit_Stat_PerLvl*(level-1))
    if level!=0 and (GetRandomReal(.0,100.)<=r or GetUnitTypeId(u)==Custom_SkillW_HeroID_B) and ( not IsUnitIllusion(GetAttacker()) ) and index==-1 then
        call SetUnitAnimation( u, "slam" )
        set SkillW_D__Source[SkillW_D__MAX]=u
        set SkillW_D__Target[SkillW_D__MAX]=t
        set SkillW_D__Trigger[SkillW_D__MAX]=CreateTrigger()
        set SkillW_D__Effect[SkillW_D__MAX]=AddSpecialEffectTarget( GetAbilityEffectById(Custom_SkillW_Ability,EFFECT_TYPE_MISSILE,0), u, "hand, left")
        call TriggerRegisterDeathEvent( SkillW_D__Trigger[SkillW_D__MAX], t)
        call TriggerRegisterUnitEvent( SkillW_D__Trigger[SkillW_D__MAX], t, EVENT_UNIT_DAMAGED)
        call TriggerRegisterDeathEvent( SkillW_D__Trigger[SkillW_D__MAX], u)
        call TriggerRegisterTimerEvent( SkillW_D__Trigger[SkillW_D__MAX], 2.,false)
        call TriggerAddCondition( SkillW_D__Trigger[SkillW_D__MAX], Condition( function SkillW_D_Func_Second) )
        set SkillW_D__MAX=SkillW_D__MAX+1
        set t=null
        set u=null
        return true
    endif
    set t=null
    set u=null
    return false
endfunction

//===========================================================================
function SkillW_E_Func_GetTriggerIndex takes trigger t returns integer
local integer index=0
loop
    exitwhen index==SkillW_E__MAX
    if t==SkillW_E__Trigger[index] then
        set t=null
        return index
    endif
    set index=index+1
endloop
set t=null
    return index
endfunction

function SkillW_E_Func_GetUnitIndex takes unit u returns integer
local integer index=0
loop
    exitwhen index==SkillW_E__MAX
    if u==SkillW_E__Unit[index] then
        set u=null
        return index
    endif
    set index=index+1
endloop
set u=null
    return index
endfunction

function SkillW_E_Func_TargetActions takes nothing returns boolean
local trigger tr=GetTriggeringTrigger()
local integer index=SkillW_E_Func_GetTriggerIndex(tr)
local unit u=SkillW_E__Unit[index]
local unit t=SkillW_E__Target[index]
local real stat=I2R(GetHeroAgi(u,true))
local real lvl=I2R(GetUnitAbilityLevel(u,Custom_SkillW_Ability)-1)
local real bd=(Custom_SkillW_E_Bonus_Dmg_Base+Custom_SkillW_E_Bonus_Dmg_PerLvl*lvl)+(stat*(Custom_SkillW_E_Bonus_Stat_Dmg_Base+Custom_SkillW_E_Bonus_Stat_Dmg_PerLvl*lvl))
local boolean b=false
if GetTriggerEventId()==EVENT_UNIT_ATTACKED then
    if GetTriggerUnit()==t and GetAttacker()==u then
        if IsUnitType(t,UNIT_TYPE_HERO) then
            set SkillW_E__Bonus[index]=bd
            set STATS_UNIT=u
            set STATS_VALUE=SkillW_E__Bonus[index]
            set STATS_ADDER=0
            call TriggerEvaluate(gg_trg_Stats)
        else
            call UnitAddAbility(u,Custom_SkillW_Ability_E)
            call SetUnitAbilityLevel(u,Custom_SkillW_Ability_E,R2I(lvl+1))
        endif
        call SetUnitAnimation( u, "slam" )
        call TriggerRegisterUnitEvent( SkillW_E__Trigger[index], t, EVENT_UNIT_DAMAGED)
    else
        call DisableTrigger(tr)
        set b=true
    endif
elseif GetTriggerEventId()==EVENT_UNIT_DAMAGED then
    if GetTriggerUnit()==t and GetEventDamageSource()==u and RegisterDamage then
        call DisableTrigger(tr)
        set b=true
    endif
else
    call DisableTrigger(tr)
    set b=true
endif
if b then
    call UnitRemoveAbility(u,Custom_SkillW_Ability_E)
    set STATS_UNIT=u
    set STATS_VALUE=-SkillW_E__Bonus[index]
    set STATS_ADDER=0
    call TriggerEvaluate(gg_trg_Stats)
    set SkillW_E__Target[index]=null
    call DestroyTrigger(tr)
    set SkillW_E__Trigger[index]=null
endif
set tr=null
set u=null
set t=null
    return false
endfunction

function SkillW_E_Func_AreaActions takes integer index returns nothing
local group g=CreateGroup()
local player p=GetOwningPlayer(SkillW_E__Unit[index])
local unit t
local real x=GetUnitX(SkillW_E__Unit[index])
local real y=GetUnitY(SkillW_E__Unit[index])
local real angle
local integer i=0
set FilterPlayer=p
set FilterVisible=true
call GroupEnumUnitsInRange(g,x,y,SkillW_E__Area[index],Filter(function GroupFilter))
set FilterPlayer=null
set FilterVisible=false
loop
    set t=FirstOfGroup(g)
    exitwhen t==null
    if GetRandomInt(0,i)==0 then
        set i=i+1
        set SkillW_E__Target[index]=t
    endif
    call GroupRemoveUnit(g,t)
endloop
if SkillW_E__Target[index]!=null then
    if GetUnitTypeId(SkillW_E__Unit[index])==Custom_SkillW_HeroID_A then
        call UnitAddAbility(SkillW_E__Unit[index],Custom_SkillW_Ability_C)
        call UnitRemoveAbility(SkillW_E__Unit[index],Custom_SkillW_Ability_C)
    endif
    call SetUnitPathing(SkillW_E__Unit[index],false)
    call SetUnitTimeScale( SkillW_E__Unit[index], 3)
    set angle=GetRandomReal(.0,bj_PI*2)
    set x=GetUnitX(SkillW_E__Target[index])+50*Cos(angle)
    set y=GetUnitY(SkillW_E__Target[index])+50*Sin(angle)
    call DestroyTrigger(SkillW_E__Trigger[index])
    call SetUnitPosition(SkillW_E__Unit[index],x,y)
    call SetUnitFacing(SkillW_E__Unit[index],bj_RADTODEG*Atan2(GetUnitY(SkillW_E__Target[index])-GetUnitY(SkillW_E__Unit[index]),GetUnitX(SkillW_E__Target[index])-GetUnitX(SkillW_E__Unit[index])))
    call IssueInstantTargetOrder(SkillW_E__Unit[index],"attack",SkillW_E__Target[index],SkillW_E__Target[index])
    set SkillW_E__Trigger[index]=CreateTrigger()
    call TriggerRegisterDeathEvent( SkillW_E__Trigger[index], SkillW_E__Target[index])
    call TriggerRegisterUnitEvent( SkillW_E__Trigger[index], SkillW_E__Target[index], EVENT_UNIT_ATTACKED)
    call TriggerRegisterDeathEvent( SkillW_E__Trigger[index], SkillW_E__Unit[index])
    call TriggerRegisterTimerEvent( SkillW_E__Trigger[index], 0.25, false)
    call TriggerAddCondition( SkillW_E__Trigger[index], Condition( function SkillW_E_Func_TargetActions) )
else
    if GetUnitTypeId(SkillW_E__Unit[index])==Custom_SkillW_HeroID_B then
        call UnitAddAbility(SkillW_E__Unit[index],Custom_SkillW_Ability_D)
        call UnitRemoveAbility(SkillW_E__Unit[index],Custom_SkillW_Ability_D)
    endif
    call SetUnitPathing(SkillW_E__Unit[index],true)
    call SetUnitTimeScale( SkillW_E__Unit[index], 1)
endif
call DestroyGroup(g)
set p=null
set t=null
set g=null
endfunction

function SkillW_E_Func_Periodic takes nothing returns nothing
local integer index=0
loop
    exitwhen index==SkillW_E__MAX
    if SkillW_E__Time[index]>.0 and (not IsUnitType(SkillW_E__Unit[index],UNIT_TYPE_DEAD)) then
        set SkillW_E__Time[index]=SkillW_E__Time[index]-Custom_SkillW_E_Interval
        if SkillW_E__Target[index]==null then
            call SkillW_E_Func_AreaActions(index)
        endif
    else
        call UnitRemoveAbility(SkillW_E__Unit[index],Custom_SkillW_Ability_E)
        set STATS_UNIT=SkillW_E__Unit[index]
        set STATS_VALUE=-SkillW_E__Bonus[index]
        set STATS_ADDER=0
        call TriggerEvaluate(gg_trg_Stats)
        if GetUnitTypeId(SkillW_E__Unit[index])==Custom_SkillW_HeroID_B then
            call UnitAddAbility(SkillW_E__Unit[index],Custom_SkillW_Ability_D)
            call UnitRemoveAbility(SkillW_E__Unit[index],Custom_SkillW_Ability_D)
        endif
        call SetUnitPathing(SkillW_E__Unit[index],true)
        call SetUnitTimeScale( SkillW_E__Unit[index], 1)
        call TriggerExecute(SkillW_E__Trigger[index])
        call SetUnitAnimation( SkillW_E__Unit[index], "stand" )
        set SkillW_E__MAX=SkillW_E__MAX-1
        set SkillW_E__Unit[index]=SkillW_E__Unit[SkillW_E__MAX]
        set SkillW_E__Time[index]=SkillW_E__Time[SkillW_E__MAX]
        set SkillW_E__Area[index]=SkillW_E__Area[SkillW_E__MAX]
        set SkillW_E__Target[index]=SkillW_E__Target[SkillW_E__MAX]
        set SkillW_E__Trigger[index]=SkillW_E__Trigger[SkillW_E__MAX]
        set SkillW_E__Unit[SkillW_E__MAX]=null
        set SkillW_E__Target[SkillW_E__MAX]=null
        set SkillW_E__Trigger[SkillW_E__MAX]=null
        set SkillW_E__Time[SkillW_E__MAX]=.0
        set SkillW_E__Area[SkillW_E__MAX]=.0
        set index=index-1
        if SkillW_E__MAX==0 then
            call PauseTimer(SkillW_E__Timer)
        endif
    endif
    set index=index+1
endloop
endfunction

function SkillW_E_Func_Condition takes nothing returns boolean
local unit u=GetTriggerUnit()
local integer level=GetUnitAbilityLevel(u,Custom_SkillW_Ability)
local real time=Custom_SkillW_E_Time_A-Custom_SkillW_E_Time_B+(Custom_SkillW_E_Time_B*level)
if GetSpellAbilityId()==Custom_SkillW_Ability then
    if SkillW_E__MAX==0 then
        call TimerStart(SkillW_E__Timer,Custom_SkillW_E_Interval,true,function SkillW_E_Func_Periodic)
    endif
    set BuffSystem_TARGET=u
    set BuffSystem_ID_Ability=Custom_SkillW_Ability_B
    set BuffSystem_ID_Buff=Custom_SkillW_Buff_B
    set BuffSystem_TIME=time
    call TriggerEvaluate(gg_trg_BuffSystem)
    set SkillW_E__Unit[SkillW_E__MAX]=u
    set SkillW_E__Time[SkillW_E__MAX]=time
    set SkillW_E__Area[SkillW_E__MAX]=Custom_SkillW_E_AoE_A-Custom_SkillW_E_AoE_B+(Custom_SkillW_E_AoE_B*level)
    set SkillW_E__Target[SkillW_E__MAX]=null
    set SkillW_E__Trigger[SkillW_E__MAX]=null
    set SkillW_E__MAX=SkillW_E__MAX+1
    set u=null
    return true
endif
set u=null
    return false
endfunction

function SkillW_Func_Init takes nothing returns nothing
local unit u=CreateUnit(Player(15),'u000',0,0,0)
local integer index=0
set Custom_SkillW_C_Strings[0]="Не прошел"
set Custom_SkillW_C_Strings[1]="Увернулся"
set Custom_SkillW_C_Strings[2]="Что-то не так"
set Custom_SkillW_C_Strings[3]="Прошло мимо"
set Custom_SkillW_C_Strings[4]="Забавно..."
set Custom_SkillW_C_StringMaxer=4
loop
    call TriggerRegisterPlayerUnitEvent(SkillW_A,Player(index),EVENT_PLAYER_HERO_SKILL,null)
    call TriggerRegisterPlayerUnitEvent(SkillW_D,Player(index),EVENT_PLAYER_UNIT_ATTACKED,null)
    call SetPlayerAbilityAvailable(Player(index),Custom_SkillW_Ability_E,false)
    set index=index+1
    exitwhen index==16
endloop
call UnitAddAbility(u,Custom_SkillW_Ability)
call UnitAddAbility(u,Custom_SkillW_Ability_B)
call UnitAddAbility(u,Custom_SkillW_Ability_C)
call UnitAddAbility(u,Custom_SkillW_Ability_D)
call UnitAddAbility(u,Custom_SkillW_Ability_E)
call RemoveUnit(u)
set u=null
call TriggerAddCondition(SkillW_A, Condition(function SkillW_A_Func_Condition))
call TriggerAddCondition(SkillW_B, Condition(function SkillW_B_Func_Condition))
call TriggerAddCondition(SkillW_C, Condition(function SkillW_C_Func_Condition))
call TriggerAddCondition(SkillW_D, Condition(function SkillW_D_Func_Condition))
call TriggerAddCondition(SkillW_E, Condition(function SkillW_E_Func_Condition))
endfunction
Custom Stats System v1.0 (not full)
globals
constant real Custom_STATS_RegenInterval=0.125

unit STATS_UNIT=null
real STATS_VALUE=.0
integer STATS_ADDER=0

integer STATS__MAX=0
trigger array STATS__TRIGGER
unit array STATS__UNIT
real array STATS__DMG
real array STATS__AS
real array STATS__DEF
real array STATS__RHP
real array STATS__RMP
real array STATS__HP
real array STATS__MP
endglobals

//===========================================================================
function Stats_Func_Adder takes unit u, integer value, string stat returns nothing
local integer i=0
local integer ii=0
local boolean b=false

if stat=="dmg" then
    loop
        call UnitMakeAbilityPermanent(u,false,STATS_DMG_Ability[i])
        call UnitRemoveAbility(u,STATS_DMG_Ability[i])
        exitwhen i==STATS_DMG_MaxInteger
        set i=i+1
    endloop

    if value>STATS_DMG_MaxValue then
        set value=STATS_DMG_MaxValue
    elseif value<-STATS_DMG_MaxValue then
        set value=-STATS_DMG_MaxValue
    endif

    if value<0 then
        set b=true
        set value=-value
    endif

    if value>0 then
        loop
            exitwhen i<0
            set ii=value/STATS_DMG_Integer[i]
            if ii>0 then
                if b then
                    call UnitAddAbility(u,STATS_DMG_Ability[i])
                    call SetUnitAbilityLevel(u,STATS_DMG_Ability[i],2)
                else
                    call UnitAddAbility(u,STATS_DMG_Ability[i])
                endif
                call UnitMakeAbilityPermanent(u,true,STATS_DMG_Ability[i])
            endif
            set value=value-(ii*STATS_DMG_Integer[i])
            set i=i-1
        endloop
    endif
    
endif
set stat=null
set u=null
endfunction

//===========================================================================
function Stats_Func_GetUnitIndex takes unit u returns integer
local integer index=0
loop
    exitwhen index==STATS__MAX
    if u==STATS__UNIT[index] then
        set u=null
        return index
    endif
    set index=index+1
endloop
set u=null
return -1
endfunction

function Stats_Func_GetTriggerIndex takes trigger t returns integer
local integer index=0
loop
    exitwhen index==STATS__MAX
    if t==STATS__TRIGGER[index] then
        set t=null
        return index
    endif
    set index=index+1
endloop
set t=null
call DisplayTimedTextToPlayer(Player(0),0,0,10.,"|cffff0000ОШИБКА! Искомый триггер не находится в цикле!|r")
return -1
endfunction

function Stats_Func_Regen takes unit u, real rh, real rm returns boolean
local real hp=GetWidgetLife(u)
local real mp=GetUnitState(u,UNIT_STATE_MANA)
if rh>.0 then
    set hp=hp+rh
elseif rh<.0 then
    if (hp-1)>-rh then
        set hp=hp+rh
    else
        set hp=1
    endif
endif
if rm>0. then
    set mp=mp+rm
elseif rm<.0 then
    if mp>-rm then
        set mp=mp+rm
    else
        set mp=0
    endif
endif
call SetWidgetLife(u,hp)
call SetUnitState(u,UNIT_STATE_MANA, mp)
set u=null
return true
endfunction


function Stats_Func_Checker takes nothing returns boolean
local trigger t=GetTriggeringTrigger()
local boolean b=false
local integer index=Stats_Func_GetTriggerIndex(t)

if index!=-1 then
    if GetTriggerEventId()==EVENT_UNIT_DEATH and not IsUnitType(STATS__UNIT[index],UNIT_TYPE_HERO) then
        set b=true
        call DisableTrigger(t)
    else
        if STATS__DMG[index]==0 and STATS__DEF[index]==0 and STATS__AS[index]==0 and STATS__RHP[index]==.0 and STATS__RMP[index]==.0 and STATS__HP[index]==.0 and STATS__MP[index]==.0 then
            set b=true
            call DisableTrigger(t)
        else
            if (STATS__RHP[index]!=0. or STATS__RMP[index]!=0) and (not IsUnitType(STATS__UNIT[index],UNIT_TYPE_DEAD)) then
                call Stats_Func_Regen(STATS__UNIT[index], STATS__RHP[index]*Custom_STATS_RegenInterval, STATS__RMP[index]*Custom_STATS_RegenInterval)
            endif
        endif
    endif
else
    set b=true
    call DisableTrigger(t)
endif

if b then
    call DestroyTrigger(t)
    call Stats_Func_Adder(STATS__UNIT[index],R2I(-STATS__DMG[index]),"dmg")
    call Stats_Func_Adder(STATS__UNIT[index],R2I(-STATS__DEF[index]),"def")
    call Stats_Func_Adder(STATS__UNIT[index],R2I(-STATS__AS[index]),"as")
    set STATS__RHP[index]=.0
    set STATS__RMP[index]=.0
    
    set STATS__MAX=STATS__MAX-1
    set STATS__UNIT[index]=STATS__UNIT[STATS__MAX]
    set STATS__TRIGGER[index]=STATS__TRIGGER[STATS__MAX]
    
    set STATS__DMG[index]=STATS__DMG[STATS__MAX]
    set STATS__DEF[index]=STATS__DEF[STATS__MAX]
    set STATS__AS[index]=STATS__AS[STATS__MAX]
    set STATS__RHP[index]=STATS__RHP[STATS__MAX]
    set STATS__RMP[index]=STATS__RMP[STATS__MAX]
    set STATS__MP[index]=STATS__MP[STATS__MAX]
    set STATS__HP[index]=STATS__HP[STATS__MAX]
    
    set STATS__UNIT[STATS__MAX]=null
    set STATS__TRIGGER[STATS__MAX]=null
    
    set STATS__DMG[STATS__MAX]=.0
    set STATS__DEF[STATS__MAX]=.0
    set STATS__AS[STATS__MAX]=.0
    set STATS__RHP[STATS__MAX]=.0
    set STATS__RMP[STATS__MAX]=.0
    set STATS__MP[STATS__MAX]=.0
    set STATS__HP[STATS__MAX]=.0
endif

set t=null
return false
endfunction

function Stats_Func_Main takes nothing returns boolean
local unit u=STATS_UNIT
local integer index=Stats_Func_GetUnitIndex(u)

if index==-1 then
    set index=STATS__MAX
    set STATS__UNIT[STATS__MAX]=u
    set STATS__TRIGGER[STATS__MAX]=CreateTrigger()
    call TriggerRegisterUnitEvent(STATS__TRIGGER[STATS__MAX],u,EVENT_UNIT_DEATH)
    call TriggerRegisterTimerEvent(STATS__TRIGGER[STATS__MAX],Custom_STATS_RegenInterval,true)
    call TriggerAddCondition(STATS__TRIGGER[STATS__MAX],Condition(function Stats_Func_Checker))
    set STATS__MAX=STATS__MAX+1
endif

if STATS_ADDER==0 then
set STATS__DMG[index]=STATS__DMG[index]+STATS_VALUE
call Stats_Func_Adder(STATS__UNIT[index],R2I(STATS__DMG[index]),"dmg")
endif

set STATS_UNIT=null
set STATS_VALUE=.0
set STATS_ADDER=0
set u=null
return true
endfunction

function STATS_Func_Init takes nothing returns nothing
set gg_trg_Stats=CreateTrigger()
call TriggerAddCondition(gg_trg_Stats, Condition( function Stats_Func_Main))
endfunction
Мое желание оптимизировать на сколько возможно. Увеличение до 0.03125 сек. периода так же входит в оптимизацию, но не могу додумать, как без периода запустить по повторной функцию на поиск цели.
Добавлю еще часть от системы добавления характеристик. На случай возникновения вопроса о системе.
Custom Stats System v1.0 (DMG part)
globals
integer STATS_DMG_MaxValue=0
integer STATS_DMG_MaxInteger=0

integer array STATS_DMG_Ability
integer array STATS_DMG_Integer
endglobals

function STATS_DMG_Func_Init takes nothing returns nothing
local integer index=1
local unit u=CreateUnit(Player(15),'hpea',0,0,0)
set STATS_DMG_Ability[0]='A009'
set STATS_DMG_Ability[1]='A00A'
set STATS_DMG_Ability[2]='A00B'
set STATS_DMG_Ability[3]='A00C'
set STATS_DMG_Ability[4]='A00D'
set STATS_DMG_Ability[5]='A00E'
set STATS_DMG_Ability[6]='A00F'
set STATS_DMG_Ability[7]='A00G'
set STATS_DMG_Ability[8]='A00H'
set STATS_DMG_Ability[9]='A00I'
set STATS_DMG_Ability[10]='A00J'
set STATS_DMG_Ability[11]='A00K'
set STATS_DMG_MaxInteger=11
set STATS_DMG_Integer[0]=1
set STATS_DMG_MaxValue=1
loop
set STATS_DMG_Integer[index]=STATS_DMG_Integer[index-1]*2
set STATS_DMG_MaxValue=STATS_DMG_MaxValue+STATS_DMG_Integer[index]
exitwhen index==STATS_DMG_MaxInteger
set index=index+1
endloop
loop
call UnitAddAbility(u,STATS_DMG_Ability[index])
exitwhen index==0
set index=index-1
endloop
call RemoveUnit(u)
set u=null
endfunction

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

SetUnitPosition довольно тяжелая операци, юзайте SetUnitX\Y в конце полета юзай SetUnitPosition для того чтобы юнит не залетел в непроходимое место.
Фильтр просто ужас, ну кто так фильтры делает?
Проверка на 'Aloc', я в ужасе на кой черт проверять на дамми если GropEnumUnitsInRange\Rect не выделяет москитов, тока EnumOfPlayer может пикнуть москитов, остальное не пикает их, на то они и москиты...
Вот как выглядит нормальный фильтр без локалок и прочего
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 ) )
endfunction
UnitDamageTargetEx - красиво но нафиг ненужно обводить в отдельную функцию с тучей аргументов + жутко неудобно, 100500 аргументов у функции и фиг знает за что какой от вечает, прямо так UnitDamageTarget, без отдельной функции.
Туча констант, тоже хорошо тока длят наработок, на деле ставь конкретные значения или юзай Difine vjass'a.
`
ОЖИДАНИЕ РЕКЛАМЫ...
4
Clamp, спору нет. Тогда тебе задачка, как сделать, чтобы функция на поиск цели (SkillW_E_Func_AreaActions) запустилась по повторной после того как Герой нанесет урон выбранной цели?
Поясняю, сия функция находится ниже чем отлов урона, далее сия функция требует целочисленную (index героя).
Запустить нативкой ExecuteFunc не выйдет, в силу требуемой целочисленной.
30
DazzleFirst, я не читал твой код, опиши механику работы способности на словах, пожалуйста.
4
Из РО.
Пассивно дает шанс при атаке нанести многократный урон и дает шанс избежать любой урон, а так же увеличивает добавку к ловкости за каждый уровень.
При использовании на время действия способности Герой приобретает максимальную скорость и телепортируется к случайному врагу, что находится в области действия. Если враг герой, то наносит доп. урон, иначе урон снижается.
Вот, вроде бы, все описано как есть.
30
Ну с пассивной частью, я думаю, проблем нет, как и со скоростью при активации (абила сапога с 522 скоростью).
При активации выбираем юнита, проверяем герой или нет и по результату проверки интовой переменной даём значение, равное нужному урону. Затем телепортируем и наносим урон из переменной.
Или я проглядел какой-то подводный камень?
4
SkillW_E_Func_GetTriggerIndex - выдает порядковый номер триггера из массива.
SkillW_E_Func_GetUnitIndex - выдает порядковый номер героя из массива.
SkillW_E_Func_TargetActions - отвечает за отлов атаки и урона.
SkillW_E_Func_AreaActions - отвечает за поиск цели.
SkillW_E_Func_Periodic - отвечает за время действия и обнуления массива.
SkillW_E_Func_Condition - отвечает за использование способности.
Clamp:
Или я проглядел какой-то подводный камень?
Причина в том что Герой меняется на Героя имеющего перезарядку атаки =0.00, тем самым атака зависит только от анимации, но этот сменный не имеет скорости ходьбы, дабы отрубить кнопку Move, иначе игрок мог запросто нажимать ПКМ и тем самым сбивать атаку сменного героя.
По поводу скорости, дается аура (торнадо), что дает макс. скорость атаки и ходьбы.
Урон зависит от ловкости, тем самым здесь участвует система добавления характеристик, в данном случае атаки. Урон же, дается только на одну атаку, поэтому я и мучаюсь с этим отловом атаки и урона.
Вроде бы пояснил, что мог.
К тому же я кинул карту с героем, если кому угодно, может протестировать героя и поделиться своим впечатлением.
НО предупреждаю, что Третья способность еще не оптимизирована как мне хочется, в силу этого из-за нее во время теста могут быть лаги.
30
По твоему первому описанию абилка просто наносит урон один раз и перемещает героя, а не накручивает на него кучу странной ненужной лабуды.
Окей, сейчас подумаем.
4
Я кажись не правильно объясняю... поэтому совет будет один, протестировать. Может тогда поймешь чего я хочу.
28
Верно, многих отлавливаю, но в случае активного режима способности проделывается как раз-таки отлов только одного.
можно создать 1 триггер с событием
TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_ATTACKED)
и обрабатывать в нём всех юнитов которые атакуют
а не плодить кучу триггеров с одинаковым событием
и не смотреть на то как они отбирают друг у друга процессорное время
4
nvc123:
Верно, многих отлавливаю, но в случае активного режима способности проделывается как раз-таки отлов только одного.
можно создать 1 триггер с событием
TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_ATTACKED)
и обрабатывать в нём всех юнитов которые атакуют
а не плодить кучу триггеров с одинаковым событием
и не смотреть на то как они отбирают друг у друга процессорное время
Насколько я понял, ты предлагаешь во время способности включать сия триггер, а по завершению вырубать?
Но как я определю нанес ли Герой урон атакованному? Ведь флудить в сия триггере
EVENT_UNIT_DAMAGED
очень плохо. А если еще один триггер создавать с отловом урона, то все равно придется обращаться к массиву и искать нанесшего урон и получившего урон.
А так выходит сразу отлов атаки => отлов урона => уничтожение триггера и обнуление массива. Иль это медленней нежили создание системы по отлову атаки и урона?
28
DazzleFirst, что за бред ты пишешь
ты же сказал что событие атаки у тебя используется не только в этом триггере и не только во время этой способности
вот я и говорю что вместо нескольких триггеров с 1 событием лучше использовать 1 триггер с этим же событием
включать/выключать триггер это больший бред чем удаление
4
что за бред ты пишешь
Разве бред? В карте всего лишь 3 триггера с таким евентом. Два для пассивок первой и второй способности, третий для активного режима. В производительности, думаю, слабо видны изменения. А есть предложения помимо евентов? Что мне действительно нужно, так это обращение внимания на функции, а не поиск проблем в евентах.
28
DazzleFirst, ну так вместо 3 триггеров можно юзать 1
учитывая вес триггера и то что триггеры это потоки получается неплохая экономия
в функции SkillW_A_Func_Adder переменная левел юзается только 1 раз а значит нафиг не нужна
также не стоит писать в условиях большой код (иногда бывает баг из за которого условие не успевает вернуть результат а действие уже проверяет результат условия)
4
ну так вместо 3 триггеров можно юзать 1
Согласен использовать лишь для пассивок. Для активного режима способности используется
EVENT_UNIT_ATTACKED
только одна боевая единица, в силу этого триггер не будет реагировать на другие атаки. Тем самым повышается производительность.
в функции SkillW_A_Func_Adder переменная левел юзается только 1 раз а значит нафиг не нужна
Верно, как-то не сообразил.
также не стоит писать в условиях большой код
С этим спору нет, но как я погляжу в моем коде особых усилий не приложено. Однако я собираюсь еще впихнуть одну проверку на неуязвимость от дядьки DioD'а из wc3c и уже после этого, думаю, возможен такой бажок.
28
Тем самым повышается производительность.
что за бред
когда твоего юнита атакуют то будет срабатывать 2 триггера и у обоих будут проверятся условия
вместо этого можно просто написать в действии триггера с общим событием атаки
if(GetTriggerUnit()==myunit){
// your code
return;
}
где myunit это юнит которому ты планировал давать событие EVENT_UNIT_ATTACKED
а your code это код который ты собирался выполнять для этого юнита
не кажется что это проще чем создавать триггер ради 1 юнита
4
Эх... опять не договариваю я...
Это не бред, ибо на время действия способности создается один триггер на отлов атаки и урона именной одной взятой цели, а не нескольких, как ты мне предлагаешь. Ибо при включенной способности каждый атакованный будет проверятся, а скорость-то велика (менее 1 раза в 0.02), тем самым пойдет конкретная нагрузка с перезапусками логических и поиск цели в массивах. А с моим способом проверяется только единственный атакованный с запуском только одного массива, а потом удаление триггера, дабы триггер более не проверял атакованного или получившего урон и не нагружал варчик.
Надеюсь толково объяснил почему производительность больше?
33
Зачем доказывать логически, когда можно померять? — Капитан Экспериментальный Метод
28
DazzleFirst, другие триггеры на отлов атаки в этот момент существуют?
до этого ты говорил что да
4
nvc123, да, т.е. для каждой пассивной способности по отдельному триггеру с евентом
EVENT_PLAYER_UNIT_ATTACKED
к ним же вдобавок тот самый отлов для активки, но он создается только на время действия способности.

Кет, так как осуществить проверку?
33
Ну если вопрос в быстродействии, то сделать таймер и вывести на экран его показания до начала работы триггеров и после их завершения, я полагаю.
Или сразу количество прошедего времени посчитать.
28
DazzleFirst, тогда перестань писать бред и противоречить сам себе
DazzleFirst:
Это не бред, ибо на время действия способности создается один триггер на отлов атаки и урона именной одной взятой цели, а не нескольких, как ты мне предлагаешь.
для каждой пассивной способности по отдельному триггеру с евентом
то есть во время действия способности любая атака отлавливается N(количество пассивок) раз
+ атаку по твоему юниту отлавливает N+1 раз
я же предлагаю отлавливать лишь 1 раз
т. к. у тебя более 1 пассивки то N>1
следовательно мой вариант выгоднее
ибо он требует в N раз меньше оперативной памяти и процессорного времени
4
Блин... я не противоречу, просто не так выражаю мысль и работу способности. Ошибка с моей стороны при которой ты не можешь понять, что мне действительно нужно.

Пробну еще раз...
SkillW_Func_Init - функция, что вызывается после создания героя. В ней:
		call TriggerRegisterPlayerUnitEvent(SkillW_D,Player(index),EVENT_PLAYER_UNIT_ATTACKED,null)
  • первый евент на отлов атаки он же основной, для пассивки многократного урона.
SkillW_A_Func_Condition - функция, которая вызывается при изучении способности. Здесь:
        call TriggerRegisterUnitEvent(SkillW_C, GetTriggerUnit(), EVENT_UNIT_DAMAGED)
  • отлов урона для триггерного уворота.
SkillW_D_Func_Condition - функция, что вызывается при каждой атаке. Здесь:
		call TriggerRegisterUnitEvent( SkillW_D__Trigger[SkillW_D__MAX], t, EVENT_UNIT_DAMAGED)
  • временный отлов урона. пассивка многократного урона, триггер уничтожается после использования
SkillW_E_Func_Condition - функция, отлавливает использование способности. начало того, что мне желанно оптимизировать.
SkillW_E_Func_Periodic - функция, что вызывается каждый период времени.
SkillW_E_Func_AreaActions - функция, что отвечает за поиск цели. Эту же функцию мне хочется вызывать после нанесения урона. В ней:
call TriggerRegisterUnitEvent( SkillW_E__Trigger[index], SkillW_E__Target[index], EVENT_UNIT_ATTACKED)
  • временный отлов атаки.
SkillW_E_Func_TargetActions - функция с триггером, что отлавливает атаку и урон, а после уничтожает триггер.
call TriggerRegisterUnitEvent( SkillW_E__Trigger[index], t, EVENT_UNIT_DAMAGED)
  • временный отлов урона.
Теперь разберу последнюю функцию, ибо без этого не понять основу моего выбора сия способа.
Первым делом функция срабатывает после того как цель способности (выбирается случайно в области действия) была атакована. Далее проверка на атакующего
local trigger tr=GetTriggeringTrigger()
local integer index=SkillW_E_Func_GetTriggerIndex(tr)
local unit u=SkillW_E__Unit[index]
local unit t=SkillW_E__Target[index]
...
if GetTriggerUnit()==t and GetAttacker()==u then
Т.е. проверяем наш герой ли атаковал.
Далее проверяем
if IsUnitType(t,UNIT_TYPE_HERO) then
атакованный герой?
  • Да.
    Даем дополнительную атаку на один удар.
  • Нет
    Даем способность, что уменьшает наносимый урон нашим Героем.
Далее...
        call SetUnitAnimation( u, "slam" )
        call TriggerRegisterUnitEvent( SkillW_E__Trigger[index], t, EVENT_UNIT_DAMAGED)
даем триггеру евент на отлов урона. После того как цель способности получит урон пойдет опять проверка на "вшивость"
local trigger tr=GetTriggeringTrigger()
local integer index=SkillW_E_Func_GetTriggerIndex(tr)
local unit u=SkillW_E__Unit[index]
local unit t=SkillW_E__Target[index]
...
elseif GetTriggerEventId()==EVENT_UNIT_DAMAGED then
    if GetTriggerUnit()==t and GetEventDamageSource()==u and RegisterDamage then
извиняюсь за каламбур с названной переменной, ибо по началу я даже не знал как обозвать ее, а потом было лениво переименовывать
Если получивший урон и есть наша цель способности, нанесший урон и есть наш Герой, полученный урон не от способности, то
        call DisableTrigger(tr)
        set b=true
...
if b then
    call UnitRemoveAbility(u,Custom_SkillW_Ability_E)
    set STATS_UNIT=u
    set STATS_VALUE=-SkillW_E__Bonus[index]
    set STATS_ADDER=0
    call TriggerEvaluate(gg_trg_Stats)
    set SkillW_E__Target[index]=null
    call DestroyTrigger(tr)
    set SkillW_E__Trigger[index]=null
endif
Иными словами уничтожаем триггер и убираем доп. атаку и способность по уменьшению урона по не героям.
Надеюсь уже совсем хорошо пояснил, что за что отвечает. И понятно почему нужно отлавливать как атаку так и урон.
Теперь задача:
Как реализовать вызов функции SkillW_E_Func_AreaActions из функции SkillW_E_Func_TargetActions после уничтожение триггера в последней функции?
28
временный отлов атаки работает одновременно с отловом у пасивки
вместо этого можно у пасивки сделать проверку что скил включен и атакованый юнит == наш юнит
я всё правильно понял
это ты просто тупиш
4
Вот ты блин... придрался к этому евенту... Пока не проверю лично, что лучше, я не признаю твою правоту.
И меня не евенты интересуют, а функции и их корешки...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.