Spell Fire Pillar

Larcenist

REP: Respect, Envy, Prosperity?
Reaction score
211
Fire Pillar:

Spell info:
GUI/JASS?: JASS
MUI?: Yes
Leaks: None
How to implement: Instructions inside the map.

Note: Requires NewGen World Editor and ABC struct attachment system.

Code:
[U][B]Description:[/B][/U]
Summons a fiery pillar at a targetted location. 
If an enemy unit walks into the pillar it will be struck by 10 fireballs, 
dealing damage and stunning for 0.1 seconds each. 
Lasts 12 seconds or until an enemy unit walks into the pillar. 
Level 1 - 30 damage per fireball. 
Level 2 - 40 damage per fireball. 
Level 3 - 50 damage per fireball.
Level 4 - 60 damage per fireball.
Cooldown 19/16/13/10 seconds.
Manacost 140/160/180/200.
Spell code:

JASS:
scope FirePillar

globals
    private constant integer SpellAID = 'A000'          //The hero spell ID
    private constant integer DummyAID = 'A001'         //The dummyspell ID
    private constant integer EffectDummyID = 'u001'   //The visible dummy's ID
    private constant integer DummyID = 'u000'        //The regular dummy's ID
    private constant real Duration = 12.0           //The duration of the spell
    private constant integer FireballCount = 10    //Number of fireballs spawned to strike the enemy unit that comes in range
    private constant real DetonationAoE = 150.0   //The detonation AoE for the Fire Pillar
    private constant string Effect = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"    //The explosion effect when the Fire Pillar activates.
    private constant boolean AoEOn = false        //"true" = AoE, "false" = single target.
//Do not change anything below here unless you know what you're doing.
    private unit FPCaster = null
    private integer FPLevel = 0
endglobals

private struct SpellData
    unit caster
    unit dummy
    real x
    real y
    trigger inrange
    timer fptimer
    boolean check
    integer level
    triggeraction inrangeaction
    method onDestroy takes nothing returns nothing
        call ClearTriggerStructA(.inrange)
        call TriggerRemoveAction(.inrange, .inrangeaction)
        call DestroyTrigger(.inrange)
        call ClearTimerStructA(.fptimer)
        call DestroyTimer(.fptimer)
    endmethod
endstruct

private function TimerActions takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local SpellData data = GetTimerStructA(t)
    
    if GetWidgetLife(data.dummy) > .405 then
        call RemoveUnit(data.dummy)
        call DestroyEffect(AddSpecialEffect(Effect, data.x, data.y))
    endif
    call data.destroy()
    
    set t = null
endfunction

private function GroupFiltering takes nothing returns boolean
    return true
endfunction

private function InRangeActions takes nothing returns nothing
    local SpellData data = GetTriggerStructA(GetTriggeringTrigger())
    local unit Victim = GetTriggerUnit()
    local location Point
    local real x
    local real y
    local integer Loop = 0
    local unit Dummy
    local group UnitGroup = CreateGroup()
    local unit First
    
    if data.check == false then
        if IsPlayerEnemy(GetOwningPlayer(data.caster), GetOwningPlayer(Victim)) == true then
            set data.check = true
            call DestroyEffect(AddSpecialEffect(Effect, data.x, data.y))
            call RemoveUnit(data.dummy)
            loop
                exitwhen Loop >= FireballCount
                    set Loop = Loop + 1
                    if AoEOn == true then
                        call GroupEnumUnitsInRange(UnitGroup, data.x, data.y, DetonationAoE, Condition(function GroupFiltering))
                        loop
                            set First = FirstOfGroup(UnitGroup)
                            exitwhen First == null
                                if IsPlayerEnemy(GetOwningPlayer(data.caster), GetOwningPlayer(First)) == true and IsUnitType(First, UNIT_TYPE_FLYING) == false then
                                    set Point = GetUnitLoc(First)
                                    set x = GetLocationX(Point)
                                    set y = GetLocationY(Point)
                                    set Dummy = CreateUnit(GetOwningPlayer(data.caster), DummyID, x, y, 0)
                                
                                    call UnitAddAbility(Dummy, DummyAID)
                                    call SetUnitAbilityLevel(Dummy, DummyAID, data.level)
                                    call IssueTargetOrder(Dummy, "thunderbolt", First)
                                    call UnitApplyTimedLife(Dummy, 'BTLF', 1.0)
                                    call RemoveLocation(Point)
                                endif
                            call GroupRemoveUnit(UnitGroup, First)
                        endloop
                        call TriggerSleepAction(.1)
                    else
                        set Dummy = CreateUnit(GetOwningPlayer(data.caster), DummyID, data.x, data.y, 0)
                        call UnitAddAbility(Dummy, DummyAID)
                        call SetUnitAbilityLevel(Dummy, DummyAID, data.level)
                        call IssueTargetOrder(Dummy, "thunderbolt", Victim)
                        call UnitApplyTimedLife(Dummy, 'BTLF', 1.0)
                        call TriggerSleepAction(.1)
                    endif
            endloop
        endif
    endif
    
    call DestroyGroup(UnitGroup)
    
    set Victim = null
    set Dummy = null
    set UnitGroup = null
    set Point = null
