• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Another damn problem with my FireBall!

Status
Not open for further replies.
Level 21
Joined
Dec 9, 2007
Messages
3,096
JASS:
library FireBall initializer Init

globals     //        Globals of the Fire Balls and the spell.

    private constant integer    AbilityID           = 'A001' //The Ability used to demonstrate the fireball.
    private constant integer    DummyID             = 'u000' //A dummy unit to keep the effects.
    private constant real       Interval            = 50 //How many times(frames) per second should the movement be executed?


    private constant real       SpeedPerSecond      = 600.00
    //    Amount of range the fire ball moves per second.
    private constant real       LifeSpam            = 2.00
    //    Amount of time the fire ball will fly.
    private constant real       AcquireTargetsRange = 60.00
    //    Enemy units in this range are considered targets; allow the fire ball to damage more than one unit.
    private constant real       DamageSplashRadius  = 125.00
    //    Enemy units in this range get damaged
    private constant real       TextTagSize         = 10.00
    //    The size of the text tags (floating texts) wich show the damage amount.
    private constant real       TextTagVelocity     = 120.00
    //    The velocity of the text tags (floating texts)
    private constant real       TextTagFadePoint    = 3.00
    //    At this point the text tag starts fading.
    private constant real       TextTagDeathPoint   = 5.50
    //    At this point the text tag dies.
    private constant real       TextTagZoffset      = 30.00
    //    Height of the text tag above the target.
    private constant boolean    TextTagVisible      = true
    //    I guess you want it visible, don't you? SET TO "false" FOR NO TEXT TAGS
    private constant string     sfxpath             = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
    //    This is the Special Effect created when the fire ball explodes.
            //    I strongly recommend using this effect for the fire ball.


    private          integer    Total               = 0
    private          timer      Cycle               = CreateTimer()

endglobals

private function Conditions takes nothing returns boolean

    return (GetSpellAbilityId() == AbilityID)

endfunction

private constant function AntiLeak takes nothing returns boolean
    return true
endfunction

private function Filtru takes nothing returns boolean
    return (GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > .405) and (IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetEnumUnit())) == true)
endfunction



public struct Data
    unit             entity
    real             lifespam
    real             speed
    real             damage
    real             ar
    real             tr
    integer          i
    string           sfx

    group            Targets = CreateGroup()

    method onDestroy takes nothing returns nothing
        set .entity = null
    endmethod

static method create takes real x, real y, real face, real end, real spd, real dmg, real ar, real tr, string sfxp returns Data
    local Data dat = Data.allocate()

    set dat.entity = CreateUnit( GetOwningPlayer(GetTriggerUnit()), DummyID, x, y, face)
    set dat.lifespam = end
    set dat.speed = spd
    set dat.damage = dmg
    set dat.ar = ar
    set dat.tr = tr
    set dat.sfx = sfxp

    if Total <= 0 then
        call TimerStart(Cycle, 1.00 / Interval, true, function Data.cycle)
    endif
    set Total = Total + 1

    return dat
endmethod

private static method ITE takes nothing returns boolean
    local Data dat

    local integer i

    call ForGroup(dat.Targets, function CountUnitsInGroupEnum)
    set i = bj_groupCountUnits
    if ( i >= 1 ) then
        return true
    endif
    if ( dat.lifespam <= 0.00 ) then
        return true
    endif

return false
endmethod

private static method Damage takes nothing returns nothing

    local Data dat

    local unit       u        = GetEnumUnit()
    local real       x        = GetUnitX(u)
    local real       y        = GetUnitY(u)
    local real       dump     = -2000000000
    local texttag    tt       = CreateTextTag()


        call SetTextTagVelocity(tt, 0, TextTagVelocity * 0.071 / 128 ) //The text will only go up. I used ' * 0.071 / 128' to avoid using a BJ.
        call SetTextTagPermanent(tt, false )
        call SetTextTagFadepoint(tt, TextTagFadePoint )
        call SetTextTagLifespan(tt, TextTagDeathPoint )
        call SetTextTagVisibility(tt, TextTagVisible )
        call SetTextTagPosUnit(tt, u, TextTagZoffset)
        if ( GetUnitAbilityLevel(u, 'Avul') == 0 ) and ( GetUnitAbilityLevel(u, 'Aloc') == 0 ) then
            call SetTextTagText(tt, ( "Damage: " + ( I2S ( R2I (dat.damage) ) + " " ) ), TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 100, 150, 255 )
            //                        R    G    B    Alpha
        elseif ( GetUnitAbilityLevel(u, 'Avul') == 1 ) then
            call SetTextTagText(tt, "Damage: none", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 200, 150, 255 )
            //                        R    G    B    Alpha
        elseif ( GetUnitAbilityLevel(u, 'Aloc') == 1 ) and GetUnitTypeId(u) != DummyID then
            call SetTextTagText(tt, "Missed", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 200, 150, 255 )
            //                        R    G    B    Alpha
            set dump = dat.damage
            set dat.damage = 0
        elseif GetUnitTypeId(u) == DummyID then
            call SetTextTagText(tt, "Collided", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 150, 200, 230, 255 )
            //                        R    G    B    Alpha
        endif


    call UnitDamageTarget( dat.entity, u, dat.damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
    if dump == -2000000000 then
        set dat.damage = dump
    endif

    set u = null

endmethod

private static method cycle takes nothing returns nothing
    local Data dat

    local real       angle    = GetUnitFacing( dat.entity )
    local real       dist     = dat.speed / Interval
    local real       x        = GetUnitX(dat.entity) + dist * Cos(angle * bj_DEGTORAD)
    local real       y        = GetUnitY(dat.entity) + dist * Sin(angle * bj_DEGTORAD)


    //Done with setup.

    set dat.lifespam = (dat.lifespam - (100 / Interval ) )
    call SetUnitPosition( dat.entity, x, y )

    call GroupEnumUnitsInRangeOfLoc(dat.Targets, Location(x, y), dat.ar, Condition(function Filtru))
    call DestroyBoolExpr( Condition( function Filtru ) )

    if ( dat.ITE() ) then
        call GroupEnumUnitsInRangeOfLoc(dat.Targets, Location(x, y), dat.tr, Condition(function Filtru))
        call DestroyBoolExpr( Condition( function Filtru ) )
        call ForGroup( dat.Targets, function Data.Damage )
        call DestroyEffect( AddSpecialEffect( dat.sfx, x, y ) )

        call RemoveUnit( dat.entity )
        call dat.destroy()
        set Total = Total - 1

        if Total <= 0 then
            call PauseTimer(Cycle)
        endif

    endif

    call GroupClear(dat.Targets)
    set dat.entity = null

endmethod

endstruct

private function FBCreate takes nothing returns nothing
    local integer damage = GetHeroInt(GetTriggerUnit(), true) * GetHeroLevel(GetTriggerUnit()) * GetUnitAbilityLevel(GetTriggerUnit(), AbilityID)

    local Data dat = Data.create(GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), GetUnitFacing(GetTriggerUnit()), LifeSpam, SpeedPerSecond, damage, DamageSplashRadius, AcquireTargetsRange, sfxpath)
