Venomus Gale

» не публикуется
Venomous Gale Venomanser'а из DotA в том виде в котором его реализовал IceFrog.
Из доты 6.83с с восстановленными именами функций и переменных.

Вспомогательные системы IceFrog'а

» DamageTarget
Фрог почему то ленится каждый раз писать тип атаки и урона, а вместо того пользуется заготовленной функцией с шаблонными типами урона.
function UnitDamageEnemy takes unit P7I,unit P8I,integer P9I,real PAI returns nothing
  if P9I==0 then
    return
  endif
  if P9I==1 then
    call UnitDamageTarget(P7I,P8I,PAI,true,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,WEAPON_TYPE_WHOKNOWS)
  elseif P9I==2 then
    call UnitDamageTarget(P7I,P8I,PAI,true,true,ATTACK_TYPE_HERO,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
  elseif P9I==3 then
    call UnitDamageTarget(P7I,P8I,PAI,true,true,ATTACK_TYPE_HERO,DAMAGE_TYPE_MAGIC,WEAPON_TYPE_WHOKNOWS)
  elseif P9I==4 then
    call UnitDamageTarget(P7I,P8I,PAI,true,true,ATTACK_TYPE_PIERCE,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
  elseif P9I==5 then
    call UnitDamageTarget(P7I,P8I,PAI,true,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
  elseif P9I==6 then
    call SetUnitState(P8I,UNIT_STATE_LIFE,RMaxBJ(GetUnitState(P8I,UNIT_STATE_LIFE)-PAI,1))
    if GetUnitState(P8I,UNIT_STATE_LIFE)<2 then
      call UnitRemoveBuffs(P8I,true,true)
      call UnitRemoveAbility(P8I,'Aetl')
      call UnitDamageTarget(CreateUnit(GetOwningPlayer(P7I),'e00E',0,0,0),P8I,100000000.00,true,false,ATTACK_TY​PE_MELEE,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
    endif
  elseif P9I==7 then
    if GetUnitAbilityLevel(P8I,'Aetl')>0 or GetUnitAbilityLevel(P8I,'B01N')>0 then
      call UnitDamageTarget(P7I,P8I,PAI,true,true,ATTACK_TYPE_HERO,DAMAGE_TYPE_MAGIC,WEAPON_TYPE_WHOKNOWS)
    else
      call UnitDamageTarget(P7I,P8I,PAI,true,true,ATTACK_TYPE_HERO,DAMAGE_TYPE_UNIVERSAL,WEAPON_TYPE_WHOKNOWS)
    endif
  elseif P9I==8 then
    call UnitDamageTarget(P7I,P8I,PAI,true,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_UNIVERSAL,WEAPON_TYPE_WHOKNOWS​)
  endif
endfunction
» GroupUtils
Кто то надаумил фрога использовать ресайклы групп...
library GroupUtils
//******************************************************************************
//* BY: Rising_Dusk
//* 
//* This library is a simple implementation of a stack for groups that need to
//* be in the user's control for greater than an instant of time. Additionally,
//* this library provides a single, global group variable for use with user-end
//* enumerations. It is important to note that users should not be calling
//* DestroyGroup() on the global group, since then it may not exist for when it
//* it is next needed.
//*
//* The group stack removes the need for destroying groups and replaces it with
//* a recycling method.
//*     function NewGroup takes nothing returns group
//*     function ReleaseGroup takes group g returns boolean
//*     function GroupRefresh takes group g returns nothing
//* 
//* NewGroup grabs a currently unused group from the stack or creates one if the
//* stack is empty. You can use this group however you'd like, but always
//* remember to call ReleaseGroup on it when you are done with it. If you don't
//* release it, it will 'leak' and your stack may eventually overflow if you
//* keep doing that.
//* 
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hash table. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it.
//* 
globals
    //* Group for use with all instant enumerations
    group ENUM_GROUP = CreateGroup()
    
    //* Temporary references for GroupRefresh
    private boolean Flag                                              = false
    private group Refr                                                = null
    
    //* Assorted constants
    private constant integer MAX_HANDLE_COUNT                         = 408000
    private constant integer MIN_HANDLE_ID                            = 0x100000
    
    //* Arrays and counter for the group stack
    private group array Groups
    private integer array Status[MAX_HANDLE_COUNT]
    private integer Count                                             = 0
endglobals

private function AddEx takes nothing returns nothing
    if Flag then
        call GroupClear(Refr)
        set Flag = false
    endif
    call GroupAddUnit(Refr, GetEnumUnit())
endfunction
    
function GroupRefresh takes group g returns nothing
    set Flag = true
    set Refr = g
    call ForGroup(Refr, function AddEx)
    if Flag then
        call GroupClear(g)
    endif
endfunction

function NewGroup takes nothing returns group
    if Count == 0 then
        set Groups[0] = CreateGroup()
    else
        set Count = Count - 1
    endif
    set Status[GetHandleId(Groups[Count])-MIN_HANDLE_ID] = 1
    return Groups[Count]
endfunction

function ReleaseGroup takes group g returns boolean
    local integer stat = Status[GetHandleId(g)-MIN_HANDLE_ID]
    local boolean b    = true
    if g == null then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Null groups cannot be released")
        set b = false
    elseif stat == 0 then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Group not part of stack")
        set b = false
    elseif stat == 2 then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Groups cannot be multiply released")
        set b = false
    elseif Count == 8191 then
        debug call BJDebugMsg(SCOPE_PREFIX+" Error: Max groups achieved, destroying group")
        call DestroyGroup(g)
        set b = false
    else
        call GroupClear(g)
        set Groups[Count]                = g
        set Count                        = Count + 1
        set Status[GetHandleId(g)-MIN_HANDLE_ID] = 2
    endif
    return b
endfunction
endlibrary
» Trigger Remover
Любовь фрога к триггерам дает свои плоды...
Собственно кто то написал систему удаления триггеров чтобы не было багов коллизии хендлов триггеров, но явно не фрог.
globals
    timer DispTimer = null
    integer nTriggerIndex = 0
    trigger array DispTriggers
    real array Elapsed
    boolean udg_ItsForDebug = true
endglobals

function ClenTrigger_ERROR takes nothing returns nothing
    local integer nPlayerIndex = 0
    if udg_ItsForDebug then
        loop
            call DisplayTimedTextToPlayer( Player( nPlayerIndex ), 0, 0, 120, "|c00ff0303Что то пошло нетак!|r" )
            call DisplayTimedTextToPlayer( Player( nPlayerIndex ), 0, 0, 120, "|c00ff0303Это все фигня, ни какайте в трусы, этот баг несерьезен!|r" )
            call DisplayTimedTextToPlayer( Player( nPlayerIndex ), 0, 0, 120, "|c00ff0303Не забудьте сохранить реплей, а так же не поленитесь отправить его мне на мыло IceFrog@gmail.com|r" )
            set nPlayerIndex = nPlayerIndex + 1
            exitwhen nPlayerIndex == 12
        endloop
    endif
endfunction

function CleanTrigger_GetTime takes nothing returns real // зачем он обьявил эту функцию?
    return TimerGetElapsed( DispTimer )                  // так как она не где не используется оптимизатор не стер её оригинальное имя
endfunction

function DisposeTrigger takes trigger t returns nothing
    call DisableTrigger( t )
    set nTriggerIndex = nTriggerIndex + 1
    set DispTriggers[nTriggerIndex] = t
    set Elapsed[nTriggerIndex] = TimerGetElapsed( DispTimer ) + 60
    if nTriggerIndex > 8000 then
        call ClenTrigger_ERROR( )
    endif
endfunction

function CleanTriggerArray takes integer nIndex returns nothing
    if nIndex != nTriggerIndex then
        set DispTriggers[nIndex] = DispTriggers[nTriggerIndex]
        set Elapsed[nIndex] = Elapsed[nTriggerIndex]
    endif
    set DispTriggers[nTriggerIndex] = null
    set Elapsed[nTriggerIndex] = 0
    set nTriggerIndex = nTriggerIndex - 1
endfunction

function Trig_Trigger_Remover_Actions takes nothing returns nothing
    local real CleanTime = TimerGetElapsed( DispTimer )
    local integer nIndex = 1

    loop
        exitwhen nIndex > nTriggerIndex
        if Elapsed[nIndex] < CleanTime then
            if DispTriggers[nIndex] == null or IsTriggerEnabled( DispTriggers[nIndex] ) == true then
                call ClenTrigger_ERROR( )
            else
                call DestroyTrigger( DispTriggers[nIndex] )
            endif
            call CleanTriggerArray( nIndex )
        else
            set nIndex = nIndex + 1
        endif
    endloop
  
    return
endfunction

//===========================================================================
function InitTrig_Trigger_Remover takes nothing returns nothing
    set gg_trg_Trigger_Remover = CreateTrigger( )
    set DispTimer = CreateTimer( )
    call TimerStart( DispTimer, 999999999999.00, false, null )
    call TriggerRegisterTimerEvent( gg_trg_Trigger_Remover, 15.00, true )
    call TriggerAddAction( gg_trg_Trigger_Remover, function Trig_Trigger_Remover_Actions )
endfunction

Venomus Gale

» Код способности
function AddTextTagTarget takes string UWI,real M5I,unit UXI,real UJI,integer r,integer g,integer b,integer a returns nothing
  local texttag tt=CreateTextTag()
  call SetTextTagText(tt,UWI,UJI)
  call SetTextTagPosUnit(tt,UXI,64)
  call SetTextTagColor(tt,r,g,b,a)
  call SetTextTagVelocity(tt,0,0.0355)
  call SetTextTagFadepoint(tt,2)
  call SetTextTagPermanent(tt,false)
  call SetTextTagLifespan(tt,M5I)
  if IsUnitVisible(UXI,GetLocalPlayer()) then //or P0I(GetLocalPlayer())then
    call SetTextTagVisibility(tt,true)
  else
    call SetTextTagVisibility(tt,false)
  endif
  set tt=null
endfunction


function UnitAddPremanentAbility takes unit whichunit,integer id returns nothing // позволяет не терять при морфах способности добавленные UnitAddAbility.
  call UnitAddAbility(whichunit,id)
  call UnitMakeAbilityPermanent(whichunit,true,id)
endfunction

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

function GaleAvalibleTargets_Cond takes nothing returns boolean // фильтр для группы Venomus Gale
    // Допустимы цели -  враги, без скилла 'A04R' (маркер курьера и осадных войск), не здания, живые.
    return IsUnitEnemy( udg_FilterUnit, GetOwningPlayer( GetFilterUnit( ) ) )and( GetUnitAbilityLevel( GetFilterUnit( ), 'A04R' ) == 0 and IsUnitType( GetFilterUnit( ), UNIT_TYPE_STRUCTURE ) == false and IsUnitDead( GetFilterUnit( ) ) == false )
endfunction

function PlaySoundAtCord takes sound Snd, real x, real y returns nothing // проигрывает некий звук в указанных координатах.
    call SetSoundPosition( Snd, x, y, 0 )
    call SetSoundVolume( Snd, 127 )
    set bj_lastPlayedSound = Snd
    if ( Snd != null ) then
        call StartSound( Snd )
    endif
endfunction

function abp takes real xa, real xb, real ya, real yb returns real // находит угол между точками координатами точек в градусах.
    return 57.295827 * Atan2( yb - ya, xb - xa )
endfunction

function Gale_Damage_Enemy takes nothing returns boolean
    local trigger trig = GetTriggeringTrigger( )
    local integer TrigId = GetHandleId( trig )
    local unit Attacker = ( LoadUnitHandle( udg_data, ( TrigId ), ( 2 ) ) )
    local unit Enemy = ( LoadUnitHandle( udg_data, ( TrigId ), ( 17 ) ) )
    local integer level = ( LoadInteger( udg_data, ( TrigId ), ( 5 ) ) )
    
    // проверка того что истекло время, с юнита рассеяли 'BEsh' (в оригинале 'B0AQ') или юнит умер (смертью считаеся даже юниты с Aegis of the Immortal или аналогом)
    if GetTriggerEvalCount( trig ) > 5 or GetUnitAbilityLevel( Enemy, 'BEsh'  ) == 0 or GetTriggerEventId( ) == EVENT_WIDGET_DEATH then
        call DisposeTrigger( trig ) // функция удаления триггеров, из системы вероятно написанной Вексом, советую посмотреть вспомогательные системы выше.
        call FlushChildHashtable( udg_data, ( TrigId ) )
    else
        call UnitDamageEnemy( Attacker, Enemy, 1, 30 * level - 30 )
        // если уровень больше 1 то создает триггер наносящий переодический урон.
        // наносит урон по формуле 30 * уровень - 30.
        // тип урона ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE - обычное заклинание.
        call AddTextTagTarget( I2S( 30 * level - 30 ), 3, Enemy, 0.025, 160, 255, 0, 230 )
        // Создает текстаг над головой цели, внешне похожый на обычный Shadow Strike.
    endif
    
    set trig = null
    set Attacker = null
    set Enemy = null
    return false
endfunction

function UnitAddGale takes unit Attacker, unit Enemy, integer level returns nothing // поражает юнитов 'A17N', так же создавая триггер следящий за целью и наносящим урон.
    local unit Caster = CreateUnit( GetOwningPlayer( Enemy ), 'hdum', GetUnitX( Enemy ), GetUnitY( Enemy ), 0 ) // в оригинале 'e00E' - обычный дамми юнит.
    local trigger trig
    local integer TrigId
                                                   // 'A17N' - обычный Shadow Strike (Отравленный нож), урон обнулен, нужен только для замедления.
    call UnitAddPremanentAbility( Caster, 'A17N' ) // фрог тронулся, дамми юнитов незачем морфить и потому способность у них не пропадет...
    
    call SetUnitAbilityLevel( Caster, 'A17N', level )
    
    if IssueTargetOrder( Caster, "shadowstrike", Enemy ) then
    
        call UnitDamageEnemy( Attacker, Enemy, 1, 25 * level ) // наносит урон цели, тип урона ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE - обычное заклинание.
        
        call AddTextTagTarget( I2S( 25 * level ) + "!", 3, Enemy, 0.025, 160, 255, 0, 230 ) // Создает текстаг над головой цели, внешне похожый на обычный Shadow Strike.
        
        if level > 1 then // если уровень больше 1 то создает триггер наносящий переодический урон.
                          // наносит урон по формуле 30 * уровень - 30.
                          // тип урона ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE - обычное заклинание.
                          
            set trig = CreateTrigger( )
            set TrigId = GetHandleId( trig )
            
            call TriggerRegisterTimerEvent( trig, 3, true )
            call TriggerRegisterDeathEvent( trig, Enemy )
            call TriggerAddCondition( trig, Condition( function Gale_Damage_Enemy ) ) // создает утечку типа triggercondition!
            
            call SaveUnitHandle( udg_data, ( TrigId ), ( 2 ), ( Attacker ) )
            call SaveUnitHandle( udg_data, ( TrigId ), ( 17 ), ( Enemy ) )
            call SaveInteger( udg_data, ( TrigId ), ( 5 ), ( level ) )
            
            set trig = null
        endif
        
    endif
    
    set Caster = null
endfunction

function Gale_Pick_Enemy takes nothing returns nothing
    local unit enemy = GetEnumUnit( )
    if IsUnitInGroup( enemy, udg_TempGroup ) == false then
        call UnitAddGale( udg_TempUnit, enemy, udg_TempInt )
        call GroupAddUnit( udg_TempGroup, enemy )
    endif
    set enemy = null
endfunction

function Gale_Move_Timer_Expires takes nothing returns boolean // передвигает дамми юнита в сторону каста, так же ищет подходящих юнитов вокруг дамми.
    local trigger trig = GetTriggeringTrigger( )
    local integer TrigId = GetHandleId( trig )
    local group grp = ( LoadGroupHandle( udg_data, ( TrigId ), ( 22 ) ) )
    local unit DummyGale = ( LoadUnitHandle( udg_data, ( TrigId ), ( 19 ) ) )
    local unit Caster = ( LoadUnitHandle( udg_data, ( TrigId ), ( 14 ) ) )
    local integer level = ( LoadInteger( udg_data, ( TrigId ), ( 5 ) ) )
    local real Ang = ( LoadReal( udg_data, ( TrigId ), ( 13 ) ) )
    local group ExtraGroup
    local real x
    local real y
    local real dx
    local real dy
    local real Dist = 30
    local unit dummy
    set udg_TempInt = level
  
    if GetTriggerEvalCount( trig ) > 28 then
        call ReleaseGroup( grp ) // в оригинале NSI( ) IceFrog использует систему внешне похожую на GroupUtils
        call FlushChildHashtable( udg_data, ( TrigId ) )
        call DisposeTrigger( trig ) // функция удаления триггеров, из системы вероятно написанной Вексом, советую посмотреть вспомогательные системы выше.
        call ShowUnit( DummyGale, false )
        call KillUnit( DummyGale )
    else
  
        set x = GetUnitX( DummyGale )
        set y = GetUnitY( DummyGale )
    
        set ExtraGroup = NewGroup( )
        set udg_TempGroup = grp
        set udg_FilterUnit = Caster
        set udg_TempUnit = Caster
    
        call GroupEnumUnitsInRange( ExtraGroup, x, y, 150.00, Condition( function GaleAvalibleTargets_Cond ) )
        call ForGroup( ExtraGroup, function Gale_Pick_Enemy )
        call ReleaseGroup( ExtraGroup ) // в оригинале NSI( ) IceFrog использует систему внешне похожую на GroupUtils.
    
        set dx = ( x + Dist * Cos( Ang * bj_DEGTORAD ) ) // в оригинале PTI - из системы проверки координат.
        set dy = ( y + Dist * Sin( Ang * bj_DEGTORAD ) ) // в оригинале PUI - из системы проверки координат.
    
        call SetUnitX( DummyGale, dx )
        call SetUnitY( DummyGale, dy )
    
        if ModuloInteger( GetTriggerEvalCount( trig ), 3 ) == 0 then
            set dummy = CreateUnit( GetOwningPlayer( DummyGale ), 'o01W', dx, dy, 0 ) // создает дамми юнитов для обзора над местностью.
            call KillUnit( dummy )
            set dummy = null
        endif
    
    endif
  
    set trig = null
    set grp = null
    set ExtraGroup = null
    set DummyGale = null
    set Caster = null
    return false
endfunction

function Trig_Venomous_Gale_Actions takes nothing returns nothing
    local trigger trig = CreateTrigger( )
    local integer TrigId = GetHandleId( trig )
    local group grp = NewGroup( ) // в оригинале NTI( ) IceFrog использует систему внешне похожую на GroupUtils
    local unit Caster = GetTriggerUnit( )
    local location SpellTargetLoc = GetSpellTargetLoc( )
    local real x1 = GetUnitX( Caster )
    local real y1 = GetUnitY( Caster )
    local real x2 = GetLocationX( SpellTargetLoc )
    local real y2 = GetLocationY( SpellTargetLoc )
    local real Ang = abp( x1, x2, y1, y2 )  // находит угол между координатами точек в градусах.
    local integer level = GetUnitAbilityLevel( Caster, 'A173' )
    local unit DummyGale = CreateUnit( GetOwningPlayer( Caster ), 'h07K', x1, y1, Ang )
  
    call PlaySoundAtCord( null , x1, y1 ) // функция которая проигрывает звук "Shadow Strike", в оригинале ссылкой на звук служит переменная VE
  
    call SaveUnitHandle( udg_data, ( TrigId ), ( 19 ), ( DummyGale ) )
    call SaveInteger( udg_data, ( TrigId ), ( 5 ), ( level ) )
    call SaveGroupHandle( udg_data, ( TrigId ), ( 22 ), ( grp ) )
    call SaveReal( udg_data, ( TrigId ), ( 13 ), ( ( Ang ) * 1.0 ) )
    call SaveUnitHandle( udg_data, ( TrigId ), ( 14 ), ( Caster ) )
  
    call TriggerRegisterTimerEvent( trig, 0.025, true )
    call TriggerAddCondition( trig, Condition( function Gale_Move_Timer_Expires ) ) // создает утечку типа triggercondition!
  
    call RemoveLocation( SpellTargetLoc )
  
    set trig = null
    set DummyGale = null
    set grp = null
    set Caster = null
    set SpellTargetLoc = null
endfunction

function Trig_Venomous_Gale_Conditions takes nothing returns boolean // проверка что юнит применил 'A173' ( Venomous Gale )
    // 'A173'(ANcl - channel) - Venomous Gale
    // Основано на канал
    // Цель - точка
    // Приказ - volcano
    // Раз. цели: "Воздушные", "Враги", "Наземные".
    if GetSpellAbilityId( ) == 'A173' then
        call Trig_Venomous_Gale_Actions( )
    endif
    return false
endfunction


//===========================================================================
function InitTrig_Venomous_Gale takes nothing returns nothing // инициализация триггера, эта функция вызовется после старта карты
    local trigger trig = CreateTrigger( )
      
    call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
  
    call TriggerAddCondition( trig , Condition( function Trig_Venomous_Gale_Conditions ) )
  
    set trig = null
endfunction

ВНИМАНИЕ

В коде встречаются ошибки и недоработки, имейте это в виду если собираетесь использовать этот код
Все претензии по качеству кода прошу писать IceFrog

  Dota,Venom

Просмотров: 2 561

Барин #1 - 4 года назад 0
Почему фрог добавляет условие а не действие в созданный при касте триггер?
quq_CCCP #2 - 4 года назад 0
DrontMAN:
Почему фрог добавляет условие а не действие в созданный при касте триггер?
Потому что ему заняться нечем, слышал звон что Boolexpr не создает утечек, а кешируется вот и юзает условия забывая про то что можно вовсе не создавать boolexpr'ов, да и еще он забыл про triggeraction и triggercondition.
Clamp #3 - 3 года назад 0
забывая про то что можно вовсе не создавать boolexpr
Вот только использование null вместо переменной типа boolexpr вызывает утечку. За пруфами в поиск, это были слова ADOLF'а, и ему определённо можно (и нужно) верить в подобных темах
quq_CCCP #4 - 3 года назад 0
Clamp:
забывая про то что можно вовсе не создавать boolexpr
Вот только использование null вместо переменной типа boolexpr вызывает утечку. За пруфами в поиск, это были слова ADOLF'а, и ему определённо можно (и нужно) верить в подобных темах
До 1.23 а, щяс вроде поправили.
ScopteRectuS #5 - 2 года назад 0
А зачем нужен этот таймер? Разве нельзя сразу же удалить триггер?
quq_CCCP #6 - 2 года назад 0
ScopteRectuS:
А зачем нужен этот таймер? Разве нельзя сразу же удалить триггер?
Может случится баг коллизии хендлов триггеров, официально его никто не фиксил с момента появления варкравта.
Можешь создать триггер с уже существующим событием и условием\действием.
Фрог явно все это просил сделать векса (или кого то еще умного), не от хорошей жизни.