Spell Footman Cannon


hook DoNothing MakeGUIUsersCrash
Reaction score
Footmen Cannon

Footman Cannon

Jass: Yes.
MUI: Yes.
Laggless: Yes.
Leakless: Yes, I think so.
Requires: NewGen Editor, TT Credits to Cohadar, HSAS Credits to Pandamine

The Footman lord summons a random amount of Footmans around him and fires them at a target with some kind of mystical psychic power.

Lvl 1: Deals 10 damage per unit and summons 3-4 Footmans.
Lvl 2: Deals 20 damage per unit and summons 6-8 Footmans.
Lvl 3: Deals 30 damage per unit and summons 9-12 Footmans.

// ==================================================================================================================================================================
//  Footman cannon created by: Gwypaas
// ==================================================================================================================================================================

// ==================================================================================================================================================================
//              How to import:
//  1. Create a spell named what you want this spell to be named and add the ID to the constants further down.
//  2. Create the dummy units for this spell and add the ID at the constants further down
//  3. Import the librarys TT made by Cohadar and HSAS made by Pandamine to your map
//  4. Change the rest of the values at the spell constants further down so the spell acts the way you want it to do.
//  Not: if you really want to you can change the HSAS calls to ABC calls or another library that uses handle attaching.
// ==================================================================================================================================================================

scope UnitCannon   // Uses HSAS and TT
    // The ID of the main ability.
    private constant integer ABILID = 'A000'  
    // The ID of the dummy unit used in this spell.
    private constant integer DUMMYID = 'h000'
    // The ID of the eye candy dummy used when summoning
    private constant integer EYECANDYDUMMY = 'h001'
    // How fast they travels.
    private constant real SPEED = 40
    // How long time it takes to summon all units.
    private constant real TOTALTIMESUMMONING = 1.25
    // The radius of the circle the units are summoned in.
    private constant real CIRCLERADIUS = 400
    // The radius of the circle it damages in.
    private constant real DAMAGERADIUS = 200
    // The ID of the ability called Crow Form, used in this spell to cause the units flying.
    private constant integer CROWFORMID = 'Amrf'
    // The max height the units are able to be on.
    private constant real MAXHEIGHT = 2 // This value is used like this: Total Distance / This.
    // The damage is calculated by this formula lvl*YourValue so change YourValue here to the value you want.
    private constant real DAMAGE = 10
    // The unit amount is calculated like this: A random integer between lvl*MINAMOUNT and lvl*MAXAMOUNT
    private constant integer MINAMOUNT = 3
    private constant integer MAXAMOUNT = 4

// ===========   DO NOT EDIT ANYTHING AFTER THIS LINE!   ===========
// ===========   DO NOT EDIT ANYTHING AFTER THIS LINE!   ===========
// ===========   DO NOT EDIT ANYTHING AFTER THIS LINE!   ===========

private function H2I takes handle h returns integer 
    return h
    return 0

//! runtextmacro HSAS_Static("Cannon")
//! runtextmacro HSAS_Static("Fire")

private struct Data
    unit caster
    unit array dummies[11]
    integer dummyAmount
    integer currentdummies = 0
    integer unitsFired = 0
    integer level
    real targetX
    real targetY


private struct SlideData
    unit damager // The hero that casts the spell
    unit whichUnit
    real totalDist
    real distDone = 0
    real startX
    real startY
    real targetX
    real targetY
    real cX
    real cY
    real angle
    real speed
    real maxHeight

private function RandomPointCircle takes real x, real y, real d returns location // Credits to Vexorian!
    local real cx = GetRandomReal(-d,d)
    local real ty = SquareRoot(d*d-cx*cx)
    return Location(x+cx, y+ GetRandomReal(-ty,ty) )

private function ParabolicMovement takes real h, real d, real x returns real // h Height, d total distance, x dist to add.
    // thanks to moyack for this function!
    return (-4*h/(3*d*d))*x*x + (4*h/(3*d))*x

private function DamageAmount takes integer lvl returns real
    return lvl*DAMAGE

private function UnitAmount takes integer lvl returns integer
    return GetRandomInt(lvl*MINAMOUNT, lvl*MAXAMOUNT)