endfunction

function Init takes nothing returns nothing

    local trigger    t        = CreateTrigger()
    local filterfunc f        = Filter(function AntiLeak)
    local integer    i        = 0

    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, f)
        set i = i + 1
        exitwhen i >= 16
    endloop
    call TriggerAddCondition(t, Condition( function Conditions ) )
    call TriggerAddAction(t, function FBCreate )
    call DestroyFilter(f)
    set f = null
endfunction

//===========================================================================
//~~~~~~  End of movement
endlibrary

GUESS WHAT?
~What?

I have another problem with my god-damn fire ball spell!

How do I make the timer work and please help me solve the (old) bug of the fireballs exploding all at the same time / at spawn!

And how do I actually make it work?
 
Level 4
Joined
Apr 16, 2009
Messages
85
First of all: don't use "library" for spells use "scope" instead (scopes can use all libraries in your map by default - for libraries you have to explicitly state that)

but anyway the reason your spell is not working is:
JASS:
private static method ITE takes nothing returns boolean
    local Data dat //<< dat is never set to anything

    local integer i

    call ForGroup(dat.Targets, function CountUnitsInGroupEnum)
    set i = bj_groupCountUnits
    if ( i >= 1 ) then
        return true
    endif
    if ( dat.lifespam <= 0.00 ) then
        return true
    endif

return false
endmethod

