Listening to game events
This is a tutorial for beginners. If you see something that can be improved, please improve it!
Contents
Introduction
This tutorial is about the API function ListenToGameEvent
. If you call ListenToGameEvent
with an event name and a function, the game will call that function every time the event happens.
Example 1
Every time a player levels up, we show a message to all players:
-- scripts/vscripts/addon_game_mode.lua
function LevelUpMessage (eventInfo)
Say(nil, "Someone just leveled up!", false)
end
function Activate ()
ListenToGameEvent("dota_player_gained_level", LevelUpMessage, nil)
end
We have two functions here:
LevelUpMessage
: Here we use the API functionSay
to show a message to all players.Activate
: The game calls this function when it loads the addon. We want the game to callLevelUpMessage
every time a player levels up. To do this we call the API functionListenToGameEvent
with"dota_player_gained_level"
andLevelUpMessage
as arguments.
Example 2
When a player reaches level 6, we show a message to all players:
-- scripts/vscripts/addon_game_mode.lua
function Level6Message (eventInfo)
if eventInfo.level == 6 then
Say(nil, "Someone just reached level 6", false)
end
end
function Activate ()
ListenToGameEvent("dota_player_gained_level", Level6Message, nil)
end
Note: In Level6Message
we use the argument, eventInfo
, to get the level that the player has reached.
The function ListenToGameEvent
Here is the signature of ListenToGameEvent
:
int ListenToGameEvent(string eventName, handle functionToCall, handle context)
Parameters
- eventName
- The name of the event that you want to listen to. See the page Built-In Engine Events for a complete list.
- functionToCall
- The function that you want the game to call when the event happens.
- context
- Here you can use either
nil
or an object (for example, a table or a function). If you use an object here, then it will be passed as the first argument to your function when the event happens.
Event details
By calling ListenToGameEvent
with an event name and a function, you tell the game to call that function every time the event happens. When the game calls the function, it passes a table as an argument to it. This table contains details of the event. On the page Built-In Engine Events you can see which keys the table has. For example, on this page you can see that the table for "dota_player_gained_level"
has a key called level
(cf. Example 2 above).
pairs
function to see which keys a table has:
for key,val in pairs(tbl) do
print(key, val)
end
tbl
with the table you want to inspect.)Your listener function
If your function is defined like this:
function MyFunction (arg)
-- some code here
end
then call ListenToGameEvent
like this:
ListenToGameEvent("some_event_name", MyFunction, nil)
But what if your function is defined like this?
function MyClass:MyFunction (arg)
-- some code here
end
Remember: This is a short way of writing
function MyClass.MyFunction (self, arg)
-- some code here
end
So your function actually takes two arguments. In this situation, call ListenToGameEvent
like this:
ListenToGameEvent("some_event_name", MyClass.MyFunction, ?)
or like this (see the FAQ below):
ListenToGameEvent("some_event_name", Dynamic_Wrap(MyClass, "MyFunction"), ?)
Replace ?
with the object that you want to use as self
in your function. Here is an example:
Example 3
When an NPC spawns, we print the total number of times this has happened:
-- scripts/vscripts/addon_game_mode.lua
if MyClass == nil then
MyClass = class({})
end
function MyClass:InitGameMode ()
self.numSpawned = 0
ListenToGameEvent("npc_spawned", MyClass.MyFunction, self)
end
function MyClass:MyFunction ()
self.numSpawned = self.numSpawned + 1
print("Number of times an NPC has spawned: " .. self.numSpawned)
end
function Activate ()
GameRules.MyAddon = MyClass()
GameRules.MyAddon:InitGameMode()
end
More examples
Example 4
When a player picks a hero, we give that hero a blink dagger:
-- scripts/vscripts/addon_game_mode.lua
function GiveBlinkDagger (hero)
if hero:HasRoomForItem("item_blink", true, true) then
local dagger = CreateItem("item_blink", hero, hero)
dagger:SetPurchaseTime(0)
hero:AddItem(dagger)
end
end
function OnHeroPicked (event)
local hero = EntIndexToHScript(event.heroindex)
GiveBlinkDagger(hero)
end
function Activate ()
ListenToGameEvent("dota_player_pick_hero", OnHeroPicked, nil)
end
Example 5
The team that first gets 5 hero kills wins:
-- scripts/vscripts/addon_game_mode.lua
if CustomGameMode == nil then
CustomGameMode = class({})
end
function Activate ()
GameRules.CustomAddon = CustomGameMode()
GameRules.CustomAddon:InitGameMode(5)
end
function CustomGameMode:InitGameMode (killLimit)
self.killLimit = killLimit
ListenToGameEvent("entity_killed", Dynamic_Wrap(CustomGameMode, "OnEntityKilled"), self)
end
function CustomGameMode:OnEntityKilled ()
if PlayerResource:GetTeamKills(DOTA_TEAM_GOODGUYS) == self.killLimit then
GameRules:SetGameWinner(DOTA_TEAM_GOODGUYS)
elseif PlayerResource:GetTeamKills(DOTA_TEAM_BADGUYS) == self.killLimit then
GameRules:SetGameWinner(DOTA_TEAM_BADGUYS)
end
end
Example 6
When a unit is killed, it drops a healing salve (item_flask
):
-- scripts/vscripts/addon_game_mode.lua
function CreateDrop (itemName, pos)
local newItem = CreateItem(itemName, nil, nil)
newItem:SetPurchaseTime(0)
CreateItemOnPositionSync(pos, newItem)
newItem:LaunchLoot(false, 300, 0.75, pos + RandomVector(RandomFloat(50, 350)))
end
function OnEntityKilled (event)
local killedEntity = EntIndexToHScript(event.entindex_killed)
if killedEntity ~= nil then
CreateDrop("item_flask", killedEntity:GetAbsOrigin())
end
end
function Activate ()
ListenToGameEvent("entity_killed", OnEntityKilled, nil)
end
RollPercentage
function to introduce a drop chance.FAQ
Question: What does Dynamic_Wrap
do?
Answer: Use Dynamic_Wrap
if you want the console command "script_reload"
to also reload your listeners.
Dynamic_Wrap
can be found here: /path/to/Steam/SteamApps/common/dota 2 beta/game/core/scripts/vscripts/utils/vscriptinit.lua
.Here are some more details: Suppose you have registered a listener like this:
ListenToGameEvent("some_event_name", MyClass.MyFunction, someObj)
If you change the source code of MyClass.MyFunction
and then type "script_reload"
in the console, your change will not be visible. It will still be the old version of MyClass.MyFunction
that gets called when the event happens. To fix this, you can use Dynamic_Wrap
:
ListenToGameEvent("some_event_name", Dynamic_Wrap(MyClass, "MyFunction"), someObj)
Now, every time the event happens, the game will look up the field MyFunction
on MyClass
and then run the function this field points to. When you change the source code of MyClass.MyFunction
and then type "script_reload"
in the console, the MyFunction
field will be updated. So when the event happens, the game will run the new version of the function.