Comprehensive reference for the Valve bot scripting API used in custom lobby bot scripts. Detailed parameter descriptions, return value semantics, usage examples, and important caveats. Ref: https://developer.valvesoftware.com/wiki/Dota_2_Workshop_Tools/Scripting/API
- Script Entry Points
- Time Functions
- Team, Players, and Bot Handles
- Hero Stats (by Player ID)
- Game State
- Map and World
- Distance Functions
- Lanes and Creep Equilibrium
- Structures
- Items (Global)
- Runes
- Unit Queries
- Projectiles and Avoidance
- Team Desires
- Roshan and Glyph
- Courier System
- Hero Selection and Captains Mode
- Math and Random
- Callbacks
- Debug and HTTP
- Unit Functions (on unit handles)
- Ability / Item Functions (on ability/item handles)
- Action System
- Constants Reference
Valve calls these functions from your script files at specific times. Define them in the appropriate file to override default bot behavior. Understanding the call order and frequency is critical for writing efficient bot code.
function GetDesire() -- Returns float 0.0-1.0 desire for this mode
function OnStart() -- Called once when this mode becomes the active mode
function OnEnd() -- Called once when this mode stops being the active mode
function Think() -- Called every frame ONLY while this mode IS the active modeImportant:
GetDesire()is called every frame for ALL modes simultaneously. The mode returning the highest desire value wins and becomes the active mode. Only that mode'sThink()is then called.
Returning nil from
GetDesire()causes the engine to fall through to Valve's built-in desire calculation for that mode. This lets you selectively override desire only when you want to.
-- Example: Override push desire only when we have a numbers advantage
function GetDesire()
local bot = GetBot()
local nearbyAllies = bot:GetNearbyHeroes(1600, false, BOT_MODE_NONE)
local nearbyEnemies = bot:GetNearbyHeroes(1600, true, BOT_MODE_NONE)
if #nearbyAllies >= #nearbyEnemies + 2 then
return BOT_MODE_DESIRE_HIGH
end
return nil -- fall through to default desire
endfunction AbilityLevelUpThink() -- Called to decide which ability to level up
function AbilityUsageThink() -- Called each frame for ability usage decisions
function ItemUsageThink() -- Called each frame for item usage decisions
function BuybackUsageThink() -- Called to decide whether to buy back
function CourierUsageThink() -- Called for courier managementImportant: All Think functions in ability/item usage scripts are called every frame regardless of the current active mode. They run independently from the mode system.
function ItemPurchaseThink() -- Called each frame for item purchasing
ItemPurchaseThink()runs independently from both the mode system and ability usage system.
-- team_desires.lua
function TeamThink() -- Override team-level desires (runs ONCE per frame, BEFORE individual bot Think)
function UpdatePushLaneDesires() -- Return {top, mid, bot} push desires
function UpdateDefendLaneDesires() -- Return {top, mid, bot} defend desires
function UpdateFarmLaneDesires() -- Return {top, mid, bot} farm desires
function UpdateRoamDesire() -- Return desire, target
function UpdateRoshanDesire() -- Return desire
-- hero_selection.lua
function Think() -- Called during hero pick phaseExecution order each frame:
TeamThink()runs first at the team level, then each individual bot's modeGetDesire()functions run, then the winning mode'sThink()runs, then ability/item Think functions run.
function MinionThink(hMinionUnit) -- Called for summoned unit AI (e.g., Necronomicon, Treants)Three distinct clocks exist and serve different purposes. Using the wrong one leads to subtle timing bugs.
Returns: float -- Game clock time in seconds.
This is the primary timer for all game logic. It matches the in-game clock visible to players.
- Negative during pre-game countdown. Starts counting up from a negative value during the strategy phase. Reaches 0.0 at the horn (creeps spawn).
- Pauses when the game is paused.
- Affected by game speed changes (e.g., demo mode fast-forward).
-- Wait until creeps have spawned
if DotaTime() < 0 then return end
-- Check if it's past the 10-minute mark
if DotaTime() > 600 then
-- Late laning / mid game logic
endReturns: float -- Time since hero selection phase began.
- Always positive (starts ticking from hero selection).
- Pauses when the game is paused.
- Use for cooldown tracking or timers that need to span the pre-game period.
-- Track when we last did something, even during pre-game
local lastActionTime = GameTime()When to use
GameTime()vsDotaTime(): UseDotaTime()for anything related to game events (rune spawns, Roshan timer, creep waves). UseGameTime()when you need a monotonically increasing timer that works during pre-game.
Returns: float -- Real-world elapsed time in seconds.
- Unaffected by pause or game speed changes.
- Use exclusively for performance measurement and profiling.
-- Measure how long a computation takes
local startTime = RealTime()
-- ... expensive computation ...
local elapsed = RealTime() - startTime
if elapsed > 0.01 then
print("WARNING: computation took " .. elapsed .. " seconds")
endReturns: hUnit -- Handle to the current script's bot hero.
This is the most frequently called function in bot scripts. It returns the hero unit handle for the bot whose script is currently executing.
ARDM caveat: In All Random Deathmatch mode,
GetBot()may return a different handle after a hero swap on death. Never cache the result across death boundaries. Bot handles can become stale in ARDM when heroes swap.
local bot = GetBot()
local myHP = bot:GetHealth()
local myLoc = bot:GetLocation()Returns: int -- The bot's team constant (TEAM_RADIANT or TEAM_DIRE).
Returns: int -- The enemy team constant.
Parameters:
nTeam(int):TEAM_RADIANTorTEAM_DIRE
Returns: {int...} -- Table of player IDs (0-9) on the specified team.
local enemies = GetTeamPlayers(GetOpposingTeam())
for _, playerID in ipairs(enemies) do
if IsHeroAlive(playerID) then
-- enemy is alive
end
endParameters:
nIndex(int): 1-indexed position (1 through 5).
Returns: hUnit or nil -- Handle to the Nth player on the bot's team.
- Returns
nilif the player slot doesn't exist (e.g., fewer than 5 players). - Can return human players too, not just bots.
- The index is 1-based, matching Dota's team slot numbering.
-- Iterate all team members safely
for i = 1, 5 do
local member = GetTeamMember(i)
if member ~= nil then
-- do something with this teammate
end
endParameters:
nPlayerID(int): Player ID (0-9).
Returns: bool -- True if the player is on our team.
Parameters:
nPlayerID(int): Player ID (0-9).
Returns: bool -- True if this player is AI-controlled (not a human).
Parameters:
nPlayerID(int): Player ID (0-9).
Returns: int -- TEAM_RADIANT or TEAM_DIRE.
All functions in this section take a player ID (0-9), not a unit handle. This is an important distinction -- these work even when you don't have a unit handle for the hero.
Parameters:
nPlayerID(int): Player ID (0-9). Not a unit handle.
Returns: bool -- True if the player's current hero is alive.
This is the authoritative alive check for a player slot. In ARDM, this reflects the player's CURRENT hero, not previous dead heroes. Always prefer this over
unit:IsAlive()when checking if a player is alive, especially in ARDM.
-- Check if a specific enemy is alive
local enemyPlayers = GetTeamPlayers(GetOpposingTeam())
for _, pid in ipairs(enemyPlayers) do
if IsHeroAlive(pid) then
-- this enemy player's hero is alive
end
endReturns: int -- Hero level (1-30).
Returns: int -- Kill / death / assist count for the player.
Returns: table -- Table with fields: location (vector), time_since_seen (float, seconds since last observed).
- Returns stale data if the hero hasn't been seen recently.
- Useful for estimating enemy positions when they're in fog.
local info = GetHeroLastSeenInfo(enemyPlayerID)
if info.time_since_seen < 5.0 then
-- fairly recent sighting, location is somewhat reliable
endReturns: string -- Internal hero name (e.g., "npc_dota_hero_axe").
Returns: int -- Current game phase. One of the GAME_STATE_* constants.
if GetGameState() ~= GAME_STATE_GAME_IN_PROGRESS then return endReturns: float -- Seconds remaining until the next state transition (e.g., time left in strategy phase).
Returns: int -- Game mode constant. See Constants: Game Modes.
if GetGameMode() == GAMEMODE_ARDM then
-- handle ARDM-specific logic (stale handles, hero swaps, etc.)
endReturns: int -- Current hero pick/ban phase state (HEROPICK_STATE_*).
Returns: float -- Day/night cycle position. 0.0 = midnight, 0.25 = dawn, 0.5 = noon, 0.75 = dusk.
local isNight = GetTimeOfDay() < 0.25 or GetTimeOfDay() > 0.75
if isNight then
-- reduced vision, play more cautiously
endReturns: minX, minY, maxX, maxY (four floats) -- The map boundary coordinates.
Parameters:
vLoc(vector): World position to test.
Returns: bool -- True if a unit can walk through this location (not blocked by terrain, cliffs, or buildings).
-- Check before trying to move somewhere
local targetLoc = Vector(1000, 2000)
if IsLocationPassable(targetLoc) then
bot:Action_MoveToLocation(targetLoc)
endReturns: bool -- True if the location is currently visible to the bot's team (in team vision).
Returns: bool -- True if the entire radius around the location is visible. More restrictive than IsLocationVisible.
Returns: int -- Terrain height level (1-5). Useful for high ground / low ground checks.
Returns: {hTree...} -- Table of all tree handles on the map. This is a large table; cache the result if called frequently.
Parameters:
nTree(handle/int): Tree handle fromGetAllTrees()orGetNearbyTrees().
Returns: vector -- World position of the tree.
Parameters:
hA,hB(hUnit): Two unit handles.
Returns: float -- Edge-to-edge distance between the two units.
Gotcha: This subtracts both units' bounding radii from the center-to-center distance. A return value of 0 means the units are touching. This matches how ability cast ranges work in Dota.
Returns: float -- Squared edge-to-edge distance. Use this for comparisons -- it avoids the expensive square root operation.
-- GOOD: Compare squared distances (fast)
if GetUnitToUnitDistanceSqr(bot, enemy) < 600 * 600 then
-- within 600 range
end
-- BAD: Unnecessary sqrt
if GetUnitToUnitDistance(bot, enemy) < 600 then
-- same check but slower
endReturns: float -- Distance (or squared distance) from a unit to a world point.
Parameters:
vStart(vector): Line segment start point.vEnd(vector): Line segment end point.vPoint(vector): The point to measure from.
Returns: Three values:
dist(float): Perpendicular distance from the point to the line segment.closest(vector): The closest point on the line segment tovPoint.within(bool): Whether the closest point falls within the segment (not past either endpoint).
local dist, closestPt, isWithin = PointToLineDistance(segStart, segEnd, myPos)
if isWithin and dist < 200 then
-- we're close to the middle of this line segment
endLane constants: LANE_TOP = 1, LANE_MID = 2, LANE_BOT = 3.
Parameters:
nTeam(int):TEAM_RADIANTorTEAM_DIRE.nLane(int):LANE_TOP,LANE_MID, orLANE_BOT.bIgnoreTowers(bool): If true, estimates where creeps would be without tower influence.
Returns: float -- Creep equilibrium position as a ratio.
0.0= your team's ancient.1.0= the enemy team's ancient.0.5= roughly the river / midpoint.
local frontAmount = GetLaneFrontAmount(GetTeam(), LANE_MID, false)
if frontAmount > 0.6 then
-- creep wave is pushed past the river toward the enemy
endParameters:
nTeam(int): Team.nLane(int): Lane.fDelta(float): Offset from the lane front.- Negative = shift toward your base.
- Positive = shift toward the enemy base.
0= exactly at the lane front.
Returns: vector -- World position of the (offset) lane front.
-- Get a position slightly behind the creep wave (safe farm spot)
local safeFarmLoc = GetLaneFrontLocation(GetTeam(), LANE_MID, -500)Parameters:
nLane(int): Lane.fAmount(float): Ratio along the lane path (0.0 = Radiant ancient, 1.0 = Dire ancient).
Returns: vector -- World position at that ratio along the lane path.
Parameters:
nLane(int): Lane.vLoc(vector): World position.
Returns: Two values:
amount(float): The closest ratio along the lane path (0.0 - 1.0).distance(float): Perpendicular distance fromvLocto the lane path.
Parameters:
nTeam(int):TEAM_RADIANTorTEAM_DIRE.nTower(int): Tower constant (e.g.,TOWER_TOP_1,TOWER_MID_2,TOWER_BASE_1).
Returns: hUnit -- Tower unit handle (may be nil or dead).
local topT1 = GetTower(GetTeam(), TOWER_TOP_1)
if topT1 ~= nil and topT1:IsAlive() then
-- our top T1 is still standing
endReturns: hUnit or nil -- The unit the tower is currently attacking. nil if the tower isn't attacking anything.
Returns: hUnit -- Barracks handle (e.g., BARRACKS_TOP_MELEE).
Returns: hUnit -- Shrine handle.
Returns: hUnit -- The Ancient/Throne unit handle.
Parameters:
nShop(int):SHOP_HOME,SHOP_SIDE,SHOP_SECRET,SHOP_SIDE2,SHOP_SECRET2.
Returns: vector -- World position of the shop.
Parameters:
nRuneLoc(int):RUNE_POWERUP_1,RUNE_POWERUP_2,RUNE_BOUNTY_1throughRUNE_BOUNTY_4.
Returns: vector -- World position of the rune spawn point.
Parameters:
sName(string): Internal item name (e.g.,"item_blink").
Returns: int -- Gold cost of the item.
Parameters:
sName(string): Internal item name.
Returns: {{string...}...} -- Nested table of recipe components. Each sub-table represents one recipe variant.
Always use this function instead of hardcoding component arrays. Valve updates recipes between patches, and this function reads the current game data.
local components = GetItemComponents("item_mekansm")
-- components[1] = {"item_headdress", "item_buckler", "item_recipe_mekansm"}Returns: bool -- Whether the item must be purchased at a secret/side shop.
Returns: int -- Current stock count. Some items have limited stock (e.g., Observer Wards, Gems).
Returns: {table...} -- Table of tables, each containing:
item(hItem): Item handle.owner(int): Owning player ID (-1 if unowned).playerid(int): Player who dropped it.location(vector): World position.
Parameters:
nLoc(int): Rune location constant (RUNE_POWERUP_1,RUNE_BOUNTY_1, etc.).
Returns: int -- Rune type constant (RUNE_DOUBLEDAMAGE, RUNE_HASTE, RUNE_BOUNTY, etc.). Returns RUNE_INVALID if no rune or unknown.
Returns: int -- One of:
RUNE_STATUS_UNKNOWN-- Haven't scouted this location.RUNE_STATUS_AVAILABLE-- Rune is confirmed present.RUNE_STATUS_MISSING-- Location scouted, no rune there.
Returns: float -- Seconds since the rune location was last observed by your team. Large values mean stale information.
-- Only go for rune if we know it's there
if GetRuneStatus(RUNE_POWERUP_1) == RUNE_STATUS_AVAILABLE then
bot:Action_MoveToLocation(GetRuneSpawnLocation(RUNE_POWERUP_1))
endParameters:
nType(int): Unit type filter. One of theUNIT_LIST_*constants.
Returns: {hUnit...} -- Table of all matching unit handles across the entire map.
Performance warning: This is an expensive function. It scans all units in the game. Use
GetNearby*functions instead whenever possible. Only useGetUnitListwhen you genuinely need a global search (e.g., finding all enemy heroes regardless of distance).
-- Find all enemy heroes (expensive, use sparingly)
local allEnemyHeroes = GetUnitList(UNIT_LIST_ENEMY_HEROES)
-- BETTER: If you only need nearby enemies
local nearbyEnemies = bot:GetNearbyHeroes(1600, true, BOT_MODE_NONE)Returns: {table...} -- Table of tables, each containing:
team(int): Which team's jungle.type(int): Camp type (small, medium, large, ancient).speed(float): Creep movement speed.location(vector): Camp spawn location.min(vector): Spawn box minimum corner.max(vector): Spawn box maximum corner.
Returns: {table...} -- Active TP scroll teleportations, each containing:
playerid(int): Who is teleporting.location(vector): TP destination.time_remaining(float): Seconds until arrival.
local tps = GetIncomingTeleports()
for _, tp in ipairs(tps) do
if tp.time_remaining < 1.0 then
-- someone is about to arrive
end
endReturns: {table...} -- All active linear (skillshot) projectiles, each containing:
location(vector): Current projectile position.caster(hUnit): Who fired it.playerid(int): Caster's player ID.ability(hAbility): Source ability handle.velocity(vector): Direction and speed.radius(float): Projectile collision radius.handle(int): Unique projectile handle.
Returns: {table...} -- Persistent area effects to avoid, each containing:
location(vector): Center position.playerid(int): Source player ID.ability(hAbility): Source ability.caster(hUnit): Source unit.radius(float): Effect radius.
Parameters:
vLocAndRadius(vector): Position (x, y) and radius (z component).fDuration(float): How long the zone persists.
Returns: int -- Handle for later removal.
Parameters:
hZone(int): Handle returned byAddAvoidanceZone.
These functions read the current team-level desires, typically set by team_desires.lua.
Parameters:
nLane(int):LANE_TOP,LANE_MID, orLANE_BOT.
Returns: float -- Desire value (0.0 - 1.0).
Returns: float -- Team roam desire.
Returns: hUnit -- The current roam target unit handle.
Returns: float -- Team Roshan desire.
Returns: float -- DotaTime() timestamp of the last Roshan kill. Returns 0 if Roshan hasn't been killed yet.
local roshTimer = DotaTime() - GetRoshanKillTime()
if GetRoshanKillTime() > 0 and roshTimer > 480 then
-- Roshan may have respawned (8-11 min window)
endReturns: float -- Seconds remaining on the team's Glyph of Fortification cooldown. Returns 0 if ready.
Returns: bool -- True if a courier is alive and usable.
Returns: int -- Number of couriers your team has.
Parameters:
nIndex(int): 0-indexed courier index.
Returns: hCourier -- Courier unit handle.
-- Get the first (usually only) courier
local courier = GetCourier(0)Returns: int -- One of the COURIER_STATE_* constants:
COURIER_STATE_IDLE-- At base, doing nothing.COURIER_STATE_AT_BASE-- At base.COURIER_STATE_MOVING-- Moving (not delivering).COURIER_STATE_DELIVERING_ITEMS-- Actively delivering to a hero.COURIER_STATE_RETURNING_TO_BASE-- Heading back to base.COURIER_STATE_DEAD-- Courier is dead.
Returns: bool -- True if the courier can fly.
Use ActionImmediate_Courier(hCourier, nAction) to command a courier. The most common action:
-- Deliver items from stash to your hero
local courier = GetCourier(0)
if GetCourierState(courier) == COURIER_STATE_IDLE then
bot:ActionImmediate_Courier(courier, COURIER_ACTION_TAKE_AND_TRANSFER_ITEMS)
endTip: Check
GetStashValue()andGetCourierValue()to decide whether it's worth using the courier.
SelectHero(nPlayerID, sHeroName) -- Select a hero for a player
IsPlayerInHeroSelectionControl(nPlayerID) -- Does this player control hero selection?IsInCMBanPhase() -- Returns bool: currently in ban phase?
IsInCMPickPhase() -- Returns bool: currently in pick phase?
GetCMPhaseTimeRemaining() -- Returns float: seconds left in current phase
GetCMCaptain() -- Returns int: captain's player ID
SetCMCaptain(nPlayerID) -- Set captain
IsCMBannedHero(sName) -- Returns bool: is this hero banned?
IsCMPickedHero(nTeam, sName) -- Returns bool: has this team picked this hero?
CMBanHero(sName) -- Execute a ban
CMPickHero(sName) -- Execute a pickMin(a, b) -- Returns the smaller value
Max(a, b) -- Returns the larger value
Clamp(val, min, max) -- Constrain val to [min, max]
RemapVal(val, fromMin, fromMax, toMin, toMax) -- Linear remap (unclamped -- can exceed output range!)
RemapValClamped(val, fromMin, fromMax, toMin, toMax) -- Linear remap (clamped to output range)Gotcha:
RemapValdoes NOT clamp output. Ifvalis outside[fromMin, fromMax], the output will be outside[toMin, toMax]. UseRemapValClampedto guarantee the output stays in range.
-- Map health percentage (0-100) to flee desire (0.0-1.0), clamped
local hpPercent = bot:GetHealth() / bot:GetMaxHealth() * 100
local fleeDesire = RemapValClamped(hpPercent, 30, 80, 1.0, 0.0)
-- At 30% HP -> desire 1.0, at 80% HP -> desire 0.0RandomInt(min, max) -- Random integer in [min, max] (inclusive)
RandomFloat(min, max) -- Random float in [min, max]
RandomVector(fLength) -- Random 2D direction vector of given length
RollPercentage(nChance) -- Returns true with nChance% probability (1-100)if RollPercentage(25) then
-- 25% chance to execute this block
end
-- Random scatter location
local scatterDir = RandomVector(200)
local scatterLoc = bot:GetLocation() + scatterDirCallbacks let you react to game events. They persist for the entire game duration -- install them once at initialization, not every frame.
Parameters:
nPlayerID(int): Player whose damage events to listen for.func(function): Called with(hVictim, hAttacker, nDamage).
InstallDamageCallback(bot:GetPlayerID(), function(victim, attacker, damage)
if damage > 200 then
-- significant damage taken
end
end)Parameters:
nPlayerID(int): Player whose casts to listen for.func(function): Called when the player casts an ability or uses an item.
Called when any courier dies.
Called when Roshan is killed.
Parameters:
func(function): Called with a table containing:string(string): The chat message text.playerid(int): Who sent the message.teamonly(bool): True if team chat, false if all chat.
InstallChatCallback(function(msg)
if msg.string == "push mid" and msg.teamonly then
-- teammate is requesting a mid push
end
end)Important: Callbacks persist for the game duration. Do not reinstall them every frame or you will get duplicate calls.
All debug draw functions render for one frame only. Call them every frame to maintain visibility.
DebugDrawLine(vStart, vEnd, r, g, b) -- Draw a line (r,g,b = 0-255)
DebugDrawCircle(vCenter, fRadius, r, g, b) -- Draw a circle
DebugDrawText(fX, fY, sText, r, g, b) -- Render text overlay (screen coordinates)
DebugPause() -- Pause game execution (for debugging)CreateHTTPRequest(url) -- Returns handle (localhost only)
CreateRemoteHTTPRequest(url) -- Returns handle (remote servers)HTTP requests are sandboxed.
CreateHTTPRequestonly allows localhost connections. UseCreateRemoteHTTPRequestfor external API calls.
All functions in this section are called on a unit handle: bot:FunctionName(...) or hUnit:FunctionName(...).
| Function | Returns | Description |
|---|---|---|
GetUnitName() |
string |
Internal unit name (e.g., "npc_dota_hero_axe", "npc_dota_creep_goodguys_melee") |
GetPlayerID() |
int |
Owning player ID. Returns -1 for non-player units (creeps, neutrals). |
GetTeam() |
int |
Team affiliation (TEAM_RADIANT, TEAM_DIRE, TEAM_NEUTRAL) |
IsBot() |
bool |
True if this is an AI-controlled player's hero |
GetDifficulty() |
int |
Bot difficulty level (DIFFICULTY_*). Only meaningful for bot-controlled heroes. |
| Function | Returns | Description |
|---|---|---|
IsHero() |
bool |
True for hero units (including illusions and clones) |
IsIllusion() |
bool |
True for illusions only. Note: Meepo clones return false. |
IsCourier() |
bool |
True for courier units |
IsCreep() |
bool |
True for lane creeps |
IsAncientCreep() |
bool |
True for ancient neutral creeps |
IsBuilding() |
bool |
True for structures (towers, barracks, ancient) |
IsTower() |
bool |
True specifically for towers |
IsFort() |
bool |
True specifically for the Ancient building |
IsMinion() |
bool |
True for bot-controlled summons (Treants, Necronomicon, etc.) |
| Function | Returns | Description |
|---|---|---|
GetHealth() |
int |
Current HP |
GetMaxHealth() |
int |
Maximum HP |
GetHealthRegen() |
float |
Total HP regeneration per second (including all bonuses) |
GetBaseHealthRegen() |
float |
Base HP regen before item/buff bonuses |
GetMana() |
int |
Current mana |
GetMaxMana() |
int |
Maximum mana |
GetManaRegen() |
float |
Total mana regeneration per second |
GetBaseManaRegen() |
float |
Base mana regen before bonuses |
Returns: bool -- True if this specific unit entity is alive.
ARDM warning: A dead hero entity may still exist in memory while the player already has a new alive hero. For checking whether a player is alive, always use the global
IsHeroAlive(playerID)instead.
Returns: float -- Seconds until this hero respawns. Returns -1 for non-hero units.
Returns: float -- Remaining duration for temporary/summoned units (e.g., Treants, illusions). Returns a large number for permanent units.
| Function | Returns | Description |
|---|---|---|
HasBuyback() |
bool |
Can this hero buy back? Returns false for enemy heroes (hidden info). |
GetBuybackCost() |
int |
Gold required. Returns -1 for enemy heroes. |
GetBuybackCooldown() |
float |
Cooldown remaining. Returns -1 for enemy heroes. |
Enemy buyback info is hidden. All buyback functions return dummy values for enemy heroes because buyback status is not public information.
| Function | Returns | Description |
|---|---|---|
GetBaseDamage() |
float |
Average base attack damage (no bonus damage) |
GetBaseDamageVariance() |
float |
+/- damage variation from base |
GetAttackDamage() |
float |
Total attack damage including all bonuses |
GetAttackRange() |
int |
Current attack range in units |
GetAttackSpeed() |
int |
Attack speed value |
GetSecondsPerAttack() |
float |
Time for one full attack cycle (wind-up + backswing) |
GetAttackPoint() |
float |
Attack animation wind-up time (damage dealt at this point) |
GetLastAttackTime() |
float |
GameTime() timestamp of last attack |
GetAttackTarget() |
hUnit |
Unit currently being attacked (nil if not attacking) |
GetAcquisitionRange() |
int |
Range at which the unit auto-acquires targets |
GetAttackProjectileSpeed() |
int |
Projectile speed for ranged attacks. 0 for melee. |
Parameters:
nDmg(int): Raw damage amount.nType(int):DAMAGE_TYPE_PHYSICAL,DAMAGE_TYPE_MAGICAL, orDAMAGE_TYPE_PURE.
Returns: float -- Damage after armor/magic resistance reduction.
-- Calculate if a nuke would kill the enemy
local actualDmg = enemy:GetActualIncomingDamage(300, DAMAGE_TYPE_MAGICAL)
if enemy:GetHealth() <= actualDmg then
-- this nuke will kill them
endParameters:
bAvailable(bool): If true, only count abilities that are currently castable (off cooldown, have mana).hTarget(hUnit): Target unit to estimate damage against.fDuration(float): Time window in seconds for the estimate.nDamageTypes(int): Bitfield of damage types to include (DAMAGE_TYPE_PHYSICAL,DAMAGE_TYPE_MAGICAL,DAMAGE_TYPE_ALL).
Returns: float -- Estimated total damage output against the target.
| Function | Returns | Description |
|---|---|---|
GetAttackCombatProficiency(hTarget) |
float |
Outgoing damage multiplier vs specific target |
GetDefendCombatProficiency(hAttacker) |
float |
Incoming damage multiplier from specific attacker |
GetOffensivePower() |
float |
Estimated total damage output (respects cooldowns/mana) |
GetRawOffensivePower() |
float |
Damage output ignoring cooldowns and mana |
| Function | Returns | Description |
|---|---|---|
GetArmor() |
float |
Current armor value (can be negative) |
GetMagicResist() |
float |
Magic resistance as a fraction (0.25 = 25%) |
GetEvasion() |
float |
Evasion chance as a fraction |
GetSpellAmp() |
float |
Spell amplification as a fraction |
| Function | Returns | Description |
|---|---|---|
GetLocation() |
vector |
Current world position |
GetFacing() |
int |
Facing direction in degrees (0-359, 0 = east, 90 = north) |
GetBaseMovementSpeed() |
int |
Base movement speed (before boots/buffs) |
GetCurrentMovementSpeed() |
int |
Current effective movement speed |
GetVelocity() |
vector |
Current velocity vector |
GetGroundHeight() |
float |
Terrain height at unit's current position |
GetBoundingRadius() |
float |
Unit's collision radius |
Parameters:
vLoc(vector): Target location.nDegrees(int): Cone half-angle in degrees.
Returns: bool -- True if the unit is facing within the specified cone toward the location.
if bot:IsFacingLocation(enemy:GetLocation(), 30) then
-- we're roughly facing the enemy (within 30 degrees)
endParameters:
fTime(float): Seconds into the future.
Returns: vector -- Predicted position assuming the unit continues its current movement.
Returns: float -- 0.0 (erratic movement) to 1.0 (stable straight-line movement). Useful for deciding whether to lead skillshots.
| Function | Returns | Description |
|---|---|---|
GetLevel() |
int |
Current hero level (1-30) |
GetPrimaryAttribute() |
int |
ATTRIBUTE_STRENGTH, ATTRIBUTE_AGILITY, or ATTRIBUTE_INTELLECT |
GetAttributeValue(nAttrib) |
int |
Value of the specified attribute. Returns -1 for non-heroes. |
GetAbilityPoints() |
int |
Unspent ability points |
HasScepter() |
bool |
Has Aghanim's Scepter upgrade (buff or item)? |
Returns: int -- Current total gold (reliable + unreliable combined).
Returns: int -- Total net worth (gold + item values + everything).
Returns: int -- Total value of items in the hero's stash. Useful for deciding when to use the courier.
Returns: int -- Total value of items assigned to this hero on the courier.
| Function | Returns | Description |
|---|---|---|
GetLastHits() |
int |
Last hit count |
GetDenies() |
int |
Deny count |
GetBountyXP() |
int |
XP reward for killing this unit |
GetBountyGoldMin() / GetBountyGoldMax() |
int |
Gold bounty range for killing this unit |
GetXPNeededToLevel() |
int |
XP needed for next level. Returns -1 for non-heroes. |
| Function | Returns | Description |
|---|---|---|
DistanceFromFountain() |
int |
Distance to fountain. Returns 0 when in fountain range. |
DistanceFromSecretShop() |
int |
Distance to nearest secret shop. 0 = in range. |
DistanceFromSideShop() |
int |
Distance to nearest side shop. 0 = in range. |
-- Check if we can buy from secret shop
if bot:DistanceFromSecretShop() == 0 then
bot:ActionImmediate_PurchaseItem("item_ultimate_orb")
end| Function | Returns | Description |
|---|---|---|
GetCurrentVisionRange() |
int |
Active vision radius (changes with day/night) |
GetDayTimeVisionRange() |
int |
Vision range during daytime |
GetNightTimeVisionRange() |
int |
Vision range at night |
CanBeSeen() |
bool |
Is this unit visible to the enemy team? |
| Function | Returns | Notes |
|---|---|---|
IsStunned() |
bool |
Unit is stunned |
IsRooted() |
bool |
Unit is rooted (can't move, can still cast/attack) |
IsSilenced() |
bool |
Unit is silenced (can't cast abilities) |
IsHexed() |
bool |
Unit is hexed/polymorphed |
IsDisarmed() |
bool |
Unit can't attack |
IsMuted() |
bool |
Item passives disabled |
IsNightmared() |
bool |
Bane Nightmare or similar |
IsBlind() |
bool |
Attacks will miss |
IsInvulnerable() |
bool |
Immune to all damage |
IsMagicImmune() |
bool |
Immune to magic damage and most spells |
IsAttackImmune() |
bool |
Cannot be attacked |
IsDominated() |
bool |
Unit is dominated (Helm of the Overlord, etc.) |
IsEvadeDisabled() |
bool |
Evasion disabled (MKB, etc.) |
IsBlockDisabled() |
bool |
Damage block disabled |
IsUnableToMiss() |
bool |
True Strike -- attacks cannot miss |
IsSpeciallyDeniable() |
bool |
Can be denied by allies (e.g., under specific DoTs) |
Returns: bool -- True if the unit has an invisibility buff.
Critical gotcha: This does NOT mean the unit is undetected. A unit can be
IsInvisible() == trueAND still fully visible to enemies via Sentry Wards, Dust, Gem, or tower true sight. To check if a unit is actually unseen, you need different logic. This function only tells you the unit has an invis modifier active.
| Function | Returns | Description |
|---|---|---|
GetStunDuration(bAvailable) |
float |
Total available stun duration in seconds. bAvailable=true only counts ready abilities. |
GetSlowDuration(bAvailable) |
float |
Total available slow duration. |
HasBlink(bAvailable) |
bool |
Has a blink ability or item? |
HasMinistunOnAttack() |
bool |
Has an attack mini-stun (e.g., MKB)? |
HasSilence(bAvailable) |
bool |
Has a silence ability? |
HasInvisibility(bAvailable) |
bool |
Has an invis ability? |
UsingItemBreaksInvisibility() |
bool |
Would using an item break this unit's current invis? |
| Function | Returns | Description |
|---|---|---|
WasRecentlyDamagedByAnyHero(fInterval) |
bool |
Damaged by any hero within fInterval seconds? |
TimeSinceDamagedByAnyHero() |
float |
Seconds since last hero damage. Large value if never damaged. |
WasRecentlyDamagedByHero(hUnit, fInterval) |
bool |
Damaged by a specific hero? |
WasRecentlyDamagedByCreep(fInterval) |
bool |
Damaged by a creep? |
WasRecentlyDamagedByTower(fInterval) |
bool |
Damaged by a tower? |
-- Flee if recently damaged by an enemy hero
if bot:WasRecentlyDamagedByAnyHero(3.0) then
-- we took hero damage in the last 3 seconds
endAll GetNearby* functions are called on a unit handle and search around that unit.
Maximum radius is 1600. Values above 1600 are silently clamped. If you need a larger search, use
GetUnitList()and filter manually.
All return tables sorted by distance (closest first). Return an empty table (not nil) if none found.
Parameters:
nRadius(int): Search radius (max 1600).bEnemies(bool):true= enemy heroes,false= allied heroes.nMode(int): Bot mode filter. PassBOT_MODE_NONEto get all heroes. Other modes filter to only heroes in that mode.
Returns: {hUnit...} -- Table of hero handles sorted by distance.
-- Get all enemies within 1200 range
local enemies = bot:GetNearbyHeroes(1200, true, BOT_MODE_NONE)
if #enemies >= 3 then
-- outnumbered, consider retreating
endParameters:
nRadius(int): Search radius (max 1600).bEnemies(bool):true= enemy creeps,false= allied creeps.
Returns: {hUnit...} -- All creeps (lane + jungle + summons) within range.
Returns: {hUnit...} -- Only lane creeps within range. Does not include jungle creeps or summoned units.
Parameters:
nRadius(int): Search radius (max 1600).
Returns: {hUnit...} -- Neutral creeps within range.
Note: This function does NOT take a
bEnemiesparameter. It always returns all nearby neutral creeps regardless of who "owns" them.
Returns: {hUnit...} -- Towers or barracks within range.
Returns: {int...} -- Tree IDs (handles) within range. Use GetTreeLocation(id) to get positions.
FindAoELocation(bEnemies, bHeroes, vBaseLocation, nMaxDistanceFromBase, nRadius, fTimeInFuture, nMaxHealth)
Parameters:
bEnemies(bool): Target enemies (true) or allies (false).bHeroes(bool): Target heroes (true) or creeps (false).vBaseLocation(vector): Center of the search area.nMaxDistanceFromBase(int): Search radius fromvBaseLocation.nRadius(int): The AoE ability's effect radius.fTimeInFuture(float): Predict unit positions this many seconds ahead. Use 0 for instant spells.nMaxHealth(int): Only target units below this HP. Pass0for no HP filter (target any HP).
Returns: A table with:
count(int): Number of units that would be hit.targetloc(vector): Optimal point to center the AoE.
-- Find best location for a 300-radius AoE spell on enemy heroes
local result = bot:FindAoELocation(true, true, bot:GetLocation(), 900, 300, 0.5, 0)
if result.count >= 2 then
bot:Action_UseAbilityOnLocation(abilityHandle, result.targetloc)
endParameters:
sName(string): The modifier's internal name (e.g.,"modifier_fountain_aura_buff","modifier_item_blade_mail_reflect").
Returns: bool
Gotcha: The modifier name must be the exact internal name, not the display name. These are typically
modifier_[source]_[effect]format.
Returns: int -- Modifier index for use with other modifier functions. Returns -1 if not found.
Parameters:
nIndex(int): Modifier index fromGetModifierByName()or iteration. Not the modifier name string.
Returns: float -- Seconds remaining. Returns -1 for permanent modifiers.
-- Check remaining duration of a specific debuff
local modIndex = bot:GetModifierByName("modifier_crystal_maiden_frostbite")
if modIndex ~= -1 then
local remaining = bot:GetModifierRemainingDuration(modIndex)
-- remaining seconds of Frostbite
end| Function | Returns | Description |
|---|---|---|
NumModifiers() |
int |
Total number of active modifiers |
GetModifierList() |
{string...} |
Table of all modifier names |
GetModifierName(nIndex) |
string |
Modifier name by index |
GetModifierStackCount(nIndex) |
int |
Stack count (e.g., Flesh Heap stacks) |
GetModifierSourceAbility(nIndex) |
hAbility |
The ability that applied this modifier |
GetModifierAuxiliaryUnits(nIndex) |
{hUnit...} |
Units associated with the modifier |
Returns: A table with:
time(float):GameTime()timestamp when the ping was placed.location(vector): World position of the ping.normal_ping(bool):true= "gather here" ping,false= danger/warning ping (X mark).player_id(int): Who placed the ping.
Only returns the single most recent ping, not a history. If you need to track multiple pings, you must poll this function and store results yourself.
local ping = bot:GetMostRecentPing()
if ping.normal_ping and GameTime() - ping.time < 5.0 then
-- teammate pinged "gather here" within the last 5 seconds
bot:Action_MoveToLocation(ping.location)
endReturns: {table...} -- Tracking projectiles heading toward this unit, each containing:
location(vector): Current projectile position.caster(hUnit): Who fired it.playerid(int): Caster's player ID.ability(hAbility): Source ability/item.is_dodgeable(bool): Can this be dodged by going invis/invulnerable?is_attack(bool): True if it's a basic attack projectile, false if ability.
local projectiles = bot:GetIncomingTrackingProjectiles()
for _, proj in ipairs(projectiles) do
if proj.is_dodgeable and not proj.is_attack then
-- incoming dodgeable spell projectile -- consider using Manta/BKB
end
end| Function | Returns | Description |
|---|---|---|
GetAnimActivity() |
int |
Current animation (ACTIVITY_* constant) |
GetAnimCycle() |
float |
Animation progress 0.0 to 1.0 |
IsChanneling() |
bool |
Currently channeling an ability or item? |
IsUsingAbility() |
bool |
Using any ability (casting, channeling, etc.)? |
IsCastingAbility() |
bool |
In the cast animation specifically? |
GetCurrentActiveAbility() |
hAbility |
The ability currently being cast/channeled (nil if none) |
| Function | Returns | Description |
|---|---|---|
GetActiveMode() |
int |
Current bot mode (BOT_MODE_*) |
GetActiveModeDesire() |
float |
Desire value of the current active mode (0.0-1.0) |
GetAssignedLane() |
int |
Lane assigned to this bot (LANE_TOP, LANE_MID, LANE_BOT) |
Parameters:
sName(string): Internal ability name (e.g.,"axe_berserkers_call").
Returns: hAbility or nil -- Ability handle if the hero has this ability, nil otherwise.
Parameters:
nSlot(int): Ability slot index (0-23). Slots 0-5 are usually regular abilities, 10+ are talents.
Returns: hAbility or nil
Parameters:
nSlot(int): Inventory slot index.- 0-5: Main inventory.
- 6-8: Backpack.
- 9-14: Stash.
- 15: TP scroll slot.
- 16: Neutral item slot.
Returns: hItem or nil
Parameters:
sName(string): Internal item name (e.g.,"item_blink").
Returns: int -- Slot index where the item is found. Returns -1 if not in inventory.
Returns: int -- ITEM_SLOT_TYPE_MAIN, ITEM_SLOT_TYPE_BACKPACK, or ITEM_SLOT_TYPE_STASH.
| Function | Returns | Description |
|---|---|---|
SetTarget(hUnit) |
-- | Store a temporary target reference on this bot |
GetTarget() |
hUnit |
Retrieve the stored target |
GetAbilityTarget() |
hUnit |
The unit targeted by the current ability being cast |
SetNextItemPurchaseValue(nGold) |
-- | Set gold goal for shop proximity logic (bot will walk to shop when gold is near this) |
GetNextItemPurchaseValue() |
int |
Get the current purchase gold goal |
All functions in this section are called on an ability or item handle: hAbility:FunctionName(...).
| Function | Returns | Description |
|---|---|---|
GetName() |
string |
Internal ability/item name |
GetLevel() |
int |
Current level (0 = not yet learned) |
GetMaxLevel() |
int |
Maximum level |
GetCaster() |
hUnit |
The unit that owns this ability |
| Function | Returns | Description |
|---|---|---|
IsPassive() |
bool |
Passive ability (no activation)? |
IsToggle() |
bool |
Can be toggled on/off? |
IsItem() |
bool |
Is this an item (vs an ability)? |
IsUltimate() |
bool |
Is this the hero's ultimate? |
IsHidden() |
bool |
Currently hidden/unavailable? |
IsTrained() |
bool |
Has been leveled at least once? |
IsStealable() |
bool |
Can Rubick steal this? |
IsStolen() |
bool |
Is this a stolen ability (Rubick)? |
IsActivated() |
bool |
Is currently active (e.g., toggled on)? |
IsAttributeBonus() |
bool |
Is the old-style attribute bonus ability? |
ProcsMagicStick() |
bool |
Will casting this trigger enemy Magic Stick/Wand charges? |
Returns: bool -- True for talent abilities (typically in slots 10+).
for i = 0, 23 do
local ability = bot:GetAbilityInSlot(i)
if ability ~= nil and ability:IsTalent() and ability:IsTrained() then
-- this talent has been learned
end
endReturns: bool -- True if the ability is not on cooldown AND the caster has enough mana. This is the primary "can I use this right now?" check.
Returns: bool -- True if the ability is off cooldown (ignores mana).
Returns: float -- Seconds of cooldown remaining.
Enemy abilities always return 0. You cannot see enemy cooldowns through the API. This function only works reliably for your own abilities and allied abilities.
| Function | Returns | Description |
|---|---|---|
GetCastRange() |
int |
Maximum cast distance in units |
GetCastPoint() |
float |
Cast animation time in seconds (delay before the spell fires) |
GetChannelTime() |
float |
Channel duration in seconds (0 for non-channeled) |
GetManaCost() |
int |
Mana cost to cast |
GetChannelledManaCostPerSecond() |
int |
Ongoing mana cost per second during channeling |
IsOwnersManaEnough() |
bool |
Does the caster have enough mana? |
IsInAbilityPhase() |
bool |
Currently in the cast animation (before spell fires)? |
IsChanneling() |
bool |
Currently channeling? |
| Function | Returns | Description |
|---|---|---|
GetAbilityDamage() |
int |
Base damage of the ability at current level |
GetDamageType() |
int |
DAMAGE_TYPE_PHYSICAL, DAMAGE_TYPE_MAGICAL, or DAMAGE_TYPE_PURE |
GetDuration() |
float |
Primary effect duration |
GetAOERadius() |
int |
Area of effect radius (0 for single-target) |
GetEstimatedDamageToTarget(hTarget, fDur, nTypes) |
float |
Predicted damage to a specific target |
Returns: int -- Bitfield of behavior flags.
Critical: This returns a bitfield, NOT a single enum value. You must use bitwise AND to check individual flags.
local behavior = ability:GetBehavior()
-- Check if ability is channeled
if bit.band(behavior, ABILITY_BEHAVIOR_CHANNELLED) ~= 0 then
-- this is a channeled ability
end
-- Check if it's a no-target ability
if bit.band(behavior, ABILITY_BEHAVIOR_NO_TARGET) ~= 0 then
bot:Action_UseAbility(ability)
end
-- Check if it's a point-target ability
if bit.band(behavior, ABILITY_BEHAVIOR_POINT) ~= 0 then
bot:Action_UseAbilityOnLocation(ability, targetLoc)
end
-- Check if it targets units
if bit.band(behavior, ABILITY_BEHAVIOR_UNIT_TARGET) ~= 0 then
bot:Action_UseAbilityOnEntity(ability, targetUnit)
end| Function | Returns | Description |
|---|---|---|
GetTargetTeam() |
int |
Target team flags bitfield (ABILITY_TARGET_TEAM_*) |
GetTargetType() |
int |
Target type flags bitfield (ABILITY_TARGET_TYPE_*) |
GetTargetFlags() |
int |
Target flag conditions bitfield (ABILITY_TARGET_FLAG_*) |
| Function | Returns | Description |
|---|---|---|
CanAbilityBeUpgraded() |
bool |
Can be leveled right now? (checks hero level requirement + available points) |
GetHeroLevelRequiredToUpgrade() |
int |
Minimum hero level needed to put a point in this ability |
| Function | Returns | Description |
|---|---|---|
GetCurrentCharges() |
int |
Current charge count (e.g., Magic Wand charges, Drum charges) |
GetInitialCharges() |
int |
Charges the item starts with |
GetSecondaryCharges() |
int |
Secondary charge pool (some abilities have two charge systems) |
| Function | Returns | Description |
|---|---|---|
GetAutoCastState() |
bool |
Is autocast currently enabled? |
GetToggleState() |
bool |
Is the toggle currently on? |
ToggleAutoCast() |
-- | Toggle autocast on/off |
Parameters:
sKey(string): The key name fromnpc_abilities.txtKeyValues data. Must match exactly (case-sensitive).
Returns: int or float -- The value at the current ability level.
-- Read a custom ability value
local stunDuration = ability:GetSpecialValueFloat("stun_duration")
local bonusDamage = ability:GetSpecialValueInt("bonus_damage")The key must match the exact string in the KV data. A typo will silently return 0. Double-check ability data files when using this.
| Function | Returns | Description |
|---|---|---|
CanBeDisassembled() |
bool |
Can this item be disassembled into components? |
IsCombineLocked() |
bool |
Is auto-combining locked for this item? |
GetPowerTreadsStat() |
int |
Current Power Treads attribute (ATTRIBUTE_STRENGTH, ATTRIBUTE_AGILITY, ATTRIBUTE_INTELLECT) |
Actions control what a bot physically does in the game world. Understanding the three action modes is critical for correct bot behavior.
Every action function exists in three variants:
Action_*-- CLEARS the entire action queue and sets this as the new (and only) action.ActionPush_*-- INSERTS at the front of the queue. The current action is paused and resumes after the pushed action completes.ActionQueue_*-- APPENDS to the end of the queue. Executes after all currently queued actions finish.
-- These have very different effects:
bot:Action_MoveToLocation(locA) -- STOP everything, move to A
bot:ActionQueue_MoveToLocation(locB) -- After reaching A, move to B
bot:ActionPush_AttackUnit(enemy, true) -- PAUSE moving to A, attack enemy once, then resume moving to AParameters:
bStop(bool):true-- Issues an immediate halt command (unit stops moving/attacking instantly).false-- Just clears the queue silently (unit finishes current action frame, then idles).
bot:Action_MoveToLocation(vLocation) -- Move via pathfinding (navigates around terrain)
bot:Action_MoveDirectly(vLocation) -- Move in a straight line (CAN GET STUCK on terrain/trees!)
bot:Action_MovePath(tWaypoints) -- Move through a table of waypoints in order
bot:Action_MoveToUnit(hUnit) -- Follow a unit continuously
bot:Action_AttackMove(vLocation) -- Move toward location, attacking anything encountered
MoveToLocationvsMoveDirectly: Always preferMoveToLocationunless you have a specific reason to walk in a straight line.MoveDirectlydoes not pathfind and the bot WILL get stuck on cliffs, trees, and buildings.
Parameters:
hUnit(hUnit): Target to attack.bOnce(bool):true-- Attack exactly once, then stop.false-- Keep attacking until the target dies or moves out of range.
-- Last-hit a creep (single attack)
bot:Action_AttackUnit(lowHPCreep, true)
-- Commit to fighting a hero
bot:Action_AttackUnit(enemy, false)bot:Action_UseAbility(hAbility) -- No-target ability (e.g., Axe Berserker's Call)
bot:Action_UseAbilityOnEntity(hAbility, hTarget) -- Unit-target ability (e.g., Lion Hex)
bot:Action_UseAbilityOnLocation(hAbility, vLocation) -- Point-target ability (e.g., Lina LSA)
bot:Action_UseAbilityOnTree(hAbility, iTree) -- Tree-target ability (e.g., Tango, Timberchain)bot:Action_PickUpRune(nRuneLoc) -- Pick up a rune at the specified rune location constant
bot:Action_PickUpItem(hItem) -- Pick up a dropped item
bot:Action_DropItem(hItem, vLocation) -- Drop an item at a location
bot:Action_UseShrine(hShrine) -- Use a shrine
bot:Action_Delay(fDelay) -- Wait for fDelay seconds (do nothing)Immediate actions execute instantly without touching the action queue. They're used for economy, UI, and inventory operations.
Parameters:
sItemName(string): Internal item name (e.g.,"item_blink").
Returns: int -- One of the PURCHASE_ITEM_* constants:
PURCHASE_ITEM_SUCCESS-- Purchased successfully.PURCHASE_ITEM_INSUFFICIENT_GOLD-- Not enough gold.PURCHASE_ITEM_NOT_AT_HOME_SHOP-- Must be at base shop.PURCHASE_ITEM_NOT_AT_SECRET_SHOP-- Must be at secret shop.PURCHASE_ITEM_NOT_AT_SIDE_SHOP-- Must be at side shop.PURCHASE_ITEM_OUT_OF_STOCK-- Item is out of stock.PURCHASE_ITEM_INVALID_ITEM_NAME-- Typo or invalid item name.PURCHASE_ITEM_DISALLOWED_ITEM-- Item is banned or unavailable in this mode.
local result = bot:ActionImmediate_PurchaseItem("item_tango")
if result ~= PURCHASE_ITEM_SUCCESS then
-- handle failure
endSells the item. Must be near a shop.
Disassembles the item into its components. Only works on items where CanBeDisassembled() returns true.
Prevents (true) or allows (false) this item from auto-combining into recipes.
Parameters:
nSlot1,nSlot2(int): Inventory slot indices.- 0-5: Main inventory.
- 6-8: Backpack.
- 9-14: Stash.
-- Move an item from backpack (slot 6) to main inventory (slot 0)
bot:ActionImmediate_SwapItems(6, 0)Parameters:
hCourier(hCourier): Courier handle fromGetCourier().nAction(int): Courier action constant (e.g.,COURIER_ACTION_TAKE_AND_TRANSFER_ITEMS).
Buy back from death. Only works when dead and buyback is available.
Activate Glyph of Fortification. Check GetGlyphCooldown() first.
Parameters:
sAbilityName(string): The ability's internal name string (e.g.,"axe_berserkers_call").
Parameters:
sMessage(string): Chat message text.bAllChat(bool):true= all chat (visible to both teams),false= team chat only.
Parameters:
fX,fY(float): World coordinates to ping.bNormalPing(bool):true= "gather here" ping,false= danger/warning ping (X mark).
bot:GetCurrentActionType() -- Returns BOT_ACTION_TYPE_* constant for the current action
bot:NumQueuedActions() -- Number of actions waiting in the queue
bot:GetQueuedActionType(n) -- BOT_ACTION_TYPE_* for the Nth queued action (0-indexed)TEAM_RADIANT TEAM_DIRE TEAM_NEUTRAL TEAM_NONE
LANE_NONE = 0 LANE_TOP = 1 LANE_MID = 2 LANE_BOT = 3
GAMEMODE_AP -- All Pick
GAMEMODE_CM -- Captain's Mode
GAMEMODE_RD -- Random Draft
GAMEMODE_SD -- Single Draft
GAMEMODE_AR -- All Random
GAMEMODE_REVERSE_CM -- Reverse Captain's Mode
GAMEMODE_MO -- Mid Only
GAMEMODE_CD -- Captain's Draft
GAMEMODE_ABILITY_DRAFT
GAMEMODE_ARDM -- All Random Deathmatch (value: 20)
GAMEMODE_1V1MID
GAMEMODE_ALL_DRAFT
GAMEMODE_TURBO -- Turbo Mode (value: 23)
BOT_MODE_NONE BOT_MODE_LANING BOT_MODE_ATTACK
BOT_MODE_ROAM BOT_MODE_RETREAT BOT_MODE_SECRET_SHOP
BOT_MODE_SIDE_SHOP BOT_MODE_PUSH_TOWER_TOP BOT_MODE_PUSH_TOWER_MID
BOT_MODE_PUSH_TOWER_BOT BOT_MODE_DEFEND_TOWER_TOP BOT_MODE_DEFEND_TOWER_MID
BOT_MODE_DEFEND_TOWER_BOT BOT_MODE_ASSEMBLE BOT_MODE_TEAM_ROAM
BOT_MODE_FARM BOT_MODE_DEFEND_ALLY BOT_MODE_EVASIVE_MANEUVERS
BOT_MODE_ROSHAN BOT_MODE_ITEM BOT_MODE_WARD
| Constant | Value | Alias |
|---|---|---|
BOT_MODE_DESIRE_NONE |
0.0 | BOT_ACTION_DESIRE_NONE |
BOT_MODE_DESIRE_VERYLOW |
0.1 | BOT_ACTION_DESIRE_VERYLOW |
BOT_MODE_DESIRE_LOW |
0.25 | BOT_ACTION_DESIRE_LOW |
BOT_MODE_DESIRE_MODERATE |
0.4-0.5 | BOT_ACTION_DESIRE_MODERATE |
BOT_MODE_DESIRE_HIGH |
0.6-0.75 | BOT_ACTION_DESIRE_HIGH |
BOT_MODE_DESIRE_VERYHIGH |
0.8-0.9 | BOT_ACTION_DESIRE_VERYHIGH |
BOT_MODE_DESIRE_ABSOLUTE |
1.0 | BOT_ACTION_DESIRE_ABSOLUTE |
BOT_ACTION_TYPE_NONE BOT_ACTION_TYPE_IDLE
BOT_ACTION_TYPE_MOVE_TO BOT_ACTION_TYPE_MOVE_TO_DIRECTLY
BOT_ACTION_TYPE_ATTACK BOT_ACTION_TYPE_ATTACKMOVE
BOT_ACTION_TYPE_USE_ABILITY BOT_ACTION_TYPE_PICK_UP_RUNE
BOT_ACTION_TYPE_PICK_UP_ITEM BOT_ACTION_TYPE_DROP_ITEM
BOT_ACTION_TYPE_SHRINE BOT_ACTION_TYPE_DELAY
DAMAGE_TYPE_PHYSICAL DAMAGE_TYPE_MAGICAL DAMAGE_TYPE_PURE DAMAGE_TYPE_ALL
UNIT_LIST_ALL UNIT_LIST_ALLIES UNIT_LIST_ALLIED_HEROES
UNIT_LIST_ALLIED_CREEPS UNIT_LIST_ALLIED_WARDS UNIT_LIST_ALLIED_BUILDINGS
UNIT_LIST_ENEMIES UNIT_LIST_ENEMY_HEROES UNIT_LIST_ENEMY_CREEPS
UNIT_LIST_ENEMY_WARDS UNIT_LIST_ENEMY_BUILDINGS UNIT_LIST_NEUTRAL_CREEPS
GAME_STATE_INIT GAME_STATE_WAIT_FOR_PLAYERS_TO_LOAD
GAME_STATE_HERO_SELECTION GAME_STATE_STRATEGY_TIME
GAME_STATE_PRE_GAME GAME_STATE_GAME_IN_PROGRESS
GAME_STATE_POST_GAME GAME_STATE_DISCONNECT
GAME_STATE_WAIT_FOR_MAP_TO_LOAD
-- Towers
TOWER_TOP_1 TOWER_TOP_2 TOWER_TOP_3
TOWER_MID_1 TOWER_MID_2 TOWER_MID_3
TOWER_BOT_1 TOWER_BOT_2 TOWER_BOT_3
TOWER_BASE_1 TOWER_BASE_2
-- Barracks
BARRACKS_TOP_MELEE BARRACKS_TOP_RANGED
BARRACKS_MID_MELEE BARRACKS_MID_RANGED
BARRACKS_BOT_MELEE BARRACKS_BOT_RANGED
-- Shops
SHOP_HOME SHOP_SIDE SHOP_SECRET SHOP_SIDE2 SHOP_SECRET2
-- Types
RUNE_INVALID RUNE_DOUBLEDAMAGE RUNE_HASTE RUNE_ILLUSION
RUNE_INVISIBILITY RUNE_REGENERATION RUNE_BOUNTY RUNE_ARCANE
-- Status
RUNE_STATUS_UNKNOWN RUNE_STATUS_AVAILABLE RUNE_STATUS_MISSING
-- Locations
RUNE_POWERUP_1 RUNE_POWERUP_2
RUNE_BOUNTY_1 RUNE_BOUNTY_2 RUNE_BOUNTY_3 RUNE_BOUNTY_4
PURCHASE_ITEM_SUCCESS PURCHASE_ITEM_OUT_OF_STOCK
PURCHASE_ITEM_DISALLOWED_ITEM PURCHASE_ITEM_INSUFFICIENT_GOLD
PURCHASE_ITEM_NOT_AT_HOME_SHOP PURCHASE_ITEM_NOT_AT_SIDE_SHOP
PURCHASE_ITEM_NOT_AT_SECRET_SHOP PURCHASE_ITEM_INVALID_ITEM_NAME
DIFFICULTY_INVALID DIFFICULTY_PASSIVE DIFFICULTY_EASY
DIFFICULTY_MEDIUM DIFFICULTY_HARD DIFFICULTY_UNFAIR
ATTRIBUTE_INVALID ATTRIBUTE_STRENGTH ATTRIBUTE_AGILITY ATTRIBUTE_INTELLECT
ABILITY_BEHAVIOR_HIDDEN ABILITY_BEHAVIOR_PASSIVE
ABILITY_BEHAVIOR_NO_TARGET ABILITY_BEHAVIOR_UNIT_TARGET
ABILITY_BEHAVIOR_POINT ABILITY_BEHAVIOR_AOE
ABILITY_BEHAVIOR_CHANNELLED ABILITY_BEHAVIOR_NOT_LEARNABLE
ABILITY_BEHAVIOR_ITEM ABILITY_BEHAVIOR_TOGGLE
ABILITY_BEHAVIOR_AUTOCAST ABILITY_BEHAVIOR_IGNORE_BACKSWING
Remember: These are bitfield flags. Always use
bit.band()to test them, never==.