private static method Damage takes nothing returns nothing

    local Data dat // << not set to any value too :/

    local unit       u        = GetEnumUnit()
    local real       x        = GetUnitX(u)
    local real       y        = GetUnitY(u)
    local real       dump     = -2000000000
    local texttag    tt       = CreateTextTag()


        call SetTextTagVelocity(tt, 0, TextTagVelocity * 0.071 / 128 ) //The text will only go up. I used ' * 0.071 / 128' to avoid using a BJ.
        call SetTextTagPermanent(tt, false )
        call SetTextTagFadepoint(tt, TextTagFadePoint )
        call SetTextTagLifespan(tt, TextTagDeathPoint )
        call SetTextTagVisibility(tt, TextTagVisible )
        call SetTextTagPosUnit(tt, u, TextTagZoffset)
        if ( GetUnitAbilityLevel(u, 'Avul') == 0 ) and ( GetUnitAbilityLevel(u, 'Aloc') == 0 ) then
            call SetTextTagText(tt, ( "Damage: " + ( I2S ( R2I (dat.damage) ) + " " ) ), TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 100, 150, 255 )
            //                        R    G    B    Alpha
        elseif ( GetUnitAbilityLevel(u, 'Avul') == 1 ) then
            call SetTextTagText(tt, "Damage: none", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 200, 150, 255 )
            //                        R    G    B    Alpha
        elseif ( GetUnitAbilityLevel(u, 'Aloc') == 1 ) and GetUnitTypeId(u) != DummyID then
            call SetTextTagText(tt, "Missed", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 200, 150, 255 )
            //                        R    G    B    Alpha
            set dump = dat.damage
            set dat.damage = 0
        elseif GetUnitTypeId(u) == DummyID then
            call SetTextTagText(tt, "Collided", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 150, 200, 230, 255 )
            //                        R    G    B    Alpha
        endif


    call UnitDamageTarget( dat.entity, u, dat.damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
    if dump == -2000000000 then
        set dat.damage = dump
    endif

    set u = null

endmethod


private static method cycle takes nothing returns nothing
    local Data dat //<< this dat is never set too

    local real       angle    = GetUnitFacing( dat.entity )
    local real       dist     = dat.speed / Interval
    local real       x        = GetUnitX(dat.entity) + dist * Cos(angle * bj_DEGTORAD)
    local real       y        = GetUnitY(dat.entity) + dist * Sin(angle * bj_DEGTORAD)


    //Done with setup.

    set dat.lifespam = (dat.lifespam - (100 / Interval ) )
    call SetUnitPosition( dat.entity, x, y )

    call GroupEnumUnitsInRangeOfLoc(dat.Targets, Location(x, y), dat.ar, Condition(function Filtru))
    call DestroyBoolExpr( Condition( function Filtru ) )

    if ( dat.ITE() ) then
        call GroupEnumUnitsInRangeOfLoc(dat.Targets, Location(x, y), dat.tr, Condition(function Filtru))
        call DestroyBoolExpr( Condition( function Filtru ) )
        call ForGroup( dat.Targets, function Data.Damage )
        call DestroyEffect( AddSpecialEffect( dat.sfx, x, y ) )

        call RemoveUnit( dat.entity )
        call dat.destroy()
        set Total = Total - 1

        if Total <= 0 then
            call PauseTimer(Cycle)
        endif

    endif

    call GroupClear(dat.Targets)
    set dat.entity = null

endmethod

to get that working you have to store your all active instances of your fireball in an array and loop over that every interval
just make your spell follow the following template i made just for you :cool:

JASS:
scope Example
    globals
        private constant real INTERVAL = 0.03
    endglobals

    struct YourSpell
        private static integer COUNT = 0
        private static thistype array ACTIVE
        private static timer PERIODIC = CreateTimer()
        private real timeout
        private integer pos
        
        private static method periodic takes nothing returns nothing
            local thistype this
            local integer i = 0
            loop
                exitwhen i == thistype.COUNT
                    set this = thistype.ACTIVE[i]
                    set this.timeout = this.timeout - INTERVAL
                    if this.timeout <= 0. then
                        //do something
                        call this.destroy()
                        set i = i - 1
                    else
                        // do something else you want to do every time the timer ticks
                    endif
                set i = i + 1
            endloop
        endmethod
        
        public static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            
            set this.timeout = 5.0 //any value
            
            set thistype.ACTIVE[thistype.COUNT] = this
            set this.pos = thistype.COUNT
            set thistype.COUNT = thistype.COUNT + 1
            if thistype.COUNT == 1 then
                call TimerStart(thistype.PERIODIC, INTERVAL, true, function thistype.periodic)
            endif
            return this
        endmethod
        
        private method onDestroy takes nothing returns nothing
            set thistype.COUNT = thistype.COUNT - 1
            set thistype.ACTIVE[this.pos] = thistype.ACTIVE[thistype.COUNT]
            set thistype.ACTIVE[this.pos].pos = this.pos
            if thistype.COUNT == 0 then
                call PauseTimer(thistype.PERIODIC)
            endif
        endmethod
    endstruct
endscope
about the .ITE() method just inline it or make dat an argument - same goes for the .Damage() method


btw you should replace thinks like:
call GroupEnumUnitsInRangeOfLoc(dat.Targets, Location(x, y), dat.ar, Condition(function Filtru))

with:
call GroupEnumUnitsInRange(dat.Targets, x, y, dat.ar, Condition(function Filtru))

(because locations have to be destroyed or they leak and they are of no use anyway - the second version is faster and doesn't leak (you could also move the condition into a global boolexpr (and set it once in your init function)))

have fun with that and good luck :thumbs_up:
 
Last edited:
Level 4
Joined
Apr 16, 2009
Messages
85
How do I inline them or make them arguments?

And it still does not work.
The fireballs don't move and they last forever. This means the cycle method doesn't run...

show me the new code (testmap would be helpful too)

btw here i inlined ITE and passed dat to Damage() with a temporary global variable (but your bug isn't fixed here) (i also removed that unnecessary Condition() calls and the Location thing):

JASS:
library FireBall initializer Init

globals     //        Globals of the Fire Balls and the spell.

    private constant integer    AbilityID           = 'A001' //The Ability used to demonstrate the fireball.
    private constant integer    DummyID             = 'u000' //A dummy unit to keep the effects.
    private constant real       Interval            = 50 //How many times(frames) per second should the movement be executed?


    private constant real       SpeedPerSecond      = 600.00
    //    Amount of range the fire ball moves per second.
    private constant real       LifeSpam            = 2.00
    //    Amount of time the fire ball will fly.
    private constant real       AcquireTargetsRange = 60.00
    //    Enemy units in this range are considered targets; allow the fire ball to damage more than one unit.
    private constant real       DamageSplashRadius  = 125.00
    //    Enemy units in this range get damaged
    private constant real       TextTagSize         = 10.00
    //    The size of the text tags (floating texts) wich show the damage amount.
    private constant real       TextTagVelocity     = 120.00
    //    The velocity of the text tags (floating texts)
    private constant real       TextTagFadePoint    = 3.00
    //    At this point the text tag starts fading.
    private constant real       TextTagDeathPoint   = 5.50
    //    At this point the text tag dies.
    private constant real       TextTagZoffset      = 30.00
    //    Height of the text tag above the target.
    private constant boolean    TextTagVisible      = true
    //    I guess you want it visible, don't you? SET TO "false" FOR NO TEXT TAGS
    private constant string     sfxpath             = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
    //    This is the Special Effect created when the fire ball explodes.
            //    I strongly recommend using this effect for the fire ball.


    private          integer    Total               = 0
    private          timer      Cycle               = CreateTimer()

    private          Data       ForGroupData    

    private          boolexpr  FILTER

endglobals

private function Conditions takes nothing returns boolean

    return (GetSpellAbilityId() == AbilityID)

endfunction

private constant function AntiLeak takes nothing returns boolean
    return true
endfunction

private function Filtru takes nothing returns boolean
    return (GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > .405) and (IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetFilterUnit())) == true) // << here was a GetEnumUnit() i replaced that with GetFilterUnit()
endfunction



public struct Data
    unit             entity
    real             lifespam
    real             speed
    real             damage
    real             ar
    real             tr
    integer          i
    string           sfx

    group            Targets = CreateGroup()

    method onDestroy takes nothing returns nothing
        set .entity = null
    endmethod

static method create takes real x, real y, real face, real end, real spd, real dmg, real ar, real tr, string sfxp returns Data
    local Data dat = Data.allocate()

    set dat.entity = CreateUnit( GetOwningPlayer(GetTriggerUnit()), DummyID, x, y, face)
    set dat.lifespam = end
    set dat.speed = spd
    set dat.damage = dmg
    set dat.ar = ar
    set dat.tr = tr
    set dat.sfx = sfxp

    if Total <= 0 then
        call TimerStart(Cycle, 1.00 / Interval, true, function Data.cycle)
    endif
    set Total = Total + 1

    return dat
endmethod

private static method Damage takes nothing returns nothing
    local Data dat = ForGroupData
    local unit       u        = GetEnumUnit()
    local real       x        = GetUnitX(u)
    local real       y        = GetUnitY(u)
    local real       dump     = -2000000000
    local texttag    tt       = CreateTextTag()


        call SetTextTagVelocity(tt, 0, TextTagVelocity * 0.071 / 128 ) //The text will only go up. I used ' * 0.071 / 128' to avoid using a BJ.
        call SetTextTagPermanent(tt, false )
        call SetTextTagFadepoint(tt, TextTagFadePoint )
        call SetTextTagLifespan(tt, TextTagDeathPoint )
        call SetTextTagVisibility(tt, TextTagVisible )
        call SetTextTagPosUnit(tt, u, TextTagZoffset)
        if ( GetUnitAbilityLevel(u, 'Avul') == 0 ) and ( GetUnitAbilityLevel(u, 'Aloc') == 0 ) then
            call SetTextTagText(tt, ( "Damage: " + ( I2S ( R2I (dat.damage) ) + " " ) ), TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 100, 150, 255 )
            //                        R    G    B    Alpha
        elseif ( GetUnitAbilityLevel(u, 'Avul') == 1 ) then
            call SetTextTagText(tt, "Damage: none", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 200, 150, 255 )
            //                        R    G    B    Alpha
        elseif ( GetUnitAbilityLevel(u, 'Aloc') == 1 ) and GetUnitTypeId(u) != DummyID then
            call SetTextTagText(tt, "Missed", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 200, 150, 255 )
            //                        R    G    B    Alpha
            set dump = dat.damage
            set dat.damage = 0
        elseif GetUnitTypeId(u) == DummyID then
            call SetTextTagText(tt, "Collided", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 150, 200, 230, 255 )
            //                        R    G    B    Alpha
        endif


    call UnitDamageTarget( dat.entity, u, dat.damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
    if dump == -2000000000 then
        set dat.damage = dump
    endif

    set u = null

endmethod

private static method cycle takes nothing returns nothing
    local Data dat

    local real       angle    = GetUnitFacing( dat.entity )
    local real       dist     = dat.speed / Interval
    local real       x        = GetUnitX(dat.entity) + dist * Cos(angle * bj_DEGTORAD)
    local real       y        = GetUnitY(dat.entity) + dist * Sin(angle * bj_DEGTORAD)
    local boolean b


    //Done with setup.

    set dat.lifespam = (dat.lifespam - (100 / Interval ) )
    call SetUnitPosition( dat.entity, x, y )

    call GroupEnumUnitsInRange(dat.Targets, x, y, dat.ar, FILTER)
    
    //inlined version of ITE()
    call ForGroup(dat.Targets, function CountUnitsInGroupEnum)
    if ( bj_groupCountUnits >= 1  or dat.lifespam <= 0.0)
        //i commented out the below line because you already have your units in the group you don't have to regroup them
        //call GroupEnumUnitsInRange(dat.Targets, x, y, dat.tr, FILTER) 
        set ForGroupData = dat
        call ForGroup( dat.Targets, function Data.Damage )
        call DestroyEffect( AddSpecialEffect( dat.sfx, x, y ) )

        call RemoveUnit( dat.entity )
        call dat.destroy()
        set Total = Total - 1

        if Total <= 0 then
            call PauseTimer(Cycle)
        endif

    endif

    call GroupClear(dat.Targets)
    set dat.entity = null

endmethod

endstruct

private function FBCreate takes nothing returns nothing
    local integer damage = GetHeroInt(GetTriggerUnit(), true) * GetHeroLevel(GetTriggerUnit()) * GetUnitAbilityLevel(GetTriggerUnit(), AbilityID)

    local Data dat = Data.create(GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), GetUnitFacing(GetTriggerUnit()), LifeSpam, SpeedPerSecond, damage, DamageSplashRadius, AcquireTargetsRange, sfxpath)
endfunction

function Init takes nothing returns nothing

    local trigger    t        = CreateTrigger()
    local filterfunc f        = Filter(function AntiLeak)
    local integer    i        = 0
    set FILTER = Filter(function Filtru)
    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, f)
        set i = i + 1
        exitwhen i >= 16
    endloop
    call TriggerAddCondition(t, Condition( function Conditions ) )
    call TriggerAddAction(t, function FBCreate )
    call DestroyFilter(f)
    set f = null