private function DamageFilter takes unit caster, unit target returns boolean
    return (IsUnitEnemy(target, GetOwningPlayer(caster))) and (IsUnitType(target, UNIT_TYPE_GROUND)) and (GetUnitState(target, UNIT_STATE_LIFE) > 0.405)

private function SlideCallback takes nothing returns boolean
    local SlideData SD = TT_GetData()
    local group g = CreateGroup()
    local unit u 
    local real z = ParabolicMovement(SD.maxHeight, SD.totalDist, SD.distDone)
    if SD.distDone >= SD.totalDist then
        call GroupEnumUnitsInRange(g, GetUnitX(SD.whichUnit), GetUnitY(SD.whichUnit), DAMAGERADIUS, null)
            set u = FirstOfGroup(g)
            exitwhen u == null
            if DamageFilter(SD.damager, u) == true then
                call UnitDamageTarget(SD.damager, u, DamageAmount(GetUnitAbilityLevel(SD.damager, ABILID)) , FALSE, FALSE, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
            call GroupRemoveUnit(g, u)
            set u = null
        set SD.damager = null
        call SetUnitExploded(SD.whichUnit, true)
        call KillUnit(SD.whichUnit)
        set SD.whichUnit = null
        call SD.destroy()
        return true
        set SD.cX = SD.cX + SD.speed * Cos(SD.angle)
        set SD.cY = SD.cY + SD.speed * Sin(SD.angle)
        call SetUnitX(SD.whichUnit, SD.cX)
        call SetUnitY(SD.whichUnit, SD.cY)
        call SetUnitFlyHeight(SD.whichUnit, z, 0)
        set SD.distDone = SD.distDone + SD.speed
    set u = null
    call DestroyGroup(g)
    set g = null
    return false

private function Slide takes unit whichUnit, real startX, real startY, real targetX, real targetY, real speed, unit damager returns nothing
    local SlideData SD = SlideData.create()
    set SD.damager = damager
    set SD.startX = startX
    set SD.startY = startY
    set SD.targetX = targetX
    set SD.targetY = targetY
    set SD.cX = startX
    set SD.cY = startY
    set SD.whichUnit = whichUnit
    set SD.angle = Atan2((SD.targetY-SD.startY),(SD.targetX-SD.startX))
    set SD.speed = speed
    set SD.totalDist = SquareRoot(Pow((SD.targetX-SD.startX), 2) + Pow((SD.targetY-SD.startY), 2))
    set SD.maxHeight = SD.totalDist/MAXHEIGHT
    call TT_Start(function SlideCallback, SD)


private function FireUnitsCallback takes nothing returns nothing
    local timer t2 = GetExpiredTimer()
    local Data data = GetAttachedStructFire(t2)
    local integer i = 0

    if data.unitsFired >= data.dummyAmount then
        call PauseTimer(t2)
        call DestroyTimer(t2)
        set data.caster = null
            exitwhen i == 11
            set data.dummies<i> = null
            set i = i+1
        call data.destroy()
        call Slide(data.dummies[data.unitsFired], GetUnitX(data.dummies[data.unitsFired]), GetUnitY(data.dummies[data.unitsFired]), data.targetX, data.targetY, SPEED, data.caster)
        set data.unitsFired = data.unitsFired + 1
    set t2 = null
private function CreateUnitAndRemoveAfter takes integer ID, real X, real Y, real time, boolean kill, player whichPlayer returns nothing
    local unit u = CreateUnit(whichPlayer, ID, X, Y, GetRandomReal(0, 360))
    if kill == true then
        call TriggerSleepAction(time)
        call KillUnit(u)
        call TriggerSleepAction(time)
        call RemoveUnit(u)
    set u = null
private function CreateUnitCallback takes nothing returns nothing 
    local timer t = GetExpiredTimer()
    local Data data = GetAttachedStructCannon(t)
    local integer i = 0
    local real casterX = GetUnitX(data.caster)
    local real casterY = GetUnitY(data.caster)
    local location l = Location(0,0)
    if data.currentdummies == data.dummyAmount then

        call PauseTimer(t)
        call DestroyTimer(t)
        set l = RandomPointCircle(casterX, casterY, CIRCLERADIUS)
        set data.dummies[data.currentdummies] = CreateUnit(GetOwningPlayer(data.caster), DUMMYID, GetLocationX(l), GetLocationY(l), GetRandomReal(0, 360))
        call UnitApplyTimedLife( data.dummies[data.currentdummies], 'BTLF', TOTALTIMESUMMONING*5 )
        call UnitAddAbility(data.dummies[data.currentdummies], CROWFORMID)
        call UnitRemoveAbility(data.dummies[data.currentdummies], CROWFORMID)
        call CreateUnitAndRemoveAfter.execute(EYECANDYDUMMY, GetLocationX(l), GetLocationY(l), TOTALTIMESUMMONING*2, false, GetOwningPlayer(data.caster))
        set data.currentdummies = data.currentdummies+1
    set t = null
    call RemoveLocation(l)
    set l = null

private function Actions takes nothing returns nothing
    local Data data = Data.create()
    local integer i = 0
    local real waitBetween

    local timer t = CreateTimer()
    local timer t2 = CreateTimer()
    local location targ = GetSpellTargetLoc()

    set data.caster = GetSpellAbilityUnit()
    set data.dummyAmount = UnitAmount(GetUnitAbilityLevel(data.caster, ABILID))
    set waitBetween = TOTALTIMESUMMONING/data.dummyAmount
    set data.level = GetUnitAbilityLevel(data.caster, ABILID)
    set data.targetX = GetLocationX(targ)
    set data.targetY = GetLocationY(targ)
    call AttachStructCannon(t, data)
    call TimerStart(t, waitBetween, true, function CreateUnitCallback)
    call AttachStructFire(t2, data)
    call TimerStart(t2, waitBetween, true, function FireUnitsCallback)

    set t = null
    set t2 = null
    call RemoveLocation(targ)
    set targ  = null

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

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 )
    set Trig = null

