Skip to content

Ability Lua Tutorial 3: Anti Mage's Spell Shield

Elfansoer edited this page Aug 1, 2018 · 1 revision

Let's get our hands dirty.

Ability Form Review

Take a brief look on the ability's .txt file. It's quite short, since it's a simple ability.
Notice that the ability has been renamed as antimage_spell_shield_lua in order to avoid collision with the real one.

It is a basic ability with a passive behavior, and nothing else. No need for cast points, unit targetting, spell immunity, etc etc. It also has just one ability specials, which defines the magic resistance bonus on each level.
In short, it's short.

Introducing: Main Lua File

Open up a text-editor, create new file and save it as antimage_spell_shield_lua.lua, placed according to the .txt file. This will be the main script file, which defines what the ability does. Let's begin with the keypoints.

Class Identifier

You should generally want this particular line at the start of your lua files. This defines the start of a class for custom lua abilities. So, at the first line of the file, write:

technical_name = class({})

in our case,

-- antimage_spell_shield_lua.lua
antimage_spell_shield_lua = class({})

Modifier Linker

Since we're going to separate each class to their own files, they should be tagged here (in the main file), so the engine know that the ability need those extra files.
Spell Shield will have a file for its passive modifier named modifier_antimage_spell_shield_lua (more on this later), let's include it by calling:

LinkLuaModifier( string className, string fileName, int motionType )

where:

  • className: name of the class that will be included
  • fileName: path for the file (base path is scripts/vscripts)
  • motionType: Fill this with LUA_MODIFIER_MOTION_NONE for now

Now, the main file should looked like:

-- antimage_spell_shield_lua.lua
antimage_spell_shield_lua = class({})
LinkLuaModifier( "modifier_antimage_spell_shield_lua", "lua_abilities/antimage_spell_shield_lua/modifier_antimage_spell_shield_lua", LUA_MODIFIER_MOTION_NONE )

Ability Functions

This is the point where the great question finally asked,
"What does this ability do?"

You can read and try this tutorial if you want some real example, or this reference for a complete list of possible things a custom ability can do. If you decided to stick with me, then let's start with a simple passive.

To make the ability have a passive effect, create this function:

function technical_name:GetIntrinsicModifierName()
	return "modifier_name"
end

By writing this, you override a base class function. Simply put, the engine asks the ability, "Do you have any passive modifier?"
Default answer is "no", but writing above code which returns the modifier name, (hang on, explanation is close) it answers, "yes, the name is modifier_name."

The Spell Shield ability should now be like:

-- antimage_spell_shield_lua.lua
antimage_spell_shield_lua = class({})
LinkLuaModifier( "modifier_antimage_spell_shield_lua", "lua_abilities/antimage_spell_shield_lua/modifier_antimage_spell_shield_lua", LUA_MODIFIER_MOTION_NONE )

function antimage_spell_shield_lua:GetIntrinsicModifierName()
	return "modifier_antimage_spell_shield_lua"
end

Introducing: Modifier

Two main base class for Lua abilities are CDOTA_Ability_Lua and CDOTA_Modifier_Lua. The first is for ability and the other is for modifier.
Err, Whats dis? For those who don't know about classes, let's say that there are 2 types of file: ability and modifier.

Ability is the thing that sticks with the hero; its code will be activated when the ability is used.

Modifier is the thing that sticks to a certain unit; a generalization of de/buff. Usually abilites create a modifier for a unit, and some time later the modifier may be destroyed, like a buff that expires. Once a modifier is created, its code will run as long as it exists in-game.

Generally, a modifier is created to:

  • Modify property of a unit
  • Change state of a unit
  • Listening events of a unit
  • Add forced movement to a unit
  • Do something that's independent from caster (the one who casts ability)

Since Spell Shield is a passive ability, it simply applies a permanent modifier for the hero. Before that, however, the modifier should be defined.

Create a blank file and save it as
"../lua_abilities/antimage_spell_shield_lua/modifier_antimage_spell_shield_lua.lua"

Inside, we do the same thing as the main file; Class Identifier:

-- modifier_antimage_spell_shield_lua.lua
modifier_antimage_spell_shield_lua = class({})

Logic: Retrieving Information

We reach this point again, "What does this modifier do?". Here's the reference list of possible functions.

This is just a habit of mine that when a modifier is created, it should load information first. Hence, use this function:

function modifier_name:OnCreated( kv )

end

function modifier_name:OnRefresh( kv )

end

I think they are pretty self-explanatory.