endfunction

//===========================================================================
//~~~~~~  End of movement
endlibrary

Edit: had to fix my own mistakes XD
 
Last edited:
You can inline

JASS:
private function FBCreate takes nothing returns nothing
    local integer damage = GetHeroInt(GetTriggerUnit(), true) * GetHeroLevel(GetTriggerUnit()) * GetUnitAbilityLevel(GetTriggerUnit(), AbilityID)

    local Data dat = Data.create(GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), GetUnitFacing(GetTriggerUnit()), LifeSpam, SpeedPerSecond, damage, DamageSplashRadius, AcquireTargetsRange, sfxpath)
endfunction

to

JASS:
private function FBCreate takes nothing returns nothing
    call Data.create(GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), GetUnitFacing(GetTriggerUnit()), LifeSpam, SpeedPerSecond, GetHeroInt(GetTriggerUnit(), true) * GetHeroLevel(GetTriggerUnit()) * GetUnitAbilityLevel(GetTriggerUnit(), AbilityID), DamageSplashRadius, AcquireTargetsRange, sfxpath)
endfunction

It has less readablity but it's more efficient than what you had. However this would be the most efficient.

JASS:
globals
    private unit u
endglobals

private function FBCreate takes nothing returns nothing
    set u = GetTriggerUnit()
    call Data.create(GetUnitX(u), GetUnitY(u), GetUnitFacing(u), LifeSpam, SpeedPerSecond, GetHeroInt(u, true) * GetHeroLevel(u) * GetUnitAbilityLevel(u, AbilityID), DamageSplashRadius, AcquireTargetsRange, sfxpath)
endfunction

Saves you like 6 function calls.

How do I make the timer work and please help me solve the (old) bug of the fireballs exploding all at the same time / at spawn!

And how do I actually make it work?

Well, whats wrong with the timer?
 
Level 4
Joined
Apr 16, 2009
Messages
85
JASS:
private function FBCreate takes nothing returns nothing
    local unit u = GetTriggerUnit()
    call Data.create(GetUnitX(u), GetUnitY(u), GetUnitFacing(u), LifeSpam, SpeedPerSecond, GetHeroInt(u, true) * GetHeroLevel(u) * GetUnitAbilityLevel(u, AbilityID), DamageSplashRadius, AcquireTargetsRange, sfxpath)
    set u = null
endfunction

works too (u is a kinda bad name for a global - too repetitive)
 
Level 4
Joined
Apr 16, 2009
Messages
85
Just an example, using a global in this case would be faster.

yeah - not that it would matter though

Edit: found your fireball submission i'm currently getting it to work

Edit2: OK - done heres your fully working spell:

