4. The YouTD API

The API of YouTD is a set off vJASS structs and functions to help you create beautiful towereffects with very few lines of code.

Before you start reading it remember one thing:
This documentation is very long since it tries to cover any aspect of the API. However you don't need all parts of it for your tower. For example, if your tower just attacks and casts a dummy spell, you can basically skip the chapters about Buffs, Auras, Autocasts and Projectiles.You can read them to understand everything, but don't get frustrated, just skip them if you don't need them.

Another thing you should always remember:
There is a commented version of the API interfaces in the Content Creation Map. So, if you want to know something, always first check the comments there.

Here is an overview of the YouTD structs, as an UML class diagram.

So the main type for creeps and towers is the type Unit. It is a wrapper for a warcraft 3 unit and offers many method to get information about the unit or alter it.
Next thing you should know, is that every tower has an owner of type Playor (which is a wrapping struct for a player) and ever Playor has a team.
You can use these structs to get/set information about the owning player and its team.
For casting dummy spells, the system first defines the dummy and ability type by creating a struct of type Cast. This struct can then create SpellDummies.
The buff engine mainly works with the type BuffType. It is a struct type that specify the type of a buff. You create it on map init and fill it with all the stuff the buff should do (icon, effects, modifications, events) and then you can call the applyBuff method to create a buff of this type on a unit.
The projectile engine (struct Projectile) allows you to create moving visible dummy units that can do different stuff and react to different events.
That was the YouTD API in a few sentences, I know that it is not understandable yet, but whenever you lose the overview over what does what, check this chapter again.
The next chapters will cover the different details of the API that it gets understandable.

Before we start talking about Projectiles, Buffs and Dummies, you should understand what you can do with the basic data structures for units and players, because you will need them almost everywhere.

Always devide between Unit with an upper case U and unit. The one is a struct member, the other one a Warcraft3 unit.
The struct Unit wraps a Warcraft3 unit. You can get the wrapped unit by calling this method for any Unit:

method getUnit takes nothing returns unit

This struct has two important structs derived from it: Creep and Tower. They offer additional functionalities which are special to creeps and towers.
So where do you get Units from? First, every event handling function looks like this:
function onXXX takes Tower tower returns nothing

As you see, it takes a parameter tower of type Tower. This is your tower. Since it is derived from the type Unit, it allows you to use all methods of Unit. In addition, for targeted events you can call the static method of the struct Event:
struct Event static method getTarget takes nothing returns Unit

It gets you the target of this event. So for example, for the onKill, onAttack, onDamage events, it gets you the killed/attacked/damaged unit.
You can get units by picking units in range or something like that. How do you get the struct wrapping them? Very simple:
The struct index is saved in their custom value, so you can just call GetUnitUserData(unit) to get a Unit. If the custom value is zero, then the unit is already dead or has no struct attached to it. In both cases, you should discard the unit.
So getting a Unit from a unit can be done like that:
local Unit u = GetUnitUserData(YOURUNIT)

