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], &#039;BTLF&#039;, 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.


  • FootmanCannonNew.jpg
    195.6 KB · Views: 2,229
  • UnitCannon.w3x
    38.9 KB · Views: 441


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.

      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.