endfunction

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

private function Actions takes nothing returns nothing
    local SpellData data = SpellData.create()
    local location Point = GetSpellTargetLoc()
    local unit Caster = GetTriggerUnit()
    local real x = GetLocationX(Point)
    local real y = GetLocationY(Point)
    local integer Level = GetUnitAbilityLevel(Caster, SpellAID)
    
    set data.dummy = CreateUnit(GetOwningPlayer(Caster), EffectDummyID, x, y, 0)
    set data.fptimer = CreateTimer()
    set data.caster = Caster
    set data.inrange = CreateTrigger()
    set data.check = false
    set data.x = x
    set data.y = y
    set data.level = Level
    
    call SetTimerStructA(data.fptimer, data)
    call TimerStart(data.fptimer, Duration, false, function TimerActions)
    call SetTriggerStructA(data.inrange, data)
    call TriggerRegisterUnitInRangeSimple(data.inrange, DetonationAoE, data.dummy)
    set data.inrangeaction = TriggerAddAction(data.inrange, function InRangeActions)
    call RemoveLocation(Point)
    
    set Point = null
    set Caster = null
endfunction

//===========================================================================
public function InitTrig takes nothing returns nothing
    local trigger trig = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( trig, Condition( function Conditions ) )
    call TriggerAddAction( trig, function Actions )
endfunction

endscope

Usage of the spell?:
It's exellent to just pop a Fire Pillar at your enemies escape route when he/she is running for his/her life. Also good for initializing an attack against someone who stands all alone with no creeps around. Also works great for harrassing with. Rather imba and could use either increased cooldown or higher manacost.

Special Thanks:
Ragnarok Online for giving me the inspiration for this spell.
Cohadar for the ABC struct attachment system.
Tinki3 for the testmap, saving me the trouble of showing people my insanely poor terrainingskills.
Flare for pointing out further customizablities.
You for reading the special thanks section for some unknown reason.

Version tracker and updates:
Version 1.1d: Fixed the TriggerAddAction leak and the spell now filters out flying units.
Version 1.1c: Now the unit is removed, not killed so it melts in better with the explosion.
Version 1.1b: Minor leakfix.
Version 1.1: Made struct private and added an easy way of making the fire pillars do AoE damage/stun.
Version 1.0b: Made the spell even easier to customize.
Version 1.0: Initial release.

Screenshot:

It's like extremely hard to get a good screenshot of this one, check it out ingame instead.

[URL=http://img230.imageshack.us/my.php?image=firepillaryn0.jpg][/URL]
 

Attachments

Vestras

Retired
Reaction score
248
You should REALLY add a death animation to the "pillar". I mean, suddenly, it's just gone.
 

Flare

Stops copies me!
Staff member
Reaction score
662
Code seems fine, I can't find any leaks, but one thing I noticed

JASS:
                exitwhen Loop >= 10


You should set that 10 to a constant, so that people can easily configure how many times a unit is hit by the fireball.

Along with the 'In Range' distance (possibly). Everything that makes the spell easy to manipulate makes it better (IMO at least).


I'll test the spell itself later though (don't have much time to test atm)
 

Larcenist

REP: Respect, Envy, Prosperity?
Reaction score
211
Made it even easier to customize, thanks for pointing that out Flare.

Also did I use the onDestroy method correctly? I really had no idea how to do it so I just read a few spells in the spell section then guessed from there.

You should REALLY add a death animation to the "pillar". I mean, suddenly, it's just gone.
It's a doodad <base> crap, it hasn't gotten any death animation.
 

Larcenist

REP: Respect, Envy, Prosperity?
Reaction score
211
Oh bummer, I always keep my structs private since I always use the struct Spelldata. Guess I forgot it this time, fixing right away.
 

Vestras

Retired
Reaction score
248
> It's a doodad <base> crap, it hasn't gotten any death animation.

Take another model? Or get Hatebreeder or someone else to animate it.
 

Larcenist

REP: Respect, Envy, Prosperity?
Reaction score
211
I provide the spell code, if you don't like the model then take those 10 seconds changing the model in the object editor.

Version 1.1 added, one question though. Will turning the AoE on result in the spell not being MUI anymore? I was just wondering since there's a wait in there, though the globals are redeclared inside the loop too so I do not know if it's MUI or not.
 

Flare

Stops copies me!
Staff member
Reaction score
662
You mean here?

JASS:
            loop
                exitwhen Loop &gt;= FireballCount
                    set Loop = Loop + 1
                    if AoEOn == true then
                        set FPCaster = data.caster
                        set FPLevel = data.level
                        call GroupEnumUnitsInRange(UnitGroup, data.x, data.y, DetonationAoE, Condition(function GroupFiltering))
                        call ForGroup(UnitGroup, function GroupActions)
                        call TriggerSleepAction(.1)
                    else
                        set Dummy = CreateUnit(GetOwningPlayer(data.caster), DummyID, data.x, data.y, 0)
                        call UnitAddAbility(Dummy, DummyAID)
                        call SetUnitAbilityLevel(Dummy, DummyAID, data.level)
                        call IssueTargetOrder(Dummy, &quot;thunderbolt&quot;, Victim)
                        call UnitApplyTimedLife(Dummy, 'BTLF', 1.0)
                        call TriggerSleepAction(.1)
                    endif
            endloop


