HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

spell performance xefx, timer

05-21-2011, 04:02 PM#1
SanKakU
i like this spell. but it seems to have performance issues if it starts wiping out a lot of enemies and a lot of tornados spawn. was wondering why that is so. is it the limitations of the timer? or the limitations of xefx? or is there some flaw in the spell coding itself? i'm not sure where to look, since i didn't make the spell. so i'm asking here.
Collapse JASS:
library TornadoOnDeath requires TimerUtils, xefx, xedamage optional BoundSentinel
//-----------------------------------------------------------------------------------------------
// Tornado by scorpion182
// requires: TimerUtils, BoundSentinel, xe by vexorian
//
//
//
//
//
//-----------------------------------------------------------------------------------------------
//----CALIBRATION SECTION------------------------------------------------------------------------
    globals
        private constant integer SPELL_ID       = 'A01U' //spell rawcode
        private constant string  TORNADO_PATH   = "Abilities\\Spells\\Other\\Tornado\\TornadoElementalSmall.mdl" //tornado effect path
        private constant real    ANGLE_SPEED    = .1 //tornado angle speed
        private constant real    TORNADO_SCALE  = 1. //tornado scaling value
        private constant real    TICK_INC       = 5. //tornado tick increment
        private constant real    MIN_TICK       = 80. //tornado minimum tick distance
    endglobals

    private constant function GetDuration takes integer lvl returns real
        return 30.+lvl*0 //spell duration in seconds
    endfunction
    
    private constant function GetCollisionSize takes integer lvl returns real
        return 150.+lvl*0 //tornado collision size
    endfunction
    
    private constant function GetAoE takes integer lvl returns real
        return 500.+lvl*0 //tornado aoe
    endfunction
    
    private constant function GetDamage takes integer lvl returns real
        return 15.*lvl + 30. //does damage per second
    endfunction
    
    //damage filter
    private function DamageOptions takes xedamage spellDamage returns nothing
        set spellDamage.dtype = DAMAGE_TYPE_UNIVERSAL
        set spellDamage.atype = ATTACK_TYPE_SIEGE
        set spellDamage.exception = UNIT_TYPE_STRUCTURE //don't damage building
        set spellDamage.visibleOnly = false //only damage visible unit
        set spellDamage.damageAllies = false //damage allies if true
        set spellDamage.damageTrees = true
    endfunction
//------------END OF CALIBRATION SECTION---------------------------------------------------------
    globals
        private xedamage xed
    endglobals
    
    private struct data

        unit caster
        timer t
        xefx fx
        real angle
        real tick
        real inc
        integer lvl
        real duration
    
        static method create takes unit c returns thistype
            local thistype this = thistype.allocate()
            local real x = GetUnitX(c)
            local real y = GetUnitY(c)
            local real a = GetRandomReal(0, 2*bj_PI)
            
            set .lvl = GetUnitAbilityLevel(c, SPELL_ID)
            set .caster = c
            set .t = NewTimer()
            set .fx = xefx.create(x, y, a)
            set .fx.fxpath = TORNADO_PATH
            set .fx.scale = TORNADO_SCALE
            set .tick = GetAoE(.lvl)
            set .inc = TICK_INC
            set .angle = a
            set .duration = GetDuration(.lvl)
            
            return this
        endmethod
    
        private method onDestroy takes nothing returns nothing
            call ReleaseTimer(.t)
            call .fx.destroy()
        endmethod
    
        static method onLoop takes nothing returns nothing
            local thistype this = thistype(GetTimerData(GetExpiredTimer()))
            local real a
            
            if .duration>0 and not IsUnitType(.caster, UNIT_TYPE_DEAD) then
                set .duration = .duration - XE_ANIMATION_PERIOD
                set a = .angle + ANGLE_SPEED
                
                set .fx.x = GetUnitX(.caster) + .tick * Cos(a)
                set .fx.y = GetUnitY(.caster) + .tick * Sin(a)
                set .tick = .tick - .inc
                    
                if .tick < MIN_TICK or .tick > GetAoE(.lvl) then
                    set .inc = .inc * -1
                endif
                
                set .angle = a
            
                call xed.damageAOE(.caster, .fx.x, .fx.y, GetCollisionSize(.lvl), GetDamage(.lvl) * XE_ANIMATION_PERIOD)
                
            else
                call .destroy()
            endif
        endmethod
    
        static method SpellEffect takes nothing returns boolean
            local thistype this
            
            if GetUnitAbilityLevel(GetKillingUnit(), SPELL_ID)>0 then
                set this = thistype.create(GetKillingUnit())
                call SetTimerData(.t, this)
                call TimerStart(.t, XE_ANIMATION_PERIOD, true, function thistype.onLoop)
            endif
        
            return false
        endmethod

        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
            call TriggerAddCondition(t, function thistype.SpellEffect)
            
            //init xedamage
            set xed=xedamage.create()
            call DamageOptions(xed)
            //call XE_PreloadAbility('A01U')
        endmethod

    endstruct

endlibrary
05-21-2011, 08:22 PM#2
Anitarf
Since each kill the caster makes creates a circling projectile and each such projectile lasts 30 seconds and deals damage during that time, giving you even more kills, it seems easy to ramp up tens of such projectiles at once. While the most efficient projectile systems out there boast the ability to run hundreds of projectiles on modern hardware, note that this is usually the case only in single-player test maps. In normal gameplay situations, especially with a less efficient system, a few dozen instances is the limit.

In the case of your spell, the two main inefficiencies are probably using one timer per instance and using the xedamage AoE method periodically. The first can be easily corrected while the second can't be easily replaced if you want to retain the same functionality - dealing damage to units near the projectile periodically instead of just once whenever they come in range, which would be slightly more efficient.
05-22-2011, 04:29 PM#3
SanKakU
ok, let's start with the apparently larger issue first. the timers. to follow your suggestion, do we simply edit the code in some way or do we make the spell use another timer system, such as TT or KT?

i plan to edit the code some...but i certainly want to be able to let's say have the hero kill 30 units in a second or two and still be able to not get hit with 30 timers all at once, i guess...speaking of which...if there's some sort of timer limit with timerutils, why isn't this mentioned? i mean it seems to me sometimes systems are not tested on lower quality hardware so the limitations of the system might not be obvious to the system developer...

at this point i'm not sure how damage is going to be done or even if i want something else to happen to units, such as getting tossed into the air. for now, let's focus on the timer problem.
05-22-2011, 07:36 PM#4
Anitarf
Note that TimerUtils were never meant to be used for things like this, they were meant for timers with longer expiration times. For fast periodic updates it is preferable to use a single timer, which you could code from scratch but it's less work to just use a system like TimedLoop.
05-23-2011, 12:31 AM#5
SanKakU
ok i think i got it working, more or less. i'm planning on doing some of my own customizing to the spell so i'm going to work on that. i'm coding in cjass now btw. i'm not sure if anyone cares about cjass or not at this site or about this spell, so unless there's an interest i'll just say thanks to anitarf and keep on working on my map. thanks, anitarf for pointing me at the system i should use. if i suspect the spell is still having trouble i'll bump the thread or make a new one if necessary.