JASS:
scope FireBall initializer Init
    
    private keyword ForGroupFireBall
    
    globals     //        Globals of the Fire Balls and the spell.

        private constant integer    AbilityID           = 'A001' //The Ability used to demonstrate the fireball.
        private constant integer    DummyID             = 'u000' //A dummy unit to keep the effects.
        private constant real       Interval            = 0.02 //The periodic method is called every Interval seconds


        private constant real       SpeedPerSecond      = 600.00
        //    Amount of range the fire ball moves per second.
        private constant real       LifeSpan            = 2.00
        //    Amount of time the fire ball will fly.
        private constant real       AcquireTargetsRange = 60.00
        //    Enemy units in this range are considered targets; allow the fire ball to damage more than one unit.
        private constant real       DamageSplashRadius  = 125.00
        //    Enemy units in this range get damaged
        private constant real       TextTagSize         = 0.023
        //    The size of the text tags (floating texts) wich show the damage amount.
        private constant real       TextTagVelocity     = 0.066563
        //    The velocity of the text tags (floating texts)
        private constant real       TextTagFadePoint    = 3.00
        //    At this point the text tag starts fading.
        private constant real       TextTagDeathPoint   = 5.50
        //    At this point the text tag dies.
        private constant real       TextTagZoffset      = 30.00
        //    Height of the text tag above the target.
        private constant boolean    TextTagVisible      = true
        //    I guess you want it visible, don't you? SET TO "false" FOR NO TEXT TAGS
        private constant string     sfxpath             = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
        //    This is the Special Effect created when the fire ball explodes.
                //    I strongly recommend using this effect for the fire ball.


        private          player     FilterPlayer

        private          boolexpr  FILTER
        
        private          group      Targets

    endglobals

    private function Conditions takes nothing returns boolean

        return (GetSpellAbilityId() == AbilityID)

    endfunction

    private constant function AntiLeak takes nothing returns boolean
        return true
    endfunction

    private function Filtru takes nothing returns boolean
        local unit u = GetFilterUnit()
        local boolean b = IsUnitEnemy(u, FilterPlayer) and not IsUnitType(u, UNIT_TYPE_UNDEAD) and GetUnitState(u, UNIT_STATE_LIFE) > .405
        set  u = null
        return  b
    endfunction



    public struct FireBall
        private unit             entity
        private real             lifespan
        private real             speed
        private real             damage
        private real             ar
        private real             tr
        private integer          i
        private string           sfx
        private static timer PERIODIC
        private static thistype array ACTIVE
        private static integer COUNT = 0
        private player owner
        private integer pos
        private real a


        private static method Damage takes nothing returns nothing
            local thistype this = ForGroupFireBall
            local unit       u        = GetEnumUnit()
            local real       x        = GetUnitX(u)
            local real       y        = GetUnitY(u)
            local texttag    tt       = CreateTextTag()
            //local boolean b = true


                call SetTextTagVelocity(tt, 0, TextTagVelocity)
                call SetTextTagPermanent(tt, false )
                call SetTextTagFadepoint(tt, TextTagFadePoint )
                call SetTextTagLifespan(tt, TextTagDeathPoint )
                call SetTextTagVisibility(tt, TextTagVisible )
                call SetTextTagPosUnit(tt, u, TextTagZoffset)
                if ( GetUnitAbilityLevel(u, 'Avul') == 0 ) and ( GetUnitAbilityLevel(u, 'Aloc') == 0 ) then
                    call SetTextTagText(tt, ( "Damage: " + ( I2S ( R2I (this.damage) ) + " " ) ), TextTagSize )
                    call SetTextTagColor(tt, 230, 100, 150, 255 )
                    //                        R    G    B    Alpha
                elseif ( GetUnitAbilityLevel(u, 'Avul') == 1 ) then
                    call SetTextTagText(tt, "Damage: none", TextTagSize )
                    call SetTextTagColor(tt, 230, 200, 150, 255 )
                    //                        R    G    B    Alpha
                /*elseif ( GetUnitAbilityLevel(u, 'Aloc') == 1 ) and GetUnitTypeId(u) != DummyID then
                    call SetTextTagText(tt, "Missed", TextTagSize )
                    call SetTextTagColor(tt, 230, 200, 150, 255 )
                    //                        R    G    B    Alpha
                    set b = false*/
                elseif GetUnitTypeId(u) == DummyID then
                    call SetTextTagText(tt, "Collided", TextTagSize )
                    call SetTextTagColor(tt, 150, 200, 230, 255 )
                    //                        R    G    B    Alpha
                endif

            //if b then
                call UnitDamageTarget( this.entity, u, this.damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
            //endif
            
            set u = null
            set tt = null
        endmethod

        private static method periodic takes nothing returns nothing
            local thistype this

            local real       dist
            local real       x
            local real       y
            local integer i = 0


            //Done with setup.
            loop
                exitwhen i == thistype.COUNT
                    set this = thistype.ACTIVE[i]
                    set dist = this.speed * Interval
                    set this.lifespan = this.lifespan - Interval
                    
                    set x = GetUnitX(this.entity) + dist * Cos(this.a)
                    set y = GetUnitY(this.entity) + dist * Sin(this.a)
                    
                    call SetUnitPosition( this.entity, x, y )
                    
                    
                    set FilterPlayer = this.owner
                    call GroupEnumUnitsInRange(Targets, x, y, this.ar, FILTER)

                    //inlined version of ITE()
                    set bj_groupCountUnits = 0
                    call ForGroup(Targets, function CountUnitsInGroupEnum)
                    if ( bj_groupCountUnits >= 1  or this.lifespan <= 0.0) then
                        call GroupEnumUnitsInRange(Targets, x, y, this.tr, FILTER)
                        set ForGroupFireBall = this
                        call ForGroup( Targets, function thistype.Damage )
                        call DestroyEffect( AddSpecialEffect( this.sfx, x, y ))
                        call this.destroy()
                        set i = i - 1
                    endif

                    call GroupClear(Targets)
                set i = i + 1
            endloop
        endmethod
            
        static method create takes player owner, real x, real y, real face, real end, real spd, real dmg, real ar, real tr, string sfxp returns thistype
            local thistype this = thistype.allocate()

            set this.entity = CreateUnit( owner, DummyID, x, y, face)
            set this.a = face * bj_DEGTORAD
            set this.owner = owner
            set this.lifespan = end
            set this.speed = spd
            set this.damage = dmg
            set this.ar = ar
            set this.tr = tr
            set this.sfx = sfxp

            
            set thistype.ACTIVE[thistype.COUNT] = this
            set this.pos = thistype.COUNT
            set thistype.COUNT = thistype.COUNT + 1
            
            if thistype.COUNT == 1 then
                call TimerStart(thistype.PERIODIC, Interval, true, function thistype.periodic)
            endif

            return this
        endmethod
        
        private method onDestroy takes nothing returns nothing
            set thistype.COUNT = thistype.COUNT - 1
            set thistype.ACTIVE[this.pos] = thistype.ACTIVE[thistype.COUNT]
            set thistype.ACTIVE[this.pos].pos = this.pos
            if thistype.COUNT == 0 then
                call PauseTimer(thistype.PERIODIC)
            endif
            if this.entity != null then
                call RemoveUnit( this.entity )
            endif
            set .entity = null
        endmethod
        
        private static method onInit takes nothing returns nothing
            set thistype.PERIODIC = CreateTimer()
        endmethod
    endstruct

    globals
        private          FireBall   ForGroupFireBall
    endglobals
    
    private function FBCreate takes nothing returns nothing
        local unit u = GetTriggerUnit()
        call FireBall.create(GetOwningPlayer(u), GetUnitX(u), GetUnitY(u), GetUnitFacing(u), LifeSpan, SpeedPerSecond, GetHeroInt(u, true) * GetHeroLevel(u) * GetUnitAbilityLevel(u, AbilityID), AcquireTargetsRange, DamageSplashRadius, sfxpath)
        set u = null
    endfunction

    private function Init takes nothing returns nothing

        local trigger    t        = CreateTrigger()
        local filterfunc f        = Filter(function AntiLeak)
        local integer    i        = 0
        set FILTER = Filter(function Filtru)
        set Targets = CreateGroup()
        loop
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, f)
            set i = i + 1
            exitwhen i >= 16
        endloop
        call TriggerAddCondition(t, Condition( function Conditions ) )
        call TriggerAddAction(t, function FBCreate )
        call DestroyFilter(f)
        set f = null
    endfunction

    //===========================================================================
    //~~~~~~  End of movement