If you are using locals (or redeclaring globals at the start of the loop), it's still MUI. Just try repeated casts and see if it works as intended.

If you are worried about it not being MUI, you could use a FirstOfGroup loop within the function that has all your locals/struct data
JASS:
loop
exitwhen FirstOfGroup (g) == null
set u = FirstOfGroup (g)
//Actions using u
call GroupRemoveUnit (u)
endloop

//When all units have been removed from the group, u will have to be null
 

Larcenist

REP: Respect, Envy, Prosperity?
Reaction score
211
Yeh it works as intended, though I should destroy that group inside the loop, not outside.

I'll just stick to this until someone jumps in saying that it's not MUI, I somehow feel more comfortable using callback functions rather than using FirstOfGroup loops.
 

GoGo-Boy

You can change this now in User CP
Reaction score
40
Hmm I like the idea and I really like the model you took for this:thup:

However as ELSKER suggested I'd add one (best one that fits :D) minor explosion effect to this Pillar when it disappears. Another thing I'd prefer is making the pillar taking a random target or multiple. Because it looks kinda weird when many units are close to it and only one gets damaged.
 

Larcenist

REP: Respect, Envy, Prosperity?
Reaction score
211
Change the constant variable AoEOn to true and it will deal damage to all nearby units.

Also it's quite difficult timing an explosion to fit when the unit dies, since it dissapears around 1-2 seconds after it's killed.
 

GoGo-Boy

You can change this now in User CP
Reaction score
40
Oh should have read what you wrote about the AOE^^

Remove the doodad then instead of destroying. That way you can time it perfeclty.
 

Tinki3

Special Member
Reaction score
418
Neat Spell.

In the "Actions" function, you are leaking a triggeraction with:
JASS:
call TriggerAddAction(data.inrange, function InRangeActions)


I would suggest adding a triggeraction struct variable and using that to define/set the action for the trigger:
JASS:
set data.trigaction = TriggerAddAction(data.inrange, function InRangeActions)


And then you'd obviously need to remove the triggeraction from the trigger inside your onDestroy method:
JASS:
call TriggerRemoveAction(.inrange, .trigaction)

(Remember to do this before destroying + clearing the trigger variable).


I also noticed that you were just filtering units that were enemies of the caster.
This means air units can trigger the effects, so filter them out.
 

SanKakU

Member
Reaction score
21
sounds interesting, but what does it look like? i might try updating this one if the original poster doesn't get to it first.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • jonas jonas:
    That sounds like fun!
    +1
  • The Helper The Helper:
    it was a blast!
  • The Helper The Helper:
    I am going to post the Youtube of the investigation in the forums when it is ready
    +1
  • jonas jonas:
    cool!
  • vypur85 vypur85:
    Sounds cool TH.
  • tom_mai78101 tom_mai78101:
    I was on a Legend of Zelda marathon...
  • tom_mai78101 tom_mai78101:
    Am still doing it now
    +1
  • jonas jonas:
    which one(s) are you playing?
  • jonas jonas:
    I played a little bit of the switch title two weeks ago and found it quite boring
  • The Helper The Helper:
    just got back from San Antonio this weekend had the best Buffalo Chicken Cheesesteak sandwhich in Universal City, TX - place was called Yous Guys freaking awesome! Hope everyone had a fantastic weekend!
    +1
  • The Helper The Helper:
    Happy Tuesday!
  • The Helper The Helper:
    We have been getting crazy numbers reported by the forum of people online the bots are going crazy on us I think it is AI training bots going at it at least that is what it looks like to me.
  • The Helper The Helper:
    Most legit traffic is tracked on multiple Analytics and we have Cloud Flare setup to block a ton of stuff but still there is large amount of bots that seem to escape detection and show up in the user list of the forum. I have been watching this bullshit for a year and still cannot figure it out it is drving me crazy lol.
    +1
  • Ghan Ghan:
    Beep boop
    +1
  • The Helper The Helper:
    hears robot sounds while 250 bots are on the forum lol
  • The Helper The Helper:
    Happy Saturday!
    +1
  • The Helper The Helper:
    and then it was Thursday...
    +2
  • tom_mai78101 tom_mai78101:
    And then Monday
    +1
  • The Helper The Helper:
    I got the day off today!
    +1
  • tom_mai78101 tom_mai78101:
    How...? (T-T)
  • The Helper The Helper:
    I took the day off. I work for myself so I can do that.
    +1
  • Varine Varine:
    Well I'm already over summer
  • jonas jonas:
    varine! good to see you
  • jonas jonas:
    what's going on, what's got you going
  • The Helper The Helper:
    good to see you varine hope you are well my friend

    The Helper Discord

    Members online

    No members online now.

    Affiliates

    Hive Workshop NUON Dome World Editor Tutorials

    Network Sponsors

    Apex Steel Pipe - Buys and sells Steel Pipe.
    Top