Screenshot: (It looks better ingame! :))
Added a special effect when summoning, speeded up a bit.



You can change this now in User CP
Reaction score
Hmm, like the idea but the result isn't that great in my opinion. I think it looks so unrealistic (even for wc3 ;) ) and boring. Since this is somewhat a telekinesis spell how about let the units flow in a certain high? And please add special effects whenever the footies are created. And you perhaps should increase the speed of at least the footmen creating.
I really like the idea, but think you should improve it in the stated points to make it more attractive^^

Edit: Ahh... now I knew your name. You're from mutedvision :s

Edit2: At least the speed issue is customizable for sure... globals ftw :D


hook DoNothing MakeGUIUsersCrash
Reaction score
I was thinking about doing something like he raises them from the ground to different heights but I scrapped that idea because I didn't really know how move their height down, so I added a parabola so it would look better :p.

But i'll add some spawning effect!
Yes I am from Mutedvision, who are you there? I can't find your name. :p

Edit: Updated now with a effect when the units summons, also speeded up a bit.


Reaction score
Oh noes! =O
TriggerSleepActionSuxorz said:
"Look mommy, I spy a TriggerSleepAction!" said Little Timmy.
"Stay away from those, dear. And we call it TSA... Your grounded for even calling it by its real name," said the mother.
"Aw... Sh*t!" said Little Timmy.
Other than that little TriggerSleepAction, it seems fine. :D

Great job Gwypaas, +rep.

Nice coding.


hook DoNothing MakeGUIUsersCrash
Reaction score
Yeah I know about the TriggerSleepAction's but they are there to make the spell look better, there's 3 parts in the spell and the SleepActions separates them :p. I could always make a timer but that would just make the code less readable so I think it will stay this way, but if someone really thinks that the SleepActions must be removed I could do it :p
General chit-chat
Help Users
  • No one is chatting at the moment.
  • jonas jonas:
    That sounds like fun!
  • 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
  • jonas jonas:
  • 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
  • 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!
  • 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.
  • Ghan Ghan:
    Beep boop
  • The Helper The Helper:
    hears robot sounds while 250 bots are on the forum lol
  • The Helper The Helper:
    Happy Saturday!
  • The Helper The Helper:
    and then it was Thursday...
  • tom_mai78101 tom_mai78101:
    And then Monday
  • The Helper The Helper:
    I got the day off today!
  • 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.
  • 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.


    Hive Workshop NUON Dome World Editor Tutorials

    Network Sponsors

    Apex Steel Pipe - Buys and sells Steel Pipe.