endscope

Finally a few things:
-the whole thing is now a scope and not a library
-i changed Interval, TextTagSize and TextTagVelocity to literals i commented out the part with the Aloc check (because you can't group units with Aloc) (i would remove that part (that also means in your its current implantation the collision doesn't work too)
fixed some typos
-i would remove all that texttag stuff because it spams texttags and theres a limit of 100 texttags at a time - people can do them themselfs if they want
-i renamed the struct - Everyone uses Data as his struct name and that really sucks
-the struct is not public anymore so you can just call FireBall.create() instead of FireBall_FireBall.create() (could result in name conflicts but that would be easily resolved)
-added an extra owner argument to create (i would remove all the arguments that just get passed a constant as they don't change anway and just set the values to the constants directly or remove them altogether and just read the constant)
-oh and it doesn't hit undead units anymore (as the tooltip says)


btw can someone explain to me the use of local filterfunc f = Filter(function AntiLeak) i don't really see a use in that as you can just pass null and you won't destroy the trigger anyway
 
Last edited:
Level 21
Joined
Dec 9, 2007
Messages
3,096
Well, no handler function runs for the timer...
Test the code, it doesn't work.
Oh and here is the non-fat-struct-ed version:
It uses a periodic timer event and the function runs, but the fireballs have a global lifespam, and that's bad.
JASS:
library FireBall initializer Init

globals     //        Globals of the Fire Balls and the spell.

    private constant integer    AbilityID           = 'A001'
    private constant integer    DummyID             = 'u000'

    
    private constant real       SpeedPerSecond      = 600.00    
    //    Amount of range the fire ball moves per second.
    private constant real       LifeSpam            = 2.00
    //    Amount of time the fire ball will fly.
    private constant real       AcquireTargetsRange = 60.00     
    //    Enemy units in this range are considered targets; allow the fire ball to damage more than one unit.
    private constant real       DamageSplashRadius  = 125.00    
    //    Enemy units in this range get damaged
    private constant real       TextTagSize         = 10.00     
    //    The size of the text tags (floating texts) wich show the damage amount.
    private constant real       TextTagVelocity     = 120.00    
    //    The velocity of the text tags (floating texts)
    private constant real       TextTagFadePoint    = 3.00      
    //    At this point the text tag starts fading.
    private constant real       TextTagDeathPoint   = 5.50      
    //    At this point the text tag dies.
    private constant real       TextTagZoffset      = 30.00     
    //    Height of the text tag above the target.
    private constant boolean    TextTagVisible      = true      
    //    I guess you want it visible, don't you? SET TO "false" FOR NO TEXT TAGS
    private constant string     sfxpath             = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
    //    This is the Special Effect created when the fire ball explodes.
            //    I strongly recommend using this effect for the fire ball.
    

    private          group      FBs                 = CreateGroup()
    private          group      Targets             = CreateGroup()
    private          unit       toPass
    
endglobals

private keyword data

globals
    private          data array DATAS
    private          integer    total               = 0
endglobals

private struct data
    unit             StructUnit
    real             lifespam
    real             speed
    real             damage
    real             ar
    real             tr
    integer          i
    string           sfx
    
    private method onDestroy takes nothing returns nothing
        set .StructUnit = null
    endmethod
endstruct

private function AttachStruct takes unit ToWhich returns data
    local data       dat      = data.create()
    set dat.StructUnit = ToWhich
    set dat.i = total
    set DATAS[total] = dat
    set total = total + 1
    return dat
endfunction

private function GetStruct takes unit FromWhich returns data
    local integer    i        = 0
    local data       toReturn = 0
    loop
        exitwhen i >= total
        if DATAS[i].StructUnit == FromWhich then
            set toReturn = DATAS[i]
        endif
        set i = i + 1
    endloop
    return toReturn
endfunction

private function DestroyStruct takes unit WhichUnit returns nothing
    local data       dat      = GetStruct(WhichUnit)
    set total = total - 1
    set DATAS[dat.i] = DATAS[total]
    set DATAS[dat.i].i = dat.i
    call dat.destroy()
endfunction
//======================================================================================================================================================
//======================================================================================================================================================
//======================================================================================================================================================
//~~~~~~ Start of creation
//===========================================================================



private function Conditions takes nothing returns boolean

    return (GetSpellAbilityId() == AbilityID)
    
endfunction

private function Create takes nothing returns nothing

    local real       ox       = GetUnitX(GetTriggerUnit())
    local real       oy       = GetUnitY(GetTriggerUnit())
    local real       px       = GetLocationX(GetSpellTargetLoc())
    local real       py       = GetLocationY(GetSpellTargetLoc())
    
    local real       angle    = bj_RADTODEG * Atan2(py - oy, px - ox)
    local real       x        = ox + 15 * Cos(angle * bj_DEGTORAD)
    local real       y        = oy + 15 * Sin(angle * bj_DEGTORAD)
    
    local unit       u        = CreateUnit( GetOwningPlayer(GetTriggerUnit()), DummyID, x, y, angle)
    
    local data       dat      = AttachStruct(u)
    
    set dat.lifespam = GetRandomReal( LifeSpam - 0.50, LifeSpam - 0.50 )
    set dat.speed = GetRandomReal(SpeedPerSecond - 200.00, SpeedPerSecond + 200.00)
    set dat.damage = ( GetHeroInt(GetSpellAbilityUnit(), true) * GetUnitAbilityLevel(GetSpellAbilityUnit(), AbilityID) )
    set dat.ar = GetRandomReal(AcquireTargetsRange - 20.00, AcquireTargetsRange + 20.00)
    set dat.tr = GetRandomReal(DamageSplashRadius - 25.00, DamageSplashRadius + 25.00)
    set dat.sfx = sfxpath
    
    call GroupAddUnit( FBs, u )
    
    set u = null

endfunction

private constant function AntiLeak takes nothing returns boolean
    return true
endfunction


//===========================================================================
//~~~~~~  End of creation
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~ Start of movement
//===========================================================================


private function Filtru takes nothing returns boolean

    return (GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > .405) and (IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetEnumUnit())) == true)

endfunction

private function ITE takes nothing returns boolean

    local data       dat = GetStruct(GetEnumUnit())

    call ForGroup(Targets, function CountUnitsInGroupEnum)
    if ( bj_groupCountUnits >= 1 ) then
        return true
    endif
    if ( dat.lifespam <= 0.00 ) then
        return true
    endif
    
set bj_groupCountUnits = 0
return false
endfunction