Here, YOURUNIT must be a variable of type unit. Now you can also check if the unit is a tower or a creep. For some events, you can just cast the Unit to Creep or Tower because it is clear what it is. Example: The Event.getTarget() function will always return a creep if used on an OnAttack event (since towers don't attack each other).
If it is unclear if the Unit is a Tower or a Creep, you have two possibilities.
Either you can call these methods of type Unit:
struct Unit method isATower takes nothing returns boolean method isACreep takes nothing returns boolean

They return true if the Unit is a Tower/Creep.
You can also call these functions for a unit to determine what it is:
function isCreepAndAlive takes unit u returns boolean function isTower takes unit u returns boolean function isDummy takes unit u returns boolean

Code example:
local Unit u = GetUnitUserData(YOURUNIT) if isTower(YOURUNIT) then call BJDebugMsg("This unit is a tower") endif if u.isATower() then call BJDebugMsg("This Unit (with capital U!) is a tower") endif

After having learned how to aquire a unit and test what kind it is, let's learn what you can do with it. There are many methods you can use.
Here are just some of them. For a complete list, just check the commented API in the Content Creation Map:

struct Unit //Returns the number of kills this unit did method getKills takes nothing returns integer //Returns the owning player of this unit method getOwner takes nothing returns Playor //Calculates a spell crit chance for this tower with bonusChance additional chance to crit // and bonusDamage anditional damage on crit method calcSpellCrit takes real bonusChance, real bonusDamage returns real //Calculate a chance for this unit's with chance percent. //Use this function whenever you want something to happen for this unit on a percent base //example: use .calcChance(0.5) to get a 50% chance //==> will return true on 50% of all invokations //however, note that the given value will be modified by the unit's chance bonuses method calcChance takes real chance returns boolean //Returns the buff of BuffType bt that the unit has //If the unit has no buff of this buff type, 0 will be returned //So you can use uc.getBuffOfType(...) != 0 to check if a unit has a specific buff method getBuffOfType takes BuffType bt returns Buff ...

These two structs represent wrappers for a player and his team. You can get Playors by using the getOwner method of Units.
You can get his team by calling his getTeam method.
Playor and Team offer some methods to display (floating) text to the playor/team, play sounds for them, alter their gold/income and so on.

Here are some methods for the struct Playor (again, a complete list can be found in the API itself):

struct Playor //Creates a floating text for this player on a Unit, uses standard values //whichText: the text to be displayed //whichUnit: the unit where the text is displayed //red,green,blue: colors of your text, between 0 and 255 method displayFloatingText takes string whichText, Unit whichUnit, integer red, integer green, integer blue returns nothing //Gets the team this player belongs to method getTeam takes nothing returns Team //Gets the number of tower this player has method getNumTower takes nothing returns integer //modifies the interest rate of this player by delta. //Delta is percent based, so 0.01 = +1% interest rate method modifyInterestRate takes real delta returns nothing //Gives gold to this player. You can specify a unit for this, if you do so then //there will be an effect at the unit's position (if showEffect is true) and a //golden floating text that shows the value of gold gained (if showText is true) //If you don't want personalized effect stuff, just set u to null //You can use a negative value to substract gold (then the floating text will be red instead of gold) method giveGold takes integer value, unit u, boolean showEffect, boolean showText returns nothing ...

and here are some methods of the struct Team:

struct Team //Gets the size of this team, i.e. how many NON-LEFT(!) players it has method getSize takes nothing returns integer //Gets the level in which this team currently is method getLevel takes nothing returns integer //Retrievs the percentage of lifes this team has //Note that this is really percentage, so 40 = 40% //(in opposite to many other functions that return a real value where 0.4 is 40%) method getLivesPercent takes nothing returns integer //Gives gold to all players of this team. You can specify a unit for this, if you do so then //there will be an effect at the unit's position (if showEffect is true) and a //golden floating text that shows the value of gold gained (if showText is true) //If you don't want personalized effect stuff, just set u to null //You can use a negative value to substract gold (then the floating text will be red instead of gold) method giveGoldToEachPlayer takes integer value, unit u, boolean showEffect, boolean showText returns nothing ...

Now you have the basic knowledge of what can be done where, how to acquire Units, their owners and their teams.

Let's start with the easiest things you can do to alter a Tower / Creep. Modifying its values like speed, damage, armor and so on.
The YouTD API allows you to alter many other advanced things like critical strike chance,item drop chance.

The most simple method to alter a Unit's stats is this one:

struct Unit //Modifies a property modId by value method modifyProperty takes integer modId, real value returns nothing

modId is an integer which can be one of these constants (excerpt from library Modifier).
All modifications are commented, so you know what you can do.
As you see, you can alter ALMOST everything you can imagine:
globals //************************************************************** //** This is a list of possible modIds which can be passed to ** //** the Modifier.addModification function ** //** ALWAYS USE THESE CONSTANTS WHEN INVOKING THIS FUNCTION ** //************************************************************** //**************************************** //** TOWER MODIFICATIONS ** //** The following modifications only ** //** make sense when applied to a tower ** //**************************************** //== DAMAGE_TO TABLE MODIFICATIONS == constant integer MOD_DMG_TO_MASS = 0 //Mass units (very unhealthy) constant integer MOD_DMG_TO_NORMAL = 1 //Normal units (the ones that spawn in normal levels) constant integer MOD_DMG_TO_CHAMPION = 2 //Champions are larger units that spawn with normal units constant integer MOD_DMG_TO_BOSS = 3 //Bosses are even mightier than champions (the will probably spawn alone) constant integer MOD_DMG_TO_AIR = 4 //Air levels constant integer MOD_DMG_TO_UNDEAD = 10 //Undead units constant integer MOD_DMG_TO_MAGIC = 11 //Magical units constant integer MOD_DMG_TO_NATURE = 12 //Nature units constant integer MOD_DMG_TO_ORC = 13 //Orcish units constant integer MOD_DMG_TO_HUMANOID = 14 //Humanoid units //== OFFENSIVE MODIFICATIONS == //Modifies the chance that attacks of this unit will hit critical //percent based so 0.01 = 1% crit chance //Note that every unit starts with a basic crit chance of 1% constant integer MOD_ATK_CRIT_CHANCE = 20 //Modifies the damage that critical attacks of this unit do //percent based so 0.01 = 1% crit damage //Note that every unit starts with a basic crit damage of 150% (+50%) constant integer MOD_ATK_CRIT_DAMAGE = 21 //Modifies the multicrit value of this unit. Multicrit is the chance that a //critical strike on attack crits again. The basic multicritValue of each unit //is 1, so a unit can crit only once. //If you increment its value, then it can crit two times, if you decrease it to //zero, than the unit cannot crit at all constant integer MOD_MULTICRIT_COUNT = 22 //Modifies the chance that spells of this unit will hit critical //percent based so 0.01 = 1% crit chance //Note that every unit starts with a basic crit chance of 1% constant integer MOD_SPELL_CRIT_CHANCE = 23 //Modifies the damage that critical spells of this unit do //percent based so 0.01 = 1% crit damage //Note that every unit starts with a basic crit damage of 150% (+50%) constant integer MOD_SPELL_CRIT_DAMAGE = 24 //Modifies the damage of all spells cast by this unit //percent based so 0.01 = 1% spell damage constant integer MOD_SPELL_DAMAGE_DEALT = 25 //Increases the chance for creeps killed by this tower to drop an item //percent based so 0.01 = +1% better chance to drop an item constant integer MOD_ITEM_CHANCE_ON_KILL = 26 //Increases the quality of items dropped by creeps which are killed by this unit //percent based so 0.01 = +1% better quality constant integer MOD_ITEM_QUALITY_ON_KILL = 27 //Modifies the attackspeed of the unit on a percentage base, //0.01 = 1% attackspeed, 0.2 = 20% attackspeed and so on constant integer MOD_ATTACKSPEED = 30 //Modifies the base damage of this unit (the white numbers) //1.0 = +1 damage constant integer MOD_DAMAGE_BASE = 31 //Modifies the base damage of this unit (the white numbers) //percent based so 0.01 = 1% damage constant integer MOD_DAMAGE_BASE_PERC = 32 //Modifies the add damage of this unit (the green numbers) //1.0 = +1 damage constant integer MOD_DAMAGE_ADD = 33 //Modifies the add damage of this unit (the green numbers) //percent based so 0.01 = 1% damage constant integer MOD_DAMAGE_ADD_PERC = 34 //Modifies the dps of this unit (the green numbers) //1.0 = +1 dps //In contrast to MOD_DAMAGE_ADD this value is altered by the tower's base attack speed //so a slower tower will get more bonus than a faster tower constant integer MOD_DPS_ADD = 35 //**************************************** //** CREEP MODIFICATIONS ** //** The following modifications only ** //** make sense when applied to a creep ** //**************************************** //== DAMAGE_FROM TABLE MODIFICATIONS == //Modify the damage a creep receives from the specified element constant integer MOD_DMG_FROM_ASTRAL = 40 constant integer MOD_DMG_FROM_DARKNESS = 41 constant integer MOD_DMG_FROM_NATURE = 42 constant integer MOD_DMG_FROM_FIRE = 43 constant integer MOD_DMG_FROM_ICE = 44 constant integer MOD_DMG_FROM_STORM = 45 constant integer MOD_DMG_FROM_IRON = 46 //== DEFENSIVE MODIFICATIONS == //Modifies the experience this unit grants upon death //percent based so 0.01 = +1% exp constant integer MOD_EXP_GRANTED = 60 //Modifies the bounty this unit grants upon death //percent based so 0.01 = +1% bounty constant integer MOD_BOUNTY_GRANTED = 61 //Modifies the spell damage this unit receives //percent based so 0.01 = +1% spell damage received constant integer MOD_SPELL_DAMAGE_RECEIVED = 62 //Modifies the attack damge this unit receives //percent based so 0.01 = +1% attack damage received constant integer MOD_ATK_DAMAGE_RECEIVED = 63 //Increases the chance for this creeps to drop an item on death //percent based so 0.01 = +1% better chance to drop an item constant integer MOD_ITEM_CHANCE_ON_DEATH = 64 //Increases the quality of items dropped by this creep on death //percent based so 0.01 = +1% better quality constant integer MOD_ITEM_QUALITY_ON_DEATH = 65 //Modifies the hitpoints of this unit //1.0 = +1 hitpoint constant integer MOD_HP = 70 //Modifies the hitpoints of this unit //percent based so 0.01 = +1% hitpoints constant integer MOD_HP_PERC = 71 //Modifies the hitpoint regeneration of this unit //1.0 = +1 hitpoint / sec regeneration constant integer MOD_HP_REGEN = 72 //Modifies the hitpoint regeneration of this unit //percent based so 0.01 = +1% hitpoint regeneration constant integer MOD_HP_REGEN_PERC = 73 //Modifies the armor //1.0 = +1 armor constant integer MOD_ARMOR = 74 //Modifies the armor //percent based so 0.01 = +1% armor constant integer MOD_ARMOR_PERC = 75 //**************************************** //** GENERAL MODIFICATIONS ** //** The following modifications ** //** make sense on creeps and towers ** //**************************************** //== MISC MODIFICATIONS == //Modifies the triggerchances of this unit //i.e.: Whenever the calcChance function is called for this unit it has bonus percents to return true //percent based so 0.01 = +1% trigger chances constant integer MOD_TRIGGER_CHANCES = 80 //Modifies buff durations for buffs cast by this unit //percent based so 0.01 = +1% duration constant integer MOD_BUFF_DURATION = 81 //Modifies buff durations for debuffs cast ONTO this unit //percent based so -0.01 = -1% duration constant integer MOD_DEBUFF_DURATION = 82 //Modifies the bounty this unit receives upon killing //percent based so 0.01 = +1% bounty constant integer MOD_BOUNTY_RECEIVED = 83 //Modifies the experience this unit receives upon killing //percent based so 0.01 = +1% experience constant integer MOD_EXP_RECEIVED = 84 //Modifies the mana of this unit //1.0 = +1 mana constant integer MOD_MANA = 90 //Modifies the mana of this unit //percent based so 0.01 = +1% mana constant integer MOD_MANA_PERC = 91 //Modifies the mana regeneration of this unit //1.0 = +1 mana / second regeneration constant integer MOD_MANA_REGEN = 92 //Modifies the mana regeneration of this unit //percent based so 0.01 = +1% mana regeneration constant integer MOD_MANA_REGEN_PERC = 93 //Modifies the movespeed of this unit //percent based so 0.01 = +1% movespeed constant integer MOD_MOVESPEED = 94 //Modifies the movespeed of this unit //1 = 1 movespeed (movespeed of units is between 100 and 500) constant integer MOD_MOVESPEED_ABSOLUTE = 95 endglobals

Using the modifyProperty you can directly modify a single property. This is not the only way to alter a unit's properties. For example, buffs can modify unit properties, too. If only the modifyProperty method existed, you had to modify all properties whenever that buff is created and modify them with their negative value whenever the buff is removed. YouTD offers a much more simple way to alter stuff like that, where the engine does the add and remove stuff for you: Modifiers.

A modifier is basically a set of modifications, which are also level dependant, that means they can get upgraded automatically if the tower/buff gains levels. This way you could make a modifier that gets stronger the higher the towers level is.
You create a modifier using the create method:

struct Modifier //Creates a new damage modifier that doesn't modify anything yet static method create takes nothing returns Modifier

As you see, the method takes nothing and creates a modifier that doesn't modify anything yet. Now we can add modifications:
struct Modifier method addModification takes integer modId, real baseValue, real levelAdd returns nothing

This method adds a modification to this modifier. modId is again one of the above mentioned constants which specifies which property to modify.
BaseValue is the value that is modified for a level 0 tower/buff. levelAdd is the value that is added for every level. You can add as many modifications as you wish to a modifier.
Example:
local Modifier myMod = Modifier.create() call myMod.addModification(MOD_ATK_CRIT_CHANCE,0.1,0.01)

This would create a modifier that modifies a tower's critical strike chance by 10% (0.1) on level 0 and +1% per level. So for a level 15 tower, this would give 25% more critical strike chance.
But this modifier is on no tower yet. It can be added to buffs (but that will be covered in the chapter on buffs). It can also be added to a Unit directly using this method:
struct Unit method addModifier takes Modifier m returns ListElement

This adds a modifier to the unit's modifier list and returns a reference to it. Note that the modifier's strength is automatically adjusted with the unit's level. If the unit gains a level also this modification's strength's will be adjusted.
If you want to, you can save this reference and hand it to the method:
struct Unit method removeModifier takes ListElement l returns nothing

which will remove the modifier from this unit.

So if we have a Tower myTower, we could add that modifier to it:

local ListElement modReference = myTower.addModifier(myModifier) call TriggerSleepAction(60.0) call myTower.removeModifier(modReference)

This would add the critical strike modifier to the tower and remove it again after one minute.

However the methods add/removeModifier should only be used in special cases. Most of the time you just add a modifier to a buff. Then the modifier will be on the unit as long as the unit has the buff. You don't have to care about adding and removing it than. This is covered in the chapter on buffs.

As I already told you, towers shouldn't cast their spells themselves. Always, a dummy should do this.
Any dummy spell is represented by an instance of the struct Cast.
Casting those spells takes TWO steps:

  • Creating an instance of Cast during map initialization (in an init function of your Header library!). This specifies things that stay the same, for every instance of this dummy spell.
  • Casting the spell.

Okay, that was very abstract, now let me get a bit more into detail:


This should be done during map initialization, so use the init function in your Header library, create the cast in it and save it in a global variable:
library myTower initializer init uses libHeader globals Cast myCast endglobals //The init function private function init takes nothing returns nothing set myCast = Cast.create(...) endfunction endlibrary

This was how to do it. But what values have to be handed to the .create method of Cast?:
static method create takes integer abil, string order, real lifetime returns Cast

So, as you see, you need the code of an ability "abil", that is the ability that should be cast. Then you need an order to cast it, this is the orderId string for the ability, for example "chainlightning" for a chainlightning. The last parameter "lifetime" is the time the dummy will live. You should set this time to how long the spell takes. So if it is a damage over time that lasts 15 seconds, "lifetime" must be at least 15 seconds!


Okay, now we have initialized our cast. But now we want to cast it. For example we want to cast it whenever the tower attacks a creep. So we use onAttack:
function onAttack takes Tower tower returns nothing

Here we have to add the dummy cast, there are many methods for doing that:

//== immediate casts == method immediateCastFromCaster takes Unit castingUnit, real dmgRatio, real critRatio returns nothing method immediateCastFromUnit takes Unit castingUnit, Unit target, real dmgRatio, real critRatio returns nothing method immediateCastFromPoint takes Unit castingUnit, real x, real y, real dmgRatio, real critRatio returns nothing //== unit targeted casts == method targetCastFromCaster takes Unit castingUnit, Unit target, real dmgRatio, real critRatio returns nothing method targetCastFromTarget takes Unit castingUnit, Unit target, real dmgRatio, real critRatio returns nothing method targetCastFromPoint takes Unit castingUnit, Unit target, real x, real y, real dmgRatio, real critRatio returns nothing //== point targeted casts == method pointCastFromTargetOnTarget takes Unit castingUnit, Unit target, real dmgRatio, real critRatio returns nothing method pointCastFromCasterOnTarget takes Unit castingUnit, Unit target, real dmgRatio, real critRatio returns nothing method pointCastFromPointOnTarget takes Unit castingUnit, real fromX, real fromY, Unit target, real dmgRatio, real critRatio returns nothing method pointCastFromUnitOnCaster takes Unit castingUnit, Unit from, real dmgRatio, real critRatio returns nothing method pointCastFromCasterOnCaster takes Unit castingUnit, real dmgRatio, real critRatio returns nothing method pointCastFromPointOnCaster takes Unit castingUnit, real fromX, real fromY, real dmgRatio, real critRatio returns nothing method pointCastFromUnitOnPoint takes Unit castingUnit,Unit from, real toX, real toY, real dmgRatio, real critRatio returns nothing method pointCastFromCasterOnPoint takes Unit castingUnit, real toX, real toY, real dmgRatio, real critRatio returns nothing method pointCastFromPointOnPoint takes Unit castingUnit, real fromX, real fromY, real toX, real toY, real dmgRatio, real critRatio returns nothing //== no casts == method noCastFromCaster takes Unit castingUnit, real dmgRatio, real critRatio returns SpellDummy method noCastFromUnit takes Unit castingUnit, Unit from, real dmgRatio, real critRatio returns SpellDummy method noCastFromPoint takes Unit castingUnit, real fromX, real fromY, real dmgRatio, real critRatio returns SpellDummy //== AoE casts == method castOnAOEfromCenter takes Unit castingUnit, real x, real y, real radius, real dmgRatio, real critRatio, TargetType targType returns nothing endmethod method castOnAOEfromCaster takes Unit castingUnit, real x, real y, real radius, real dmgRatio, real critRatio, TargetType targType returns nothing endmethod method castOnAOEfromTarget takes Unit castingUnit, real x, real y, real radius, real dmgRatio, real critRatio, TargetType targType returns nothing endmethod

Getting a system into these function is very simple. The name is assembled like this:
[CASTTYPE]CastFrom[FROMTYPE]On[TARGETLOC]

CASTTYPE is the type of the order the dummy gets -> immediate: An immediate cast like warstomp -> target: A unit targeted spell like thunderbolt -> point: A point / location targeted cast like rain of fire FROMTYPE represents from where the spell is cast: -> Caster: The spell is cast from the caster's position -> Unit: The spell is cast from another unit (which has to be specified) -> Target: The spell is casted from the target's position -> Point: The spell is casted from a point which has to be specified (x,y) TARGETLOC is only used by point cast spells and specifies which point to target: -> Caster: The spell is cast onto the caster's position -> Target: The spell is casted onto a target's position (you must specify the targ) -> Point: The spell is casted onto a point which has to be specified (x,y)

So basically you have to answer two questions to choose the correct method:

  • What kind of spell am I casting? (Immediate, Unit Targeted, Ground Targeted)
  • From what position should the spell be cast? (The caster's position? The target's position? Any other position?)
  • Only for ground targeted spells: Onto which point should be cast? (Onto a target? Onto the caster? Onto another position?)
The noCast methods just create and return a dummy without casting anything. This is useful for giving the dummy passive abilities afterwards like an aura.

The castOnAOE methods cast a unit targeted spell onto all units in a range around a target point. The target area is specified by the center (x,y) and the radius. The spell is cast onto every unit in that area which satisfies the target type.
The three methods differ in the position from where the spell is cast: The center of the target area, the caster or from each target to itself.


dmgRatio is just a real percentage value of how much damage this spell should deal. So, if it should deal as much as stated in the object editor, set it to 1.0. If you set it to 3.0 for example, the spell will do 300% of the damage in the object editor. This is an easy way to enhance spells for example based on the level of the tower, for each level, the tower could get +10% spell damage without having to create an ability with many levels.

critRatio is a function from the crit System. To get a critRatio, use the tower method:

method calcSpellCrit takes real bonusChance, real bonusDamage returns real

This function calculates all crit modifiers the tower has from items and buffs and returns a value that can be inserted as critRatio.
The two parameters bonusChance and bonusDamage state values to alter the crit chance of the tower and the crit damage. Basic spell crit damage is 125-175% (depending on the tower's level). So if you insert 0.5 = 50% as bonusDamage the tower will do 120% + 50% = 170% damage upon critting (for a level 0 tower). bonusChance is a value to increase the crit chance of the tower. The basic crit chance is 1.25%. But this chance is raised by items, buffs and can also be raised for certain spells by inserting a number greater than 0 into bonusChance. If you insert 1, the tower will always crit with this spell, if you insert 0.5 there will be a 50% increased chance for critting.


Okay, so lets say, our spell is unit targeted, should do as much spell damage as stated in the object editor (dmgRatio = 1) and should have a 50% chance to hit critical with no extra bonus damage, so the call would look like this:
function onAttack takes Tower tower returns nothing call myCast.targetCastFromCaster(tower,Event.getTarget(),1.0,tower.calcSpellCrit(0.5,0.0)) endfunction

That's it! Remember from the first chapter that we can use Event.getTarget() to get the creep that is attacked.
I apologize for the complicated system with dmgRatio and critRatio. But that was the only way to implement an efficient spell damage crit and amplifying system. I know that these values, paired with the calcSpellCrit function will take you some extra time to learn. But once you have learned it, you will have easy access to let your spells do as much damage as YOU want.

So as a small recap, dummy spells a cast in two stages: Create a cast object upon map init. Then in reaction to an event, cast it!

I didn't want to make you mad in the last chapter, so I didn't tell you everything about the great stuff the API allows you to do with dummy units. So if you are quite familiar with the last chapter and know the three steps it takes to cast a dummy spell, then read on and I show you how deep the rabbit hole goes. If you have already enough for your spells, skip this chapter.

You can give your cast a custom damage table. A damage table alters the dummy's damage against specific categories or sizes. So, for example you can make a chainlightning that deals +100% damage to undead units.
You do this right after creating your cast (at map init).

The method to do so is:

method setCustomDamageTable takes DamageTable table returns nothing

but it needs a damage table. We can create an "empty damage table" i.e. a table that doesn't modify anything yet by invoking the constructor:
local DamageTable myTable = DamageTable.create()

Now we want to set the values for different sizes/categories for this table like the +100% to undead. we use these table's methods:
struct DamagaTable //Sets the bonus against a category whichCategory for this damage table to value method setBonusToCategory takes integer whichCategory, real value returns nothing //Sets the bonus against a size whichSize for this damage table to value method setBonusToSize takes integer whichSize, real value returns nothing

The first function sets the damage against a category (human, undead,...), the second one for a size (mass,boss,normal...).
They need a modId and a real value. The real value is just the value we want to (de-)amplify the cast, so it would be 1.0 for our +100%.
The modId is just one of the creep categories/sizes (You can find them in the Creep library):
//Creep size constants constant integer SIZE_MASS = 0 //Mass creeps (very unhealthy) constant integer SIZE_NORMAL = 1 //Normal creeps (the ones that spawn in normal levels) constant integer SIZE_AIR = 2 //Flying units constant integer SIZE_CHAMPION = 3 //Champions are larger units that spawn with normal creeps constant integer SIZE_BOSS = 4 //Bosses are even mightier than champions (the will spawn alone) constant integer SIZE_CHALLENGE = 7 //Challenge //Creep categorization constants constant integer CATEGORY_UNDEAD = 0 //Undead creeps constant integer CATEGORY_MAGIC = 1 //Magical creeps constant integer CATEGORY_NATURE = 2 //Nature creeps constant integer CATEGORY_ORC = 3 //Orc creeps constant integer CATEGORY_HUMANOID = 4 //Humanoid creeps

so for giving +100% to undead we would call:
call myTable.setBonusToCategory(CATEGORY_UNDEAD,1.0)

Always use the constants when invoking such a method, not the raw integer values! Raw integer values will make your code unreadable (and readable code is a big issue if you want your tower in the map!). With the category constant, everybody immediatly sees that you want to alter damage to undead by +100%.

After we have altered the table we can just hand it to the setCustomDamageTable method and every dummy created from this Cast will have this table.

Putting it all together: This would be your init library with the +100% to undead modification:

library myTower initializer init uses libHeader globals Cast myCast endglobals //The init function private function init takes nothing returns nothing local DamageTable myTable set myCast = Cast.create(...) //Creating the cast set myTable = DamageTable.create() //Creating the damage table call myTable.setBonusToCategory(CATEGORY_UNDEAD,1.0) //Altering the table call myCast.setCustomDamageTable(myTable) //Setting the table for the cast endfunction endlibrary

That's it! Quite straight, isn't it? Note one last thing: In addition to this table, ALWAYS also the table of the tower applies to a cast. Their values are added. So if your tower alread deals +100% damage to undead, your spell would now deal +200% damage to undead. If you don't want your tower's modifications in your spell, just "include" the exactly opposite value's in your towers table.

Example:
Your tower deals only 50% (-50%) to bosses and 150% (+50%) to undead. Now you want a spell that deals 100% to everything. So you would add +0.5 to bosses and -0.5 to undead to the Cast's table. This would compensate the tower's table.

There are two events you can react to for certain dummies. They are onKill (when the dummy kills a creep) and onDamage (when the dummy damages a creep). The methods for setting them are the following:

function interface EventHandler takes DummyUnit dummy returns nothing struct Cast method setDamageEvent takes EventHandler ev returns nothing method setKillEvent takes EventHandler ev returns nothing

So you need to create a function that satisfies the interface EventHandler. Then you can set this function as damage or kill event for your Cast.

Okay, for those who are not familiar with vJASS function interfaces a short explanation of what you need to know:
As you see, the two methods take a variable of type "EventHandler". This type is declared as a function interface with the signature
"takes DummyUnit dummy returns nothing". This just means we have to code a function with EXACTLY this signature (the name "dummy" doesn't matter, but the take and return types and order must be exactly the same!). If we have coded such a function, we can hand it to the method setKillEvent by just writing

<NAME OF THE FUNCTION INTERFACE IT SHOULD SATISIFY>.<NAME OF OUR FUNCTION>

So if our written function was named myFunction, you would write
call myCast.setKillEvent(EventHandler.myFunction)

to entry the function as kill event.

An example:
Let's create a small example that displays a debug message whenever our dummy kills a creep.

First we create an event reaction function with the signature of EventHandler (in our myTower library):

function myKillEvent takes DummyUnit d returns nothing call BJDebugMessage("The dummy has killed a creep!") endfunction

As you see, the function MUST take a DummyUnit to satisfy the function interface (Even if we don't need it here, we just simply display a debug message as event reaction).

The next and already final step is to add this function as kill event for our cast by invoking the setKillEventMethod. This is how our myTower library would look like now:

library myTower initializer init uses libHeader globals Cast myCast endglobals //Our kill event reaction function function myKillEvent takes DummyUnit d returns nothing call BJDebugMessage("The dummy has killed a creep!") endfunction //The init function private function init takes nothing returns nothing local DamageTable myTable set myCast = Cast.create(...) //Creating the cast call myCast.setKillEvent(EventHandler.myKillEvent) //Setting the kill event reaction function endfunction endlibrary

That's it! And believe me this is a mighty tool! Examples:
A chainlightning that stuns everything it hits: Just code a method that casts a dummy stun on the victim and set it as "setDamageEvent".
A fireball that bounces to the nearest creep if it kills a creep: Write a function that finds the nearest target and casts a fireball on it and set this function as "setKillEvent" for your fireball. Since the casted spell and the spell it is cast from are the same this is automatically recursive: Whenever the new spawned fireball kills a unit it will again jump to the next target. You could also make a chain reaction by spawning two fireballs for each fireball that kills a creep. Phat stuff, isn't it?

To get the damaged / killed creeps, use Event.getTarget() (like for all event reactions). For the onDamage event, you can get and set the event damage with Event.damage.
So you could make a spell that gives its tower one mana for each 10 damage point it deals (for example). Just add Event.damage/10 to the tower's mana as an event reaction to the dummy damage event.

The buff system allows you to create triggered buffs that modify the creep/tower values and can react to events like the buffed unit dies, the buff expires and so on. This system is very mighty, it allows you hundred times more stuff than you could do with normal untriggered buffs.

Just like each dummy belongs to a type that specifies its values, which is represented by the struct Cast, each buff belongs to a type that represents its values, represented by the struct BuffType. To create a new kind of buff, you create a BuffType in your init function, save it in a global variable and then you can apply it to units.

The create method of BuffType looks like this:

static method create takes real timeBase, real timeLevelAdd, boolean friendly returns BuffType

The values have the following meaning:
  • timeBase: The time in seconds a buff last on a unit, if its power level is 0. If this value is set to -1, then the buff is permanent.
  • timeLevelAdd: The time in seconds that is added to the base time for each power level a buff has. So for example if this is 10, then a power level 3 buff will last 30 seconds longer than a level 0 buff. If the buff is permanent, you can just set this to zero (or any other value).
  • friendly: If this buff is a positive (true) or negative (false) buff.

The "power level" of a buff is a measure for how strong it is. We will talk about that later.
If you set timeBase to -1.0 then the buff will last infinitly on its target. This can be used to create permanent buffs or buffs that are removed when some conditions are true, like when the unit attacks or something like that.
After invoking this method, we have a BuffType with a specific duration. However the buff doesn't do anything yet.

The easiest way to let our buff do something is setting its modifier. A modifier set this way modifies the unit values of the buff as long as the unit has the buff on it. As soon as the buff is removed, the modifications will vanish from the unit.
To set the modifier of a buff type use these methods:

method setModifier takes Modifier mod returns nothing method setBuffModifier takes Modifier mod returns nothing

this will set the buff type's modifier. Whenever a buff of this type is applied to a unit, all modifications stated in the modifier will be applied. The difference between this two functions is this:
If setModifier is used the modifier gets stronger according to the TOWER LEVEL.
If setBuffModifier is used the modifier gets stronger according to the BUFF POWER LEVEL.

Example how to create a buff type and add some modifications to it: (In the header trigger)

library myTower initializer init uses libHeader globals BuffType myType endglobals //The init function private function init takes nothing returns nothing local Modifier mod = Modifier.create() //Create the modifier set myType = BuffType.create(10.0,1.0,true) //Create the bufftype call myType.setBuffModifier(mod) //Set the modifier for the bufftype call mod.addModification(MOD_DMG_TO_ORC,0.2,0.05) call mod.addModification(MOD_MANA,100,10) endfunction endlibrary

This creates a buffType that lasts 10 seconds + 1 sec/power Level on a unit, it is a positive buff (true). It adds 20% damage against orc creeps on power level 0 and additional 5% for each power level. It also adds 100 to the tower's mana and +10 mana for each power level.

Okay, now you have created a buff type. So lets use it!

You can apply a buff onto a unit by just invoking this method of BuffType:

method apply takes Unit caster, Unit target, integer level returns Buff

It takes two Units, the unit that casted the buff (caster) and the unit it was cast on (target). As a third parameter it takes the power level the buff should have (level).

Just call this function whenever you want to place a buff. The engine does the rest (i.e. checking if the unit already has the buff, doing the modifications, removing it when its time is up and so on).

As already stated, the buff's power level is used for the following things:

  • It modifies the time, the buff will last on the unit
  • It modifies the strength of its modifier (if it has one)
  • It is used for stacking purposes. A power level 5 buff will overwrite a power level 3 buff, but not vice versa.
  • Example:
    Lets take the buff type we have created in the example above. Lets make that our tower has a 10% chance on each attack, that the tower will buff itself with the buff. As buff power level, the tower will use its own level.
    So we enable the onAttack trigger and alter it this way:

    globals constant real ONATTACK_chance = 0.1 //10% chance to cast the buff constant real ONATTACK_chanceLevelAdd = 0.0 //No additional chance per tower level endglobals function onAttack takes Tower tower returns nothing call myType.apply(tower,tower,tower.getLevel()) //Apply the buff onto the tower, casted by the tower itself endfunction

    One line of code. Easy, isn't it?

    When you create a buff type, I showed you how to add modifications to it. However, modifications alone are not too flexible. Event reactions are much more powerful.
    A buff (or the unit it is cast on) produces a large number of events, that we can catch by setting event reaction handlers.
    We can react to the following events:

  • On Create: This event is fired whenever the buff is created (i.e. applied to a unit that doesn't have it yet).
  • On Refresh: This event is fired whenever the buff is refreshed (i.e. applied to a unit that already has it with the same level).
  • On Upgrade: This event is fired whenever the buff is upgraded (i.e. applied to a unit that already has it with a lower level).
  • On Cleanup: This event is fired when the buff is somehow removed or the unit it is on dies. Do your garbage collection tasks here.
  • On Purge: This event is fired when the buff is forcibly removed by a purge like ability.
  • On Expire: This event is fired when the buff is removed due to having used up its duration.
  • Periodic: This event is fired periodically (you can specify the period).
  • On Spell Cast: This event is fired whenever the buffed unit casts a spell
  • On Spell Target: This event is fired whenever the buffed unit is targeted by a spell
  • On Death: This event is fired when the buffed unit dies
  • On Kill: This event is fired whenever the buffed unit kills a unit
  • On Attack: This event is fired whenever the buffed unit attacks
  • On Attacked: This event is fired whenever the buffed unit is attacked
  • On Damage: This event is fired whenever the buffed unit damages a creep
  • On Damaged: This event is fired whenever the buffed unit is damaged
  • On Unit Comes In Range: This event is fired whenever a unit (which can be filtered like in the tower event) comes in a specific range to the buffed unit.
  • Ability: No real event reaction but something else. The unit will have an ability as long as the buff is on it.
    You can specify arbitrary abilCodes here.
  • Autocast: This is no real event reaction. We can even add an autocast to the buff, so the unit will be granted an active ability that is also automatically cast, as long as it has the buff. However, this is very exotic and shouldn't be used often and won't be covered here.
  • Aura: We can also add an aura to this buff. So the buffed unit will have an aura as long as it has this buff. This is also very exotic, so it won't be covered here, too.

So as you can see, you can react to almost everything. This gives you limitless possibilities.
Note that not all events make sense for all kinds of units. For example, a tower will never be damaged or attacked!
Now I will tell you how this works in code. To add an event reaction handler, first write the reaction handler function. It should (must) have this signature:
function NAME takes Buff PARAMNAME returns nothing

NAME and PARAMNAME can be set arbitrarily (as names don't matter for function interfaces).
After you have written such a function you can call the appropriate setEvent or addEvent method of the BuffType to set this function as an event handler. (Note that most of these functions are not defined in the struct BuffType but in the struct EventTypeList which is a supertype of the struct BuffType.
Whenever the method's starts with "add" instead of "set", it shows that you can add more than one of those event handlers to it. However, more than one event handler for one event is not very useful. Here they are:
struct EventTypeList //Sets the event handler that is called when the event placer is somehow destroyed (expired or removed or its target dies...) method setEventOnCleanup takes EventHandler be returns nothing //Sets the modifier of this event placer method setModifier takes Modifier mod returns nothing //Sets the event handler that is called when this EventTypeList is applied onto a unit method addEventOnCreate takes EventHandler be returns nothing //Adds an event handler that is called every seconds (period can not be smaller than 0.2) method addPeriodicEvent takes EventHandler be, real period returns nothing //Adds an event handler that is called when the buffed unit casts a spell method addEventOnSpellCast takes EventHandler be returns nothing //Adds an event handler that is called when the buffed unit is targeted by a spell method addEventOnSpellTarget takes EventHandler be returns nothing //Adds an event handler that is called when the buffed unit dies //or if it is destroyed / upgraded method addEventOnDeath takes EventHandler be returns nothing //Adds an event handler that is called when the buffed unit kills a unit method addEventOnKill takes EventHandler be returns nothing //Adds an event handler that is called when the buffed unit gains a level method addEventOnLevelUp takes EventHandler be returns nothing //Adds an event handler that is called with chance% when the buff's target attacks //chanceLevelAdd increases the chance for each level the unit has, //so if it is set to 0.04 for example, a level 10 unit will have a 40% (0.04*10=0.4) higher chance than a level 0 unit method addEventOnAttack takes EventHandler be, real chance, real chanceLevelAdd returns nothing //Adds an event handler that is called with chance% when the buffed unit is attacked //chanceLevelAdd increases the chance for each level the unit has, //so if it is set to 0.04 for example, a level 10 unit will have a 40% (0.04*10=0.4) higher chance than a level 0 unit method addEventOnAttacked takes EventHandler be, real chance, real chanceLevelAdd returns nothing //Adds an event handler that is called with chance% when the buffed unit deals damage //chanceLevelAdd increases the chance for each level the unit has, //so if it is set to 0.04 for example, a level 10 unit will have a 40% (0.04*10=0.4) higher chance than a level 0 unit method addEventOnDamage takes EventHandler be, real chance, real chanceLevelAdd returns nothing //Adds an event handler that is called with chance% when the buffed unit is damaged //chanceLevelAdd increases the chance for each level the unit has, //so if it is set to 0.04 for example, a level 10 unit will have a 40% (0.04*10=0.4) higher chance than a level 0 unit method addEventOnDamaged takes EventHandler be, real chance, real chanceLevelAdd returns nothing //Adds an event handler that is called when a unit which passes the target type test against targType // comes in range range of the buffed unit. method addEventOnUnitComesInRange takes EventHandler be, real range, TargetType targType returns nothing endmethod //Adds an ability placer. The ability abilId will be added automatically //to the unit as long as is it has the event placer //If safeStacking is set to true, then the unit will keep a reference count //for this ability to check how many event placers place it on this unit //So if two placers place it and one is removed the ability is still on the unit //If safeStacking is false, then the ability will be removed upon removing the //first placer, even if there is another one that gives it. //Enable safeStacking if you know that the ability can be granted more than once //Disable it if you are sure that this event placer (and any other that grants that ability) //is never twice on a unit method addAbility takes integer abilId, boolean safeStacking returns nothing endmethod //Adds an aura of type auraType, for more infos check the struct AuraType method addAura takes AuraType auraType returns nothing struct BuffType extends EventTypeList //Sets the event function that is called when this buff is cast on a unit that already has it (with the same level) method setEventOnRefresh takes EventHandler be returns nothing //Sets the event function that is called when this buff is upgraded (i.e. cast on a unit that has it with a lower level) method setEventOnUpgrade takes EventHandler be returns nothing //Sets the event function that is called when the buff runs out of lifetime (just before it is removed) method setEventOnExpire takes EventHandler be returns nothing //Sets the event function that is called when the buff is forcibly removed from the unit (by a purge like ability for example) method setEventOnPurge takes EventHandler be returns nothing //Sets the buff modifier of this buff //The buff modifier is a modifier (just like when using a normal addModifier on EventPlacer (the parent type of this type)) //The difference is that this modifier is only modified by the BUFF LEVEL, not the unit's level method setBuffModifier takes Modifier m returns nothing

In the event reaction function itself, you can use many methods of the buff. For example getBuffedUnit() will get the unit container, on which the buff is cast.

An example will make this more clear and will show how mighty this concept is. Let's create a buff, that whenever the buffed tower attacks, there is a 10% chance that the tower will deal 100 bonus spell damage to the attacked creep.
First the reaction function:

function myEventHandler takes Buff b returns nothing call b.getBuffedUnit().doSpellDamage(Event.getTarget(),100,b.getCaster().calcSpellCrit(0.0,0.0)) endfunction

Again we use Event.getTarget() to refer to the attacked unit.
This is the function. Now just tell the bufftype to call this function in 10% of all attacks.
We do this in the init function after creating our buff type:
private function init takes nothing returns nothing set myType = BuffType.create(10.0,1.0,true) //Create the bufftype call myType.addEventOnAttack(EventHandler.myEventHandler,0.1,0.0) //Set our event handler with a 10% chance endfunction

The whole code would be:

library myTower initializer init uses libHeader globals BuffType myType endglobals function myEventHandler takes Buff b returns nothing call b.getBuffedUnit().doSpellDamage(Event.getTarget(),100,t.calcSpellCrit()) endfunction private function init takes nothing returns nothing set myType = BuffType.create(10.0,1.0,true) //Create the bufftype call myType.addEventOnAttack(EventHandler.myEventHandler,0.1,0.0) //Set our event handler with a 10% chance endfunction endlibrary

As you saw in this example we used .getBuffedUnit() on the buff to get the unit it buffs, there are some more useful methods for buffs, some are lended from its super type EventPlacer (Again, this list is incomplete, for a full list, check the API in the content creation map):

struct EventPlacer //Gets the Unit that is buffed by this eventPlacer method getBuffedUnit takes nothing returns Unit struct Buff extends EventPlacer //Gets the power of this buff (for normal buffs, this is equal to the level) method getPower takes nothing returns integer //Gets the Unit that casted this buff method getCaster takes nothing returns Unit //Returns how many seconds this buff still has until it expires method getRemainingDuration takes nothing returns real //Removes this buff from the unit method removeBuff takes nothing returns nothing

A very useful function is the removeBuff function. It removes the buff immediately. Another one is getCaster which gets you the Unit that casted the buff.


Maybe you want your buff to have a buff icon with description as normal WC3 buffs have or maybe an effect on its target. That is no problem. Just create a buff in the object editor (here you can also choose an effect). Then, you can set it as a BuffType's WC3 buff by using this method:
struct BuffType //Sets the buff icon for this buff, i.e. the buff that will //be displayed on the unit. Enter a valid buff id for your buff here like 'B000' method setBuffIcon takes integer buffId returns nothing

Example, lets assume you created an Object Editor buff with the id 'B000' and myBuffType is the buff type you want to add that buff to:
call myBuffType.setBuffIcon('B000')

That's all! Now the buff will have a buff icon/effect as long as it is on the unit.


When using buff icons like stated in the last chapter, you are able to attach a special effect to that buff icon, hence allowing you to apply models to buffed units. The problem is that these special effects need attachment points. Many tower models don't have any attachment points since they are assembled from doodads. However, we might want to create a buff that targets tower and has a special effect. In this case (whenever you target towers), don't give your buff in the object editor a special effect. Instead add a tower effect to the buff type using one of these functions:
//Sets the effect for this buff //IMPORTANT: You should use this method only for buffs that target towers, //because the effect will NOT move with the unit. //For creeps, just set the BuffIcon and give the Buff in the object editor a model //For towers, this is not always possible since towers often lack attachment points // //Note that there are also two simpler functions below this one that use standard //parameters for some of the values // must be a valid path to the effect model //example: "Abilities\\Spells\\Human\\InnerFire\\InnerFireTarget.mdl" //x,y,z are offsets for the effect //scale, rot are the rotation and the scale of the effect //r,g,b,alpha are colors for the effect ranging from 0 to 255 (0xFF) each //pitch is the pitch angle of the effect method setSpecialEffectAdvanced takes string effectPath, real x, real y, real z, real scale, real rot, integer r, integer g, integer b, integer alpha, real pitch returns nothing //Like .setSpecialEffectAdvanced but uses standard parameters method setSpecialEffect takes string effectPath, real z, real scale returns nothing //Like .setSpecialEffectAdvanced but uses standard parameters method setSpecialEffectSimple takes string effectPath returns nothing //Like .setSpecialEffectAdvanced but uses standard parameters method setSpecialEffectColored takes string effectPath, real z, real scale, integer r, integer g, integer b, integer alpha returns nothing

As you can see the simplest way to add such a tower effect is calling "setSpecialEffectSimple" for your BuffType and just stating the path to the model.
However, if you use one of the other functions, you are able to set many values for your effect (scale, color, pitch,...).
But always remember one thing:
Use these effects ONLY for buffs that target ONLY towers, because these effects do not move with the unit, so for any moving unit, these effects will produce strange results

Example, lets assume myBuffType is the buff type you want to add that tower effect to with some z offset and a scale of 2.0 (200%):

call myBuffType.setSpecialEffect("Abilities\\Spells\\Human\\InnerFire\\InnerFireTarget.mdl",30.0,2.0)

There are some common buff types already predefined in the library "Predefined Buffs". You can use them for those common tasks. Check the library, everything is explained there.

Here I will tell you some more things you can do with buffs. If you feel safe with buff handling, read on here.

Until now, each buff stacks with each other buff except for two buffs of the really same buff type. However, you might want to design a whole family of towers that apply the same buffs, but these buffs shouldn't stack, only the strongest one should be on a unit at one time.
Another example would be slows. There are a few major slow categories in the game, like ice slow, poison slow, heat slow,....
Many towers of the element "Ice/Water/However it will be named in the final version of YouTD" will have slows. If all of these slows would stack, this element could slow all creeps to minimum speed without a big effort. So most of the buffs of this element should share the same "stacking group", i.e. none of them should stack with each other, only the strongest one will be on a unit.

A stacking group in YouTD is just a string. All buffs that have the same string will not stack with each other. If you want your BuffType to be in a stacking group, you first have to find its name. If it is your own stacking group, that is only for your family of towers, you can give it an arbitrary name, but try to give it one that will not collide with other stacking groups. Including your nick for example in the group's name will make collisions very unlikely.
If the stacking group is a global one like the ice / poison / heat slow, then you will find its name in a list on the YouTD homepage where all major global stacking groups will be released.

If you know the name of the group, just call the buff types method setStackingGroup which takes the string as its only parameter.
Example:

call myType.setStackingGroup("gexStackingGroup")

will set the stacking group of the buff type myType to "gexStackingGroup". If other buff types share this group, they won't stack with this buff type.

There are some problems that arise from two different buffs not stacking with each other. The first problem: If one buff is on the unit and another buff of the same level wants to be applied to the unit, should the old buff replaced or be kept?

So for buffs that belong to a stacking group, other rules apply:
-If a buff of a stacking group tries to apply to a unit that has a buff of the same level, the old buff will be kept but NOT refreshed. The new buff will just be rejected.
-If a buff of a stacking group tries to apply to a unit that has a buff of a lower level, then the old buff will be removed (so cleanup events will fire on it) and then the new buff will be applied (which fires on creation events for it). No refresh or upgrade events will be fired.

The other problem with stacking groups is that for stacking groups level and power level are two different things. The power level of a buff tells, how its modifications and its duration are amplified. The level of a buff compares it with other buffs and decides if a new incoming buff is refreshed, upgraded or declined.

For normal buffs these two values were the same, since a single buff is always stronger if is modifications are more intense and vice versa. For stacking groups this is not true anymore. Imagine one buff, lets call it A, that has +100% damage base and +1% damage per power level. Another buff, called B, of the same stacking group has +10% damage and +10% damage per power level. Imagine a unit has Buff B with a power level of 5 on it. so it does +60% damage. Then Buff A with power level 0 is applied. Buff A has a lower power level, but has better values (100% damage versus 60%). So if only the power level was used for stacking, A wouldn't overwrite B, but everybody would agree that A should overwrite B. So we have level and power level for stacking group buffs.
To differ between them, use the advanced apply function instead of the normal one:

method applyCustomPower takes Unit caster, Unit target, integer level, integer power returns Buff

in this function, you can state a level and a power value. The power value will alter the buff's modifications and the duration, the level will do the stacking stuff.

How to set the level for such a buff?
For your own private stacking groups, you will have no problem in finding apropriate level values. For global stacking groups a metric must be found to map buff strength to level. You will find such metrices on the page where the global stacking groups are defined.


You already saw, that when using stacking groups, you should use the function "applyCustomPower", not only "apply". There are two other advanced apply functions that let you specify explicitly the time the buff will last:
//Applies a buff of this type, cast by caster onto the target target with level level //If the target doesn't have this buff already, then it is created (onCreate event is fired) //If the unit already has this buff, there are three possibilities //--> The unit's buff has a higher level than this one -> nothing is done //--> The unit's buff has the same level than this one -> the buff duration is refreshed and an onRefresh event is fired //--> The unit's buff has a lower level than this one -> the buff duration is refreshed and an onUpgrade event is fired method apply takes Unit caster, Unit target, integer level returns Buff //Applies a buff with custom time (rest is the same as in normal apply) method applyCustomTimed takes Unit caster, Unit target, integer level, real time returns Buff

The difference is that in the upper one, power and level are divided, so the upper one should be used for stacking group buffs.

If you use these, your buff will exactly last "time" seconds on the unit. So the values you stated in the BuffType's constructor will not matter anymore. You might want this to add buffs that last a random period of time on units or which's duration is altered by some other things.

You can use these two methods of Unit to check/get the buff a unit has of a specific type/stacking group:

//Returns the buff of BuffType that the unit has //If the unit has no buff of this buff type, 0 will be returned //So you can use uc.getBuffOfType(...) != 0 to check if a unit container has a specific buff method getBuffOfType takes BuffType bt returns Buff //Returns the buff of the stacking group named that the unit has //If the unit has no buff of this stacking group, 0 will be returned //So you can use uc.getBuffOfGroup(...) != 0 to check if a unit container has a buff of //a specific stacking group method getBuffOfGroup takes string s returns Buff

You could use these for example for a tower that has a chance to place a buff and then on each attack deals bonus damage if the creep has the buff.


You might want to create an ability that removes positive/negative buffs from units. For this, this method can be used:
//Forces to remove a buff (the first in the buff list) //positive: If set to true a positive buff is removed, else a negative one //This method returns true if there was a buff to remove, otherwise false //Buffs removed by this method will trigger a purge buff event before beeing destroyed method purgeBuff takes boolean positive returns boolean

It removes either a positive or a negative buff and fires the buff's onPurge events. It will return true if there was a buff to remove and false otherwise.

Projectiles are moving visble dummy units, that can move in many ways (home at a target, fly straight ahead, fly balistically impacting or bouncing from the ground and some more). By default, they don't do anything but moving. However, just like with buffs, you can react to many events, thus giving them influence on the gameplay.

Before I tell you how to create projectiles, I want to talk about the two major types of projectiles the YouTD engine supports.
The first type of projectiles are normally moving projectiles.
These have a direction and a speed and just fly into that direction. They can be altered in many ways by giving them acceleration, rotation, homing, gravitation movement and much more.

The second type are interpolated projectiles. Interpolated projectiles follow a fixed trajectory with a fixed interpolation stepsize. That means you cannot set things as freely as you can for moving projectiles. But don't abandon them! They can easily create very cool curves like projectiles that fly a looping and such stuff.

Keep those two types in mind, I will always refer to them.

I will call the first one "normally moving projectile" or just "moving projectile" and the second one "interpolated projectile". Of couse, also interpolated projectiles are moving. But they are not really moving with some movespeed but follow an invisible curve.

This chapter will cover the basic aspects that have to be done for each type of projectile.

Creating a projectile type

Just like for dummies and buffs, projectiles are created in two steps: First creating a ProjectileType on map init and then launching projectiles of this type.

You create a new projectile by invoking one of these constructors

struct ProjectileType //Constructors for normally moving projectiles static method create takes string model, real lifetime, real speed returns ProjectileType static method createRanged takes string model, real range, real speed returns ProjectileType //Constructor for interpolated projectiles static method createInterpolate takes string model,real speed returns ProjectileType

The first constructor for example takes the following values:
  • model: The path of the model that should be used like: "Abilities\\Weapons\\Mortar\\MortarMissile.mdl" (remember to escape the backslashes with another backslash)
  • lifetime: The time in seconds this projectile lives until it is removed.
  • speed: The starting speed of the projectile, measured in length units per second. (The same measure as object editor speed values)

As you see, an interpolated projectile has no lifetime. This is because it just interpolates between two points. As soon as it reaches the target point, it expires. No lifetime needed.

Now you have a projectile type with a model, lifetime and speed. Let's launch some of these projectiles!

Launching a projectile

After you have created a ProjectileType, you can launch projectiles from it. You do this by invoking one of the constructors of the struct Projectile. There are different constructors for normally moving and interpolated projectiles. Don't let the number of existing constructors scare you! There are many, but only differ in the start and target of that projectile. Projectiles can be launched from Units or points (x,y,z), they can target a point a Unit or just face a specific angle. There are constructors for each combination of start types and targets.
This chapter will just tell the stuff that every constructor has.Everyone of them takes these values:

  • pt: The projectile type to which this projectile belongs
  • caster: The unit that casted the projectile
  • dmgRatio, critRatio: The same as for dummy casts. Check the chapter Damage- and Critratio for more information.

All constructors are well commented in the API. Just check the trigger "Projectiles". There you will find all necessary information about every parameter.

Example

Let's create a tower that shoots a projectile into the direction of the attacked creep everytime it attacks.
First we create the projectile type in our header:

library myTower initializer init uses libHeader globals ProjectileType myPT endglobals private function init takes nothing returns nothing set myPT = ProjectileType.create("Abilities\\Weapons\\Mortar\\MortarMissile.mdl",4.0,500.0) endfunction endlibrary

The projectile uses the mortar missle model, and flies with 500 speed for 4.0 seconds.
For launching the projectile we search a constructor that launches a projectile from one unit to another one. Here it is:

static method createFromUnitToUnit takes ProjectileType pt, Unit caster, real damageRatio, real critRatio, Unit from, Unit target, boolean targeted, boolean ignoreTargetZ, boolean expireWhenReached returns Projectile

Now, launch the projectile upon attack (In the "On Attack" trigger):

globals constant real ONATTACK_chance = 1.0 //Launch on every attack constant real ONATTACK_chanceLevelAdd = 0.0 endglobals function onAttack takes Tower tower returns nothing call Projectile.createFromUnitToUnit(myPT,tower,1.0,tower.calcSpellCrit(),tower,Event.getTarget(),true,false,false) endfunction

The projectile is casted by the tower, so we insert tower as caster. We give our projectile 100% damage (1.0). For the critRatio we use the tower.calcSpellCrit() method. However, if the projectile deals no damage, we can also put an arbitrary number here. The projectile should fly from our tower ("tower") to the attackted creep ("Event.getTarget()").
We set homing to "true" to make the projectile follow the target. The last two values are set to false.

This example created a simple projectile. However, that projectile doesn't do anything yet. So read on to give your projectile more possibilities.

This chapter will tell you which things can be done with EACH type of projectile. The following chapter will then narrate some details that can be done with only some specific types of projectiles.

Adding basic event reactions to the projectile

There is one basic event that you can always react to: onCleanup.
OnCleanup is fired when the projectile is somehow destroyed.

You can react to it by first writing an event reaction handler which has to satisfy this function interface:

//A function that is used to react on untargeted projectile events function interface ProjectileEvent takes Projectile p returns nothing

(This just means that your function has to take a parameter of type Projectile and has to return nothing)

After writing this function you can pass it to this method of your projectile type of your projectile to set it as an event handler:

struct ProjectileType //Sets the event on cleanup, //cleanup is executed whenever the projectile is destroyed, no matter how it was destroyed //Do your cleanup and garbage collection tasks here method setEventOnCleanup takes ProjectileEvent e returns nothing

As the event is called "onCleanup" you should do your cleanup tasks here. If you have allocated any resources, effects or other things, destroy them here.

This was only one event for your projectile and not a very mighty one!
Next, I will tell you about modes you can enable for your projectile (like collision). These modes always come with more events (like onCollision) that you can react to. This will make your projectile useful to influence gameplay.

Enabling projectile modes

There are some "modes" you can enable for all kinds of projectiles. They are:

  • collision mode: If this projectile hits a unit of a specified target type an onCollision event will be triggered.
  • periodic mode: An event is fired periodically for this projectile

You can enable one or many of these modes for a projectile type by calling the appropriate methods of ProjectileType:

struct ProjectileType //Enables the collision for this projectile. This means whenever a unit that satisfies the <targetType> comes into //<collRadius> range of this projectile <collEvent> will be invoked. //If <destroyOnCollision> is true, then the projectile will be destroyed upon collison //If not, then the projectile will move on. The projectile can hit every unit only once! method enableCollision takes ProjectileTargetEvent collEvent, real collRadius, TargetType targetType, endmethod //Enables a periodic events for this projectile //The event function <periodEvent> will be invoked every <period> seconds method enablePeriodic takes ProjectileEvent periodEvent, real period returns nothing endmethod

These methods are commented well, so I won't renarrate their usage again. However there are still some small things to say:
  • enableCollision (and some more events you will learn about later) use ProjectileTargetEvents. This is another function interface which needs more parameters:
    //A function that is used to react on targeted projectile events (like onHit, onCollision...) function interface ProjectileTargetEvent takes Projectile p, Unit target returns nothing

    As you can see, these take a second parameter target, which states the target hit by/colliding with the projectile.
  • enableCollision uses a targetType. Read more about target types at the chapter Target Types.

Now you have the knowledge to react to events and set modes for projectiles. However, there are some more things you can do for each type of projectile:

Setting additional values for ProjectileTypes

You can set more values for a ProjectileType than the few parameters of its constructor and the modes.
Here are some values you can change:
struct ProjectileType //Sets a custom damage table for these projectiles (check the HowTo for more information on damage tables) method setCustomDamageTable takes DamageTable dTable returns nothing endmethod //The rotation of the projectile, i.e. the rate at which the projectile //changes its direction (to create projectiles that fly a curve) method setStartRotation takes real rotation returns nothing endmethod //Enables free rotation, that means that the projectile direction //is no longer coupled with the projectile facing. //If this is enabled, projectile rotation will only be applied to the facing of the model method enableFreeRotation takes nothing returns nothing endmethod //Disable the z compensation for this projectile type //This means that if the projectile flies over a cliff, it will drop down //instead of flying straight ahead //Note that this also disables all internal z calculations, //so it is no good choice for physical and bezier projectiles with an arc! method disableZcompensation takes nothing returns nothing endmethod //Disables the death animation of the projectile if it expires //If it hits a target (On Collide, On Target Hit) or impacts (Physical) //it will stil play its death animation. method disableExplodeOnExpiration takes nothing returns nothing endmethod //Disables the projectiles death animation when it hits a target, collides or impacts method disableExplodeOnHit takes nothing returns nothing endmethod

Check the API for a full list of possibilities. There are MANY more. Note that some of these possibilities are not useful for interpolated projectiles. However, the API is well sorted by which functions you can use for which types of projectiles.

Changing projectile values during its flight

Until now, you have statically created ProjectileTypes and then launched projectiles that used their parameters.
It is also possible to change projectile parameters dynamically.
For example, you could react to an onBounce event and disable physics mode in the event handler. So the projectile will fly straight after its first bounce. Or you might want to react to an onCollision event and increase the projectiles speed whenever it collides with something.
These things can all be done by just invoking the methods / using the members of the struct Projectile.
You gain access to the projectile itself since the constructor returns the projectile and the projectile is also handed to the event reaction handler functions you may have set.
First you can get and set some of the projectiles values by just using the struct members and method:

struct Projectile extends DummyUnit //The projectiles position (note that you cannot SET these values for interpolated projectiles, just get them) real x real y real z //The projectile's flight direction (again, you can't SET it for interpolated projectiles) real direction //The projectile's speed real speed //The remaining lifetime of the Projectile in seconds real remainingLifetime //Sets the homing target for this projectile //If <targ> is 0, then the homing mode will be disabled method setHomingTarget takes Unit targ returns nothing endmethod //This function can be called in an event that would destroy the projectile //to avert this destruction. But be careful, after that, you have to resetup that projectile's movement //This method cannot be used to avert destruction on Collision. For making your //Projectile survive a collision, just set destroyOnCollision to false for its projectile type. // //If used in an OnInterpolationFinished event, the interpolation will be deactivated. //If used in an OnTargetHit event, the target homing will be deactivated. //If used in an OnImpact event, the physical mode will be deactivated. //If used in an OnInterpolationFinished or OnExpiration event, //you should set the remainingLifetime to a new value. Otherwise, the projectile //will be destroyed again in the next frame since its lifetime is still used up method avertDestruction takes nothing returns nothing //Sets if the projectile should explode (play its death animation) or not //You must set this variable right before destroying the projectile yourself //or in the event that kills it (onHit, onExpire,...) //Otherwise, it will be overwritten by the destroying event boolean explode //If enabled, the projectile direction is decoupled from its direction //By enabling this and setting the rotation of the projectile, you //can archieve projectiles that rotate their model without changing their trajectory boolean rotateFreely //Gets the time in seconds since this projectile was spawned method getAge takes nothing returns real //sets the teamcolor of the projectile method setTeamcolor takes playercolor c returns nothing //Sets the scale of the projectile method setScale takes real value returns nothing //Colors the projectile with red green blue and alpha value //These must be between 0 and 255 //a = 0 -> totally invisible projectile, a = 255 -> totally opaque projectile method color takes integer r, integer g , integer b, integer a returns nothing //Adds an ability to the projectile (use only passive abilities, a projectile //cannot cast active abilities method addAbility takes integer abilId returns nothing //Removes an ability from the projectile method removeAbility takes integer abilId returns nothing //Changes the model of the projectile, use "" for no model method setModel takes string fxpath returns nothing

So you can get and set the projectile's position, its speed, remaining lifetime and other stuff. You can also change/set/unset the homing target of your projectile. You can recolor and rescale your projectile and add/remove abilities to/from it (like phoenixfire or something like that).

Another mentionable thing is the "avertDestruction" method, that allows you, if used in an event handler for an event that would normally destroy the projectile, to keep the projectile alive. So you could make a homing projectile which onHit does not die but do something else.

In addition, you can disable and enable modes for your projectile even during flight!
Just use one of these functions to disable or reenable them:

struct Projectile extends DummyUnit //Collision mode method setCollisionEnabled takes boolean enable returns nothing //Periodic mode method disablePeriodic takes nothing returns nothing method enablePeriodic takes integer period returns nothing

You can also specify new parameters for these modes, like changing the target type of your collision or the collision size of your projectile. You can find the corresponding functions in the API.

Now you have the tools to alter projectiles during their flight.

There are even more methods that can be called during the projectile flight. However, these methods can only be used on certain types of projectiles. So they are stated in the chapters about these types.

Projectiles are dummy units

As you might have seen, the struct Projectile extends the struct DummyUnit. So you can use all methods of DummyUnit also on projectiles.
For example, you can set a kill or damage event for them, get the caster or let them do spell damage (for a complete list check the API for DummyUnits):
struct DummyUnit //Gets the casting Unit of this dummy unit method getCaster takes nothing returns Unit //Sets the damage event of this dummy unit //(The handed function will be executed when this dummy damages a unit) method setDamageEvent takes EventHandler ev returns nothing //Sets the kill event of this dummy unit //(The handed function will be executed when this dummy kills a unit) method setKillEvent takes EventHandler ev returns nothing //Lets this dummy do spell damage //Attention: The spell damage is modified by the dummy's crit and dmgRatio, //so if your dummy had 10.0 dmgRatio and did a crit with 2.0 times damage //a call to this function with amount = 10 would do 10*10*2 = 200 damage // //Note that in addition, the damage done by this function is modified //by the spell resistance of the target and the spell damage amplification of the tower //so if the upper 200 damage was cast from a tower with 1.5x damage multiplier //onto a creep with 50% spell damage reduction, it would cause 200*1.5*0.5 = 150 damage method doSpellDamage takes Unit target, real amount returns nothing

This chapter will tell you about the normally moving, non-interpolated projectiles.

Constructors for normally moving projectiles

There are these constructors available for normally moving projectiles:

struct Projectile extends DummyUnit //Creates a projectile at a point facing a direction static method create takes ProjectileType pt, Unit caster, real damageRatio, real critRatio, real x, real y, real z, real facing returns Projectile //Creates a projectile at the position of a unit facing a direction static method createFromUnit takes ProjectileType pt, Unit caster, real damageRatio, real critRatio, Unit from, real facing returns Projectile //Creates a projectile at a point facing another point static method createFromPointToPoint takes ProjectileType pt, Unit caster, real damageRatio, real critRatio, real startX, real startY, real startZ, real targetX, real targetY, real targetZ, boolean ignoreTargetZ, boolean expireWhenReached returns Projectile //Creates a projectile at the postion of a Unit facing a point static method createFromUnitToPoint takes ProjectileType pt, Unit caster, real damageRatio, real critRatio, Unit from, real targetX, real targetY, real targetZ, boolean ignoreTargetZ, boolean expireWhenReached returns Projectile //Creates a projectile at a point facing a Unit static method createFromPointToUnit takes ProjectileType pt, Unit caster, real damageRatio, real critRatio, real startX, real startY, real startZ, Unit target, boolean targeted, boolean ignoreTargetZ, boolean expireWhenReached returns Projectile //Creates a projectile at the position of a Unit facing another Unit static method createFromUnitToUnit takes ProjectileType pt, Unit caster, real damageRatio, real critRatio, Unit from, Unit target, boolean targeted, boolean ignoreTargetZ, boolean expireWhenReached returns Projectile

The first 4 parameters from each constructor were already explained in the first chapter about projectiles. As already stated there, the constructors just differ in the type of start points / targets. Their names can be pretty much readed as a sentence, so "createFromPointToUnit" creates a projectile from a x,y,z position (point) to a Unit.

All the constructors take either Units or points as targets and start positions. The first two take a starting angle instead of a target.

The constructors that take a unit as target always have a boolean parameter "targeted". This parameter specifies if the projectile should home to that unit or just fly to the position the unit had when starting the projectile.

Another parameter many constructors have is the ignoreTargetZ parameter. If it is set to true, then the target height value will be ignored. Instead, the projectile will fly straight without changing its height.

The last parameter is "expireWhenReached". If it is set to true, the projectile lifetime will be adjusted so that the projectile expires upon reaching the target point. Otherwise, the projectile lifetime will be taken from the projectile type.
Note that if the projectile accelerates, then "expireWhenReached" will not show the wanted behaviour because it calculates the projectile lifetime statically from the speed the projectile had at its start.

Also note that you should not set "targeted" and "expireWhenReached" both to true. Because then, it is undefined if the projectile first expires without hitting the target or first hits the target hence dying and not expiring.

Additional parameters for normally moving projectiles

Normal moving projectiles have some more values that you can alter, either in the projectile type, in the projectile itself during its flight or both.

These are:

struct ProjectileType //The acceleration of the projectile in length units per second˛ method setAcceleration takes real acceleration returns nothing //The rotation of the projectile, i.e. the rate at which the projectile //changes its direction (to create projectiles that fly a curve) //If rotate freely is enabled, then this is not the change of direction //but the change of model facing. method setStartRotation takes real rotation returns nothing //Sets the event that is executed when the projectile reaches its maximum lifetime //(just before it dies) method setEventOnExpiration takes ProjectileEvent e returns nothing struct Projectile extends DummyUnit real accelerate real rotation

So, moving projectiles have a new event "onExpire" that is fired if the projectile dies due to using up its lifetime.
They also have two control values which can be set in the type and in the projectile itself:
Rotation will make the projectile fly a curve, acceleration will make it gain/lose speed.

Modes for moving projectiles: Homing Mode

Moving projectiles offer two modes which can also be combined which offer some more features. One of them completely alters the projectile's behaviour while the other one just adds some utilitity. Let's start with the second one: Homing Mode.

Of course, you already had seen how to make a projectile home: By just setting the "targeted" flag in the appropriate constructor!
So what's the difference to this? There are two major differences:
Until now, you didn't have an event handler for hitting. The second one is that you can specify a "controlValue" for homing, moving projectiles. That control value states how fast the projectile can turn around to follow its target. By default, the control value is 0. If it is set to this special value, the projectile will instantly turn to its target. So it will never miss its target.
If you specify a value other than 0, the projectile will only be able to turn by that many degrees per frame. So, the target can dodge the projectile if the control value is only low enough (and the target changes its direction).
So here is the method for setting the handler and the control value in the projectile type:

struct ProjectileType method enableHoming takes ProjectileTargetEvent e, real controlValue returns nothing

You can enable/disable homing by just setting the homing target (0 as target will disable homing).

Note that if the target died during the projectiles flight, the onHit event will still be called. However, then the target parameter will be 0. So check in your event reaction if the target is zero if you want to alter only living targets!

Modes for moving projectiles: Physical Mode

Physical mode will make your projectile behave like a real cannon ball. So it will start with a positive zSpeed (it flies into the air) and then it will be pulled back to ground by gravity. When it reaches the ground, it will either bounce from it or impact into it (getting destroyed of course).

You can enable physics mode in the projectile type by calling the first method:

struct ProjectileType method enablePhysics takes ProjectileEvent eventOnBounce, ProjectileEvent eventOnImpact, real zSpeed, integer numBounces, real bounceFactor returns nothing method setGravity takes real gravity returns nothing

Enable physics is used to enable the physical mode and set the needed parameters. "setGravity" can be used to give your projectile type a special gravity value. If it is not called the standard gravity of 1.0 will be used (which means the zSpeed of your projectile is decreased by 1.0 each frame).

You can set these things in the "enablePhysics" method:

  • eventOnBounce,eventOnImpact: Event handling functions for bouncing and impacting. Use 0 for no reaction handler.
  • zSpeed: The zSpeed the projectile will have when it is launched. I.e. how fast the projectile flies into the air at the start.
  • numBounces: How often the projectile will bounce from the ground until it finally impacts. Set to 0 for impacting on first groud hit.
  • bounceFactor: A factor that states with which part of the incoming speed the projectile will bounce from the ground. The higher this value is, the higher the projectile will fly after a bounce. Normally 100% == 1.0 should make each bounce equally high. However, due to some discretization errors in the engine 0.96 is the value to choose if you want equally high bounces. If the value is below 0.96 each consecutive bounce will be lower, if it is above 0.96 the projectile will bounce higher each time.

As you see, you can hand a handler for a bounce and for an impact event. Then you can state the zSpeed, i.e. the speed which the projectile will have into the z direction when it is launched.
Next, you can specify how often the projectile will bounce from the ground.

Just like with every other mode, you can enable/disable physics mode during the projectile flight or even change the physics parameters by invoking the corresponding methods.

Reaiming moving projectiles

You can reaim moving projectiles during their flight to a new point or unit by calling one of these methods:

struct Projectile extends DummyUnit method aimAtUnit takes Unit target, boolean targeted, boolean ignoreZ, boolean expireWhenReached returns nothing method aimAtPoint takes real x, real y, real z, boolean ignoreZ, boolean expireWhenReached returns nothing

The parameters are basically the same as for the constructors. Of course caster, projectile type, crit and damage ratio and starting point are missing because the projectile already has these.

Interpolated projectiles allow you to do some cool stuff that is impossible with moving projectiles.
If you want to learn some more stuff about interpolation or the techniques used in the engine, check Wikipedia. They are all documented there. However, it is not important here how these work internally. That is already done in the engine. You just have to use them which is much easier than understanding the math behind them.

The different types of interpolations

There are three kinds of interpolated projectiles in YouTD. They are using linear interpolation, bezier interpolation and cubic spline interpolation.

Linear interpolation allows just an interpolation between two points. As an addition a zOffset can be added which allows the projectile to fly an arc between those two points.
Bezier interpolation uses 4 control points to guide a projectile between two points. This enables more complex trajectories like a looping or a side arc (like a boomerang) and other cool stuff.
Splines are basically cubic polynomes, glued together to form a more or less arbitrary trajectory. This means your projectile could fly 3 loopings then rotate 2 times around the tower and do other fancy stuff before hitting the target. Even if this sounds uber cool on first sight, it has some problems. The biggest problem is that this arbitrary trajectory has to be coded by YOU. So if you are not good at vector and derivates, it will be hard to code such a trajectory.
This is why I will not mention splines again in this tutorial. If you want to try to create one, read the comments in the API. Basically you have to create a spline at map init and fill it with control points (this is the hard part). Then you can just hand that spline to a start interpolation function to make projectiles use this spline as trajectory.

Common facts about interpolated projectiles

Since interpolated projectiles follow a fixed trajectory, you cannot set their coordinates or their speed after the interpolation has started. If you want to fly to new coordinates or change the speed, you have to start a new interpolation.
You also cannot set acceleration or a rotation for these projectiles. If you do, it will just be ignored.

As you have already seen in the constructor for their ProjectileType, interpolated projectiles do not have a lifetime. Instead, they "expire" when they have finished the interpolation between two points.

For interpolated projectiles, you can react to the end of the interpolation, with this method in your projectile type:

struct ProjectileType method setEventOnInterpolationFinished takes ProjectileTargetEvent e returns nothing

This function takes a ProjectileTargetEvent, even if it does not target a unit all the time. The convention is the following: If the interpolation is finished and the projectile was homing, then it will have reached its target and the hit unit will be the target in your event handler. If the projectile was not targeted or the unit has died, the target in the event handler will be 0.
So, if compared to normally moving projectiles, this event serves as onExpire and onTargetHit event. There are no two events for interpolated projectiles since these will always appear together for en interpolation (since the finish point of an interpolation is always the target).

You do not specify in the ProjectileType which type of interpolation the projectile will use. Instead, the constructor determines which interpolation is chosen (there are constructors for each of the interpolation methods).

Linear Projectiles

Linear projectiles are launched by invoking one of these constructors

struct Projecitle static method createLinearInterpolationFromPointToPoint takes ..., real zArc returns Projectile static method createLinearInterpolationFromPointToUnit takes ..., real zArc, boolean targeted returns Projectile static method createLinearInterpolationFromUnitToPoint takes ..., real zArc returns Projectile static method createLinearInterpolationFromUnitToUnit takes ..., real zArc, boolean targeted returns Projectile

The difference between these constructors is just if the start/target is a unit/point. I omited the first parameters because they are the same as for moving projectiles. The only new parameter is zArc. And that is the specialty that linear projectiles have! You can specify an arc that your projectile will fly. Just like for attacks in the object editor where you can specify a missle arc.
The values match more or less the ones of the object editor. So if you specify 0.2 as zArc, the projectile will fly the same arc as a missle of a unit that has 0.2 missle arc in the object editor. This is just an approximation, no exact match.

So, linear projectiles grant you projectiles that can hit or home at a target and fly an arc. Physical projectiles can also create arcs, but they cannot guarantee that the projectile will land at the target point. Linear projectiles always land there.

Bezier Projectiles

Bezier projectiles are launched by invoking one of these constructors

struct Projecitle static method createBezierInterpolationFromPointToPoint takes ..., real zArc, real sideArc, real steepness returns Projectile static method createBezierInterpolationFromPointToUnit takes ..., real zArc, real sideArc, real steepness, boolean targeted returns Projectile static method createBezierInterpolationFromUnitToPoint takes ..., real zArc, real sideArc, real steepness returns Projectile static method createBezierInterpolationFromUnitToUnit takes ..., real zArc, real sideArc, real steepness, boolean targeted returns Projectile

I have again omitted the first parameters as they do match the moving projectile constructors agian.

A bezier interpolation is mightier than a linear interpolation. Because of that, we now have two addtional parameters: sideArc and steepness.
While zArc is, just as for linear projectiles, the z-axis arc the side are is an arc to the side. Imagine something like a boomerang that hits its target from the side. This can be done by setting values != 0 as sideArc.
The steepness how steep the projectile impacts/is launched. The default value for that is 0.17. This will create a normal arc. Values below 0.0 make the projectile fly a looping. Values > 0.5 make the projectile get launched/impact from the back of the unit. Just try to play with these parameters in the projectile example map. Then you can see what is possible.

Here are some pictures to show you the influence of the settings:

zArc: 0.4 (everything else 0)

sideArc: 0.4 (everything else 0)

zArc: 0.6, steepness: 0.17 (default) (no side arc)

zArc: 0.6, steepness: 0.9 (no side arc)

zArc: 0.6, steepness: 2.0 (no side arc)

zArc: 0.6, steepness: -1.5 (no side arc)

Advanced settings
If you know bezier interpolation, you know that this interpolation normally takes 4 control points. The constructors do not take these. It sets them all internally.
However, if these constructors are not enough for you, you can explicitly alter the control points. The first and the last one are fixed, they are the start and the target point. So the remaining two points in the middle can be set explicitly by calling this method

//Advanced function for Bezier Interpolated Projectiles (No Splines!): //Sets one control point to a position relative to startpoint and endpoint //imagine a coordinate system where the x-axis goes from start to end with start beiing at 0 and //end beiing at 1. with the other two axis it is an orthonormal basis with the y-axis having a z of 0. //<index> tells which point to set, should be either 1 or 2 to set the first or the second control point respectively //This method should be called right after starting the Bezier Interpolation //Otherwise this will look strange or have no effect at all! method setBezierPointRelative takes integer index ,real x, real y, real z returns nothing

Call this function right after the constructor (or a start interpolation method). Then you can set the coordinates relatively. This grants you the full freedom bezier curves offer. However it is not as convenient as the constructors.

Reaiming interpolated projectiles

As I told you, you cannot just change the direction or coordination of an interpolated projectile. The engine will just ignore that. But if you want to change its trajectory during flight, you have some other possibilities.

The first one is to stop the interpolation by calling this method:

method stopInterpolation takes nothing returns nothing

Then, the projectile becomes a normally moving one. You can then alter its speed, direction or coordinates and do all the stuff with it that can be done with normally moving projectiles.

You can also just start a new interpolation. There are methods for that that match the constructors. The difference is that they need less parameters (no crit ratio, damage ratio, caster, projectile type. The projectile has these values already) and take no starting point (since the projectile will just start from its own position).
So the only values you really need is the target point / unit and the interpolation parameters.

Here are the methods:

//Linear interpolation method startLinearInterpolationToPoint takes real targetX, real targetY, real targetZ, real zArc returns nothing method startLinearInterpolationToUnit takes Unit target, real zArc, boolean targeted returns nothing //Bezier interpolation method startBezierInterpolationToPoint takes real targetX, real targetY, real targetZ, real zArc, real sideArc, real steepness returns nothing method startBezierInterpolationToUnit takes Unit target, real zArc, real sideArc, real steepness, boolean targeted returns nothing //Spline interpolation method startSplineInterpolationToPoint takes real targetX, real targetY, real targetZ, Spline s returns nothing method startSplineInterpolationToUnit takes Unit target, Spline s, boolean targeted returns nothing

The engine offers many different types of projectiles and modes to alter these. This chapter will tell you where the pros and cons of each mode are.
I have created an example map where you can play with the different projectile types and alter their parameters ingame, to choose which one suits your needs best. It can be found HERE

Normally moving projectiles

PROs

  • The computation time for normal projectiles is the lowest of all. When considering performance, normal projectiles are always the best choice
  • The trajectory of normal projeciles can be altered conveniently by setting the x,y,z, direction, rotation, speed and acceleration.
  • By default, they fly on and on until their lifetime is used up. So they do not have to end at a specific point (in contrast, interpolated projectiles always have a fixed end point)
  • Normal projectiles can be launched easily at a specific direction. So if you want a tower that shoots 10 projectiles equaly into all directions, you can just lauch 10 projectiles with 36° increased angle each time.

CONs

  • Normal projectiles cannot fly arcs. So if you want a projectile with an arc, you better use interpolated ones.
  • For very fast speeds, homing can get buggy for normal projectiles. So if you need a homing projectile with 2000+ speed, use an interpolated one!
  • By default, they fly on and on until their lifetime is used up. So they do not have to end at a specific point (in contrast, interpolated projectiles always have a fixed end point)

When to use?

  • Basically, whereever possible, use them, since they are best in computation time.
  • For very slow (<600 speed) homing projectiles, use them.
  • Use them for normal, straight trajectories where no interpolation is needed.

Linear interpolated projectiles

PROs

  • Less computation time than bezier or spline projectiles
  • Can create arcs, even with homing

CONs

  • Need more computation time than normally moving projectiles
  • For slow projectiles (<800 speed), the homing mode will not look very smooth if the target comes closer fast

When to use?

  • Whenever you need simple projectiles with arcs, use linear interpolated projectiles
  • If you need simple homing with projectile arcs and the projectile is fast enough, you can use them. If the homing should look more smooth, use bezier projectiles. If you just need homing without an arc, always use normal projectiles.

Bezier interpolated projectiles

PROs

  • Can create arcs, sidearcs, arcs with different steepness
  • Can create very cool trajectories like loopings or arcs that get launched / impact from the units back
  • Homing looks very smooth

CONs

  • Need more computation time than normally moving and linear interpolated projectiles.
  • For homing projectiles, if the projectile is fairly slow and the target comes closer fast, the projectile can change its speed abnormally.

When to use?

  • If you need those trajectories with steepness, side arcs or looping use bezier projectiles.
  • These trajectories can all be combined with homing. However, if you need normal homing without an arc, use normally moving projectiles instead.

Spline interpolated projectiles

This projectile type offers almost arbitrary trajectories. However these are hard to code and use much computation time. So the simple rule for splines is: If you really need such a complex trajectory and cannot archieve it otherwise use splines. In all other cases, don't use them!

Physically moving projectiles

PROs

  • Very unique movement with bouncing and gravity which cannot be archieved otherwise.
  • Do not need too much computation time, very performant.
  • Paired with homing with a non-zero control value, funny effects can be created.

CONs

  • You cannot choose where exactly that projectile lands. So if you need projectiles with an arc that hit exactly a unit or a point, use interpolated ones instead.

When to use?

  • Use them whenever you need this specific physical behavior

Comparing performance

When I talked about the projectiles, I always mentioned which one is better in computation time. Here is a quick overview how much that computation time really is. The values are based on normally moving projectiles without homing. Their computation time is set to 100%.

Computation times of the different projectile types:

  • Normal moving, no homing: 100%
  • Normal moving, homing: 137%
  • Physical, no homing: 122%
  • Physical, homing: 171%
  • Linear interpolated, no homing: 148%
  • ´
  • Linear interpolated, homing: 220%
  • Bezier interpolated, no homing: 171%
  • ´
  • Bezier interpolated, homing: 220%
  • Spline interpolated, no homing: 203%
  • ´
  • Spline interpolated, homing: 229%

So as you see, homing always costs some computation and interpolated projectiles are slower than normal ones. However, the factor is a maximum of 2.29, so this is not a too big gap! So the rule is: Use the projectile that suits your job best. Only if different kinds suit your job equally good, use the one with the least computation time.

Some methods of the API require you to hand a variable of type "TargetType to it.
These target types specify which type of units this method will target (for example for auras, the target type tells which units will receive the aura effect).

Target Types should be created at initialization and then used.

A target type is created by calling its constructor:

struct TargetType static method create takes integer targetCode returns TargetType

As you can see, this method requires an integer "targetCode" as parameter.

This integer specifies which things can be targeted by this target type.
This code is assembled from the following constants (which can be found in the library "TargetTypes":

//General types constant integer TARGET_TYPE_CREEPS = 0x1 //Target only creeps constant integer TARGET_TYPE_TOWERS = 0x2 //Target friendly towers constant integer TARGET_TYPE_PLAYER_TOWERS = 0x4 //Target player towers //Race targeting constant integer TARGET_TYPE_RACE_UNDEAD = 0x8 constant integer TARGET_TYPE_RACE_MAGIC = 0x10 constant integer TARGET_TYPE_RACE_NATURE = 0x20 constant integer TARGET_TYPE_RACE_ORC = 0x40 constant integer TARGET_TYPE_RACE_HUMANOID = 0x80 //Size targeting constant integer TARGET_TYPE_SIZE_MASS = 0x100 constant integer TARGET_TYPE_SIZE_NORMAL = 0x200 constant integer TARGET_TYPE_SIZE_CHAMPION = 0x400 constant integer TARGET_TYPE_SIZE_BOSS = 0x800 constant integer TARGET_TYPE_SIZE_AIR = 0x1000 //Element targeting constant integer TARGET_TYPE_ELEMENT_ASTRAL = 0x2000 constant integer TARGET_TYPE_ELEMENT_DARKNESS = 0x4000 constant integer TARGET_TYPE_ELEMENT_NATURE = 0x8000 constant integer TARGET_TYPE_ELEMENT_FIRE = 0x10000 constant integer TARGET_TYPE_ELEMENT_ICE = 0x20000 constant integer TARGET_TYPE_ELEMENT_STORM = 0x40000 constant integer TARGET_TYPE_ELEMENT_IRON = 0x80000

The most important codes are the first three. These types devide units into three major categories. Creeps, friendly towers and player towers. The difference between friendly and player towers is that friendly towers will target all towers from your team, while player towers only targets your really own towers.

So this example call would create a target type that targets only creeps:

set myTargetType = TargetType.create(TARGET_TYPE_CREEPS)

There are more constants that can be used to further specify which things to target.
For creeps, race and size targeting is enabled. So you can specify that this target type should only target creeps of a specific race or size.
As soon as you specify one or more races/sizes all non specified races/size will not be targeted anymore
For tower targets, you can specify which element may be targeted.
Target types are composed by just adding the constants.

Examples:

set myTargetType = TargetType.create(TARGET_TYPE_CREEPS + TARGET_TYPE_SIZE_MASS + TARGET_TYPE_SIZE_NORMAL)

Targets only mass and normal creeps.

Note that you cannot compose the three base types, target types may only target either creeps OR friendly towers OR player towers. This is because there is hardly a scenario where you want an ability to target friends AND foes.

If you want to pick units in range of some point in normal WC3, you need a call to GroupEnumUnitsInRange(...) call and then iterate over them with a ForGroup() call. These calls however are very unsatisfying. They are slow and need more coding overhead to archieve the effect that you want. They need a group which has to be cleared or removed and nulled to avoid memory leaks.

Because of that inconveniences, the YouTD engine offers you a more convenient and fast way (up to 50% faster) than those ugly calls. Whenever you want to pick all units in range of something and iterate over them, you can create an instance of the struct "Iterate", it can be created with one of these functions

static method overUnitsInRange takes Unit caster, TargetType t, real x, real y, real radius returns Iterate static method overUnitsInRangeOfCaster takes Unit caster, TargetType t, real radius returns Iterate static method overUnitsInRangeOfUnit takes Unit caster, TargetType t, Unit center, real radius returns Iterate

They all take a casting unit (the unit that casts the spell for which you do the iteration), a target type (check the chapter about Target Types).
Then, they need something to specify the center of the InRange call and the radius of the pick call.

After you have create such an Iterate, you can use its .next() method to get the next picked Unit. The next method returns 0 if no more units to iterate over exist.

Here is an example:

//Start the iteration local Iterate it = Iterate.overUnitsInRangeOfCaster(yourTower,yourTargetType,1000.0) local Unit u loop set u = it.next() //Get the next unit exitwhen u == 0 //Stop when there are no more units //Do something with the Unit u here endloop

This way, you can iterate over units conveniently and fast. If you iterate until the end (i.e. until u == 0 in this example), then you don't have to destroy the iterator afterwards. The iterator will destroy itself when there is no more unit to iterate over.
However, if you stop the iteration before, for example after 5 units, you must call .destroy() on the iterator to deallocate it. Otherwise you will have a memory leak.

So, if you stop the iteration if all units are iterated or another condition (for example after 5 units), you should check if the last unit returned by .next() was 0. If it wasn't you have to destroy it, if it was, don't destroy or you will make a double free. Here is an example with a second condition:

//Start the iteration local Iterate it = Iterate.overUnitsInRangeOfCaster(yourTower,yourTargetType,1000.0) local Unit u loop set u = it.next() //Get the next unit exitwhen u == 0 //Stop when there are no more units exitwhen ... //Second condition //Do something with the Unit u here endloop //That's the way to destroy the iteration here! if u != 0 then call it.destroy() endif

Note that those iterations can only iterate over living units. If you want to iterate over dead creeps, use these methods of iterate:

static method overCorpsesInRange takes Unit caster, real x, real y, real radius returns Iterate method nextCorpse takes nothing returns unit

So you have to create the iteration with the "overCorpsesInRange" method. Then you can get the next corpse with the .nextCorpse() function.
Note that this function returns a raw unit (no Unit), because dead units are no longer wrapped with a Unit struct in the YouTD engine.


You know normal auras from Warcraft3. YouTD allows you to create triggered auras just like Buffs. A triggered aura is basically an ability that applies a buff to every unit that matches some targeting criteria and comes into the range of the aura.
Your tower/item content creation map has the following trigger called "Tower / Item Aura":
globals real AURA_auraRange = RANGE integer AURA_targetType = TARGET TYPE boolean AURA_targetSelf = TARGET SELF integer AURA_level = LEVEL integer AURA_levelAdd = LEVEL ADD integer AURA_power = POWER integer AURA_powerAdd = POWER ADD BuffType AURA_auraEffect = AURA EFFECT endglobals

To give your tower / item a triggered aura. Just fill in the according values for these variables:
  • AURA_auraRange:The range of your aura.
  • AURA_targetType:Insert a target type constant here. You can check which constants are allowed at the chapter Target Types.
  • AURA_targetSelf:A boolean value if the aura also targets the tower itself.
  • AURA_level:The level of the aura buff if the casting tower is level 0
  • AURA_levelAdd:The value that is added to the level of the aura buff for each level the tower has
  • AURA_power:The power level of the aura buff if the casting tower is level 0
  • AURA_powerAdd:The value that is added to the power level of the aura buff for each level the tower has
  • AURA_auraEffect:A bufftype defining the buff that will be applied to every unit the aura targets

So basically, creating an aura is very simple. First create a BuffType and give it the events/modifier that the aura effect should have. Then state it as auraEffect in this trigger.
There are some points to know about auras:
  • If you forgot what a power level or the normal level for a buff is read this chapter again
  • The level of the aura buff is calculated using the both values "AURA_level" and "AURA_levelAdd". So if you set level to 5 and levelAdd to 2, then a level 10 tower having the aura will apply the aura effect with level 5 + 2 * 10 = 25. The power level is calculated in the same way using the "AURA_power" and "AURA_powerAdd" fields
  • When creating the buff type for the aura effect use this constructor instead of the normal one:
    struct BuffType static method createAuraEffectType takes boolean friendly returns BuffType

    It takes less parameters than the normal constructor, since the values for duration don't matter for auras (they have no duration, they expire when the unit gets out of the aura range).
  • When the aura buff is removed due to the unit getting out of the aura range, an onExpiration event is triggered.
  • Whenever the aura giving unit changes, an onRefresh event is triggered. This happens, if there are two units giving the aura and the unit moves out of the range of the first one, so the second one will then give the aura effect to it. It also happens if a unit already has the aura effect but then walks into the aura range of another tower that gives the same aura but with a higher level.
    So, for auras, onRefresh is also fired if the buff level changes! OnUpgrade is never fired for auras.
    If the two aura buffs are from a stacking group but not from the same buff type, the old one will be removed and the new one will be created if the aura giver changes. An onCleanup and an onCreate event will be triggered, but no onRefresh event (just as for normal stacking group buffs).
  • Aura Effect buffs cannot be purged.
  • Note that you have to fill in the field "Name" for auras (in the aura trigger comment) even if you leave visible at false.This is because aura names are displayed in the towers stats multiboard.

You could also create own other auras and even give them to buffs (this would be a buff that grants an aura to the buffed unit). But since those kind of buffs seem very bizarre and I don't think you will ever want to create such a buff, this is not covered here.
If you really need to create a buff that gives an aura to the buffed unit, open a thread in the YouTD support forum.

Since the last chapter was very abstract I will now show you an example, that tells you how to create an aura.

Let's start with the buff type. As you might remember an aura is just an ability that automatically applies a buff onto every unit that comes in range and matches the targeting critiria.
So this buff type (here called "myAuraBuff") is the buff effect that will be applied.
Here, we want to create a movespeed slowing aura against creeps:

library myTower initializer init uses libHeader globals BuffType myAuraBuff endglobals private function init takes nothing returns nothing local Modifier m = Modifier.create() // (0) set myAuraBuff = BuffType.createAuraEffectType(false) // (1) call m.addModification(MOD_MOVESPEED,-0.2,-0.01) // (2) call myAuraBuff.setBuffModifier(m) // (3) call myAuraBuff.setStackingGroup("frost_aura") // (4) call myAuraBuff.setBuffIcon('B000') // (5) endfunction endlibrary

Explanation:

  • (0): First we create a local modifier, since our buff will require one for the movementspeed slow. If you have forgotton how to add modifications to BuffTypes read the chapter about adding modifications to buffs again.
  • (1):Then we set the global aura buff type using the "createAuraEffectType" constructor
  • (2):Now, the movementspeed modification is added to the modifier. It has -20% movespeed base and an additional -1% per level. A fairly strong aura, since it can slow up to -45% (at tower level 25)!
  • (3):Of course, we have to add the modifier as Buff Modifier to the aura buff type.
  • (4):We give our aura a stacking group since there might be more buffs that do that kind of slow and should not stack with our Aura. (More about stacking groups)
  • (5):Finally we set a buff icon for our aura, that people will see an icon in the creeps status bar and a special effect is applied onto the units. Of course we have to create a Buff in the Object Editor with the ID 'B000' and set its values appropriately (More about buff icons)

We just created the buff type for that aura. That was the biggest part of the work. All we have to do now, is filling in the values in the aura trigger:

globals real AURA_auraRange = 1000 integer AURA_targetType = TARGET_TYPE_CREEPS boolean AURA_targetSelf = false integer AURA_level = 0 integer AURA_levelAdd = 1 integer AURA_power = 0 integer AURA_powerAdd = 1 BuffType AURA_auraEffect = myAuraBuff endglobals

Explanation:
Our aura has 1000 range and targets creeps ("TARGET_TYPE_CREEPS"), the tower it self is of course not targeted. The level and power level of the buff start at 0 and then increase by 1 each tower level.
Finally we set myAuraBuff (our created BuffType) as aura effect.

That's it! Of course we should fill in the fields in the trigger header, to give the aura a nice explanation icon.

You can give your tower/item an active ability which will be cast automatically by the tower that has the ability / carries the item.
However, you may not create really an active ability in your Object Editor and give it to the tower, instead just fill in the autocast trigger. The active ability will be created by the script.
Just like for auras, there is a trigger for creating an autocast in the content creation map, called "Autocast":

globals integer AUTOCAST_autocastType = AUTOCAST TYPE real AUTOCAST_range = RANGE real AUTOCAST_autoRange = AUTO RANGE real AUTOCAST_cooldown = COOLDOWN integer AUTOCAST_manacost = MANA COST //Only for ACs that can target towers. This flag determines if the tower can target itself. private boolean AUTOCAST_targetSelf = true //Only for NON-buffs (set to false otherwise) private boolean AUTOCAST_isExtended = IS EXTENDED //Only for buffs (set all three to 0 if this is no buff) integer AUTOCAST_targetType = TARGET TYPE integer AUTOCAST_numBuffsBeforeIdle = NUM BUFFS BEFORE IDLE BuffType AUTOCAST_buffType = BUFF TYPE endglobals function onAutocast takes Tower tower returns nothing endfunction

As you see, it contains an event reaction handling function "onAutocast" and several values you must set for your autocast.


This is just an event handler that will be called whenever your tower uses this autocast. It is called when the tower casts the spell by itself using the autocast engine. However, it is also cast if the player disables autocasts for this tower and casts the ability manually.
So this function is basically the effect of your cast, put the actions that your cast does here.


AUTOCAST_cooldown, AUTOCAST_manacost are self explanatory. These are the manacost and the cooldown of the ability.
AUTOCAST_range is the spells range. For immediate spells (which don't have a range) use 0 or an other value. This value will be displayed as ability range when you display the tower's details.
AUTOCAST_autoRange is the range where the AI will start casting the ability automatically. For non immediate spells, this range should be less than the spell range (otherwise the AI would try to cast onto targets that are out of range).
AUTOCAST_autocastType is the type of cast, it can have the following values (which are defined in the API library called "Autocast Types"):
constant integer AC_TYPE_ALWAYS_BUFF constant integer AC_TYPE_OFFENSIVE_BUFF constant integer AC_TYPE_OFFENSIVE_UNIT constant integer AC_TYPE_OFFENSIVE_IMMEDIATE constant integer AC_TYPE_OFFENSIVE_POINT

The last three are offensive spells. The tower will cast them whenever it attacks a creep and that creep is in their range, the spell is not in cooldown and the tower has enough mana. Just like for dummy spells you can have unit targeted spells (AC_TYPE_OFFENSIVE_UNIT), ground targeted spells (AC_TYPE_OFFENSIVE_POINT) and spells without a target (AC_TYPE_OFFENSIVE_IMMEDIATE). Unit targeted spells will target the attacked unit, ground targeted spells will target the position of the attacked unit.
The upper two autocast types are the buff types. Buff autocast types use a special tower AI: The tower will try to buff units in its range. However, it will NOT buff units that already have the buff on them. Only if the buff expires it will try to recast it on them.
The difference between the two types, is that AC_TYPE_ALWAYS_BUFF tries to buff units around it whenever it has enough mana.
AC_TYPE_OFFENSIVE_BUFF tries to buff units only as long as the tower is attacking (because it is no use to buff certain buffs as long as the towers are not in combat). You should always prefer AC_TYPE_OFFENSIVE_BUFF. Use AC_TYPE_ALWAYS_BUFF only if you really want your tower to buff all the time, even if there are no creeps around.

Besides these constants, also NOAC constants exist:

constant integer AC_TYPE_NOAC_CREEP constant integer AC_TYPE_NOAC_IMMEDIATE constant integer AC_TYPE_NOAC_POINT constant integer AC_TYPE_NOAC_TOWER constant integer AC_TYPE_NOAC_PLAYER_TOWER

These are used to create an autocast that is not autocasted (controversly, I know ;) ). I.e. it will just create a button that the player can press and once it is pressed, the event reaction will be triggered. The different types just tell which things can be targeted with the non-autocast ability. CREEP, TOWER and PLAYER_TOWER target creeps, team towers and player towers respectively. IMMEDIATE creates an ability with no target, POINT one with a ground target.

If you choose one of the two BUFF autocast types, you have to fill in the last three values (If you don't use a BUFF autocast type, just set the three values to 0):

  • AUTOCAST_targetType is the target type of the buff (like explained in the chapter about target types).
    Use TARGET_TYPE_CREEPS if this is a debuff for creeps and one of the other target types if it is a positive buff for towers.
  • AUTOCAST_numBuffsBeforeIdle states, how often the tower tries to buff units around it before it rechecks if it still in combat. For AC_TYPE_ALWAYS_BUFF autocast types, this value doesn't matter since the tower doesn't check if it is in combat. For AC_TYPE_OFFENSIVE_BUFF however, this value should be set appropriately. A good value is 1, so the tower checks after each successful buff if it is still in combat. If your buff spell has low cooldown and can be cast many times in a few seconds, you should set this value higher that the tower will buff some units before it rechecks if it is in combat (since this rechecking costs time, thus making the tower stop between casts for a small period of time).
  • AUTOCAST_buffType is the buff type of the buff, that this autocast will apply onto units. You must enter your used BuffType here for two reasons:
    First, the tower uses this buff type to check if a unit already has the buff. If it has, it will target another unit instead. Second, if you leave the event handler "onAutocast" totally empty, but state a buff type here, then the autocast will automatically apply this buff onto the targeted unit using the towers level as buff power and buff level.
    If the only thing your autocast does is applying the buff onto the target, then do it like this. Leave the handler empty. The enigne will apply the buff for you (which is a bit more performant than calling the autocast handler which then applies the buff). However, if your spell does something else in addition to casting this buff, you have to use the autocast handler and apply the buff yourself. If the autocasthandler is not completely emtpy, the engine won't apply the buff for you.
    But even if you apply the buff by yourself, state its buff type here, so the engine will do the check if the target already has the buff.
    If you want to cast your buff by yourself and want that the tower also casts it onto units that already have it, then set AUTOCAST_buffType to 0, then the engine will not check if the unit already has the buff.

Finally there are two more field that you will sometimes have to use:

AUTOCAST_isExtended works only for non buff autocasts (i.e. offensive autocasts). If it is set to true, an advanced autocasting mechanism is used for this Autocast. Use this flag if the autocast range greatly differs from the towers attack range. While normal autocasts always cast onto the target that the tower attacks, advanced autocasts choose their target themselves.

So if your tower has 500 attack range but a 1500 range for its autocast, you should use isExtended. If you don't, your tower will only cast on units it can attack, so its range will be limited to 500.

Note that extended autocasts need more computation time, so try to avert them and try to make autocasts that have the same range as the towers attack. Only use extended if you have a good reason why your autocast range differs from your attack range

The final parameter is AUTOCAST_targetSelf. This one is only needed for buffs that can target towers. It states if the tower should be able to target itself (true) or not (false).

As you know, for every event reaction trigger you may set @visible to true in its trigger comment and fill in the values there to create a dummy tooltip ability for this event.
For autocasts, there is no @visible flag in the trigger comment, because you MUST ALWAYS entry values here. The reason is clear:
An autocast is an ability. This ability always needs an icon and a tooltip. So insert these values here. Then, the autocast ability will have this tooltip and icon. Note that you don't have to include the manacost, range or cooldown into your explain tooltip. They will be added by the script.

The autocast trigger comment looks a bit different than other ones, it has the following parameters:

@icon= @name= @short_explain= @long_explain= @caster_art= @target_art=

The first four parameters are in every trigger comment. However, @caster_art and @target_art are new. Here, you can set model paths of effects that you want your spell to create on its caster / target. Since an active spell should have a cool effect, set appropriate values here.
The trigger comment could then look like this for a buff:
@icon=ReplaceableTextures\CommandButtons\BTNInnerFire.blp @name=Example buff @short_explain=Buffs towers around it increasing their damage. @long_explain=Buffs towers around it increasing their damage by 100%. The buff lasts 10 seconds. @caster_art=Abilities\Spells\Human\Heal\HealTarget.mdl @target_art=

This would add an effect with model "Abilities\Spells\Human\Heal\HealTarget.mdl" onto the caster. The target receives no effect.

Let's repeat the steps it takes to create an autocast:

  • Enable the "Autocast" trigger
  • Fill in the global parameters
  • Write an event handler (unless you just apply a buff and use the engine for it)
  • Fill in the parameters in the trigger's comment.

The engine allows you to create special effects for your abilities.


At first you have these two basic effect functions in the header:
//Creates and removes an effect at the position of a unit //(can also be used on units which's model doesn't have attachement points) function SFXAtUnit takes string effectPath, unit onWhichUnit returns nothing //Creates and removes an effect on a unit at attachementpoint "where" //(can also be used on units which's model doesn't have attachement points) function SFXOnUnit takes string effectPath, unit onWhichUnit, string where returns nothing

They create the effect with path "effectPath" either at the position of a unit (SFXAtUnit) or attached to a unit's attachment point (SFXOnUnit). The effect is also immediately destroyed, so you don't have to garbage collect it later. If the effect is longer than just a birth or death animation, you cannot use these functions.

For more advanced and enduring effects, you can use the struct Effect. Note that those effects cannot be attached to a unit, they are only at one point and don't move with a unit. First you create an Effect with one of the constructors:

struct Effect static method createColored takes string modelPath, real x, real y, real z, real facing, real scale, integer r, integer g, integer b, integer alpha returns Effect static method createScaled takes string modelPath, real x, real y, real z, real facing, real scale returns Effect static method create takes string modelPath, real x, real y, real z, real facing returns Effect static method createSimple takes string modelPath, real x, real y returns Effect

The constructors differ in their parameter count. While the most simple version only allows you to specify the model paht, x and y position, the more advanced constructors allow you to set the height(z), the facing, scale and color. Choose the constructor that suits your parameter needs.

The methods return an instance of Effect. The effect will be shown until you destroy it (using the .destroy() method). In addition, the struct allows a convenient way to automatically destroy the effect after a given duration. For this sake, you have to call this method:

struct Effect method setLifetime takes real time returns nothing

If you have set the lifetime with this method, the effect will automatically disappear after the time has expired.

There are some additional methods in the struct Effect that you can use to alter effects during their lifetime (like increasing their scale, moving them, recoloring them, setting a specific animation or setting animation speed). If you need those, check the documentation in the API Trigger.

The YouTD engine is coded to be very robust. However, if you mess up some things in your triggers, you can cause harm. The most important things are waits in your event reactions. As soon as you do a wait like calling "TriggerSleepAction" in your event reaction function, you can no longer be sure if your lokal Units are what you suppose them to be. I will start with an example.
Let's pretend you want a tower that casts a buff (the buff type is called myBuffType) onto the creep it attacks, but with a delay of two seconds.
The first approach would be this one:

function onAttack takes Tower tower returns nothing call TriggerSleepAction(2.0) call myBuffType.apply(tower,Event.getTarget(),tower.getLevel()) endfunction

That this is wrong will be obvious to most of you. The engine cannot ensure (or let's rather say it is VERY UNLIKELY) that Event.getTarget() will return the real target of this event after a wait, since other events that happend in this period will have overwritten it.
So you try the next approach by saving Event.getTarget() in a local variable:
function onAttack takes Tower tower returns nothing local Unit enemy = Event.getTarget() call TriggerSleepAction(2.0) call myBuffType.apply(tower,enemy,tower.getLevel()) endfunction

This would look correct to most of you. However, it is still not correct. The problem is that struct indices are reused.
Let's pretend this happens in the 2 seconds you wait:
The creep "enemy" gets killed. After that a player builds a tower.
As soon as the creep dies, its struct id is freed. Since creep and tower share the same base struct Unit, it is possible that the tower will get the freed id of the unit. So in this situation, your trigger would now cast the buff onto the new built tower instead of the attacked creep! This would lead to strange bugs.
So the engine allows you to do a safety check on those structs, to check if they are still the same thing that they were when you aquired them.
Almost all structs that can be destoryed in the YouTD engine offer this method:
method getUID takes nothing returns integer

It returns a Unique Identifier (UID). This is an integer that is never used twice for two different instances of a struct. So if the struct variable you saved would have changed in that time and you saved its old UID, you would see that it was replaced, because the UIDs wouldn't match. Note that also the UID of a struct changes as soon as it gets destroyed (it becomes 0 or -1). So by comparing a structs UID with its old UID, you can ALWAYS be sure that it is exactly the same struct instance than before and is not destroyed yet., the old and the new UID wouldn't match.
So this would be the correct sollution:

function onAttack takes Tower tower returns nothing local Unit enemy = Event.getTarget() local integer uid = enemy.getUID() call TriggerSleepAction(2.0) if uid == enemy.getUID() then call myBuffType.apply(tower,enemy,tower.getLevel()) endif endfunction

So as you see, we save the UID before the wait and then check after the wait if the UID is still the same. That's the way to handle waits.
I am sorry that this is a bit cumbersome. However, there was no way to make it easier.
This works also for buffs and items (they have also a getUID() method).

One more thing concerning that example:
To be 100% correct, we should also verify that the tower is still the same after the wait. The tower may have been sold in that 2 seconds and another one could have been built that took the old tower's id. But if the buff has no effect on the caster but only on the target that is no severe thing and can be skipped. However, if this buff is a buff that grants something to the caster (for example adds mana to the caster periodically) such a check is necessary.
So if you really need waits, use the UID for safety reasons, but in most cases, just try to avoid waits where possible!

Now you might wonder: What is with buffs and the .getCaster() method? If you add a periodic event into a buff that does something with its caster, then you would use the .getCaster() method. But what if the caster already died? Wouldn't you then alter another unit?
The answer is: No, the engine ensures that this doesn't happen by performing internal UID checks. If the caster of a buff has died, the .getCaster() method will return 0. You can check for .getCaster() != 0 if you want to know if the caster of a buff is still alive.


Most of the time you won't want to create a unique tower but a whole family of towers. That means, you want to create towers with similar abilities that just differ in their goldcost and ability strength.
For example you might create a tower with 3 versions. One for 50 gold one for 400 gold and one for 2000 gold. They should all have an armor reduction attack which gets stronger for each tower (of course). So the basic way would be to create an armor reduction BuffType in each tower and use it (with a stacking group that these buffs don't stack with each other). However, that is very lame, since you are creating 3 times the same buff with just different strengths. That increases the map size and is some unwanted copy&paste work for you. You might want to just reuse the buff type from one tower in another one.
By default, you cannot use a global variable in one tower in another tower, because these global variables are distinct (they are made private by the script).


YouTD offers you a way to get around this: You can "reuse" global variables for your towers by using reference annotations. Lets say you define the buff type in the first of your three towers and safe it in a global variable:
globals BuffType YOUR_BUFF_TYPE endglobals //Initialize YOUR_BUFF_TYPE somewhere in your init function

The easiest way would be, if you could simply use this variable in your other towers as well. This is possible with annotations. In the tower where you define the buff type you use the "export" annotation to tell the script that this variable will be used by other towers:
globals //@export BuffType YOUR_BUFF_TYPE endglobals //Initialize YOUR_BUFF_TYPE somewhere in your init function

In towers where you want to use this buff type, use the import annotation, to refer to your buff type:

globals //@import BuffType YOUR_BUFF_TYPE endglobals

Now, the variables will be the same and you can use the buff type from one tower in another one.
The syntax for annotations is very strict: It must be "//@import" or "//@export" without a space between it. (You may add spaces before and after if you want). Nothing else may be written in this line and the variable that should be im-/exported must be declared in the next line. It may not be declared private or public, just VARTYPE VARNAME.

The variable names when importing/exporting must be, of course, the same. Choose unique variable names to avoid collisions with the exports of other users. For example add your nick name into the variable name.
Example for a good reference name: gex_lightning_cast
Example for a bad reference name: mycast

You should use reference annotations when you create a family of towers or items that use the same buffType / cast / ... with just different power levels. This will keep the map small and the loading time low.
You should only use reference annotations for variables that are defined in your init function and do not change after it. So good candidates for references would be:

  • BuffType
  • AuraType
  • Cast
  • ProjectileType

You should NOT use references for towers that share the same buff "by accident". So for example if you see that a user uses an armor reduction buff for his towers and has exported it, then YOU should NOT import it, because if that user changes something for his towers, it will also be changed for your towers and you will wonder why your towers changed even if you have not changed anything about them.

References make the map small, so they are a good thing, if used for families of towers.
However, if used wrong, they can become a hazard.
As already mentioned above you should use them ONLY for families of towers, not for towers that share the same buff types by accident.
The next inconvenience is that you cannot simply test towers that require imported variables, because their functionality will only be in the final map, where all towers are linked together.
But since families of towers shouldn't change too much, it will be okay, if you just test your first tower that exports the ability. If it works correct, then the ability will also work on the towers that import it.
But, with a very small effort, you CAN test towers with imports. When compiling a tower GMSI asks you if you want to add this tower to a test map or create a new one. If you add the tower that imports stuff to the test map of the tower that exported that stuff, you can also test the importing tower.
So do this, for towers that import stuff!
If you want to add more towers with imports and exports to one test map. Always add the exporting towers first and then the importing ones. Otherwise you might get strange bugs because the importing ones would be inited before the exporting ones (where the exported variable does not exist yet).

There are still some uncovered parts of the API that can useful often.
The first one is user values that you can set for certain structs.

Often, you want your spells, towers, items or buffs to have some custom data attached to them, that should be kept even between events. For this purpose many structs offer these two members:

//A real number attached to this struct that you can use for whatever you want. //Note that its value is undefined until you define it real userReal //An integer number attached to this tower that you can use for whatever you want. //Note that its value is undefined until you define it integer userInt

These are two numbers, an integer and a real number that you can get and set arbitrarily.
Following structs have them:
  • Unit and thus its descendants Creep and Tower, however you should always only use the user Values of your tower, as the user values of other towers and creeps may be used by their abilities!
  • DummyUnit and thus its descendants SpellDummy and Projectile. So you can attach values to your projectiles like counting the damage it already did or such things.
  • Items. Again only use the values of your own item to avoid collisions with other user's values.
  • EventPlacers and thus its decendants Buff, ItemEffect and AuraEffect.

So you can attach an int and a real to your stuff. If you need more variables attached, create your own struct type and then just set the struct as userInt (since structs are integers). Using this, you can add an arbitrary amount of data to your tower/item/dummies/buffs.
Note that these members have no specific value unless you define them.


You can get the time elapsed since game start by calling:
Game.getGameTime()

It returns an integer number that measures the time since game start in 1/25 seconds. So if it returns 1000, the game runs for 1000/25 = 40 seconds.

Inside a periodic event handling function (the function you assigned to as periodic event handler), you can call:

Event.getCurrentPeriodicEvent()

This function will return you a variable of type PeriodicEvent. Storing and using this variable, you can disable / reenable the periodic event or do other stuff with it. Here are the methods for PeriodicEvent:
//A periodic event can be aquired with the Event.getCurrentPeriodicEvent() method //(Only inside a periodic event reaction function) struct PeriodicEvent //Disables this periodic event. It will not fire until it is enabled again method disable takes nothing returns nothing //Enables the periodic event (if previously disabled) using its standard timeout method enable takes nothing returns nothing //Enables the periodic event (if previously disabled) using its standard timeout //It will only fire once and then never again (unless you enable it again sometime in the future) //(So the event is not really periodic but a one shot event) method enableOnce takes nothing returns nothing //Enables the periodic event using a custom timeout //If is true, then the event will fire only once (so no real periodic event) //If is false, then the event will be normally periodically method enableAdvanced takes real timeout, boolean oneShot returns nothing //Gets the time elapsed since this event last fired method getElapsed takes nothing returns real //Gets the time until this event fires the next time method getRemaining takes nothing returns real

So if you want to deactivate a periodic event, let it run first. Inside its handler, save the variable somewhere (for example in an user int of youer tower) and deactivate it. Then, you can later reenable the event
with the saved variable.

There is a trigger in the test map called "On Tower Details". It contains this function:

function onTowerDetails takes Tower tower returns MultiboardValues

It looks like the normal event reaction functions, but it returns a variable of type MultiboardValues.
This function is called whenever a user presses the "tower details" button for this tower and is shown the tower multiboard.

With this function you can display text messages with status information about your tower or even inject key/value pairs into the tower details multiboard.
This is useful for towers that grant bonuses that may change over time. There could be for example a tower, that permanently gains +1 damage for each 1000 damage it does and +2 damage for each kill it archieves. Its owner might want to know how much damage this tower already has gained through this skill. With this function, you can inject this into the then displayed multiboard.

To inject values, add them to the struct "MultiboardValues" and return it. For performance reason, you shouldn't create such a struct whenever the values are called. Rather create it on map init and set its keys (since these won't change). Then upon displaying it, just set the keys and return it.

If you just want to display some information with text messages in your function and not insert something into the multiboard, just return 0 in the function.

Here are the methods for the struct MultiboardValues:

struct MultiboardValues //Creates multiboard values // is the number of key,value pairs to be added to the multiboard // must be between 0 and 4 inclusively // //If you use Multiboard Values for your tower, you should create these at map init //and set the keys there. Then, just set the values correctly when the multiboard //is opened static method create takes integer size returns MultiboardValues //Resizes these MultiboardValues // must be between 0 and 4 inclusively method setSize takes integer size returns nothing //Sets the key number to //Index should be between 0 and size - 1 method setKey takes integer index, string s returns nothing //Sets the value number to //Index should be between 0 and size - 1 method setValue takes integer index, string s returns nothing

You can add up to 4 lines to the multiboard. The constructor (which should be called at map init already takes the size (i.e. the number of key value pairs). However, you can still alter the number of lines whit hte setSize(size) method. Then you can use setKey and setValue to set keys and values.

Note that keys and values shouldn't be too long, or the will not fit into the multiboard. Test your solution to check if the multiboard size is sufficient for your values.

Example

Let's implement the above mentioned example with the tower that gains damage for killing and damaging. We want to display the damage for killing and for damaging in different rows. Let's assume that we have saved the damage through killing in the tower's userInt and the damage through damaging in the towers userInt2.

So, first on map init we create the multiboard values, set the keys and save them in a global variable:

library myTower initializer init uses libHeader globals MultiboardValues myMBValues endglobals private function init takes nothing returns nothing set myMBValues = MultiboardValues.create(2) //Create with 2 initial rows call myMBValues.setKey(0,"Bonus by DMG") call myMBValues.setKey(1,"Bonus by Kills") endfunction endlibrary

We create the mb values, save them and already set the keys. The values will now be set in the on tower details function:
function onTowerDetails takes Tower tower returns MultiboardValues call myMBValues.setValue(0,I2S(tower.userInt)) call myMBValues.setValue(1,I2S(tower.userInt2)) return myMBValues endfunction

That's it! The lines will be in the multiboard.
Here we used I2S to format the userInt to a string. However, especially for floats, we can use one of the formating functions from the header to quickly create formated strings.
They are listed here:
//*********************** FORMATING FUNCTIONS *********************** //Formats a positive integer number to a string inserting a comma each three digits function formatPositiveInt takes integer i returns string //Formats a floating point number to a string, using fraction digits function formatFloat takes real r, integer fractionDigits returns string //Same as format float, but the number is multiplied with 100 and a percent sign (%) is appended to the string function formatPercent takes real r, integer fractionDigits returns string //Same as format percent, but will color values below 100% red and values above 100% green //(Exactly 100% will not get colored at all) function formatPercentColor takes real r, integer fractionDigits returns string //Same as format percent, but will color values below 0% red and values above 0% green //(Exactly 0% will not get colored at all) function formatPercentAddColor takes real r, integer fractionDigits returns string

If the small columns of the multiboard are too small for your needs and you rather want to display a text message containing the your tower's status, you can do that conveniently by calling the .displayText(message) method of player.

Example

This time, we want to display a message instead of putting the values into the multiboard. We erase the MultiboardValues from our header and instead use this as onTowerDetails function:

function onTowerDetails takes Tower tower returns MultiboardValues call tower.getOwner().displayText("Damage gained through damage: |cffFFFF80" + I2S(tower.userInt) + "|r") call tower.getOwner().displayText("Damage gained through kills: |cffFFFF80" + I2S(tower.userInt2) + "|r") return 0 //Return 0 to not insert any values into the multiboard endfunction

For some event types, the engine allows you to create two or more triggers that react to it. Every event reaction trigger that is surrounded by a scope can be duplicated.

At the moment, these events may be duplicated

  • OnDamage
  • OnAttack
  • Periodic
  • Autocast
  • UnitInRange
  • Aura

So you could for example create a tower with two auras, two periodic events or two onAttack events with different ratios.

If you want two or more events, use this procedure:

First, copy paste the event reaction trigger that you want twice. Name the new trigger like the old one, but insert the number behind it (without a space). So if copied trigger was "On Attack", name the new trigger "On Attack2", or if it is the third trigger for onAttack, name it "On Attack3". Do not name the first trigger "On Attack1", just leave it as "On Attack".

Next rename the scope in your new trigger. I recommend just insertin the number behind it, so for on attack rename "eventOnAttack" to "eventOnAttack2".

Now you are done, just code your second event reaction.

Do not rename the event reaction function! Just copy the trigger, rename it, rename the scope and you're done.