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

  • Fire Pillar v1.1d by Larcenist.w3x
    62 KB · Views: 596

Vestras

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

Flare

Stops copies me!
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
249
> 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!
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, &#039;BTLF&#039;, 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.
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/

      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