private function Damage takes nothing returns nothing

    local unit       u        = GetEnumUnit()
    local real       x        = GetUnitX(u)
    local real       y        = GetUnitY(u)
    local real       dump
    local data       dat      = GetStruct( toPass )
    local texttag    tt       = CreateTextTag()
    
    
        call SetTextTagVelocity(tt, 0, TextTagVelocity * 0.071 / 128 ) //The text will only go up. I used ' * 0.071 / 128' to avoid using a BJ.
        call SetTextTagPermanent(tt, false )
        call SetTextTagFadepoint(tt, TextTagFadePoint )
        call SetTextTagLifespan(tt, TextTagDeathPoint )
        call SetTextTagVisibility(tt, TextTagVisible )
        call SetTextTagPosUnit(tt, u, TextTagZoffset)
        if ( GetUnitAbilityLevel(u, 'Avul') == 0 ) and ( GetUnitAbilityLevel(u, 'Aloc') == 0 ) then
            call SetTextTagText(tt, ( "Damage: " + ( I2S ( R2I (dat.damage) ) + " " ) ), TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 100, 150, 255 )
            //                        R    G    B    Alpha
        elseif ( GetUnitAbilityLevel(u, 'Avul') == 1 ) then
            call SetTextTagText(tt, "Damage: none", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 200, 150, 255 )
            //                        R    G    B    Alpha
        elseif ( GetUnitAbilityLevel(u, 'Aloc') == 1 ) and GetUnitTypeId(u) != DummyID then
            call SetTextTagText(tt, "Missed", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 230, 200, 150, 255 )
            //                        R    G    B    Alpha
            set dump = dat.damage
            set dat.damage = 0
        elseif GetUnitTypeId(u) == DummyID then
            call SetTextTagText(tt, "Collided", TextTagSize * 0.023 / 10 )
            call SetTextTagColor(tt, 150, 200, 230, 255 )
            //                        R    G    B    Alpha
        endif
        
    
    call UnitDamageTarget( toPass, u, dat.damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
    set dat.damage = dump
    
    set u = null
    
endfunction

private function Move takes nothing returns nothing

    local unit       u        = GetEnumUnit()
    local data       dat      = GetStruct( u )

    local real       angle    = GetUnitFacing( u )
    local real       dist     = dat.speed / 50
    local real       x        = GetUnitX(u) + dist * Cos(angle * bj_DEGTORAD)
    local real       y        = GetUnitY(u) + dist * Sin(angle * bj_DEGTORAD)
    local location   loc      = Location(x, y)
    

    //Done with setup.
    
    set dat.lifespam = (dat.lifespam - 0.02)
    call SetUnitPosition( u, x, y )
    
    call GroupEnumUnitsInRangeOfLoc(Targets, loc, dat.ar, Condition(function Filtru))
    call DestroyBoolExpr( Condition( function Filtru ) )
    
    if ( ITE() ) then
        set toPass = u
        call GroupEnumUnitsInRangeOfLoc(Targets, loc, dat.tr, Condition(function Filtru))
        call DestroyBoolExpr( Condition( function Filtru ) )
        call ForGroup( Targets, function Damage )
        call DestroyEffect( AddSpecialEffect( sfxpath, x, y ) )

        call DestroyStruct( u )
        call RemoveUnit( u )
        call GroupClear(Targets)
        
    endif
    
    call GroupClear(Targets)
    call RemoveLocation(loc)
    set u = null
    
endfunction

private function Pick takes nothing returns nothing

    call ForGroup( FBs, function Move )
    
endfunction

private function Init takes nothing returns nothing

    local trigger    t        = CreateTrigger()
    local filterfunc f        = Filter(function AntiLeak)
    local integer    i        = 0
    
    local trigger    trg      = CreateTrigger(  )
    
    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, f)
        set i = i + 1
        exitwhen i >= 16
    endloop
    call TriggerAddCondition(t, Condition( function Conditions ) )
    call TriggerAddAction(t, function Create )
    call DestroyFilter(f)
    set f = null
    
    call TriggerRegisterTimerEvent( trg, 0.02, true )
    call TriggerAddAction( trg, function Pick )
    
endfunction

//===========================================================================
//~~~~~~  End of movement
endlibrary
 
Level 4
Joined
Apr 16, 2009
Messages
85
i edited my last post it now constains the complete working spell (here again)

i changed some extra stuff so i would read what i said in my last posts edit

