Spell Rain Of Footmen

gref

New Member
Reaction score
33
Rain Of Footmen

MUI: Yes.
Leakless: Yes.
Lagless: Yes.
JESP: Yes.
Jass/GUI: Jass

Screenshot:
RainOfFootmenScreen.jpg


Description:
Channeled: Footmen fall from the sky and explode, dealing damage.
Inspired by: http://www.thehelper.net/forums/showthread.php?t=80226

Made in Jass
JASS:

//===============================>
// Rain of Footmen by gref.
//===============================>

scope RainOfFootmen
    // Follows JESP
    
    //===========================>
    //Constants
    //===========================>
   globals
       private constant integer AbilCode = 'A000' //Name of triggering ability
       private constant integer UnitCode = 'h000' //Name of unit used for footman
       private constant real maxheight = 1200.0 //maximum height
       private constant real minheight = 1000.0 //minimum height
       private constant real range = 400.0 //range from center. ie 400 = 800 aoe.
       private constant real damagerange = 130.0 //range a unit has to be in to be hit by a footman's damage
       private constant real damagefactor = 50.0 //damage per level
       private constant real damagebase = 50.0 //constant damage
       private constant real footmenpersecond = 10 //number of footmen per second.
    endglobals          
    
 
    //===========================>
    //Data Types
    //===========================>
 
    struct FootmenStruct
        real x
        real y
        integer level
        unit caster 
                  
        static method create takes unit caster, real x, real y, integer level returns FootmenStruct
            local FootmenStruct f = FootmenStruct.allocate()
            
            set f.level = level 
            set f.x = x
            set f.y = y
            set f.caster = caster
             
            return f 
        endmethod    
    endstruct
    
    struct TimerStruct
        timer t
        method onDestroy takes nothing returns nothing
            local FootmenStruct f = GetAttachedInt(.t, "FootmenStruct")
            call FootmenStruct.destroy(f)
            call CleanAttachedVars(.t)
            call PauseTimer(.t)
            call DestroyTimer(.t)
        endmethod    
        static method create takes nothing returns TimerStruct
            local TimerStruct ts = TimerStruct.allocate()
            
            set ts.t = CreateTimer()
            
            return ts
        endmethod
    endstruct      
 
    //===========================>
    // Functions
    //===========================>                
    
    
    private function DamageFilter takes nothing returns boolean
        return (GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0) and not(IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE))
    endfunction
     
    private function TimerTick takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local FootmenStruct f = GetAttachedInt(t, "FootmenStruct")
        
        local real angle = GetRandomReal(0, 6.28318)
        local real distance = GetRandomReal(0, range)
        local real x = f.x + distance*Cos(angle)
        local real y = f.y + distance*Sin(angle)
                                                   
        local real tangle = GetRandomReal(0, 6.28318)
        local real tdistance = GetRandomReal(0, 200)
        
        local real tx = x + tdistance*Cos(tangle)
        local real ty = y + tdistance*Sin(tangle)
        
        local unit d = CreateUnit(GetOwningPlayer(f.caster), UnitCode, x, y, GetRandomReal(0, 360))
        local real height = GetRandomReal(minheight, maxheight)
        local real timedlife = height/(1500)
        
        call AttachInt(d, "FootmanLevel", f.level)
        call UnitApplyTimedLife(d, 'BTLF', timedlife) 
        call IssuePointOrder(d, "move", tx, ty)
        call UnitAddAbility(d, 'Arav')
        call UnitRemoveAbility(d, 'Arav')
        call SetUnitFlyHeight(d, height, 0) 
        call SetUnitFlyHeight(d, 0, (height/timedlife))
        
        set d = null
        set t = null
    endfunction

    private function RainOfFootmen_Actions takes nothing returns nothing 
         local TimerStruct ts 
         local FootmenStruct f 
         local unit u
         local group g
         local integer level
         local real x 
         local real y
         local player owner
         
         if(GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_EFFECT) then //Footman cast
             if(GetSpellAbilityId() == AbilCode) then
                 set ts = TimerStruct.create()
                 set f = FootmenStruct.create(GetTriggerUnit(), GetLocationX(GetSpellTargetLoc()), GetLocationY(GetSpellTargetLoc()), GetUnitAbilityLevel(GetTriggerUnit(), AbilCode))
         
                 call AttachInt(GetTriggerUnit(), "TimerStruct", ts)
                 call AttachInt(ts.t, "FootmenStruct", f)
                 call TimerStart(ts.t, 1/footmenpersecond, true, function TimerTick)
             endif
         elseif(GetTriggerEventId() == EVENT_PLAYER_UNIT_DEATH) then  //Footman dies  
             if (GetUnitTypeId(GetTriggerUnit()) == UnitCode) then
                 set g = CreateGroup()
                 set level = GetAttachedInt(GetTriggerUnit(), "FootmanLevel")
                 set x = GetUnitX(GetTriggerUnit())
                 set y = GetUnitY(GetTriggerUnit())       
                 set owner = GetOwningPlayer(GetTriggerUnit())                       
        
                 call CleanAttachedVars(GetTriggerUnit())
        
                 call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Human\\HumanLargeDeathExplode\\HumanLargeDeathExplode.mdl", x, y))
               
                 call GroupEnumUnitsInRange(g, x, y, damagerange, Condition(function DamageFilter))
                 loop
                     set u = FirstOfGroup(g)
                     exitwhen u == null
            
                     if(IsUnitEnemy(u, owner) == true) then            
                         call UnitDamageTarget(GetTriggerUnit(), u, damagebase+damagefactor*level, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
                         endif
            
                         call GroupRemoveUnit(g, u)
                endloop  
        
                call DestroyGroup(g)
            endif
             
         else  //stopped cast.       
             if(GetSpellAbilityId() == AbilCode) then
                 set ts = GetAttachedInt(GetTriggerUnit(), "TimerStruct")
        
                 call TimerStruct.destroy(ts)
                 call CleanAttachedVars(GetTriggerUnit()) 
             endif               
         endif
         
         
         set u = null
         set owner = null
         set g = null
    endfunction
    
    
//===========================================================================
    function InitTrig_RainOfFootmen takes nothing returns nothing
        local trigger t = CreateTrigger(  )     
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_DEATH )           
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_ENDCAST )
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddAction( t, function RainOfFootmen_Actions )      
         
        call Preload("Objects\\Spawnmodels\\Human\\HumanLargeDeathExplode\\HumanLargeDeathExplode.mdl")
       
        set t = null
    endfunction