Spell Shield grants the caster 20%/30%/40%/50% bonus Magical Resistance; get the number first. They have been defined in base .txt file, and we'll gonna load that. Use:

self:GetAbility():GetSpecialValueFor( "key_name" )

Fancy? Quick explanation:
That function returns the value of the specified key according to the ability's level at the time it is called.

  • self: A reference to this modifier.
  • GetAbility(): A modifier function which gets the ability reference who created this modifier
  • GetSpecialValueFor(): An ability function which gets the value of the specified key
  • key_name: The key name in AbilitySpecial, from the base .txt file

When first learned, a modifier is created and the function above will return 20 as integer. Store this number in the modifier's table:

-- modifier_antimage_spell_shield_lua.lua
function modifier_antimage_spell_shield_lua:OnCreated( kv )
	self.bonus = self:GetAbility():GetSpecialValueFor("bonus_resist_pct")
end

When Spell Shield is upgraded, the modifier will be refreshed (as per GetIntrinsicModifierName's definition), and we need to update its value

-- modifier_antimage_spell_shield_lua.lua
function modifier_antimage_spell_shield_lua:OnCreated( kv )
	self.bonus = self:GetAbility():GetSpecialValueFor("bonus_resist_pct")
end

function modifier_antimage_spell_shield_lua:OnRefresh( kv )
	self.bonus = self:GetAbility():GetSpecialValueFor("bonus_resist_pct")
end

Modifier: Static Property

Spell Shield grants bonus Magic Resistance. This falls into Modifying Property category. To modify a property, declare it using a function:

-- modifier_antimage_spell_shield_lua.lua
function modifier_antimage_spell_shield_lua:DeclareFunctions()
	local funcs = {
		MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS,
	}

	return funcs
end

Let's just write as is, or read this reference for other possible properties.

The above code is a declaration that "this modifier will give bonus magical resistance to the parent."
(by the way, "parent" means the unit this modifier is attached to)

Once declared, it needs implementation. According to reference above, the appropriate function for MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS is "GetModifierMagicalResistanceBonus", so let's write it:

-- modifier_antimage_spell_shield_lua.lua
function modifier_antimage_spell_shield_lua:GetModifierMagicalResistanceBonus( params )

end

The reference says: "Increases the magical resistance of the parent by a constant returned to it. Can return negative values."

Well, this is simple. It would be:

-- modifier_antimage_spell_shield_lua.lua
function modifier_antimage_spell_shield_lua:GetModifierMagicalResistanceBonus( params )
	return self.bonus
end

Now, the modifier is complete. Here's the full version:

-- modifier_antimage_spell_shield_lua.lua
modifier_antimage_spell_shield_lua = class({})

function modifier_antimage_spell_shield_lua:OnCreated( kv )
	self.bonus = self:GetAbility():GetSpecialValueFor("bonus_resist_pct")
end

function modifier_antimage_spell_shield_lua:OnRefresh( kv )
	self.bonus = self:GetAbility():GetSpecialValueFor("bonus_resist_pct")
end

function modifier_antimage_spell_shield_lua:DeclareFunctions()
	local funcs = {
		MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS
	}

	return funcs
end

function modifier_antimage_spell_shield_lua:GetModifierMagicalResistanceBonus( params )
	return self.bonus
end

Additional Bonus

What if the ability gives bonus magical resistance, but reducing its armor? Here:

-- modifier_antimage_spell_shield_lua_altered.lua
modifier_antimage_spell_shield_lua = class({})

function modifier_antimage_spell_shield_lua:OnCreated( kv )
	self.bonus = self:GetAbility():GetSpecialValueFor("bonus_resist_pct")
	self.armor = -7 --(bad practice here, write in base.txt instead)
end

function modifier_antimage_spell_shield_lua:OnRefresh( kv )
	self.bonus = self:GetAbility():GetSpecialValueFor("bonus_resist_pct")
	-- self.armor = -7 (no need to change constants)
end

function modifier_antimage_spell_shield_lua:DeclareFunctions()
	local funcs = {
		MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS,
		MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS,
	}

	return funcs
end

function modifier_antimage_spell_shield_lua:GetModifierMagicalResistanceBonus( params )
	return self.bonus
end

function modifier_antimage_spell_shield_lua:GetModifierPhysicalArmorBonus( params )
	return self.armor
end

Conclusion

I hope this may give insights in creating a modifier and specifying which property would be modified by modifier.
Next on, we still create a passive ability, Troll Warlord's Fervor. This is a little more advance than Spell Shield, but it's still simple. See you later!