JASS:
scope FireBall initializer Init
    
    private keyword ForGroupFireBall
    
    globals     //        Globals of the Fire Balls and the spell.

        private constant integer    AbilityID           = 'A001' //The Ability used to demonstrate the fireball.
        private constant integer    DummyID             = 'u000' //A dummy unit to keep the effects.
        private constant real       Interval            = 0.02 //The periodic method is called every Interval seconds


        private constant real       SpeedPerSecond      = 600.00
        //    Amount of range the fire ball moves per second.
        private constant real       LifeSpan            = 2.00
        //    Amount of time the fire ball will fly.
        private constant real       AcquireTargetsRange = 60.00
        //    Enemy units in this range are considered targets; allow the fire ball to damage more than one unit.
        private constant real       DamageSplashRadius  = 125.00
        //    Enemy units in this range get damaged
        private constant real       TextTagSize         = 0.023
        //    The size of the text tags (floating texts) wich show the damage amount.
        private constant real       TextTagVelocity     = 0.066563
        //    The velocity of the text tags (floating texts)
        private constant real       TextTagFadePoint    = 3.00
        //    At this point the text tag starts fading.
        private constant real       TextTagDeathPoint   = 5.50
        //    At this point the text tag dies.
        private constant real       TextTagZoffset      = 30.00
        //    Height of the text tag above the target.
        private constant boolean    TextTagVisible      = true
        //    I guess you want it visible, don't you? SET TO "false" FOR NO TEXT TAGS
        private constant string     sfxpath             = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
        //    This is the Special Effect created when the fire ball explodes.
                //    I strongly recommend using this effect for the fire ball.


        private          player     FilterPlayer

        private          boolexpr  FILTER
        
        private          group      Targets

    endglobals

    private function Conditions takes nothing returns boolean

        return (GetSpellAbilityId() == AbilityID)

    endfunction

    private constant function AntiLeak takes nothing returns boolean
        return true
    endfunction

    private function Filtru takes nothing returns boolean
        local unit u = GetFilterUnit()
        local boolean b = IsUnitEnemy(u, FilterPlayer) and not IsUnitType(u, UNIT_TYPE_UNDEAD) and GetUnitState(u, UNIT_STATE_LIFE) > .405
        set  u = null
        return  b
    endfunction



    public struct FireBall
        private unit             entity
        private real             lifespan
        private real             speed
        private real             damage
        private real             ar
        private real             tr
        private integer          i
        private string           sfx
        private static timer PERIODIC
        private static thistype array ACTIVE
        private static integer COUNT = 0
        private player owner
        private integer pos
        private real a


        private static method Damage takes nothing returns nothing
            local thistype this = ForGroupFireBall
            local unit       u        = GetEnumUnit()
            local real       x        = GetUnitX(u)
            local real       y        = GetUnitY(u)
            local texttag    tt       = CreateTextTag()
            //local boolean b = true


                call SetTextTagVelocity(tt, 0, TextTagVelocity)
                call SetTextTagPermanent(tt, false )
                call SetTextTagFadepoint(tt, TextTagFadePoint )
                call SetTextTagLifespan(tt, TextTagDeathPoint )
                call SetTextTagVisibility(tt, TextTagVisible )
                call SetTextTagPosUnit(tt, u, TextTagZoffset)
                if ( GetUnitAbilityLevel(u, 'Avul') == 0 ) and ( GetUnitAbilityLevel(u, 'Aloc') == 0 ) then
                    call SetTextTagText(tt, ( "Damage: " + ( I2S ( R2I (this.damage) ) + " " ) ), TextTagSize )
                    call SetTextTagColor(tt, 230, 100, 150, 255 )
                    //                        R    G    B    Alpha
                elseif ( GetUnitAbilityLevel(u, 'Avul') == 1 ) then
                    call SetTextTagText(tt, "Damage: none", TextTagSize )
                    call SetTextTagColor(tt, 230, 200, 150, 255 )
                    //                        R    G    B    Alpha
                /*elseif ( GetUnitAbilityLevel(u, 'Aloc') == 1 ) and GetUnitTypeId(u) != DummyID then
                    call SetTextTagText(tt, "Missed", TextTagSize )
                    call SetTextTagColor(tt, 230, 200, 150, 255 )
                    //                        R    G    B    Alpha
                    set b = false*/
                elseif GetUnitTypeId(u) == DummyID then
                    call SetTextTagText(tt, "Collided", TextTagSize )
                    call SetTextTagColor(tt, 150, 200, 230, 255 )
                    //                        R    G    B    Alpha
                endif

            //if b then
                call UnitDamageTarget( this.entity, u, this.damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
            //endif
            
            set u = null
            set tt = null
        endmethod

        private static method periodic takes nothing returns nothing
            local thistype this

            local real       dist
            local real       x
            local real       y
            local integer i = 0


            //Done with setup.
            loop
                exitwhen i == thistype.COUNT
                    set this = thistype.ACTIVE[i]
                    set dist = this.speed * Interval
                    set this.lifespan = this.lifespan - Interval
                    
                    set x = GetUnitX(this.entity) + dist * Cos(this.a)
                    set y = GetUnitY(this.entity) + dist * Sin(this.a)
                    
                    call SetUnitPosition( this.entity, x, y )
                    
                    
                    set FilterPlayer = this.owner
                    call GroupEnumUnitsInRange(Targets, x, y, this.ar, FILTER)

                    //inlined version of ITE()
                    set bj_groupCountUnits = 0
                    call ForGroup(Targets, function CountUnitsInGroupEnum)
                    if ( bj_groupCountUnits >= 1  or this.lifespan <= 0.0) then
                        call GroupEnumUnitsInRange(Targets, x, y, this.tr, FILTER)
                        set ForGroupFireBall = this
                        call ForGroup( Targets, function thistype.Damage )
                        call DestroyEffect( AddSpecialEffect( this.sfx, x, y ))
                        call this.destroy()
                        set i = i - 1
                    endif

                    call GroupClear(Targets)
                set i = i + 1
            endloop
        endmethod
            
        static method create takes player owner, real x, real y, real face, real end, real spd, real dmg, real ar, real tr, string sfxp returns thistype
            local thistype this = thistype.allocate()

            set this.entity = CreateUnit( owner, DummyID, x, y, face)
            set this.a = face * bj_DEGTORAD
            set this.owner = owner
            set this.lifespan = end
            set this.speed = spd
            set this.damage = dmg
            set this.ar = ar
            set this.tr = tr
            set this.sfx = sfxp

            
            set thistype.ACTIVE[thistype.COUNT] = this
            set this.pos = thistype.COUNT
            set thistype.COUNT = thistype.COUNT + 1
            
            if thistype.COUNT == 1 then
                call TimerStart(thistype.PERIODIC, Interval, true, function thistype.periodic)
            endif

            return this
        endmethod
        
        private method onDestroy takes nothing returns nothing
            set thistype.COUNT = thistype.COUNT - 1
            set thistype.ACTIVE[this.pos] = thistype.ACTIVE[thistype.COUNT]
            set thistype.ACTIVE[this.pos].pos = this.pos
            if thistype.COUNT == 0 then
                call PauseTimer(thistype.PERIODIC)
            endif
            if this.entity != null then
                call RemoveUnit( this.entity )
            endif
            set .entity = null
        endmethod
        
        private static method onInit takes nothing returns nothing
            set thistype.PERIODIC = CreateTimer()
        endmethod
    endstruct

    globals
        private          FireBall   ForGroupFireBall
    endglobals
    
    private function FBCreate takes nothing returns nothing
        local unit u = GetTriggerUnit()
        call FireBall.create(GetOwningPlayer(u), GetUnitX(u), GetUnitY(u), GetUnitFacing(u), LifeSpan, SpeedPerSecond, GetHeroInt(u, true) * GetHeroLevel(u) * GetUnitAbilityLevel(u, AbilityID), AcquireTargetsRange, DamageSplashRadius, sfxpath)
        set u = null
    endfunction

    private function Init takes nothing returns nothing

        local trigger    t        = CreateTrigger()
        local filterfunc f        = Filter(function AntiLeak)
        local integer    i        = 0
        set FILTER = Filter(function Filtru)
        set Targets = CreateGroup()
        loop
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, f)
            set i = i + 1
            exitwhen i >= 16
        endloop
        call TriggerAddCondition(t, Condition( function Conditions ) )
        call TriggerAddAction(t, function FBCreate )
        call DestroyFilter(f)
        set f = null
    endfunction

    //===========================================================================
    //~~~~~~  End of movement
endscope

Edit: one last change: now uses a global group instead of a single one for every FireBall
 
Level 4
Joined
Apr 16, 2009
Messages
85
1. you got at least JassHelper version 0.9.H.0 ? (most likely yes or you would get an error)
2. save DIRECTLY before clicking at test map - don't click at you triggers before clicking test map
 
Status
Not open for further replies.
Top