endscope

How to Implement:
Spell Requirements:
1. A vJass preprocessor (If you don't have one you can download one from wc3campaigns.net)

How to implement this in your map:
1. Copy the CSCache and RainOfFootmen triggers to your map. (If you have CSCache, don't worry about it)
2. Copy the unit called Footman in the object editor.
3. Copy the ability called Rain Of Footmen in the object editor.
4. Make sure that the constants in the RainOfFootmen trigger match the ability and unit.
 

Attachments

  • RainOfFootmen.w3x
    155.5 KB · Views: 235

ReVolver

Mega Super Ultra Cool Member
Reaction score
609
This has to be the funniest spell I have ever seen, maybe better than sheepfall :p
 

Tom Jones

N/A
Reaction score
437
You should pause your timer before destroying it, destroying a running timer can horrible cripple a map. You could also clean the code a little if you used one trigger and then checked for the triggering event id. Other than that it's fine, keep it up.
 

gref

New Member
Reaction score
33
I won't lie, I kind of just made this for joke value. But I will go over and fix those things.

And I'm assuming you mean, add all events to one trigger, and just have a massive nested if then else, for the trigger event id part.
 
Reaction score
456
You don't have to null struct's members. So that method onDestroy can be removed.
 

gref

New Member
Reaction score
33
All comments taken into account, updated.

Footmen fall faster now because I felt they fell to slow.
It's actually attached as well. (I have 50 kilobytes left. :( )

Yes the footmen walk, I order them to move to get a more varied and interesting distribution. (And I've always found footmen moving makes jump animations look better too) It will look less strange now that the fall speed has been increased though.

@ nulling struct members: that would be alot easier for me to be comfortable about if I saw what happened to code afterwards --.

Also any more spells I release, I plan to release in spell packs. I hadn't realised I'd be this prolific.
 

Tinki3

Special Member
Reaction score
418
> I have 50 kilobytes left

Ask TH if you can have more space, I'm sure he'd be more than happy to give you some, since you're contributing helpful maps for other members to download and learn from.

Nice spell, though, I'll have to test this later :)

EDIT:

Tested the spell, so funny.
Though, to reduce lag, you should "remove" the units when they hit the ground.
There's not much point in leaving 100 Footmen there to rot, decay, and add to lag.

In the function "RainOfFootmen_Actions", this line leaks:
Code:
set f = FootmenStruct.create(GetTriggerUnit(), GetLocationX([COLOR="Red"]GetSpellTargetLoc()[/COLOR]), GetLocationY([COLOR="Red"]GetSpellTargetLoc()[/COLOR]), GetUnitAbilityLevel(GetTriggerUnit(), AbilCode))

And, I noticed, also, that you did this:
JASS:
call AttachInt(d, "FootmanLevel", f.level)

Is that attachment cleaned?
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      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