Dota Bot Scripting: Difference between revisions
Line 426: | Line 426: | ||
''[https://developer.valvesoftware.com/wiki/Dota_Bot_Scripting#Item_Purchase_Results int]'' '''Action_PurchaseItem '''( ''sItemName'' ) | ''[https://developer.valvesoftware.com/wiki/Dota_Bot_Scripting#Item_Purchase_Results int]'' '''Action_PurchaseItem '''( ''sItemName'' ) | ||
:<small>Command a bot to purchase the specified item | :<small>Command a bot to purchase the specified item. Item names can be found [https://developer.valvesoftware.com/wiki/Dota_2_Workshop_Tools/Scripting/Built-In_Item_Names here].</small> | ||
'''Action_SellItem'''( ''hItem'' ) | '''Action_SellItem'''( ''hItem'' ) |
Revision as of 14:03, 2 February 2017
Overview
Bot scripting in Dota is done via lua scripting. This is done at the server level, so there's no need to do things like examine screen pixels or simulate mouse clicks; instead scripts can query the game state and issue orders directly to units. Scripts have full access to all the entity locations, cooldowns, mana values, etc that a player on that team would expect to. The API is restricted such that scripts can't cheat -- units in FoW can't be queried, commands can't be issued to units the script doesn't control, etc.
There is a dev subforum for bot scripting now.
In addition to lua scripting, the underlying C++ bot code still exists, and scripts can decide how much or little of the underlying bot structure to use.
Bots are organized into three levels of evaluation and decisionmaking:
Team Level
This is code that determines how much the overall team wants to push each lane, defend each lane, farm each lane, or kill Roshan. These desires exist independent of the state of any of the bots. They are not authoritative; that is, they do not dictate any actions taken by any of the bots. They are instead just desires that the bots can use for decisionmaking.
Mode Level
Modes are the high-level desires that individual bots are constantly evaluating, with the highest-scoring mode being their currently active mode. Examples of modes are laning, trying to kill a unit, farming, retreating, and pushing a tower.
Action Level
Actions are the individual things that bots are actively doing on a moment-to-moment basis. These loosely correspond to mouse clicks or button presses -- things like moving to a location, or attacking a unit, or using an ability, or purchasing an item.
The overall flow is that the team level is providing top-level guidance on the current strategy of the team. Each bot is then evaluating their desire score for each of its modes, which are taking into account both the team-level desires as well as bot-level desires. The highest scoring mode becomes the active mode, which is solely responsible for issuing actions for the bot to perform.
Directory Structure
All in-development bot scripts live in the game/dota/scripts/vscripts/bots directory within your Dota 2 install. When you upload your bot script to the workshop, it will upload the contents of this directory. Downloaded scripts live in their own location within your Steam install.
The bot scripting API is structured such that there are multiple elements that can be independently implemented by bot scripts. What logic is overriden is determined by which functions you implement and the files in which they are implemented.
Each of the following scripting elements has its own script scope.
Complete takeover
If you'd like to completely take over control of a hero, you can implement a Think() function in a file called bot_generic.lua, which is called every frame in lieu of the normal bot thinking code. This will completely take over all bots -- no team-level or mode-level thinking will happen. You will be responsible for issuing all action-level commands to all bots. If you'd like to just take over a specific hero's bot, for example Lina, you can implement a Think() function in a file called bot_lina.lua.
Bots that have been completely taken over still respect the difficulty modifiers (see Appendix B), and still calculate their estimated damage.
Mode Override
If you'd like to work within the existing mode architecture but override the logic for mode desire and behavior, for example the Laning mode, you can implement the following functions in a mode_laning_generic.lua file:
- GetDesire() - Called every ~300ms, and needs to return a floating-point value between 0 and 1 that indicates how much this mode wants to be the active mode.
- OnStart() - Called when a mode takes control as the active mode.
- OnEnd() - Called when a mode relinquishes control to another active mode.
- Think() - Called every frame while this is the active mode. Responsible for issuing actions for the bot to take.
You can additionally just override the mode logic for a specific hero, such as Lina, with a mode_laning_lina.lua file. Please see Appendix A for implementation details if you'd like to chain calls from a hero-specific mode override back to a generic mode override.
The list of valid bot modes to override are:
- laning
- attack
- roam
- retreat
- secret_shop
- side_shop
- rune
- push_tower_top
- push_tower_mid
- push_tower_bot
- defend_tower_top
- defend_tower_mid
- defend_tower_bottom
- assemble
- team_roam
- farm
- defend_ally
- evasive_maneuvers
- roshan
- item
- ward
Ability and Item usage
If you'd like to just override decisionmaking around ability and item usage, you can implement the following functions in an ability_item_usage_generic.lua file:
- ItemUsageThink() - Called every frame. Responsible for issuing item usage actions.
- AbilityUsageThink() - Called every frame. Responsible for issuing ability usage actions.
- CourierUsageThink() - Called every frame. Responsible for issuing commands to the courier.
- BuybackUsageThink() - Called every frame. Responsible for issuing a command to buyback.
- AbilityLevelUpThink() - Called every frame. Responsible for managing ability leveling.
If any of these functions are not implemented, it will fall back to the default C++ implementation.
You can additionally just override the ability/item usage logic for a single hero, such as Lina, with an ability_item_usage_lina.lua file. Please see Appendix A for implementation details if you'd like to chain calls from a hero-specific item/ability implementation back to a generic item/ability implementation.
Minion Control
If you would like to override minions, which are illusions, summoned units, dominated units, etc. Basically anything that's under control of your hero. But not couriers. Then you can override the think function inside your hero file.
- MinionThink( hMinionUnit )
This function will be called once for every minion under control by a bot. For example, if you implemented it in bot_beastmaster.lua, it would get called once each for your boar and hawk when they were summoned passing in the handle to the unit as hMinionUnit. Action commands that are usable on your hero are usable on the passed-in hMinionUnit.
Item Purchasing
If you'd like to just override decisionmaking around item purchasing, you can implement the following function in an item_purchase_generic.lua file:
- ItemPurchaseThink() - Called every frame. Responsible for purchasing items.
You can additionally just override the item purchasing logic for a single hero, such as Lina, with an item_purchase_lina.lua file.
Team Level Desires
If you'd like to supply team-level desires, you can implement the following functions in a team_desires.lua file:
- UpdatePushLaneDesires() - Called every frame. Returns floating point values between 0 and 1 that represent the desires for pushing the top, middle, and bottom lanes, respectively.
- UpdateDefendLaneDesires() - Called every frame. Returns floating point values between 0 and 1 that represent the desires for defending the top, middle, and bottom lanes, respectively.
- UpdateFarmLaneDesires() - Called every frame. Returns floating point values between 0 and 1 that represent the desires for farming the top, middle, and bottom lanes, respectively.
- UpdateRoamDesire() - Called every frame. Returns a floating point value between 0 and 1 and a unit handle that represents the desire for someone to roam and gank a specified target.
- UpdateRoshanDesire() - Called every frame. Returns a floating point value between 0 and 1 that represents the desire for the team to kill Roshan.
If any of these functions are not implemented, it will fall back to the default C++ implementation.
Hero Selection
If you'd like to handle hero picking and lane assignment, you can implement the following functions in a hero_selection.lua file:
- Think() - Called every frame. Responsible for selecting heroes for bots.
- UpdateLaneAssignments() - Called every frame prior to the game starting. Returns ten PlayerID-Lane pairs.
Uploading scripts to the Workshop
Scripts can be uploaded to the workshop using the Workshop Tools DLC. You will need to make sure that this option is selected in the DLC section of Dota 2 in Steam.
Once installed, when launching Dota 2 you will see a "Launch Dota 2 - Tools" option. This will launch the workshop tools, which now has a section for uploading your bot scripts. This will upload the entire contents of your scripts/bots directory, under a specific name and description. The description can be updated, but the title cannot! Once uploaded, your scripts will show up to all Dota 2 users in the dialog that appears when they select Browse On Workshop when selecting bots in a private lobby. They will be able to upvote and subscribe to your bot scripting, and select it for their games.
API Reference
There are few useful files located in the game\dota\scripts\npc folder inside your Dota 2 install directory:
- npc_abilities.txt - all the abilities in the game, and their parameters
- npc_heroes.txt - all the heroes in the game
- npc_units.txt - all the non-hero units in the game
Note: In the following documentation, hUnit indicates a handle to a unit, and hAbility indicates a handle to an ability.
Global
hUnit GetBot()
- Returns a handle to the bot on which the script is currently being run (if applicable).
int GetTeam()
- Returns the team for which the script is currently being run. If it's being run on a bot, returns the team of that bot.
{int, ...} GetTeamPlayers( nTeam )
- Returns a table of the Player IDs on the specified team
hUnit GetTeamMember( nPlayerNumber )
- Returns a handle to the Nth player on the team.
bool IsTeamPlayer( nPlayerID )
- Returns whether the player is on Radiant or Dire
bool IsPlayerBot( nPlayerID )
- Returns whether the specified playerID is a bot.
int GetTeamForPlayer( nPlayerID )
- Returns the team for the specified playerID
{ hUnit, ... } GetUnitList( nUnitType )
- Returns a list of units matching the specified unit type Please keep in mind performance considerations when using GetUnitList(). The function itself is reasonably fast because it will build the lists on-demand and no more than once per frame, but the lists can be long and performing logic on all units (or even all creeps) can easily get pretty slow.
float DotaTime()
- Returns the game time. Matches game clock. Pauses with game pause.
float GameTime()
- Returns the time since the hero picking phase started. Pauses with game pause.
float RealTime()
- Returns the real-world time since the app has started. Does not pause with game pause.
float GetUnitToUnitDistance( hUnit1, hUnit2 )
- Returns the distance between two units.
float GetUnitToLocationDistance( hUnit, Vector )
- Returns the distance between a unit and a location.
{ float, float, float, float } GetWorldBounds()
- Returns a table containing the min X, min Y, max X, and max Y bounds of the world.
bool IsLocationPassable( vLocation )
- Returns whether the specified location is passable.
int GetHeightLevel( vLocation )
- Returns the height value (1 through 5) of the specified location.
{ { string, vector }, ... } GetNeutralSpawners()
- Returns a table containing a list of camp-type and location pairs. Camp types are one of "basic_N", "ancient_N", "basic_enemy_N", "ancient_enemy_N", where N counts up from 0.
int GetItemCost( sItemName )
- Returns the cost of the specified item.
bool IsItemPurchasedFromSecretShop( sItemName )
- Returns if the specified item is purchased from the secret shops.
bool IsItemPurchasedFromSideShop( sItemName )
- Returns if the specified item can be purchased from the side shops.
int GetItemStockCount( sItemName )
- Returns the current stock count of the specified item.
{ hItem, vLocation, ...} GetDroppedItemList()
- Returns a table that alternates between script handles and locations of visible items that have been dropped on the ground.
float GetPushLaneDesire( nLane )
- Returns the team's current desire to push the specified lane.
float GetDefendLaneDesire( nLane )
- Returns the team's current desire to defend the specified lane.
float GetFarmLaneDesire( nLane )
- Returns the team's current desire to farm the specified lane.
float GetRoamDesire()
- Returns the team's current desire to roam to a target.
hUnit GetRoamTarget()
- Returns the team's current roam target.
float GetRoshanDesire()
- Returns the team's current desire to kill Roshan.
int GetGameState()
- Returns the current game state
float GetGameStateTimeRemaining()
- Returns how much time is remaining in the current game state, if applicable.
int GetGameMode()
- Returns the current game mode
int GetHeroPickState()
- Returns the current hero pick state
bool IsPlayerInHeroSelectionControl( nPlayerID )
- Returns whether the specified player is in selection control when picking a hero.
SelectHero( nPlayerID, sHeroName )
- Selects a hero for the specified player.
string GetSelectedHeroName( nPlayerID )
- Returns the name of the hero the specified player has selected.
bool IsInCMBanPhase()
- Returns whether we're in a Captains Mode ban phase.
bool IsInCMPickPhase()
- Returns whether we're in a Captains Mode pick phase.
float GetCMPhaseTimeRemaining()
- Gets the time remaining in the current Captains Mode phase.
int GetCMCaptain()
- Gets the Player ID of the Captains Mode Captain.
SetCMCaptain( nPlayerID )
- Gets the Captains Mode Captain to the specified Player ID.
bool IsCMBannedHero( sHeroName )
- Returns whether the specified hero has been banned in a Captains Mode game.
bool IsCMPickedHero( nTeam, sHeroName )
- Returns whether the specified hero has been picked in a Captains Mode game.
CMBanHero( sHeroName )
- Bans the specified hero in a Captains Mode game.
CMPickHero( sHeroName )
- Picks the specified hero in a Captains Mode game.
int RandomInt( nMin, nMax )
- Returns a random integer between nMin and nMax, inclusive.
float RandomFloat( fMin, fMax )
- Returns a random float between nMin and nMax, inclusive.
vector RandomYawVector( fLength )
- Returns a vector of fLength pointing in a random direction in the X/Y axis.
bool RollPercentage( nChance )
- Rolls a numbmer from 1 to 100 and returns whether it is less than or equal to the specified number.
float Min( fOption1, fOption2 )
- Returns the smaller of fOption1 and fOption2.
float Max( fOption1, fOption2 )
- Returns the larger of fOption1 and fOption2.
float Clamp( fValue, fMin, fMax )
- Returns fValue clamped within the bounds of fMin and fMax.
float RemapVal( fValue, fFromMin, fFromMax, fToMin, fToMax )
- Returns fValue linearly remapped onto fFrom to fTo.
float RemapValClamped( fValue, fFromMin, fFromMax, fToMin, fToMax )
- Returns fValue linearly remapped onto fFrom to fTo, while also clamping within their bounds.
int GetUnitPotentialValue( hUnit, vLocation, fRadius )
- Gets the 0-255 potential location value of a hero at the specified location and radius.
bool IsCourierAvailable()
- Returns if the courier is available to use.
int GetNumCouriers()
- Returns the number of team couriers
hCourier GetCourier( nCourier )
- Returns a handle to the specified courier (zero based index)
int GetCourierState( hCourier )
- Returns the current state of the specified courier possible states
vector GetTreeLocation( nTree )
- Returns the specified tree location.
vector GetRuneSpawnLocaion( nRuneLoc )
- Returns the location of the specified rune spawner.
vector GetShopLocaion( nTeam, nShop )
- Returns the location of the specified shop.
float GetTimeOfDay()
- Returns the time of day -- 0.0 is midnight, 0.5 is noon.
hUnit GetTower( nTeam, nTower )
- Returns the specified tower.
hUnit GetBarracks( nTeam, nBarracks )
- Returns the specified barracks.
hUnit GetShrine( nTeam, nShrine )
- Returns the specified shrine.
hUnit GetAncient( nTeam )
- Returns the specified ancient.
float GetRoshanKillTime()
- Get the last time that Roshan was killed.
float GetLaneFrontAmount( nTeam, nLane, bIgnoreTowers )
- Return the lane front amount (0.0 - 1.0) of the specified team's creeps along the specified lane. Optionally can ignore towers.
vector GetLaneFrontLocation( nTeam, nLane, fDeltaFromFront )
- Returns the location of the lane front for the specified team and lane. Always ignores towers. Has a third parameter for a distance delta from the front.
vector GetLocationAlongLane( nLane, fAmount )
- Returns the location the specified amount (0.0 - 1.0) along the specified lane..
nTeam GetOpposingTeam( nLane, fAmount )
- Returns the opposing Team ID.
bool IsHeroAlive( nPlayerID )
- Returns whether the specified PlayerID's hero is alive.
int GetHeroLevel( nPlayerID )
- Returns the specified PlayerID's hero's level.
int GetHeroKills( nPlayerID )
- Returns the specified PlayerID's hero's kill count.
int GetHeroDeaths( nPlayerID )
- Returns the specified PlayerID's hero's death count.
int GetHeroAssists( nPlayerID )
- Returns the specified PlayerID's hero's assists count.
{location, time} GetHeroLastSeenInfo( nPlayerID )
- Returns a table containing a location member with the last seen location of the PlayerID's hero and a time member with how it's been since they've been seen..
int GetRuneType( nRuneLoc )
- Returns the rune type of the rune at the specified location, if known.
float GetShrineCooldown( nShrine )
- Returns the current cooldown of the specified Shrine.
bool IsShrineHealing( nShrine )
- Returns whether the specified shrine is currently healing.
DebugDrawLine( vStart, vEnd, nRed, nGreen, nBlue )
- Draws a line from vStar to vEnd in the specified color for one frame.
DebugDrawCircle( vCenter, fRadius, nRed, nGreen, nBlue )
- Draws a circle at vCenter with radius fRadius in the specified color for one frame.
DebugDrawText( fScreenX, fScreenY, sText, nRed, nGreen, nBlue )
- Draws the specified text at fScreenX, fScreenY on the screen in the specified color for one frame.
Unit-Scoped
Action_AttackMove( vLocation )
- Tell a unit to attack-move a location.
Action_AttackUnit( hUnit, bOnce )
- Tell a unit to attack a unit with an option bool to stop after one attack if true
Action_Buyback()
- Tell a hero to buy back from death
Action_Chat( sMessage, bAllChat )
- Have a bot say something in team chat, bAllChat true to say to all chat instead
Action_ClearActions( bStop )
- Clear action queue and return to idle and optionally stop in place with bStop true
Action_Courier( hCourier, nAction )
- Command the courier specified by hCourier to perform one of the courier Actions
Action_DisassembleItem( hItem )
- Command a bot to disassemble the specified item
Action_DropItem( hItem, vLocation )
- Command a bot to drop the specified item and the provided location
Action_LevelAbility ( sAbilityName )
- Command a bot to level an ability or a talent. Ability and talent names can be found here
Action_MoveToLocation( vLocation )
- Command a bot to move to the specified location, this is not a precision move
Action_MoveToUnit( hUnit )
- Command a bot to move to the specified unit, this will continue to follow the unit
Action_PickUpItem( hItem )
- Command a bot to pick up the specified item
Action_PickUpRune( nRune )
- Command a hero to pick up the rune at the specified rune location
Action_Ping( fXCoord, fYCoord, bNormalPing )
- Command a bot to ping the specified coordinates with bNormalPing setting the ping type
int Action_PurchaseItem ( sItemName )
- Command a bot to purchase the specified item. Item names can be found here.
Action_SellItem( hItem )
- Command a bot to sell the specified item
Action_SetItemCombineLock( hItem, bLocked )
- Command a bot to lock or unlock combining of the specified item
Action_SwapItems( index1, index2 )
- Command a bot to swap the items in index1 and index2 in their inventory. Indices are zero based with 0-5 corresponding to inventory, 6-8 are backpack and 9-15 are stash
Action_UseAbility( hAbility )
- Command a bot to use a non-targeted ability or item
Action_UseAbilityOnEntity( hAbility, hTarget )
- Command a bot to use a unit targeted ability or item on the specified target unit
Action_UseAbilityOnLocation( hAbility, vLocation )
- Command a bot to use a ground targeted ability or item on the specified location
Action_UseAbilityOnTree( hAbility, iTree )
- Command a bot to use a tree targeted ability or item on the specified tree
Action_UseShrine( hShrine )
- Command a bot to use the specified shrine
bool CanBeSeen()
- Check if a unit can currently be seen by your team.
int DistanceFromFountain()
- Gets the unit’s straight-line distance from the team’s fountain (0 is in the fountain).
int DistanceFromSecretShop()
- Gets the unit’s straight-line distance from the closest secret shop (0 is in a secret shop).
int DistanceFromSideShop()
- Gets the unit’s straight-line distance from the closest side shop (0 is in a side shop).
{ int count, vector targetloc } FindAoELocation( bEnemies, bHeroes, vBaseLocation, nMaxDistanceFromBase, nRadius, fTimeInFuture, nMaxHealth)
- Gets the optimal location for AoE to hit the maximum number of units described by the parameters. Returns a table containing the values targetloc that is a vector for the center of the AoE and count that will be equal to the number of units within the AoE that mach the description
int FindItemSlot( sItemName )
- Get the slot the specified item is in. Item names can be found here.
handle GetAbilityByName( sAbilityName )
- Get a handle to the specified ability. Ability names can be found in here
int GetAbilityPoints()
- Get the number of ability points available to this bot
int GetActiveMode()
- Get the bots currently active mode. This may not track modes in complete takeover bots
float GetActiveModeDesire()
- Get the desire of the currently active mode
float GetActualDamage( nDamage, nDamageType )
- Gets damage value after reductions depending on damage type
int GetArmor()
- Get the armor of this unit
int GetAssignedLane()
- Get the assigned lane of this unit
- GetAttackDamage
- GetAttackPoint
- GetAttackRange
- GetAttackSpeed
- GetAttackTarget
- GetAttributeValue
- GetBaseDamage
- GetBaseDamageVariance
- GetBaseMovementSpeed
- GetBoundingRadius
- GetBuybackCooldown
- GetBuybackCost
- GetCourierValue
- GetCurrentActionType List of Action Types
- GetCurrentActiveAbility
- GetCurrentMovementSpeed
- GetDifficulty
- GetEstimatedDamageToTarget
- GetEvasion
- GetExtrapolatedLocation
- GetFacing
- GetGold
- GetGroundHeight
- GetHealth
- GetHealthRegen
- GetItemInSlot
- GetItemSlotType
- GetLastAttackTime
- GetLastSeenLocation
- GetLevel
- GetLocation
- GetMagicResist
- GetMana
- GetManaRegen
- GetMaxHealth
- GetMaxMana
- GetModifier( modifierIndex )
- GetModifierStackCount
- GetMostRecentPing
- GetMovementDirectionStability
- All GetNearby* functions return a sorted list
- GetNearbyBarracks
- GetNearbyCreeps
- GetNearbyHeroes
- GetNearbyShrines
- GetNearbyTowers
- GetNearbyTrees ( radius )
- GetNextItemPurchaseValue
- GetPlayerID
- GetPowerTreadsStat
- GetPrimaryAttribute
- GetRespawnTime
- GetRoshanKillTime
- GetSecondsPerAttack
- GetSlowDuration
- GetSpellAmp
- GetStashValue
- GetStunDuration
- GetTarget
- GetTimeSinceLastSeen
- GetUnitName
- GetVelocity
- HasBlink
- HasBuyback
- HasInvisibility
- HasMinistunOnAttack
- HasModifier( name )
- HasScepter
- HasSilence
- IsAlive
- IsAttackImmune
- IsBlind
- IsBlockDisabled
- IsBot
- IsBuilding
- IsChanneling
- IsCreep
- IsDisarmed
- IsDominated
- IsEvadeDisabled
- IsFort
- IsHero
- IsHexed
- IsIllusion
- IsInvisible - returns if the unit has an "invisibility-causing" modifier on them, NOT whether they're currently visible by the other team. A unit that has used shadow blade but is dusted or under truesight will still return true.
- IsInvulnerable
- IsMagicImmune
- IsMuted
- IsNightmared
- IsRooted
- IsSilenced
- IsSpeciallyDeniable
- IsStunned
- IsTower
- IsUnableToMiss
- IsUsingAbility
- NumModifiers
- SetNextItemPurchaseValue
- SetTarget
- TimeSinceDamagedByAnyHero
- TimeSinceDamagedByCreep
- TimeSinceDamagedByHero
- TimeSinceDamagedByTower
- UsingItemBreaksInvisibility
- WasRecentlyDamagedByAnyHero
- WasRecentlyDamagedByCreep
- WasRecentlyDamagedByHero
- WasRecentlyDamagedByTower
Ability-Scoped
- CanAbilityBeUpgraded
- GetAbilityDamage
- GetAutoCastState
- GetBehavior
- GetCaster
- GetCastPoint
- GetCastRange
- GetChannelledManaCostPerSecond
- GetChannelTime
- GetCooldownTimeRemaining - Only works on yourself and allies
- GetCurrentCharges
- GetDamageType
- GetHeroLevelRequiredToUpgrade
- GetInitialCharges
- GetLevel
- GetManaCost
- GetMaxLevel
- GetName
- GetSecondaryCharges
- GetSpecialValueFloat
- GetSpecialValueInt
- GetTargetFlags
- GetTargetTeam
- GetTargetType
- GetToggleState
- IsActivated
- IsAttributeBonus
- IsChanneling
- IsCooldownReady - Only works on yourself and allies
- IsFullyCastable
- IsHidden
- IsInAbilityPhase
- IsItem
- IsOwnersManaEnough
- IsPassive
- IsStealable
- IsStolen
- IsToggle
- IsTrained
- ProcsMagicStick
- ToggleAutoCast
- Item Only Functions
- CanBeDisassembled
- IsCombineLocked
Bot Difficulties
There are five bot difficulties:
Passive
- Cannot use abilities or items or the courier.
- Will always remain in laning mode.
- When attempting to last hit creeps, their estimation of the best time to land the attack randomly varies by 0.4 seconds.
- When attempting to last hit allied creeps, their estimation of the best time to land the attack randomly varies by 0.2 seconds.
Easy
- Ability and item usage gets a random delay of 0.5 to 1.0 seconds
- Every 8 seconds, ability and item usage is disallowed for 6 seconds.
- Whenever an ability or item is used, ability and item usage is disallowed for 6 seconds.
- When attempting to last hit enemy creeps, their estimation of the best time to land the attack randomly varies by 0.4 seconds.
- When attempting to last hit allied creeps, their estimation of the best time to land the attack randomly varies by 0.2 seconds.
Medium
- Ability and item usage gets a random delay of 0.3 to 0.6 seconds
- Every 10 seconds, ability and item usage is disallowed for 3 seconds.
- Whenever an ability or item is used, ability and item usage is disallowed for 3 seconds.
- When attempting to last hit creeps, their estimation of the best time to land the attack randomly varies by 0.2 seconds.
- When attempting to last hit allied creeps, their estimation of the best time to land the attack randomly varies by 0.1 seconds.
Hard
- Ability and item usage gets a random delay of 0.1 to 0.2 seconds.
Unfair
- Ability and item usage gets a random delay of 0.075 to 0.15 seconds.
- XP and Gold earned gets a 25% bonus.
Debugging
There are a number of commands that help you debug what's happening with bots.
dota_bot_debug_team
Brings up a panel for the specified team (2 is Radiant, 3 is Dire) that displays:
- Team-level desires for pushing/defending/farming lanes, and Roshan.
- Bot names and levels
- Bot current and maximum "power" levels (see Appendex D for how power levels are calculated). Can be disabled with "dota_bot_debug_team_power 0".
- Active modes, and those mode desires.
- All modes for a bot ( individually collapsable), and each of those modes' desires.
- Current bot action along with that action's target if applicable.
- Total bot execution time for the overall team calculations, and each individual bot.
It additionally enables the line-sphere rendering of dota_bot_select_debug for all bots on the specified team.
dota_bot_debug_grid
dota_bot_debug_grid_cycle
dota_bot_debug_minimap
dota_bot_debug_minimap_cycle
Causes a grid to draw on the world or the minimap. The *_cycle variants just cycle through all the values, whereas the other commands just set the mode directly.
- 0 - Off
- 1 - Radiant avoidance
- 2 - Dire avoidance
- 3 - Potential enemy locations for the Radiant
- 4 - Potential enemy locations for the Dire
- 5 - Enemy visibility to the Radiant
- 6 - Enemy visibility to the Dire
- 7 - Height Values
- 8 - Passability
dota_bot_select_debug
Activates the following displays on the under-the-cursor bot:
- The current mode and action of the selected bot.
- A white line-sphere list to it current pathfind.
- A blue line-sphere to its current laning last-hit target.
- A red line-sphere to its current attack target.
dota_bot_select_debug_attack
Displays how much the under-the-cursor bot wants to attack any nearby enemies.
dota_bot_debug_clear
Clears the dota_bot_select_debug and dota_bot_select_debug_attack states of the under-the-cursor bot.
dota_bot_debug_lanes
Shows the path of all of the lanes, along with a sphere at the "lane front".
dota_bot_debug_ward_locations
Shows small yellow spheres at each of ward locations the bots will consider.
Potential Locations
One of the utility functions available to bot scripts is GetUnitPotentialValue(), which returns a value between 0 and 255 that represents an estimation of how likely it is that a hero is within the specified radius of the specified location. This value is based the potential location grid that is updated during bot games.
It works like this:
- When a team loses visibility of that hero, there's a floodfill that starts through the passable areas of the map, moving at the movement speed of that missing hero.
- The intensity of the "potential location" starts out high, and then decreases as the potential location becomes larger and more diffuse.
- This floodfill happens for each enemy hero independently.
This does have some limitations:
- It doesn't take into account heroes that teleport or otherwise have speed/movement bursts (Lycan's wolf form, AM's blinking, etc)
- It doesn't include any logic about why a hero is missing or where they've gone -- it assumes an equal chance of a hero moving in any direction under FoW
Still, it can be useful for some decisionmaking, especially when the potential location values are high. You obviously don't want to stop considering a hero nearby the moment you lose sight of them, so using the potential location grid to help evaluate how dangerous a location is can be helpful.
Hero Power
It's often useful to understand how powerful a teammate bot is, or how dangerous an enemy is. One rough estimate is the Hero Power concept, which is updated for each hero each frame.
Here is how it's calculated (done per-hero):
- For each enemy hero, calculate the amount of damage done over a time interval to that enemy hero.
- That time interval is defined as 5 seconds, plus the duration that the hero can stun a unit, plus half the duration that the hero can slow a unit.
- That damage includes both attacking for that duration, plus damage done by abilities.
- Attack damage includes procs as well as debuffs.
- Ability damage is based on available mana, cast time, cooldowns, silence status, etc.
- That damage is then averaged over all enemy heroes.
Additionally, we calculate each hero's Raw Power as well, which ignores all cooldowns and hero state (mana, debuffs, etc). It's more a representation of how powerful a hero theoretically is than how powerful they are at any given moment.
Note that this is an indicator of offensive power only, not tankiness or durability.
GetRawOffensivePower() can be called on teammates or enemies that you can see.
GetOffensivePower() can be called only on teammates.
Appendix A - Chaining to a generic implementation in Lua
If you're implementing a generic version of a mode or ability/item usage, you should add this code to the top of your generic file (adjusting the name of the module appropriately):
_G._savedEnv = getfenv()
module( "mode_defend_ally_generic", package.seeall ) |
and the following code to the end of your file:
for k,v in pairs( mode_defend_ally_generic) do _G._savedEnv[k] = v end |
Then in your hero-specific implementation, you can start your file with this:
require( GetScriptDirectory().."/mode_defend_ally_generic" ) |
which allows you to do things like the following in your bot-specific code:
mode_defend_ally_generic.OnStart(); |
Appendix B - List of available constants
Bot Modes
- 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
Action Desires
These can be useful for making sure all action desires are using a common language for talking about their desire.
- BOT_ACTION_DESIRE_NONE - 0.0
- BOT_ACTION_DESIRE_VERYLOW - 0.1
- BOT_ACTION_DESIRE_LOW - 0.25
- BOT_ACTION_DESIRE_MODERATE - 0.5
- BOT_ACTION_DESIRE_HIGH - 0.75
- BOT_ACTION_DESIRE_VERYHIGH - 0.9
- BOT_ACTION_DESIRE_ABSOLUTE - 1.0
Mode Desires
These can be useful for making sure all mode desires as using a common language for talking about their desire.
- BOT_MODE_DESIRE_NONE" - 0
- BOT_MODE_DESIRE_VERYLOW - 0.1
- BOT_MODE_DESIRE_LOW - 0.25
- BOT_MODE_DESIRE_MODERATE - 0.5
- BOT_MODE_DESIRE_HIGH - 0.75
- BOT_MODE_DESIRE_VERYHIGH - 0.9
- BOT_MODE_DESIRE_ABSOLUTE - 1.0
Damage Types
- DAMAGE_TYPE_PHYSICAL
- DAMAGE_TYPE_MAGICAL
- DAMAGE_TYPE_PURE
- DAMAGE_TYPE_ALL
Unit Types
- UNIT_LIST_ALL
- UNIT_LIST_ALLIES
- UNIT_LIST_ALLIED_HEROES
- UNIT_LIST_ALLIED_CREEPS
- UNIT_LIST_ALLIED_WARDS
- UNIT_LIST_ENEMIES
- UNIT_LIST_ENEMY_HEROES
- UNIT_LIST_ENEMY_CREEPS
- UNIT_LIST_ENEMY_WARDS
- UNIT_LIST_NEUTRAL_CREEPS
Difficulties
- DIFFICULTY_INVALID
- DIFFICULTY_PASSIVE
- DIFFICULTY_EASY
- DIFFICULTY_MEDIUM
- DIFFICULTY_HARD
- DIFFICULTY_UNFAIR
Item Purchase Results
- 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
Game Modes
- GAMEMODE_NONE
- GAMEMODE_AP
- GAMEMODE_CM
- GAMEMODE_RD
- GAMEMODE_SD
- GAMEMODE_AR
- GAMEMODE_REVERSE_CM
- GAMEMODE_MO
- GAMEMODE_CD
- GAMEMODE_ABILITY_DRAFT
- GAMEMODE_ARDM
- GAMEMODE_1V1MID
- GAMEMODE_ALL_DRAFT (aka Ranked All Pick)
Teams
- TEAM_RADIANT
- TEAM_DIRE
- TEAM_NEUTRAL
- TEAM_NONE
Lanes
- LANE_NONE
- LANE_TOP
- LANE_MID
- LANE_BOT
Game States
- 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_TEAM_SHOWCASE
- GAME_STATE_CUSTOM_GAME_SETUP
- GAME_STATE_WAIT_FOR_MAP_TO_LOAD
- GAME_STATE_LAST
Hero Pick States
- HEROPICK_STATE_NONE
- HEROPICK_STATE_AP_SELECT
- HEROPICK_STATE_SD_SELECT
- HEROPICK_STATE_CM_INTRO
- HEROPICK_STATE_CM_CAPTAINPICK
- HEROPICK_STATE_CM_BAN1
- HEROPICK_STATE_CM_BAN2
- HEROPICK_STATE_CM_BAN3
- HEROPICK_STATE_CM_BAN4
- HEROPICK_STATE_CM_BAN5
- HEROPICK_STATE_CM_BAN6
- HEROPICK_STATE_CM_BAN7
- HEROPICK_STATE_CM_BAN8
- HEROPICK_STATE_CM_BAN9
- HEROPICK_STATE_CM_BAN10
- HEROPICK_STATE_CM_SELECT1
- HEROPICK_STATE_CM_SELECT2
- HEROPICK_STATE_CM_SELECT3
- HEROPICK_STATE_CM_SELECT4
- HEROPICK_STATE_CM_SELECT5
- HEROPICK_STATE_CM_SELECT6
- HEROPICK_STATE_CM_SELECT7
- HEROPICK_STATE_CM_SELECT8
- HEROPICK_STATE_CM_SELECT9
- HEROPICK_STATE_CM_SELECT10
- HEROPICK_STATE_CM_PICK
- HEROPICK_STATE_AR_SELECT
- HEROPICK_STATE_MO_SELECT
- HEROPICK_STATE_FH_SELECT
- HEROPICK_STATE_CD_INTRO
- HEROPICK_STATE_CD_CAPTAINPICK
- HEROPICK_STATE_CD_BAN1
- HEROPICK_STATE_CD_BAN2
- HEROPICK_STATE_CD_BAN3
- HEROPICK_STATE_CD_BAN4
- HEROPICK_STATE_CD_BAN5
- HEROPICK_STATE_CD_BAN6
- HEROPICK_STATE_CD_SELECT1
- HEROPICK_STATE_CD_SELECT2
- HEROPICK_STATE_CD_SELECT3
- HEROPICK_STATE_CD_SELECT4
- HEROPICK_STATE_CD_SELECT5
- HEROPICK_STATE_CD_SELECT6
- HEROPICK_STATE_CD_SELECT7
- HEROPICK_STATE_CD_SELECT8
- HEROPICK_STATE_CD_SELECT9
- HEROPICK_STATE_CD_SELECT10
- HEROPICK_STATE_CD_PICK
- HEROPICK_STATE_BD_SELECT
- HERO_PICK_STATE_ABILITY_DRAFT_SELECT
- HERO_PICK_STATE_ARDM_SELECT
- HEROPICK_STATE_ALL_DRAFT_SELECT
- HERO_PICK_STATE_CUSTOMGAME_SELECT
- HEROPICK_STATE_SELECT_PENALTY
Rune Types
- RUNE_INVALID (used as return value)
- RUNE_DOUBLEDAMAGE
- RUNE_HASTE
- RUNE_ILLUSION
- RUNE_INVISIBILITY
- RUNE_REGENERATION
- RUNE_BOUNTY
- RUNE_ARCANE
Rune Status
- RUNE_STATUS_UNKNOWN
- RUNE_STATUS_AVAILABLE
- RUNE_STATUS_MISSING
Rune Locations
- RUNE_POWERUP_1
- RUNE_POWERUP_2
- RUNE_BOUNTY_1
- RUNE_BOUNTY_2
- RUNE_BOUNTY_3
- RUNE_BOUNTY_4
Item Slot Types
- ITEM_SLOT_TYPE_INVALID
- ITEM_SLOT_TYPE_MAIN
- ITEM_SLOT_TYPE_BACKPACK
- ITEM_SLOT_TYPE_STASH
Action Types
- BOT_ACTION_TYPE_NONE
- BOT_ACTION_TYPE_IDLE
- BOT_ACTION_TYPE_MOVE_TO
- 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
Courier Actions and States
- COURIER_ACTION_BURST
- COURIER_ACTION_ENEMY_SECRET_SHOP
- COURIER_ACTION_RETURN
- COURIER_ACTION_SECRET_SHOP
- COURIER_ACTION_SIDE_SHOP
- COURIER_ACTION_SIDE_SHOP2
- COURIER_ACTION_STASH_ITEMS
- COURIER_ACTION_TAKE_AND_TRANSFER_ITEMS
- COURIER_ACTION_TRANSFER_ITEMS
- COURIER_STATE_IDLE
- COURIER_STATE_AT_BASE
- COURIER_STATE_MOVING
- COURIER_STATE_DELIVERING_ITEMS
- COURIER_STATE_RETURNING_TO_BASE
- COURIER_STATE_DEAD
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
Shrines
- SHRINE_BASE_1
- SHRINE_BASE_2
- SHRINE_BASE_3
- SHRINE_BASE_4
- SHRINE_BASE_5
- SHRINE_JUNGLE_1
- SHRINE_JUNGLE_2
Shops
- SHOP_HOME
- SHOP_SIDE
- SHOP_SECRET
- SHOP_SIDE2
- SHOP_SECRET2
Ability Target Teams
- ABILITY_TARGET_TEAM_NONE
- ABILITY_TARGET_TEAM_FRIENDLY
- ABILITY_TARGET_TEAM_ENEMY
Ability Target Types
- ABILITY_TARGET_TYPE_NONE
- ABILITY_TARGET_TYPE_HERO
- ABILITY_TARGET_TYPE_CREEP
- ABILITY_TARGET_TYPE_BUILDING
- ABILITY_TARGET_TYPE_COURIER
- ABILITY_TARGET_TYPE_OTHER
- ABILITY_TARGET_TYPE_TREE
- ABILITY_TARGET_TYPE_BASIC
- ABILITY_TARGET_TYPE_ALL
Ability Target Flags
- ABILITY_TARGET_FLAG_NONE
- ABILITY_TARGET_FLAG_RANGED_ONLY
- ABILITY_TARGET_FLAG_MELEE_ONLY
- ABILITY_TARGET_FLAG_DEAD
- ABILITY_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES
- ABILITY_TARGET_FLAG_NOT_MAGIC_IMMUNE_ALLIES
- ABILITY_TARGET_FLAG_INVULNERABLE
- ABILITY_TARGET_FLAG_FOW_VISIBLE
- ABILITY_TARGET_FLAG_NO_INVIS
- ABILITY_TARGET_FLAG_NOT_ANCIENTS
- ABILITY_TARGET_FLAG_PLAYER_CONTROLLED
- ABILITY_TARGET_FLAG_NOT_DOMINATED
- ABILITY_TARGET_FLAG_NOT_SUMMONED
- ABILITY_TARGET_FLAG_NOT_ILLUSIONS
- ABILITY_TARGET_FLAG_NOT_ATTACK_IMMUNE
- ABILITY_TARGET_FLAG_MANA_ONLY
- ABILITY_TARGET_FLAG_CHECK_DISABLE_HELP
- ABILITY_TARGET_FLAG_NOT_CREEP_HERO
- ABILITY_TARGET_FLAG_OUT_OF_WORLD
- ABILITY_TARGET_FLAG_NOT_NIGHTMARED
- ABILITY_TARGET_FLAG_PREFER_ENEMIES