Creating units or illusions in DotA 2 Lua Modding

The following shows several examples of creating units or illusions in DotA 2 modding. This is with regards to server sided modding (custom games in DotA 2). It uses Lua as a language and requires some prior understanding of DotA 2 modding and Lua.

With exception to creating illusions, the code below shows creation of units in a synchronous manner. Take a careful look at the differences. In particular, when creating units, you set both SetControllableByPlayer and SetOwner. However, when creating illusions of heroes, you only set SetControllableByPlayer and SetPlayerID. This is very important as using SetOwner on a hero unit will cause some issues whereas non-hero entities does not have the method SetPlayerID.

Illusions of heroes should be done in an asynchronous manner. This is because the copying of items and abilities takes a longer execution time. Else, you will face latency issues while playing DotA 2. If you want to, you can make creation of both illusions and units asynchronous too.

Creating a unit

local vTargetPosition = self:GetCursorPosition()
local hTreant = CreateUnitByName( "npc_dota_furion_treant", vTargetPosition, true, self:GetCaster(), self:GetCaster():GetPlayerOwner(), self:GetCaster():GetTeamNumber() )
if hTreant ~= nil then
	hTreant:SetControllableByPlayer( self:GetCaster():GetPlayerID(), false )
	hTreant:SetOwner( self:GetCaster() )
end

Creating a unit with timed lifespan of 5 seconds


modifier_furion_force_of_nature_lua = class({})

function modifier_furion_force_of_nature_lua:OnDestroy()
	if IsServer() then
		self:GetParent():ForceKill( false )
	end
end

--------------------------------------------------------------------------------

function modifier_furion_force_of_nature_lua:DeclareFunctions()
	local funcs = {
		MODIFIER_PROPERTY_LIFETIME_FRACTION
	}

	return funcs
end

--------------------------------------------------------------------------------

function modifier_furion_force_of_nature_lua:GetUnitLifetimeFraction( params )
	return ( ( self:GetDieTime() - GameRules:GetGameTime() ) / self:GetDuration() )
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

local vTargetPosition = self:GetCursorPosition()
local hTreant = CreateUnitByName( "npc_dota_furion_treant", vTargetPosition, true, self:GetCaster(), self:GetCaster():GetPlayerOwner(), self:GetCaster():GetTeamNumber() )
if hTreant ~= nil then
	hTreant:SetControllableByPlayer( self:GetCaster():GetPlayerID(), false )
	hTreant:SetOwner( self:GetCaster() )
    local kv = {
		duration = 5 -- Unit will live for 5 seconds
	}

	hTreant:AddNewModifier( self:GetCaster(), self, "modifier_furion_force_of_nature_lua", kv )
end

Creating an illusion of a hero (asynchronous)

local caster = self:GetCaster()
local unit_name = caster:GetUnitName()
local origin = caster:GetAbsOrigin() + RandomVector(100)
local modifyIllusion = function ( illusion )
	illusion:SetControllableByPlayer( caster:GetPlayerID(), false )
	illusion:SetPlayerID( caster:GetPlayerID() )

	-- Set the unit as an illusion
	-- modifier_illusion controls many illusion properties like +Green damage not adding to the unit damage, not being able to cast spells and the team-only blue particle
	illusion:AddNewModifier(caster, self:GetAbility(), "modifier_illusion", { duration = 5, outgoing_damage = 0, incoming_damage = 0 })
	-- Without MakeIllusion the unit counts as a hero, e.g. if it dies to neutrals it says killed by neutrals, it respawns, etc.
	illusion:MakeIllusion()

	-- Level Up the unit to the casters level
	local casterLevel = self:GetCaster():GetLevel()
	for i = 2, casterLevel do
		illusion:HeroLevelUp(false)
	end

	-- Set the skill points to 0 and learn the skills of the caster
	illusion:SetAbilityPoints(0)

	local maxAbilities = caster:GetAbilityCount() - 1

	for ability_id = 0, maxAbilities do
		local ability = caster:GetAbilityByIndex(ability_id)
		if ability then
			local abilityLevel = ability:GetLevel()
			if abilityLevel ~= 0 then
				local illusionAbility = illusion:GetAbilityByIndex(ability_id)
				if illusionAbility then
					illusionAbility:SetLevel(abilityLevel)
				else
					-- Add ability
					local abilityName = ability:GetAbilityName()
					local newAbility = illusion:AddAbility(abilityName)
					newAbility:SetLevel(abilityLevel)
				end
			end
		end
	end

	-- Recreate the items of the caster
	for itemSlot=0,5 do
		local item = caster:GetItemInSlot(itemSlot)
		if item ~= nil then
			local itemName = item:GetName()
			local newItem = CreateItem(itemName, illusion, illusion)
			illusion:AddItem(newItem)
		end
	end
end

-- Create unit
CreateUnitByNameAsync(
	unit_name, -- szUnitName
	origin, -- vLocation,
	true, -- bFindClearSpace,
	caster, -- hNPCOwner,
	caster:GetPlayerOwner(), -- hUnitOwner,
	caster:GetTeamNumber(), -- iTeamNumber
	modifyIllusion
)

Author: Woo Huiren

Currently a student at National University of Singapore. I contribute to opensource projects - primarily PHP and Angular related. I write about PCF and PWS related stuff too.

Leave a Reply