Using Dummy Casters

Back to lessons

Dummy Casters

Dummy casting is another key concept to write complex spells. This tutorial will have far less code than others, since it's about half object work. A dummy caster is an invisible, unselectable unit that casts another spell for you. The purpose of this will be more clear with an example. Let's say we want to create an ability that 1) deals damage, 2) slows for 10 seconds.

We can easily damage a target through a trigger, but we cannot easily slow a unit. So you might just base this spell off the default WC3 slow and make it deal damage. That is an acceptable solution to the problem. But now let's say we want a spell that also stuns for 2 seconds in addition to slowing.

There is no default wc3 ability that both stuns and slows, so this is a problem. We need a way to inflict at least one status through a trigger and for this we must use a dummy caster.

If we base our spell of Storm Bolt to make it stun and code in damage, then all we're missing is the slow. We need another unit to simultaneously cast a Slow ability that lasts 10 seconds, right when we do the other effects. In order to do this we'll first look at some native functions we must use for this.

native CreateUnit takes player id, integer unitid, real x, real y, real face returns unit

Almost an equivalent to GUI's create unit. There are a few differences. This takes an x and y instead of a Location. Also, this does not allow us to use Last Created Unit. Instead, it directly returns the unit it creates.

native UnitAddAbility takes unit whichUnit, integer abilityId returns boolean

Attempts to add the requested ability to the unit. The return value says whether the add succeeded or failed. We'll ignore this value for now.

native UnitApplyTimedLife takes unit whichUnit, integer buffId, real duration returns nothing

Pretty self explanatory, adds an expiration timer. In here, buffId is the buff to add (for example Water Elemental is added to units called by Summon Water Elemental). We can add a buff value of 0 if we do not care about the buff.

native IssueTargetOrder takes unit whichUnit, string order, widget targetWidget returns boolean

Forces a unit to cast an ability on another unit. We pass in the order string found in World Editor, as well as the source/target units. The boolean return specifies whether it was successful or not.

Before we get to code, there's quite a bit to do in the object editor.

1. Create a dummy unit. Requirement for dummy:
    - No model
    - Locust ability
    - Invulnerable ability
    - 0 cast point
    - Max turn speed
    - No shadow art
2. Create a dummy ability based off Slow. Requirements for a dummy ability:
    - Max missile speed, if applicable
    - No art (in most cases)
    - 0 mana cost
    - 0 cooldown
    - High cast range (something like 9999999)
    - Targets Allowed should be correct for what the spell does (or optionally, can target anything)
    - Applies correct (usually custom) buff
    - No techtree requirement

This tutorial assumes you're capable of using the object editor, so I will not show these. But you must have this unit and a dummy slow skill. You must also get the rawcodes for these skills. For the purposes of this tutorial, these are my rawcodes:

    - Custom skill, to be coded = 'A000'
    - Dummy slow skill = 'A001'
    - Dummy unit = 'h000'

And now, the general process for using a dummy:

    - Create dummy and assign to a variable
    - Add the dummy skill to dummy unit
    - Cause the dummy unit to cast on target
    - Add a short expiration timer to the dummy unit
    - Null the dummy unit

Let's see how this looks in code. This will only cast a dummy ability, not damage the target:

scope VJassDummySpell initializer onInit

    function onCast takes nothing returns nothing
        local unit source = GetTriggerUnit()
        local unit target = GetSpellTargetUnit()
        local unit dummy = CreateUnit(GetOwningPlayer(source),'h000',GetUnitX(source),GetUnitY(source),0)
        call UnitAddAbility(dummy,'A001')
        call IssueTargetOrder(dummy,"slow",target)
        call UnitApplyTimedLife(dummy,0,1.0)
        set dummy = null
        set source = null
        set target = null
    endfunction

    function onCheck takes nothing returns boolean
        return(GetSpellAbilityId() == 'A000')
    endfunction

    function onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,Condition(function onCheck))
        call TriggerAddAction(t,function onCast)
        set t = null
    endfunction

endscope


The relevant bit to dummy casting is this:

local unit dummy = CreateUnit(GetOwningPlayer(source),'h000',GetUnitX(source),GetUnitY(source),0)
call UnitAddAbility(dummy,'A001')
call IssueTargetOrder(dummy,"slow",target)
call UnitApplyTimedLife(dummy,0,1.0)
set dummy = null


Let's look whether this code satisfies the process:

1. Create dummy and assign to a variable

local unit dummy = CreateUnit(GetOwningPlayer(source),'h000',GetUnitX(source),GetUnitY(source),0)

2. Add the dummy skill to dummy unit

call UnitAddAbility(dummy,'A001')

3. Cause the dummy unit to cast on target

call IssueTargetOrder(dummy,"slow",target)

4. Add a short expiration timer to the dummy unit

call UnitApplyTimedLife(dummy,0,1.0)

5. Null the dummy unit

set dummy = null

Debugging:

Dummy unit issues are some of the worst ever to debug because of the connection with the Object Editor. Sometimes you'll have a dummy that just doesn't cast, and you won't have any idea what went wrong. In these cases, I use the following debug checklist:

    - You are ordering the unit to use the right order string. These can be found in the object editor.
    - This is NOT always the same as the ability name. For example, Storm Bolt has ability string "thunderbolt"
    - Rawcodes for dummy unit and ability are correct
    - They are case sensitive
    - Re-check settings for dummy unit
    - Re-check settings for dummy ability
    - If none of this helps, take Locust off the dummy unit and give it a model. Then observe to see what the unit is actually doing.

Review:

    - Create a dummy unit in the object editor and dummy ability
    - Create dummy and assign to a variable
    - Add the dummy skill to dummy unit
    - Cause the dummy unit to cast on target
    - Add a short expiration timer to the dummy unit


Back to lessons