View Single Post
Old 12-18-2008, 02:59 AM   #24
moyack
Evil Emoticon
 
moyack's Avatar


Respected User
Project Leader: PoC
 
Join Date: Jan 2006
Posts: 3,279

Submissions (17)

moyack is a splendid one to behold (666)moyack is a splendid one to behold (666)moyack is a splendid one to behold (666)moyack is a splendid one to behold (666)

AI Tournament #2 - 2nd PlaceHero Contest - Second place

Send a message via MSN to moyack
Default

Added new info about an efficient way to iterate through data using unit groups.

Quote:
Originally Posted by me
Addition: Looping throught units.

As a part of development of spells controlled with a single timer, we need to iterate through an array of data. this can be done in several way, ones are less efficients than others. If we have units as a part of the data struct, we can use them to develop a very safe way of iteration using the ForGroup command. An example can help us right now.

Collapse Spell that uses second approach and uses units as reference...:
// ================================================================= \\
// Custom Immolation modifier spell, so it targets destructables too \\
// Request by Abriko, by moyack. 2008.                               \\
// ================================================================= \\
// Requires Table to work...                                         \\
// ================================================================= \\
scope Immolation2 initializer init

// Configuration Part...
globals
    private constant integer SpellID = 'AEi2' //Spell based on Immolation
    private constant integer BuffID = 'BEim' //Immolation buff, please base it on the immolation buff.
    private constant real dt = 1.
endglobals

private constant function DamageRate takes integer level returns real
    return 10. + 5. * (level - 1)
endfunction

private constant function AOE takes integer level returns real
    return 160.
endfunction
// End configuration Part...

private struct data
    static HandleTable T
    static group G
    static rect R
    static unit U

    unit c
    boolean flag = false
    
    static method Start takes unit c returns nothing
        local data D = data.allocate()
        set D.c = c
        call GroupAddUnit(data.G, c)
        set data.T[c] = integer(D)
    endmethod
    
    method onDestroy takes nothing returns nothing
        call GroupRemoveUnit(data.G, .c)
        call data.T.flush(.c)
    endmethod
endstruct

private function GetLivingDestructables takes nothing returns boolean
    return GetDestructableLife(GetFilterDestructable()) > 0.405
endfunction 

private function BurnDestructables takes nothing returns nothing
    local destructable d = GetEnumDestructable()
    call DestroyEffect(AddSpecialEffect(GetAbilityEffectById(BuffID, EFFECT_TYPE_SPECIAL, 0), GetWidgetX(d), GetWidgetY(d)))
    call UnitDamageTarget(data.U, d, DamageRate(GetUnitAbilityLevel(data.U, SpellID)) * dt, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
    set d = null
endfunction

private function CheckStatus takes nothing returns nothing
    local unit u = GetEnumUnit()
    local data D = data( data.T[u] )
    if not D.flag and GetUnitAbilityLevel(u, BuffID) > 0 then
        set D.flag = true
    endif
    if D.flag and GetUnitAbilityLevel(u, BuffID) > 0 then
        call SetRect(data.R, GetUnitX(u) - AOE(GetUnitAbilityLevel(u, SpellID)), GetUnitY(u) - AOE(GetUnitAbilityLevel(u, SpellID)), GetUnitX(u) + AOE(GetUnitAbilityLevel(u, SpellID)), GetUnitY(u) + AOE(GetUnitAbilityLevel(u, SpellID))) 
        set data.U = u
        call EnumDestructablesInRect(data.R, Condition(function GetLivingDestructables), function BurnDestructables)
    endif
    if D.flag and GetUnitAbilityLevel(u, BuffID) < 1 then
        call D.destroy()
    endif
    set u = null
endfunction

private function Loop takes nothing returns nothing
    call ForGroup(data.G, function CheckStatus)
endfunction

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == SpellID
endfunction

private function Actions takes nothing returns nothing
    if not IsUnitInGroup(GetTriggerUnit(), data.G) then
        call data.Start(GetTriggerUnit())
    endif
endfunction

//===========================================================================
private function init takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Conditions ) )
    call TriggerAddAction( t, function Actions )
    set t = null
    set data.T = HandleTable.create()
    set data.G = CreateGroup()
    set data.R = Rect(0,0,1,1)
    call TimerStart(CreateTimer(), dt, true, function Loop)
endfunction

endscope

What I'm doing in this spell:
  • I use a group to store all the units that cast the spell. Those units will be my reference or index key.
  • I use a HandleTable to store the struct data ID related to the unit.
  • To iterate though all the active struct data, I call the ForGroup command and in the enumerating function I retrieve the data struct related to the unit with the command data.

The advantages of this iterating process:
  • You can have total control of the structs.
  • Therefore, you'll have control over all the struct in a safe way.
  • Data search is practically O(1) thanks to the usage of table (gamecache search property)

Disadvantage:
  • Only can be used with handles which support grouper handles (units > groups, player > force)

This procedure is very useful in spells because almost in all the cases (for not saying all the cases) there's a unit involved in the spell process, and that unit can serve us as a index for the spell itself.
moyack is offline   Reply With Quote