├── webserver ├── constants.py ├── requirements.pip ├── utils.py ├── main.py ├── web_dota_mapper.py ├── game_data.py ├── local_server.py ├── hero.py ├── items.py └── world.py ├── web_config.lua ├── .gitignore ├── game_data ├── skillshots.json ├── permanent_buffs.json ├── item_colors.json ├── player_colors.json ├── xp_level.json ├── region.json ├── ancients.json ├── cluster.json ├── lobby_type.json ├── LICENSE-SOURCE ├── order_types.json ├── patch.json ├── game_mode.json ├── neutral_abilities.json ├── item_ids.json ├── item_groups.json └── countries.json ├── constants ├── roles.lua ├── fountains.lua ├── generic.lua ├── runes.lua ├── shops.lua ├── tables.lua ├── jungle.lua └── item_mappings.lua ├── actions ├── complex_actions │ ├── TODO_complex_actions.md │ ├── action_block_unit.lua │ ├── approach_and_execute_action_on_location.lua │ ├── action_place_ward.lua │ ├── action_pulling.lua │ └── action_stacking.lua ├── basic_actions │ ├── action_pick_up_item.lua │ ├── action_pick_up_rune.lua │ ├── action_use_shrine.lua │ ├── action_drop_item.lua │ ├── action_use_ability.lua │ ├── action_attack_move.lua │ ├── action_move_to_location.lua │ ├── action_rotate_away.lua │ ├── action_use_ability_on_tree.lua │ ├── action_rotate_towards.lua │ ├── action_use_ability_on_location.lua │ ├── action_move_to_unit.lua │ ├── action_use_ability_on_entity.lua │ └── action_attack_unit.lua └── manuevers │ └── TODO_manuevers.md ├── item_purchase_generic.lua ├── heroes ├── _heroes.lua └── antimage.lua ├── modes └── none.lua ├── ability_item_usage_generic.lua ├── bot_antimage.lua ├── danger ├── danger.lua ├── fountain_danger.lua ├── tower_danger.lua └── hero_danger.lua ├── bot_info.lua ├── LICENSE ├── helper ├── minion_helper.lua ├── vector_helper.lua ├── map_helper.lua ├── unit_helper.lua ├── courier_helper.lua ├── global_helper.lua └── inventory_helper.lua ├── think.lua ├── data_packet.lua ├── abilities └── hero │ └── antimage │ └── antimage_blink.lua ├── debug.lua ├── hero_selection.lua ├── README.md ├── HeroData.py ├── web_data └── dotabuff.json ├── decision.lua └── webserver_out.lua /webserver/constants.py: -------------------------------------------------------------------------------- 1 | 2 | RADIANT = 2 3 | DIRE = 3 -------------------------------------------------------------------------------- /webserver/requirements.pip: -------------------------------------------------------------------------------- 1 | cherrypy 2 | lxml 3 | requests 4 | -------------------------------------------------------------------------------- /web_config.lua: -------------------------------------------------------------------------------- 1 | { 2 | "IP_ADDR" = "127.0.0.1", 3 | "IP_PORT" = 2222 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | .idea/ 6 | -------------------------------------------------------------------------------- /game_data/skillshots.json: -------------------------------------------------------------------------------- 1 | { 2 | "mirana_arrow": true, 3 | "pudge_meat_hook": true, 4 | "rattletrap_hookshot": true, 5 | "invoker_sun_strike": true 6 | } -------------------------------------------------------------------------------- /constants/roles.lua: -------------------------------------------------------------------------------- 1 | ROLE_CARRY = 1 2 | ROLE_MID = 2 3 | ROLE_OFFLANE = 3 4 | ROLE_SEMISUP = 4 5 | ROLE_FULLSUP = 5 6 | ROLE_GANKER = 6 7 | ROLE_JUNGLER = 7 -------------------------------------------------------------------------------- /constants/fountains.lua: -------------------------------------------------------------------------------- 1 | FOUNTAIN = {} 2 | FOUNTAIN[TEAM_RADIANT] = Vector(-6874.625000, -6378.750000, 500.991577) 3 | FOUNTAIN[TEAM_DIRE] = Vector(7015.625000, 6389.593750, 458.515015) -------------------------------------------------------------------------------- /webserver/utils.py: -------------------------------------------------------------------------------- 1 | # Designed to provide compatibility for Python 2 & 3 2 | import future 3 | import six 4 | 5 | from __future__ import print_function 6 | from future.utils import raise_ 7 | 8 | -------------------------------------------------------------------------------- /webserver/main.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import local_server as webserver 4 | 5 | if __name__ == "__main__": 6 | print('starting web server...') 7 | webserver.start() 8 | -------------------------------------------------------------------------------- /game_data/permanent_buffs.json: -------------------------------------------------------------------------------- 1 | { 2 | "1": "moon_shard", 3 | "2": "ultimate_scepter", 4 | "3": "silencer_glaives_of_wisdom", 5 | "4": "pudge_flesh_heap", 6 | "5": "legion_commander_duel" 7 | } 8 | -------------------------------------------------------------------------------- /game_data/item_colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "rare": "#1A87F9", 3 | "artifact": "#E29B01", 4 | "secret_shop": "#FFFFFF", 5 | "consumable": "#1D80E7", 6 | "common": "#2BAB01", 7 | "epic": "#B812F9", 8 | "component": "#FFFFFF" 9 | } 10 | -------------------------------------------------------------------------------- /game_data/player_colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": "#2E6AE6", 3 | "1": "#5DE6AD", 4 | "2": "#AD00AD", 5 | "3": "#DCD90A", 6 | "4": "#E66200", 7 | "128": "#E67AB0", 8 | "129": "#92A440", 9 | "130": "#5CC5E0", 10 | "131": "#00771F", 11 | "132": "#956000" 12 | } -------------------------------------------------------------------------------- /constants/generic.lua: -------------------------------------------------------------------------------- 1 | VERY_LOW_INT = -99999999 2 | VERY_HIGH_INT = 99999999 3 | 4 | ABILITY_STANDARD = 0 5 | ABILITY_PUSH = 1 6 | ABILITY_QUEUE = 2 7 | 8 | WARD_OBSERVER = "item_ward_observer" 9 | WARD_SENTRY = "item_ward_sentry" 10 | WARD_DISPENSER = "item_ward_dispenser" -------------------------------------------------------------------------------- /actions/complex_actions/TODO_complex_actions.md: -------------------------------------------------------------------------------- 1 | Complex Actions Needed (these are defined as being comprised of 2 or more basic actions needed to accomplish): 2 | 3 | - place ward 4 | - deward 5 | - drop item 6 | - pick-up item 7 | - go to side-shop 8 | - go to secret-shop 9 | - drop tower aggro 10 | - stack neutral camp 11 | - pull neutral camp -------------------------------------------------------------------------------- /webserver/web_dota_mapper.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | class WebMapper(): 4 | 5 | def __init__(self,trans_file="../web_data/dotabuff.json"): 6 | with open(trans_file) as ifile: 7 | self.mappings = json.load(ifile) 8 | 9 | def item_name(self, web_name): 10 | return "item_" + self.mappings["items"][web_name] 11 | -------------------------------------------------------------------------------- /game_data/xp_level.json: -------------------------------------------------------------------------------- 1 | [ 2 | 0, 3 | 240, 4 | 600, 5 | 1080, 6 | 1680, 7 | 2300, 8 | 2940, 9 | 3600, 10 | 4280, 11 | 5080, 12 | 5900, 13 | 6740, 14 | 7640, 15 | 8865, 16 | 10115, 17 | 11390, 18 | 12690, 19 | 14015, 20 | 15415, 21 | 16905, 22 | 18405, 23 | 20155, 24 | 22155, 25 | 24405, 26 | 26905 27 | ] 28 | -------------------------------------------------------------------------------- /item_purchase_generic.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-FullOverwrite 4 | ------------------------------------------------------------------------------- 5 | 6 | --[[ 7 | function ItemPurchaseThink() 8 | return 9 | end 10 | --]] 11 | -------------------------------------------------------------------------------- /constants/runes.lua: -------------------------------------------------------------------------------- 1 | BOUNTY_RUNES = { 2 | RUNE_BOUNTY_1, 3 | RUNE_BOUNTY_2, 4 | RUNE_BOUNTY_3, 5 | RUNE_BOUNTY_4 6 | } 7 | 8 | -- Better Bounty Naming 9 | RUNE_RADIANT_SAFE = RUNE_BOUNTY_1 10 | RUNE_RADIANT_OFF = RUNE_BOUNTY_2 11 | RUNE_DIRE_SAFE = RUNE_BOUNTY_3 12 | RUNE_DIRE_OFF = RUNE_BOUNTY_4 13 | 14 | POWER_RUNES = { 15 | RUNE_POWERUP_1, 16 | RUNE_POWERUP_2 17 | } 18 | 19 | function GetRuneLocation( nRuneLoc ) 20 | return GetRuneSpawnLocation(nRuneLoc) 21 | end 22 | -------------------------------------------------------------------------------- /game_data/region.json: -------------------------------------------------------------------------------- 1 | { 2 | "1": "US WEST", 3 | "2": "US EAST", 4 | "3": "EUROPE", 5 | "5": "SINGAPORE", 6 | "6": "DUBAI", 7 | "7": "AUSTRALIA", 8 | "8": "STOCKHOLM", 9 | "9": "AUSTRIA", 10 | "10": "BRAZIL", 11 | "11": "SOUTHAFRICA", 12 | "12": "PW TELECOM SHANGHAI", 13 | "13": "PW UNICOM", 14 | "14": "CHILE", 15 | "15": "PERU", 16 | "16": "INDIA", 17 | "17": "PW TELECOM GUANGDONG", 18 | "18": "PW TELECOM ZHEJIANG", 19 | "19": "JAPAN", 20 | "20": "PW TELECOM WUHAN", 21 | "25": "PW UNICOM TIANJIN" 22 | } -------------------------------------------------------------------------------- /heroes/_heroes.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- --- AUTHOR: Nostrademous, iSarCasm --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI ------------------------------------------------------------------------------- local Heroes = {} -------------------------------------- function Heroes:GetHero() local hero_name = GetUnitName(GetBot()) local required = require(GetScriptDirectory().."/heroes/"..hero_name) return required:new() end -------------------------------------- return Heroes -------------------------------------------------------------------------------- /modes/none.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | BotsInit = require( "game/botsinit" ) 7 | local X = BotsInit.CreateGeneric() 8 | 9 | function X:GetName() 10 | return "none" 11 | end 12 | 13 | function X:OnStart() 14 | end 15 | 16 | function X:OnEnd() 17 | end 18 | 19 | function X:Think() 20 | end 21 | 22 | return X -------------------------------------------------------------------------------- /actions/basic_actions/action_pick_up_item.lua: -------------------------------------------------------------------------------- 1 | local PickUpItem = {} 2 | 3 | PickUpItem.Name = "Pick Up Item" 4 | 5 | ------------------------------------------------- 6 | function PickUpItem:Call( hUnit, hItem, iType ) 7 | if iType == nil or iType == ABILITY_STANDARD then 8 | hUnit:Action_PickUpItem(hItem) 9 | elseif iType == ABILITY_PUSH then 10 | hUnit:ActionPush_PickUpItem(hItem) 11 | elseif iType == ABILITY_QUEUE then 12 | hUnit:ActionQueue_PickUpItem(hItem) 13 | end 14 | end 15 | ------------------------------------------------- 16 | 17 | return PickUpItem -------------------------------------------------------------------------------- /actions/basic_actions/action_pick_up_rune.lua: -------------------------------------------------------------------------------- 1 | local PickUpRune = {} 2 | 3 | PickUpRune.Name = "Pick Up Rune" 4 | 5 | ------------------------------------------------- 6 | function PickUpRune:Call( hUnit, iRune, iType ) 7 | if iType == nil or iType == ABILITY_STANDARD then 8 | hUnit:Action_PickUpRune(iRune) 9 | elseif iType == ABILITY_PUSH then 10 | hUnit:ActionPush_PickUpRune(iRune) 11 | elseif iType == ABILITY_QUEUE then 12 | hUnit:ActionQueue_PickUpRune(iRune) 13 | end 14 | end 15 | ------------------------------------------------- 16 | 17 | return PickUpRune -------------------------------------------------------------------------------- /actions/basic_actions/action_use_shrine.lua: -------------------------------------------------------------------------------- 1 | local UseShrine = {} 2 | 3 | UseShrine.Name = "Use Shrine" 4 | 5 | ------------------------------------------------- 6 | function UseShrine:Call( hUnit, hShrine, iType ) 7 | if iType == nil or iType == ABILITY_STANDARD then 8 | hUnit:Action_UseShrine(hShrine) 9 | elseif iType == ABILITY_PUSH then 10 | hUnit:ActionPush_UseShrine(hShrine) 11 | elseif iType == ABILITY_QUEUE then 12 | hUnit:ActionQueue_UseShrine(hShrine) 13 | end 14 | end 15 | ------------------------------------------------- 16 | 17 | return UseShrine -------------------------------------------------------------------------------- /actions/basic_actions/action_drop_item.lua: -------------------------------------------------------------------------------- 1 | local DropItem = {} 2 | 3 | DropItem.Name = "Drop Item" 4 | 5 | ------------------------------------------------- 6 | function DropItem:Call( hUnit, hItem, vLoc, iType ) 7 | if iType == nil or iType == ABILITY_STANDARD then 8 | hUnit:Action_DropItem(hItem, vLoc) 9 | elseif iType == ABILITY_PUSH then 10 | hUnit:ActionPush_DropItem(hItem, vLoc) 11 | elseif iType == ABILITY_QUEUE then 12 | hUnit:ActionQueue_DropItem(hItem, vLoc) 13 | end 14 | end 15 | ------------------------------------------------- 16 | 17 | return DropItem -------------------------------------------------------------------------------- /game_data/ancients.json: -------------------------------------------------------------------------------- 1 | { 2 | "npc_dota_neutral_black_drake": 1, 3 | "npc_dota_neutral_black_dragon": 1, 4 | "npc_dota_neutral_blue_dragonspawn_sorcerer": 1, 5 | "npc_dota_neutral_blue_dragonspawn_overseer": 1, 6 | "npc_dota_neutral_granite_golem": 1, 7 | "npc_dota_neutral_elder_jungle_stalker": 1, 8 | "npc_dota_neutral_rock_golem": 1, 9 | "npc_dota_neutral_small_thunder_lizard": 1, 10 | "npc_dota_neutral_jungle_stalker": 1, 11 | "npc_dota_neutral_big_thunder_lizard": 1, 12 | "npc_dota_neutral_prowler_acolyte": 1, 13 | "npc_dota_neutral_prowler_shaman": 1 14 | } 15 | -------------------------------------------------------------------------------- /ability_item_usage_generic.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-FullOverwrite 4 | ------------------------------------------------------------------------------- 5 | 6 | --[[ 7 | function ItemUsageThink() 8 | return 9 | end 10 | 11 | function AbilityUsageThink() 12 | return 13 | end 14 | 15 | function CourierUsageThink() 16 | return 17 | end 18 | 19 | function BuybackUsageThink() 20 | return 21 | end 22 | 23 | function AbilityLevelUpThink() 24 | return 25 | end 26 | --]] -------------------------------------------------------------------------------- /heroes/antimage.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous, iSarCasm 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | local BotInfo = require(GetScriptDirectory().."/bot_info") 7 | 8 | ------------------------------------------- 9 | 10 | local Hero = BotInfo:new() 11 | 12 | ------------------------------------------- 13 | 14 | Hero = Hero:new() 15 | 16 | Hero:Init(Safelane(), ROLE_CARRY) 17 | Hero.projectileSpeed = 0 18 | 19 | ------------------------------------------- 20 | 21 | return Hero -------------------------------------------------------------------------------- /actions/basic_actions/action_use_ability.lua: -------------------------------------------------------------------------------- 1 | local UseAbility = {} 2 | 3 | UseAbility.Name = "Use Ability" 4 | 5 | ------------------------------------------------- 6 | function UseAbility:Call( hUnit, hAbility, fCastPoint, iType ) 7 | if hUnit:IsHero() then 8 | hUnit.mybot.ability_completed = GameTime() + fCastPoint 9 | end 10 | 11 | if iType == nil or iType == ABILITY_STANDARD then 12 | hUnit:Action_UseAbility(hAbility) 13 | elseif iType == ABILITY_PUSH then 14 | hUnit:ActionPush_UseAbility(hAbility) 15 | elseif iType == ABILITY_QUEUE then 16 | hUnit:ActionQueue_UseAbility(hAbility) 17 | end 18 | end 19 | ------------------------------------------------- 20 | 21 | return UseAbility -------------------------------------------------------------------------------- /actions/manuevers/TODO_manuevers.md: -------------------------------------------------------------------------------- 1 | Manuevers Needed (some of these will be "team-wide" and some can be "individual") - these are more complicated than complex actions in that they require timing and orientation outside of single-hero scope: 2 | 3 | - team_spread_out (to avoid AOE's like Lich's Ult, Faceless Chronosphere, etc) 4 | - team_collapse (to cluster - not sure why, except perhaps inside Roshan pit) 5 | - solo_flank (for in-lane harassment/zoning by supports against specific enemy hero) 6 | - solo_juke (retreating into trees and losing enemies sight of us) 7 | - team_bait (for baiting attacks by enemy) 8 | - solo_roam (this is a manuever that takes us through enemy areas of interest - like jungle, bounty runes, between T1 & T2 towers to try and courier snipe, etc) -------------------------------------------------------------------------------- /constants/shops.lua: -------------------------------------------------------------------------------- 1 | SHOP_RADIANT = "SHOP_RADIANT" 2 | SHOP_DIRE = "SHOP_DIRE" 3 | SHOP_SIDE_TOP = "SHOP_SIDE_TOP" 4 | SHOP_SIDE_BOT = "SHOP_SIDE_BOT" 5 | SHOP_SECRET_RADIANT = "SHOP_SECRET_RADIANT" 6 | SHOP_SECRET_DIRE = "SHOP_SECRET_DIRE" 7 | 8 | SHOP = {} 9 | SHOP[SHOP_RADIANT] = Vector(-6874.625000, -6378.750000, 500.991577) 10 | SHOP[SHOP_DIRE] = Vector(7015.625000, 6389.593750, 458.515015) 11 | SHOP[SHOP_SIDE_TOP] = Vector(-7113.072266, 4490.548828, 499.016693) 12 | SHOP[SHOP_SIDE_BOT] = Vector(7168.000000, -4126.375000, 455.781769) 13 | SHOP[SHOP_SECRET_RADIANT] = Vector(-4727.650391, 1291.642090, 319.396210) 14 | SHOP[SHOP_SECRET_DIRE] = Vector(4548.312500, -1552.406250, 329.177521) 15 | 16 | SHOP_USE_DISTANCE = 200 -------------------------------------------------------------------------------- /bot_antimage.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | local dt = require( GetScriptDirectory().."/decision" ) 7 | local inv = require( GetScriptDirectory().."/helper/inventory_helper" ) 8 | local heroes = require(GetScriptDirectory().."/heroes/_heroes") 9 | 10 | local botAM = dt:new(heroes:GetHero()) 11 | 12 | function botAM:new(o) 13 | o = o or dt:new(o) 14 | setmetatable(o, self) 15 | self.__index = self 16 | return o 17 | end 18 | 19 | local amBot = botAM:new{} 20 | 21 | function Think() 22 | local hBot = GetBot() 23 | 24 | amBot:Think( hBot ) 25 | end 26 | -------------------------------------------------------------------------------- /webserver/game_data.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import json 3 | 4 | class GameData(): 5 | game_data_json_dir = "../game_data" 6 | web_trans_file = "../web_data/dotabuff.json" 7 | 8 | def __init__(self,): 9 | files = Path(GameData.game_data_json_dir).glob("*.json") 10 | for filepath in files: 11 | with open(str(filepath)) as ifile: 12 | data = json.load(ifile) 13 | #print("{}".format(data)) 14 | self.__dict__.update({filepath.stem : data}) 15 | 16 | with open(GameData.web_trans_file) as ifile: 17 | self.mappings = json.load(ifile) 18 | 19 | def item_name(self, web_name): 20 | return "item_" + self.mappings["items"][web_name] 21 | 22 | if __name__ == "__main__": 23 | gd = GameData() 24 | -------------------------------------------------------------------------------- /actions/basic_actions/action_attack_move.lua: -------------------------------------------------------------------------------- 1 | local AttackMove = {} 2 | 3 | AttackMove.Name = "Attack Move" 4 | 5 | ------------------------------------------------- 6 | 7 | function AttackMove:Call( hUnit, fPoint, iType ) 8 | if hUnit:IsHero() then 9 | hUnit.mybot.moving_location = fPoint 10 | end 11 | 12 | DebugDrawCircle(fPoint, 25, 255, 255 ,255) 13 | DebugDrawLine(hUnit:GetLocation(), fPoint, 255, 255, 255) 14 | 15 | if iType == nil or iType == ABILITY_STANDARD then 16 | hUnit:Action_AttackMove(fPoint) 17 | elseif iType == ABILITY_PUSH then 18 | hUnit:ActionPush_AttackMove(fPoint) 19 | elseif iType == ABILITY_QUEUE then 20 | hUnit:ActionQueue_AttackMove(fPoint) 21 | end 22 | end 23 | 24 | ------------------------------------------------- 25 | 26 | return AttackMove -------------------------------------------------------------------------------- /actions/basic_actions/action_move_to_location.lua: -------------------------------------------------------------------------------- 1 | local MoveToLocation = {} 2 | 3 | MoveToLocation.Name = "Move to Location" 4 | 5 | ------------------------------------------------- 6 | 7 | function MoveToLocation:Call( hUnit, vLoc, iType ) 8 | if hUnit:IsHero() then 9 | hUnit.mybot.moving_location = vLoc 10 | end 11 | 12 | DebugDrawCircle(vLoc, 25, 255, 255 ,255) 13 | DebugDrawLine(hUnit:GetLocation(), vLoc, 255, 255, 255) 14 | 15 | if iType == nil or iType == ABILITY_STANDARD then 16 | hUnit:Action_MoveToLocation(vLoc) 17 | elseif iType == ABILITY_PUSH then 18 | hUnit:ActionPush_MoveToLocation(vLoc) 19 | elseif iType == ABILITY_QUEUE then 20 | hUnit:ActionQueue_MoveToLocation(vLoc) 21 | end 22 | end 23 | 24 | ------------------------------------------------- 25 | 26 | return MoveToLocation -------------------------------------------------------------------------------- /actions/basic_actions/action_rotate_away.lua: -------------------------------------------------------------------------------- 1 | local VectorHelper = require(GetScriptDirectory().."/helper/vector_helper") 2 | 3 | local RotateAway = {} 4 | 5 | RotateAway.name = "Rotate Away" 6 | 7 | ------------------------------------------------- 8 | function RotateAway:Call( hUnit, fPoint, iType ) 9 | local away_vector = VectorHelper:Normalize(hUnit:GetLocation() - fPoint) 10 | 11 | if iType == nil or iType == ABILITY_STANDARD then 12 | hUnit:Action_MoveToLocation(hUnit:GetLocation() + away_vector) 13 | elseif iType == ABILITY_PUSH then 14 | hUnit:ActionPush_MoveToLocation(hUnit:GetLocation() + away_vector) 15 | elseif iType == ABILITY_QUEUE then 16 | hUnit:ActionQueue_MoveToLocation(hUnit:GetLocation() + away_vector) 17 | end 18 | end 19 | ------------------------------------------------- 20 | 21 | return RotateAway -------------------------------------------------------------------------------- /actions/basic_actions/action_use_ability_on_tree.lua: -------------------------------------------------------------------------------- 1 | local UseAbilityOnTree = {} 2 | 3 | UseAbilityOnTree.Name = "Use Ability On Tree" 4 | 5 | ------------------------------------------------- 6 | function UseAbilityOnTree:Call( hUnit, hAbility, iTree, fCastPoint, iType ) 7 | if hUnit:IsHero() then 8 | hUnit.mybot.ability_location = GetTreeLocation(iTree) 9 | hUnit.mybot.ability_completed = GameTime() + fCastPoint 10 | end 11 | 12 | if iType == nil or iType == ABILITY_STANDARD then 13 | hUnit:Action_UseAbilityOnTree(hAbility, iTree) 14 | elseif iType == ABILITY_PUSH then 15 | hUnit:ActionPush_UseAbilityOnTree(hAbility, iTree) 16 | elseif iType == ABILITY_QUEUE then 17 | hUnit:ActionQueue_UseAbilityOnTree(hAbility, iTree) 18 | end 19 | end 20 | ------------------------------------------------- 21 | 22 | return UseAbilityOnTree -------------------------------------------------------------------------------- /actions/basic_actions/action_rotate_towards.lua: -------------------------------------------------------------------------------- 1 | local VectorHelper = require(GetScriptDirectory().."/helper/vector_helper") 2 | 3 | local RotateTowards = {} 4 | 5 | RotateTowards.name = "Rotate Towards" 6 | 7 | ------------------------------------------------- 8 | function RotateTowards:Call( hUnit, fPoint, iType ) 9 | local towards_vector = VectorHelper:Normalize(fPoint - hUnit:GetLocation()) 10 | 11 | if iType == nil or iType == ABILITY_STANDARD then 12 | hUnit:Action_MoveToLocation(hUnit:GetLocation() + towards_vector) 13 | elseif iType == ABILITY_PUSH then 14 | hUnit:ActionPush_MoveToLocation(hUnit:GetLocation() + towards_vector) 15 | elseif iType == ABILITY_QUEUE then 16 | hUnit:ActionQueue_MoveToLocation(hUnit:GetLocation() + towards_vector) 17 | end 18 | end 19 | ------------------------------------------------- 20 | 21 | return RotateTowards -------------------------------------------------------------------------------- /actions/basic_actions/action_use_ability_on_location.lua: -------------------------------------------------------------------------------- 1 | local UseAbilityOnLocation = {} 2 | 3 | UseAbilityOnLocation.Name = "Use Ability On Location" 4 | 5 | ------------------------------------------------- 6 | function UseAbilityOnLocation:Call( hUnit, hAbility, vLoc, fCastPoint, iType ) 7 | if hUnit:IsHero() then 8 | hUnit.mybot.ability_location = vLoc 9 | hUnit.mybot.ability_completed = GameTime() + fCastPoint 10 | end 11 | 12 | if iType == nil or iType == ABILITY_STANDARD then 13 | hUnit:Action_UseAbilityOnLocation(hAbility, vLoc) 14 | elseif iType == ABILITY_PUSH then 15 | hUnit:ActionPush_UseAbilityOnLocation(hAbility, vLoc) 16 | elseif iType == ABILITY_QUEUE then 17 | hUnit:ActionQueue_UseAbilityOnLocation(hAbility, vLoc) 18 | end 19 | end 20 | ------------------------------------------------- 21 | 22 | return UseAbilityOnLocation -------------------------------------------------------------------------------- /game_data/cluster.json: -------------------------------------------------------------------------------- 1 | { 2 | "111": 1, 3 | "112": 1, 4 | "113": 1, 5 | "121": 2, 6 | "122": 2, 7 | "123": 2, 8 | "124": 2, 9 | "131": 3, 10 | "132": 3, 11 | "133": 3, 12 | "134": 3, 13 | "135": 3, 14 | "136": 3, 15 | "137": 3, 16 | "138": 3, 17 | "144": 19, 18 | "145": 19, 19 | "151": 5, 20 | "152": 5, 21 | "153": 5, 22 | "154": 5, 23 | "155": 5, 24 | "156": 5, 25 | "161": 6, 26 | "171": 7, 27 | "172": 7, 28 | "181": 8, 29 | "182": 8, 30 | "183": 8, 31 | "184": 8, 32 | "185": 8, 33 | "186": 8, 34 | "187": 8, 35 | "188": 8, 36 | "191": 9, 37 | "192": 9, 38 | "193": 9, 39 | "201": 10, 40 | "202": 10, 41 | "204": 10, 42 | "213": 11, 43 | "214": 11, 44 | "223": 18, 45 | "224": 12, 46 | "225": 17, 47 | "227": 20, 48 | "231": 13, 49 | "232": 25, 50 | "241": 14, 51 | "242": 14, 52 | "251": 15, 53 | "261": 16 54 | } -------------------------------------------------------------------------------- /actions/complex_actions/action_block_unit.lua: -------------------------------------------------------------------------------- 1 | 2 | local actionMove = require( GetScriptDirectory().."/actions/basic_actions/action_move_to_location" ) 3 | 4 | ------------------------------------------------- 5 | 6 | local ActionBlockUnit = {} 7 | ActionBlockUnit.Name = "Action Block Unit" 8 | ActionBlockUnit.Sensitivity = 0.25 9 | 10 | ------------------------------------------------- 11 | 12 | function ActionBlockUnit:Call( hUnit, hBlockUnit, iType ) 13 | if not ValidTarget(hBlockUnit) then return end 14 | 15 | local futureLoc = hBlockUnit:GetExtrapolateLocation(self.Sensitivity) 16 | if GetUnitToLocationDistance( hUnit, futureLoc ) < 15.0 then 17 | hUnit:Action_ClearActions(true) 18 | return 19 | else 20 | actionMove:Call(hUnit, futureLoc, iType) 21 | return 22 | end 23 | end 24 | 25 | ------------------------------------------------- 26 | 27 | return ActionBlockUnit -------------------------------------------------------------------------------- /actions/basic_actions/action_move_to_unit.lua: -------------------------------------------------------------------------------- 1 | local MoveToUnit = {} 2 | 3 | MoveToUnit.Name = "Move to Unit" 4 | 5 | ------------------------------------------------- 6 | function MoveToUnit:Call( hUnit, hUnit2, iType ) 7 | if hUnit2 and not hUnit2:IsNull() and hUnit2:IsAlive() then 8 | if hUnit:IsHero() then 9 | hUnit.mybot.moving_location = hUnit2:GetLocation() 10 | end 11 | 12 | DebugDrawCircle(hUnit2:GetLocation(), 25, 255, 255 ,255) 13 | DebugDrawLine(hUnit:GetLocation(), hUnit2:GetLocation(), 255, 255, 255) 14 | 15 | if iType == nil or iType == ABILITY_STANDARD then 16 | hUnit:Action_MoveToUnit( hUnit2 ) 17 | elseif iType == ABILITY_PUSH then 18 | hUnit:ActionPush_MoveToUnit( hUnit2 ) 19 | elseif iType == ABILITY_QUEUE then 20 | hUnit:ActionQueue_MoveToUnit( hUnit2 ) 21 | end 22 | end 23 | end 24 | ------------------------------------------------- 25 | 26 | return MoveToUnit -------------------------------------------------------------------------------- /danger/danger.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous, iSarCasm 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | local Danger = {} 7 | 8 | local VectorHelper = require(GetScriptDirectory().."/helper/vector_helper") 9 | 10 | ----------------------------------------- 11 | 12 | local FountainDanger = require(GetScriptDirectory().."/danger/fountain_danger") 13 | local TowerDanger = require(GetScriptDirectory().."/danger/tower_danger") 14 | local HeroDanger = require(GetScriptDirectory().."/danger/hero_danger") 15 | 16 | ----------------------------------------- 17 | 18 | Danger.danger_sources = { 19 | FountainDanger, 20 | TowerDanger, 21 | HeroDanger 22 | } 23 | 24 | ----------------------------------------- 25 | local DANGER_TRAVEL_DISTANCE = 1000 26 | ----------------------------------------- 27 | 28 | return Danger -------------------------------------------------------------------------------- /actions/basic_actions/action_use_ability_on_entity.lua: -------------------------------------------------------------------------------- 1 | local UseAbilityOnEntity = {} 2 | 3 | UseAbilityOnEntity.Name = "Use Ability On Entity" 4 | 5 | ------------------------------------------------- 6 | function UseAbilityOnEntity:Call( hUnit, hAbility, hTarget, fCastPoint, iType ) 7 | if hUnit:IsHero() and not hTarget:IsNull() then 8 | hUnit.mybot.ability_location = hTarget:GetLocation() 9 | hUnit.mybot.ability_completed = GameTime() + fCastPoint 10 | end 11 | 12 | if not hTarget:IsNull() then 13 | if iType == nil or iType == ABILITY_STANDARD then 14 | hUnit:Action_UseAbilityOnEntity(hAbility, hTarget) 15 | elseif iType == ABILITY_PUSH then 16 | hUnit:ActionPush_UseAbilityOnEntity(hAbility, hTarget) 17 | elseif iType == ABILITY_QUEUE then 18 | hUnit:ActionQueue_UseAbilityOnEntity(hAbility, hTarget) 19 | end 20 | end 21 | end 22 | ------------------------------------------------- 23 | 24 | return UseAbilityOnEntity -------------------------------------------------------------------------------- /game_data/lobby_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": { 3 | "id": 0, 4 | "name": "lobby_type_normal", 5 | "balanced": true 6 | }, 7 | "1": { 8 | "id": 1, 9 | "name": "lobby_type_practice", 10 | "balanced": true 11 | }, 12 | "2": { 13 | "id": 2, 14 | "name": "lobby_type_tournament", 15 | "balanced": true 16 | }, 17 | "3": { 18 | "id": 3, 19 | "name": "lobby_type_tutorial" 20 | }, 21 | "4": { 22 | "id": 4, 23 | "name": "lobby_type_coop_bots" 24 | }, 25 | "5": { 26 | "id": 5, 27 | "name": "lobby_type_ranked_team_mm", 28 | "balanced": true 29 | }, 30 | "6": { 31 | "id": 6, 32 | "name": "lobby_type_ranked_solo_mm", 33 | "balanced": true 34 | }, 35 | "7": { 36 | "id": 7, 37 | "name": "lobby_type_ranked", 38 | "balanced": true 39 | }, 40 | "8": { 41 | "id": 8, 42 | "name": "lobby_type_1v1_mid" 43 | }, 44 | "9": { 45 | "id": 9, 46 | "name": "lobby_type_battle_cup", 47 | "balanced": true 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /actions/basic_actions/action_attack_unit.lua: -------------------------------------------------------------------------------- 1 | local AttackUnit = {} 2 | 3 | AttackUnit.Name = "Attack Unit" 4 | 5 | ------------------------------------------------- 6 | function AttackUnit:Call( hUnit, hTarget, bOnce, fAttackPoint, iType ) 7 | if hUnit:IsHero() and not hTarget:IsNull() then 8 | hUnit.mybot.attack_target = hTarget 9 | if hTarget:IsHero() then 10 | hUnit.mybot.attack_target_id = hTarget:GetPlayerID() 11 | end 12 | hUnit.mybot.attack_completed = GameTime() + fAttackPoint 13 | end 14 | 15 | if not hTarget:IsNull() then 16 | if iType == nil or iType == ABILITY_STANDARD then 17 | hUnit:Action_AttackUnit(hTarget, bOnce) 18 | elseif iType == ABILITY_PUSH then 19 | hUnit:ActionPush_AttackUnit(hTarget, bOnce) 20 | elseif iType == ABILITY_QUEUE then 21 | hUnit:ActionQueue_AttackUnit(hTarget, bOnce) 22 | end 23 | end 24 | end 25 | ------------------------------------------------- 26 | 27 | return AttackUnit -------------------------------------------------------------------------------- /bot_info.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous, iSarCasm 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | local MapHelper = require(GetScriptDirectory().."/helper/map_helper") 7 | local UnitHelper = require(GetScriptDirectory().."/helper/unit_helper") 8 | 9 | -------------------------------------------------------- 10 | 11 | local BotInfo = {} 12 | 13 | -------------------------------------------------------- 14 | 15 | function BotInfo:new(o) 16 | o = o or {} 17 | setmetatable(o, self) 18 | self.__index = self 19 | return o 20 | end 21 | 22 | function BotInfo:Init(lane, role) 23 | self.LANE = lane 24 | self.ROLE = role 25 | 26 | self.lastHealth = 0 27 | self.health = 0 28 | self.lastHealthCapture = DotaTime() 29 | self.healthDelta = 0 30 | end 31 | 32 | -------------------------------------------------------- 33 | 34 | return BotInfo -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nostrademous 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /actions/complex_actions/approach_and_execute_action_on_location.lua: -------------------------------------------------------------------------------- 1 | 2 | local actionMove = require( GetScriptDirectory().."/actions/basic_actions/action_move_to_location" ) 3 | local actionUseAbility = require( GetScriptDirectory().."/actions/basic_actions/action_use_ability_on_location" ) 4 | 5 | local ApproachAndExecuteActionOnLocation = {} 6 | ApproachAndExecuteActionOnLocation.Name = "Approach And Execute Action On Location" 7 | 8 | ------------------------------------------------- 9 | 10 | function ApproachAndExecuteActionOnLocation:Call( hUnit, vLoc, fPrecision, iType, hAbility, execLoc, fCastPoint ) 11 | local unitsToLocation = fPrecision 12 | if unitsToLocation == nil then 13 | unitsToLocation = 16.0 14 | end 15 | 16 | local distToLoc = GetUnitToLocationDistance(hUnit, vLoc) 17 | if distToLoc > unitsToLocation then 18 | actionMove:Call(hUnit, vLoc, iType) 19 | return 20 | else 21 | actionUseAbility(hUnit, hAbility, execLoc, fCastPoint, iType) 22 | return 23 | end 24 | end 25 | 26 | ------------------------------------------------- 27 | 28 | return ApproachAndExecuteActionOnLocation -------------------------------------------------------------------------------- /helper/minion_helper.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHORS: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | --- IMPORTS 7 | ------------------------------------------------- 8 | 9 | ------------------------------------------------- 10 | 11 | local MinionHelper = {} 12 | 13 | ------------------------------------------------- 14 | --- NOTE: List of API functions useful for minions 15 | --- float GetRemainingLifespan() 16 | --- { handles } GetModifierAuxiliaryUnits( nModifier ) 17 | ------------------------------------------------- 18 | 19 | function MinionHelper:MinionAboutToDie( hMinion ) 20 | local percHealth = hMinion:GetHealth() / hMinion:GetMaxHealth() 21 | local fTimeRemaining = hMinion:GetRemainingLifespan() 22 | 23 | if percHealth > 50.0 then return false end 24 | 25 | if fTimeRemaining < 1.0 then return false end 26 | 27 | if percHealth < fTimeRemaining*5.0 then return true end 28 | 29 | return false 30 | end 31 | 32 | return MinionHelper -------------------------------------------------------------------------------- /actions/complex_actions/action_place_ward.lua: -------------------------------------------------------------------------------- 1 | 2 | local goAndDo = require( GetScriptDirectory().."/actions/complex_actions/approach_and_execute_action_on_location" ) 3 | local useAbilityOnLoc = require( GetScriptDirectory().."/actions/basic_actions/action_use_ability_on_location" ) 4 | local invHelper = require( GetScriptDirectory().."/helper/inventory_helper" ) 5 | 6 | local PlaceWard = {} 7 | PlaceWard.Name = "Place Ward" 8 | 9 | ------------------------------------------------- 10 | 11 | function PlaceWard:Call( hUnit, vWardLoc, wardType, iType ) 12 | local wardCastRange = 500.0 13 | self.Name = self.Name .. " " .. wardType 14 | 15 | local hWardAbility = invHelper:GetItemByName( hUnit, wardType, true ) 16 | if hWardAbility then 17 | print("Ward Cast Point: " .. hWardAbility:GetCastPoint()) 18 | print("Ward Cast Range: " .. hWardAbility:GetCastRange()) 19 | local fCastPoint = hWardAbility:GetCastPoint() 20 | goAndDo:Call( hUnit, vWardLoc, wardCastRange, iType, hWardAbility, vWardLoc, fCastPoint ) 21 | else 22 | dbg.pause(self.Name, "Not Found") 23 | end 24 | end 25 | 26 | ------------------------------------------------- 27 | 28 | return PlaceWard -------------------------------------------------------------------------------- /think.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous 3 | ------------------------------------------------------------------------------- 4 | 5 | local freqTeamThink = 0.25 6 | local lastTeamThink = -1000.0 7 | 8 | local X = {} 9 | 10 | function X.MainThink() 11 | 12 | -- Exercise TeamThink() at coded frequency 13 | if GameTime() > lastTeamThink then 14 | X.TeamThink() 15 | lastTeamThink = GameTime() + freqTeamThink 16 | end 17 | 18 | -- Exercise individual Hero think at every frame (if possible). 19 | -- HeroThink() will check assignments from TeamThink() 20 | -- for that individual Hero that it should perform, if any. 21 | return X.HeroThink() 22 | end 23 | 24 | function X.TeamThink() 25 | dbg.myPrint("TeamThink") 26 | end 27 | 28 | function X.HeroThink() 29 | dbg.myPrint("HeroThink") 30 | 31 | local bot = GetBot() 32 | 33 | local highestDesireValue = BOT_MODE_DESIRE_NONE 34 | local highestDesireMode = noneMode 35 | 36 | local evaluatedDesireValue = BOT_MODE_DESIRE_NONE 37 | 38 | return highestDesireMode, highestDesireValue 39 | end 40 | 41 | return X 42 | -------------------------------------------------------------------------------- /game_data/LICENSE-SOURCE: -------------------------------------------------------------------------------- 1 | Taken from and maintained using the the odota-dotaconstants project: https://github.com/odota/dotaconstants 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 The OpenDota Project 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /webserver/local_server.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import cherrypy 4 | import json 5 | import world 6 | 7 | myWorld = world.World() 8 | 9 | @cherrypy.expose 10 | @cherrypy.tools.json_out() 11 | @cherrypy.tools.json_in() 12 | class Root(object): 13 | 14 | @cherrypy.tools.accept(media='application/json') 15 | def GET(self): 16 | return {"operation": "GET", "result": "success"} 17 | 18 | @cherrypy.tools.accept(media='application/json') 19 | def POST(self): 20 | input_json = cherrypy.request.json 21 | myWorld.update(input_json) 22 | return myWorld.decision() 23 | 24 | @cherrypy.expose 25 | def shutdown(self): 26 | print('shutting down') 27 | cherrypy.engine.exit() 28 | 29 | def start(): 30 | conf = { 31 | 'global' : { 32 | 'server.socket_host' : '127.0.0.1', 33 | 'server.socket_port' : 2222, 34 | 'server.thread_pool' : 8 35 | }, 36 | '/': { 37 | 'request.dispatch': cherrypy.dispatch.MethodDispatcher(), 38 | 'tools.sessions.on': True, 39 | } 40 | } 41 | cherrypy.quickstart(Root(), '/', conf) 42 | 43 | if __name__ == "__main__": 44 | print('starting web server') 45 | start() 46 | print('done') 47 | -------------------------------------------------------------------------------- /helper/vector_helper.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHORS: iSarCasm, Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | local VectorHelper = {} 7 | 8 | --------------------------------------- 9 | function VectorHelper:Normalize(vec) 10 | local x = vec[1] 11 | local y = vec[2] 12 | local vec_length = math.pow(x*x+y*y, 0.5) 13 | return Vector(x/vec_length, y/vec_length) 14 | end 15 | 16 | function VectorHelper:Length(vec) 17 | return (math.sqrt(math.pow(vec.x, 2) + math.pow(vec.y, 2))) 18 | end 19 | 20 | function VectorHelper:GetDistance(s, t) 21 | return math.sqrt(math.pow(s[1]-t[1], 2) + math.pow(s[2]-t[2], 2)) 22 | end 23 | 24 | function VectorHelper:VectorTowards(start, towards, distance) 25 | local facing = towards - start 26 | local direction = facing / self:GetDistance(facing, Vector(0,0)) --normalized 27 | return start + (direction * distance) 28 | end 29 | 30 | function VectorHelper:VectorAway(start, towards, distance) 31 | local facing = start - towards 32 | local direction = facing / self:GetDistance(facing, Vector(0,0)) --normalized 33 | return start + (direction * distance) 34 | end 35 | 36 | --------------------------------------- 37 | return VectorHelper 38 | -------------------------------------------------------------------------------- /actions/complex_actions/action_pulling.lua: -------------------------------------------------------------------------------- 1 | 2 | local Stacking = require( GetScriptDirectory().."/actions/complex_actions/action_stacking" ) 3 | local VectorHelper = require(GetScriptDirectory().."/helper/vector_helper") 4 | local Pulling = {} 5 | 6 | Pulling.Name = "Pulling" 7 | Pulling.state = {} -- doesnt require init, because same behaviour for state[bot_name] being `nil` or `false` 8 | 9 | ------------------------------------------------- 10 | 11 | function Pulling:Call(lane, camp_type, camp_location, camp_wait_at) 12 | local bot = GetBot() 13 | local bot_name = bot:GetUnitName() 14 | 15 | if Pulling.state[bot_name] then 16 | print("Add the last-hitting/farming func!") 17 | -- Need to reset/clear Pulling.state for this hero after finished farming 18 | else 19 | local amount_along_lane = GetAmountAlongLane(lane, camp_location) 20 | local camp_pull_to = GetLocationAlongLane(lane, amount_along_lane.amount) 21 | -- move a bit further so we de-aggro creeps 22 | camp_pull_to = camp_pull_to + VectorHelper:Normalize(camp_pull_to - camp_location) * 200 23 | local camp_timing = (camp_type == "large") and 23 or 13 24 | if GetSeconds() > 30 then 25 | camp_timing = camp_timing + 30 26 | end 27 | Pulling.state[bot_name] = Stacking:Call(bot, camp_location, camp_timing, camp_wait_at, camp_pull_to) 28 | end 29 | 30 | end 31 | 32 | return Pulling -------------------------------------------------------------------------------- /constants/tables.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHORS: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | tableBotActions = { 7 | BOT_ACTION_TYPE_NONE = 0, 8 | BOT_ACTION_TYPE_IDLE = 1, 9 | BOT_ACTION_TYPE_MOVE_TO = 2, 10 | BOT_ACTION_TYPE_ATTACK = 3, 11 | BOT_ACTION_TYPE_ATTACKMOVE = 4, 12 | BOT_ACTION_TYPE_USE_ABILITY = 5, 13 | BOT_ACTION_TYPE_PICK_UP_RUNE = 6, 14 | BOT_ACTION_TYPE_PICK_UP_ITEM = 7, 15 | BOT_ACTION_TYPE_DROP_ITEM = 8 16 | } 17 | 18 | tableItemPurchaseResults = { 19 | PURCHASE_ITEM_SUCCESS = -1, 20 | PURCHASE_ITEM_UNIT_NOT_PLAYER_CONTROLED = 0, 21 | PURCHASE_ITEM_OUT_OF_STOCK = 82, 22 | PURCHASE_ITEM_DISALLOWED_ITEM = 78, 23 | PURCHASE_ITEM_INSUFFICIENT_GOLD = 63, 24 | PURCHASE_ITEM_NOT_AT_HOME_SHOP = 67, 25 | PURCHASE_ITEM_NOT_AT_SIDE_SHOP = 66, 26 | PURCHASE_ITEM_NOT_AT_SECRET_SHOP = 62, 27 | PURCHASE_ITEM_INVALID_ITEM_NAME = 33 28 | } 29 | 30 | tableShops = { 31 | SHOP_HOME_RADIANT = 0, 32 | SHOP_SIDE_RADIANT = 1, 33 | SHOP_SECRET_RADIANT = 2, 34 | SHOP_HOME_DIRE = 3, 35 | SHOP_SIDE_DIRE = 4, 36 | SHOP_SECRET_DIRE = 5 37 | } 38 | 39 | tableShopTypes = { 40 | SHOP_HOME = 0, 41 | SHOP_SIDE = 1, 42 | SHOP_SECRET = 2 43 | } -------------------------------------------------------------------------------- /game_data/order_types.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": "DOTA_UNIT_ORDER_NONE", 3 | "1": "DOTA_UNIT_ORDER_MOVE_TO_POSITION", 4 | "2": "DOTA_UNIT_ORDER_MOVE_TO_TARGET", 5 | "3": "DOTA_UNIT_ORDER_ATTACK_MOVE", 6 | "4": "DOTA_UNIT_ORDER_ATTACK_TARGET", 7 | "5": "DOTA_UNIT_ORDER_CAST_POSITION", 8 | "6": "DOTA_UNIT_ORDER_CAST_TARGET", 9 | "7": "DOTA_UNIT_ORDER_CAST_TARGET_TREE", 10 | "8": "DOTA_UNIT_ORDER_CAST_NO_TARGET", 11 | "9": "DOTA_UNIT_ORDER_CAST_TOGGLE", 12 | "10": "DOTA_UNIT_ORDER_HOLD_POSITION", 13 | "11": "DOTA_UNIT_ORDER_TRAIN_ABILITY", 14 | "12": "DOTA_UNIT_ORDER_DROP_ITEM", 15 | "13": "DOTA_UNIT_ORDER_GIVE_ITEM", 16 | "14": "DOTA_UNIT_ORDER_PICKUP_ITEM", 17 | "15": "DOTA_UNIT_ORDER_PICKUP_RUNE", 18 | "16": "DOTA_UNIT_ORDER_PURCHASE_ITEM", 19 | "17": "DOTA_UNIT_ORDER_SELL_ITEM", 20 | "18": "DOTA_UNIT_ORDER_DISASSEMBLE_ITEM", 21 | "19": "DOTA_UNIT_ORDER_MOVE_ITEM", 22 | "20": "DOTA_UNIT_ORDER_CAST_TOGGLE_AUTO", 23 | "21": "DOTA_UNIT_ORDER_STOP", 24 | "22": "DOTA_UNIT_ORDER_TAUNT", 25 | "23": "DOTA_UNIT_ORDER_BUYBACK", 26 | "24": "DOTA_UNIT_ORDER_GLYPH", 27 | "25": "DOTA_UNIT_ORDER_EJECT_ITEM_FROM_STASH", 28 | "26": "DOTA_UNIT_ORDER_CAST_RUNE", 29 | "27": "DOTA_UNIT_ORDER_PING_ABILITY", 30 | "28": "DOTA_UNIT_ORDER_MOVE_TO_DIRECTION", 31 | "29": "DOTA_UNIT_ORDER_PATROL", 32 | "30": "DOTA_UNIT_ORDER_VECTOR_TARGET_POSITION", 33 | "31": "DOTA_UNIT_ORDER_RADAR", 34 | "32": "DOTA_UNIT_ORDER_SET_ITEM_COMBINE_LOCK", 35 | "33": "DOTA_UNIT_ORDER_CONTINUE" 36 | } -------------------------------------------------------------------------------- /data_packet.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | local dbg = require( GetScriptDirectory().."/debug" ) 7 | 8 | local DataPacket = {} 9 | 10 | DataPacket.LastPacket = {} 11 | 12 | DataPacket.TYPE_AUTH = "X" 13 | DataPacket.TYPE_WORLD = "W" 14 | DataPacket.TYPE_PLAYER = "P" 15 | DataPacket.TYPE_ENEMIES = "E" 16 | DataPacket.TYPE_ALLIES = "A" 17 | 18 | function DataPacket:CreatePacket(key, packet) 19 | --local seq = Round(RealTime(), 3) * 1000 20 | 21 | if not DataPacket.LastPacket[key] then 22 | DataPacket.LastPacket[key] = {} 23 | end 24 | 25 | --DataPacket.LastPacket[key].seq = seq 26 | DataPacket.LastPacket[key].lastSent = packet 27 | DataPacket.LastPacket[key].processed = false 28 | end 29 | 30 | function DataPacket:ProcessPacket(key, reply) 31 | if DataPacket.LastPacket[key] then 32 | --dbg.myPrint("Got Reply Key: ", key) 33 | DataPacket.LastPacket[key].lastReply = reply 34 | DataPacket.LastPacket[key].processed = true 35 | DataPacket.LastPacket[key].reported = false 36 | else 37 | dbg.pause('Bad Reply Key:', key) 38 | end 39 | end 40 | 41 | function DataPacket:GetLastReply(key) 42 | if DataPacket.LastPacket[key] and not DataPacket.LastPacket[key].reported then 43 | DataPacket.LastPacket[key].reported = true 44 | return DataPacket.LastPacket[key].lastReply 45 | end 46 | return nil 47 | end 48 | 49 | return DataPacket -------------------------------------------------------------------------------- /abilities/hero/antimage/antimage_blink.lua: -------------------------------------------------------------------------------- 1 | local VectorHelper = require(GetScriptDirectory().."/dev/helper/vector_helper") 2 | local Action = require(GetScriptDirectory().."/actions/basic_actions/action_use_ability_on_location") 3 | 4 | local AntimageBlink = {} 5 | AntimageBlink.Name = heroData.antimage.SKILL_1 6 | 7 | function AntimageBlink:Ability() 8 | return GetBot():GetAbilityByName(self.Name) 9 | end 10 | 11 | function AntimageBlink:Desire() 12 | local bot = GetBot() 13 | 14 | local ability = self:Ability() 15 | 16 | if not ability:IsFullyCastable() then 17 | return BOT_ACTION_DESIRE_NONE 18 | end 19 | 20 | local move_loc = bot.mybot.moving_location 21 | if not move_loc then 22 | return BOT_ACTION_DESIRE_NONE 23 | end 24 | 25 | local distance = GetUnitToLocationDistance(bot, move_loc) 26 | 27 | -- minimum blink distance is 200 28 | if distance < 200 then 29 | return BOT_ACTION_DESIRE_NONE 30 | end 31 | 32 | if bot:GetMana() > 120 and (distance > 900) then 33 | return BOT_ACTION_DESIRE_MEDIUM 34 | end 35 | 36 | return BOT_ACTION_DESIRE_NONE 37 | end 38 | 39 | function AntimageBlink:Call() 40 | local bot = GetBot() 41 | 42 | local ability = self:Ability() 43 | local max_blink_range = ability:GetSpecialValueInt("blink_range") 44 | local move_loc = bot.mybot.moving_location 45 | 46 | local distance = GetUnitToLocationDistance(bot, move_loc) 47 | 48 | if distance > max_blink_range then 49 | move_loc = VectorHelper:VectorTowards(bot:GetLocation(), move_loc, max_blink_range) 50 | end 51 | 52 | Action:Call(ability, move_loc, ability:GetCastPoint()) 53 | end 54 | 55 | return AntimageBlink -------------------------------------------------------------------------------- /danger/fountain_danger.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous, iSarCasm 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | local FountainDanger = {} 7 | FountainDanger.name = "fountain" 8 | 9 | ------------------------------------------ 10 | 11 | VectorHelper = require(GetScriptDirectory().."/helper/vector_helper") 12 | 13 | ------------------------------------------ 14 | 15 | local DANGER_FOUNTAIN = 500 16 | local DANGER_FOUNTAIN_FAR = 5000 17 | local DANGER_FOUNTAIN_BASE = 500 18 | 19 | ------------------------------------------ 20 | 21 | function FountainDanger:Power(distance) 22 | if (distance < 1000) then 23 | return DANGER_FOUNTAIN 24 | elseif (distance < 3000) then 25 | return DANGER_FOUNTAIN_FAR / (distance*distance) 26 | else 27 | return DANGER_FOUNTAIN_BASE / (distance*distance) -- fountain is basic safety 28 | end 29 | end 30 | 31 | function FountainDanger:OfLocation( vLocation, team ) 32 | local length = VectorHelper:Length(vLocation - FOUNTAIN[team]) 33 | -- print("input vector") 34 | -- print(vLocation) 35 | -- print("team "..team) 36 | -- print(FOUNTAIN[team]) 37 | -- print("l "..length) 38 | return self:Power(length) 39 | end 40 | 41 | function FountainDanger:ResultVector(team, unit, distance) 42 | return self:PowerDelta(team, unit, distance) * self:Location(team) 43 | end 44 | 45 | function FountainDanger:PowerDelta(team, unit, distance) 46 | local current_distance = GetUnitToLocationDistance(unit, self:Location(team)) 47 | local delta = ((team == GetTeam()) and -distance or distance) 48 | return math.abs(self:Power(Max(1, current_distance + delta)) - self:Power(current_distance)) 49 | end 50 | 51 | function FountainDanger:Location(team) 52 | return FOUNTAIN[team] 53 | end 54 | 55 | ------------------------------------------ 56 | 57 | return FountainDanger -------------------------------------------------------------------------------- /game_data/patch.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "6.70", 4 | "date": "2010-12-24T00:00:00Z" 5 | }, 6 | { 7 | "name": "6.71", 8 | "date": "2011-01-21T00:00:00Z" 9 | }, 10 | { 11 | "name": "6.72", 12 | "date": "2011-04-27T00:00:00Z" 13 | }, 14 | { 15 | "name": "6.73", 16 | "date": "2011-12-24T00:00:00Z" 17 | }, 18 | { 19 | "name": "6.74", 20 | "date": "2012-03-10T00:00:00Z" 21 | }, 22 | { 23 | "name": "6.75", 24 | "date": "2012-09-30T00:00:00Z" 25 | }, 26 | { 27 | "name": "6.76", 28 | "date": "2012-10-21T00:00:00Z" 29 | }, 30 | { 31 | "name": "6.77", 32 | "date": "2012-12-15T00:00:00Z" 33 | }, 34 | { 35 | "name": "6.78", 36 | "date": "2013-05-30T00:00:00Z" 37 | }, 38 | { 39 | "name": "6.79", 40 | "date": "2013-11-24T00:00:00Z" 41 | }, 42 | { 43 | "name": "6.80", 44 | "date": "2014-01-27T00:00:00Z" 45 | }, 46 | { 47 | "name": "6.81", 48 | "date": "2014-04-29T00:00:00Z" 49 | }, 50 | { 51 | "name": "6.82", 52 | "date": "2014-09-24T00:00:00Z" 53 | }, 54 | { 55 | "name": "6.83", 56 | "date": "2014-12-17T00:00:00Z" 57 | }, 58 | { 59 | "name": "6.84", 60 | "date": "2015-04-30T21:00:00Z" 61 | }, 62 | { 63 | "name": "6.85", 64 | "date": "2015-09-24T20:00:00Z" 65 | }, 66 | { 67 | "name": "6.86", 68 | "date": "2015-12-16T20:00:00Z" 69 | }, 70 | { 71 | "name": "6.87", 72 | "date": "2016-04-26T01:00:00Z" 73 | }, 74 | { 75 | "name": "6.88", 76 | "date": "2016-06-12T08:00:00Z" 77 | }, 78 | { 79 | "name": "7.00", 80 | "date": "2016-12-13T00:00:00Z" 81 | }, 82 | { 83 | "name": "7.01", 84 | "date": "2016-12-21T03:00:00Z" 85 | }, 86 | { 87 | "name": "7.02", 88 | "date": "2017-02-09T04:00:00Z" 89 | }, 90 | { 91 | "name": "7.03", 92 | "date": "2017-03-16T00:00:00Z" 93 | }, 94 | { 95 | "name": "7.04", 96 | "date": "2017-03-23T18:00:00Z" 97 | }, 98 | { 99 | "name": "7.05", 100 | "date": "2017-04-09T22:00:00Z" 101 | }, 102 | { 103 | "name": "7.06", 104 | "date": "2017-05-15T15:00:00Z" 105 | } 106 | ] 107 | -------------------------------------------------------------------------------- /helper/map_helper.lua: -------------------------------------------------------------------------------- 1 | local MapHelper = {} 2 | ---------------------- 3 | function MapHelper:LaneFrontLocation(team, lane, ignoreTowers) 4 | return GetLaneFrontLocation(team, lane, GetLaneFrontAmount(team, lane, ignoreTowers)) 5 | end 6 | 7 | function MapHelper:GetFrontTowerAt(LANE) 8 | local T1 = -1 9 | local T2 = -1 10 | local T3 = -1 11 | 12 | if(LANE == LANE_TOP) then 13 | T1 = TOWER_TOP_1 14 | T2 = TOWER_TOP_2 15 | T3 = TOWER_TOP_3 16 | elseif(LANE == LANE_MID) then 17 | T1 = TOWER_MID_1 18 | T2 = TOWER_MID_2 19 | T3 = TOWER_MID_3 20 | elseif(LANE == LANE_BOT) then 21 | T1 = TOWER_BOT_1 22 | T2 = TOWER_BOT_2 23 | T3 = TOWER_BOT_3 24 | end 25 | 26 | local tower = GetTower(GetTeam(),T1) 27 | if(tower ~= nil and tower:IsAlive())then 28 | return tower 29 | end 30 | 31 | tower = GetTower(GetTeam(),T2) 32 | if(tower ~= nil and tower:IsAlive())then 33 | return tower 34 | end 35 | 36 | tower = GetTower(GetTeam(),T3) 37 | if(tower ~= nil and tower:IsAlive())then 38 | return tower 39 | end 40 | return nil 41 | end 42 | 43 | function MapHelper:GetEnemyFrontTowerAt(LANE) 44 | local T1 = -1 45 | local T2 = -1 46 | local T3 = -1 47 | 48 | if(LANE == LANE_TOP) then 49 | T1 = TOWER_TOP_1 50 | T2 = TOWER_TOP_2 51 | T3 = TOWER_TOP_3 52 | elseif(LANE == LANE_MID) then 53 | T1 = TOWER_MID_1 54 | T2 = TOWER_MID_2 55 | T3 = TOWER_MID_3 56 | elseif(LANE == LANE_BOT) then 57 | T1 = TOWER_BOT_1 58 | T2 = TOWER_BOT_2 59 | T3 = TOWER_BOT_3 60 | end 61 | local team = (GetTeam() == 2) and 3 or 2 62 | local tower = GetTower(team,T1) 63 | if(tower ~= nil and tower:IsAlive())then 64 | return tower 65 | end 66 | 67 | tower = GetTower(team,T2) 68 | if(tower ~= nil and tower:IsAlive())then 69 | return tower 70 | end 71 | 72 | tower = GetTower(team,T3) 73 | if(tower ~= nil and tower:IsAlive())then 74 | return tower 75 | end 76 | return nil 77 | end 78 | ---------------------- 79 | return MapHelper -------------------------------------------------------------------------------- /game_data/game_mode.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": { 3 | "id": 0, 4 | "name": "game_mode_unknown", 5 | "balanced": true 6 | }, 7 | "1": { 8 | "id": 1, 9 | "name": "game_mode_all_pick", 10 | "balanced": true 11 | }, 12 | "2": { 13 | "id": 2, 14 | "name": "game_mode_captains_mode", 15 | "balanced": true 16 | }, 17 | "3": { 18 | "id": 3, 19 | "name": "game_mode_random_draft", 20 | "balanced": true 21 | }, 22 | "4": { 23 | "id": 4, 24 | "name": "game_mode_single_draft", 25 | "balanced": true 26 | }, 27 | "5": { 28 | "id": 5, 29 | "name": "game_mode_all_random", 30 | "balanced": true 31 | }, 32 | "6": { 33 | "id": 6, 34 | "name": "game_mode_intro" 35 | }, 36 | "7": { 37 | "id": 7, 38 | "name": "game_mode_diretide" 39 | }, 40 | "8": { 41 | "id": 8, 42 | "name": "game_mode_reverse_captains_mode" 43 | }, 44 | "9": { 45 | "id": 9, 46 | "name": "game_mode_greeviling" 47 | }, 48 | "10": { 49 | "id": 10, 50 | "name": "game_mode_tutorial" 51 | }, 52 | "11": { 53 | "id": 11, 54 | "name": "game_mode_mid_only" 55 | }, 56 | "12": { 57 | "id": 12, 58 | "name": "game_mode_least_played", 59 | "balanced": true 60 | }, 61 | "13": { 62 | "id": 13, 63 | "name": "game_mode_limited_heroes" 64 | }, 65 | "14": { 66 | "id": 14, 67 | "name": "game_mode_compendium_matchmaking" 68 | }, 69 | "15": { 70 | "id": 15, 71 | "name": "game_mode_custom" 72 | }, 73 | "16": { 74 | "id": 16, 75 | "name": "game_mode_captains_draft", 76 | "balanced": true 77 | }, 78 | "17": { 79 | "id": 17, 80 | "name": "game_mode_balanced_draft", 81 | "balanced": true 82 | }, 83 | "18": { 84 | "id": 18, 85 | "name": "game_mode_ability_draft" 86 | }, 87 | "19": { 88 | "id": 19, 89 | "name": "game_mode_event" 90 | }, 91 | "20": { 92 | "id": 20, 93 | "name": "game_mode_all_random_death_match" 94 | }, 95 | "21": { 96 | "id": 21, 97 | "name": "game_mode_1v1_mid" 98 | }, 99 | "22": { 100 | "id": 22, 101 | "name": "game_mode_all_draft", 102 | "balanced": true 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /game_data/neutral_abilities.json: -------------------------------------------------------------------------------- 1 | { 2 | "dark_troll_warlord_ensnare": { 3 | "img": "/assets/images/dota2/neutral_abilities/dark_troll_warlord_ensnare.png" 4 | }, 5 | "ogre_magi_frost_armor": { 6 | "img": "/assets/images/dota2/neutral_abilities/ogre_magi_frost_armor.png" 7 | }, 8 | "polar_furbolg_ursa_warrior_thunder_clap": { 9 | "img": "/assets/images/dota2/neutral_abilities/polar_furbolg_ursa_warrior_thunder_clap.png" 10 | }, 11 | "centaur_khan_war_stomp": { 12 | "img": "/assets/images/dota2/neutral_abilities/centaur_khan_war_stomp.png" 13 | }, 14 | "dark_troll_warlord_raise_dead": { 15 | "img": "/assets/images/dota2/neutral_abilities/dark_troll_warlord_raise_dead.png" 16 | }, 17 | "tornado_tempest": { 18 | "img": "/assets/images/dota2/neutral_abilities/enraged_wildkin_tornado.png" 19 | }, 20 | "mud_golem_hurl_boulder": { 21 | "img": "/assets/images/dota2/neutral_abilities/mud_golem_hurl_boulder.png" 22 | }, 23 | "satyr_trickster_purge": { 24 | "img": "/assets/images/dota2/neutral_abilities/satyr_trickster_purge.png" 25 | }, 26 | "satyr_soulstealer_mana_burn": { 27 | "img": "/assets/images/dota2/neutral_abilities/satyr_soulstealer_mana_burn.png" 28 | }, 29 | "satyr_hellcaller_shockwave": { 30 | "img": "/assets/images/dota2/neutral_abilities/satyr_hellcaller_shockwave.png" 31 | }, 32 | "harpy_storm_chain_lightning": { 33 | "img": "/assets/images/dota2/neutral_abilities/harpy_storm_chain_lightning.png" 34 | }, 35 | "hill_troll_priest_heal": { 36 | "img": "/assets/images/dota2/neutral_abilities/hill_troll_priest_heal.png" 37 | }, 38 | "visage_summon_familiars_stone_form": { 39 | "img": "/assets/images/dota2/neutral_abilities/visage_summon_familiars_stone_form.png" 40 | }, 41 | "necronomicon_warrior_mana_break": { 42 | "img": "/assets/images/dota2/neutral_abilities/necronomicon_warrior_mana_break.png" 43 | }, 44 | "necronomicon_warrior_last_will": { 45 | "img": "/assets/images/dota2/neutral_abilities/necronomicon_warrior_last_will.png" 46 | }, 47 | "necronomicon_warrior_true_sight": { 48 | "img": "/assets/images/dota2/neutral_abilities/necronomicon_warrior_true_sight.png" 49 | }, 50 | "necronomicon_archer_mana_burn": { 51 | "img": "/assets/images/dota2/neutral_abilities/necronomicon_archer_mana_burn.png" 52 | }, 53 | "necronomicon_archer_archer_aura": { 54 | "img": "/assets/images/dota2/neutral_abilities/necronomicon_archer_archer_aura.png" 55 | }, 56 | "doom_bringer_infernal_blade": { 57 | "img": "/assets/images/dota2/neutral_abilities/doom_bringer_infernal_blade.png" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /danger/tower_danger.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous, iSarCasm 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | local TowerDanger = {} 7 | TowerDanger.name = "tower" 8 | ------------------------------------------ 9 | 10 | local VectorHelper = require(GetScriptDirectory().."/helper/vector_helper") 11 | local Game = require(GetScriptDirectory().."/game") 12 | 13 | ----------------------------------------- 14 | 15 | local DANGER_TOWER = 15 16 | local DANGER_TOWER_FAR = 100 17 | 18 | ------------------------------------------ 19 | 20 | function TowerDanger:OfLocation( vLocation, team ) 21 | local all_towers = Game:GetTowersForTeam(team) 22 | if (#all_towers == 0) then 23 | return 0 24 | end 25 | local total_danger = 0 26 | for i = 1, #all_towers do 27 | local tower = all_towers[i] 28 | total_danger = total_danger + self:Power(GetUnitToLocationDistance(tower, vLocation)) 29 | end 30 | return total_danger 31 | end 32 | 33 | function TowerDanger:Power(distance) 34 | if (distance < 800) then -- below attack range (actual tower range is 700 + ~100 for bounding radius) 35 | return DANGER_TOWER 36 | elseif (distance < 5000) then -- 14000 is random range from the tower which is `kinda safe` 37 | return DANGER_TOWER_FAR / (distance*distance) 38 | else 39 | return 0 40 | end 41 | end 42 | 43 | function TowerDanger:PowerDelta(team, unit, distance) 44 | self.TowerVector = Vector(0, 0) 45 | 46 | local total_delta = 0 47 | local all_towers = Game:GetTowersForTeam(team) 48 | 49 | if (#all_towers == 0) then 50 | return 0 51 | end 52 | 53 | for i = 1, #all_towers do 54 | local tower = all_towers[i] 55 | local current_distance = GetUnitToLocationDistance(unit, tower:GetLocation()) 56 | local delta_distance = ((team == GetTeam()) and -distance or distance) 57 | local delta = math.abs(self:Power(Max(1, current_distance + delta_distance)) - self:Power(current_distance)) 58 | total_delta = total_delta + delta 59 | self.TowerVector = self.TowerVector + tower:GetLocation() * delta 60 | end 61 | self.TowerVector = self.TowerVector / total_delta 62 | return total_delta 63 | end 64 | 65 | function TowerDanger:Location(team) 66 | return self.TowerVector 67 | end 68 | 69 | ------------------------------------------ 70 | 71 | return TowerDanger -------------------------------------------------------------------------------- /danger/hero_danger.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous, iSarCasm 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | local HeroDanger = {} 7 | HeroDanger.name = "hero" 8 | 9 | ------------------------------------------ 10 | 11 | local VectorHelper = require(GetScriptDirectory().."/dev/helper/vector_helper") 12 | local Game = require(GetScriptDirectory().."/dev/game") 13 | 14 | ------------------------------------------ 15 | 16 | function HeroDanger:Power(distance) 17 | if (distance < 800) then 18 | return 0.0005 / (distance*distance) 19 | elseif (distance < 1000) then 20 | return 0.000025 / (distance*distance) 21 | else 22 | return 0 23 | end 24 | end 25 | 26 | function HeroDanger:OfLocation( vLocation, team ) 27 | local bEnemies = (team == GetEnemyTeam()) 28 | local all_heroes = MyGetNearbyHeroes(1599, bEnemies) 29 | if (#all_heroes == 0 or ((not bEnemies) and #all_heroes == 1)) then 30 | return 0 31 | end 32 | local total_danger = 0 33 | for i = 1, #all_heroes do 34 | local hero = all_heroes[i] 35 | total_danger = total_danger + self:Power(GetUnitToLocationDistance(hero, vLocation)) 36 | end 37 | return total_danger 38 | end 39 | 40 | function HeroDanger:PowerDelta(team, unit, distance) 41 | -- print("hero danger") 42 | self.HeroLocation = Vector(0, 0) 43 | 44 | local total_delta = 0 45 | local bEnemies = (team == GetEnemyTeam()) 46 | local all_heroes = MyGetNearbyHeroes(1599, bEnemies) 47 | if (#all_heroes == 0 or ((not bEnemies) and #all_heroes == 1)) then 48 | return 0 49 | end 50 | 51 | for i = 1, #all_heroes do 52 | local hero = all_heroes[i] 53 | if (hero ~= GetBot()) then 54 | local current_distance = GetUnitToLocationDistance(unit, hero:GetLocation()) 55 | local delta_distance = ((team == GetTeam()) and -distance or distance) 56 | local delta = math.abs(self:Power(Max(1, current_distance + delta_distance)) - self:Power(current_distance)) 57 | total_delta = total_delta + delta 58 | -- print("delta "..delta) 59 | -- print(hero:GetLocation()) 60 | self.HeroLocation = self.HeroLocation + hero:GetLocation() * delta 61 | end 62 | end 63 | self.HeroLocation = self.HeroLocation / total_delta 64 | -- print("total delta "..total_delta) 65 | return total_delta 66 | end 67 | 68 | function HeroDanger:Location(team) 69 | -- print("hero location") 70 | -- print(self.HeroLocation) 71 | return self.HeroLocation 72 | end 73 | 74 | ------------------------------------------ 75 | 76 | return HeroDanger -------------------------------------------------------------------------------- /helper/unit_helper.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHORS: iSarCasm, Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | --- IMPORTS 7 | ------------------------------------------------- 8 | 9 | ------------------------------------------------- 10 | 11 | local UnitHelper = {} 12 | 13 | -- this can be used for unit reaching another unit or projectile reaching a unit 14 | function UnitHelper:TimeToReachTarget( hUnit, hTarget, fSpeed ) 15 | if not hUnit:IsNull() then 16 | local speed = fSpeed or hUnit:GetCurrentMovementSpeed() 17 | if not hTarget:IsNull() then 18 | return GetUnitToUnitDistance( hUnit, hTarget ) / speed 19 | end 20 | end 21 | return VERY_HIGH_INT 22 | end 23 | 24 | function UnitHelper:IsRanged( hUnit ) 25 | return ( hUnit:GetAttackRange() == 300 ) 26 | end 27 | 28 | function UnitHelper:IsTargetMagicImmune( hUnit ) 29 | return hUnit:IsInvulnerable() or hUnit:IsMagicImmune() 30 | end 31 | 32 | function UnitHelper:IsCrowdControlled( hUnit ) 33 | return hUnit:IsRooted() or hUnit:IsHexed() or hUnit:IsStunned() 34 | end 35 | 36 | function UnitHelper:IsUnableToCast( hUnit ) 37 | return UnitHelper:IsCrowdControlled(hUnit) or hUnit:IsNightmared() or hUnit:IsSilenced() 38 | end 39 | 40 | function UnitHelper:IsUnitCrowdControlled( hUnit ) 41 | return UnitHelper:IsCrowdControlled(hUnit) or hUnit:IsNightmared() or 42 | hUnit:IsDisarmed() or hUnit:IsBlind() or hUnit:IsSilenced() or hUnit:IsMuted() 43 | end 44 | 45 | function UnitHelper:UnitHasBreakableBuff( hUnit ) 46 | if not ValidTarget(hUnit) then return false end 47 | 48 | if hUnit:HasModifier("modifier_clarity_potion") or 49 | hUnit:HasModifier("modifier_flask_healing") or 50 | hUnit:HasModifier("modifier_bottle_regeneration") then 51 | return true 52 | end 53 | return false 54 | end 55 | 56 | function UnitHelper:DistanceFromNearestShop( hUnit ) 57 | local vecShops = { 58 | GetShopLocation(TEAM_RADIANT, SHOP_HOME), 59 | GetShopLocation(TEAM_DIRE, SHOP_HOME), 60 | GetShopLocation(TEAM_NONE, SHOP_SECRET), 61 | GetShopLocation(TEAM_NONE, SHOP_SECRET_2), 62 | GetShopLocation(TEAM_NONE, SHOP_SIDE), 63 | GetShopLocation(TEAM_NONE, SHOP_SIDE_2) 64 | } 65 | 66 | local dist = VERY_HIGH_INT 67 | for _, vecShop in pairs(vecShops) do 68 | local thisDist = GetUnitToLocationDistance(hUnit, vecShop) 69 | if thisDist < SHOP_USE_DISTANCE then 70 | return thisDist 71 | end 72 | 73 | if thisDist < dist then 74 | dist = thisDist 75 | end 76 | end 77 | 78 | return dist 79 | end 80 | 81 | return UnitHelper 82 | -------------------------------------------------------------------------------- /webserver/hero.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | from items import ItemKB 4 | 5 | class Unit(object): 6 | def __init__(self, name, id, team, jsonData): 7 | self.jsonReply = None 8 | 9 | self.name = name 10 | self.id = id 11 | self.team = team 12 | 13 | self.health = jsonData['Health'] 14 | self.max_health = jsonData['MaxHealth'] 15 | 16 | self.mana = jsonData['Mana'] 17 | self.max_mana = jsonData['MaxMana'] 18 | 19 | self.move_speed = jsonData['MS'] 20 | 21 | self.location = (jsonData['X'], jsonData['Y'], jsonData['Z']) 22 | 23 | def __repr__(self): 24 | str = 'Unit ID: %d, %s - %s' % (self.id, self.name, self.team) 25 | str += '\tHealth: %d / %d\n' % (self.health, self.max_health) 26 | return str 27 | 28 | class Hero(Unit): 29 | def __init__(self, name, id, team, jsonData): 30 | Unit.__init__(self, name, id, team, jsonData) 31 | self.health_regen = jsonData['HealthReg'] 32 | self.mana_regen = jsonData['ManaReg'] 33 | self.level = jsonData['Level'] 34 | self.gold = jsonData['Gold'] 35 | self.ability_pts = jsonData['AP'] 36 | self.next_abilities = jsonData['NextAbs'] 37 | self.next_items = jsonData['NextItems'] 38 | 39 | self.items = [] 40 | for item in jsonData['Items']: 41 | self.items.append(item) 42 | 43 | def __repr__(self): 44 | str = 'Hero ID: %d, %s - %d :: %s\n' % (self.id, self.name, self.level, self.team) 45 | str += '\tHealth: %d / %d (regen: %f)\n' % (self.health, self.max_health, self.health_regen) 46 | str += '\tMana: %d / %d (regen: %f)\n' % (self.mana, self.max_mana, self.mana_regen) 47 | str += '\tLocation: <%f, %f, %f>\n' % (self.location[0],self.location[1],self.location[2]) 48 | str += '\tGold: %f\n' % (self.gold) 49 | return str 50 | 51 | def canLevelUp(self): 52 | return self.ability_pts > 0 53 | 54 | def pickAbility(self): 55 | # TODO - Fill Me Out 56 | level_abs = None # <-- fill this with scraped info 57 | self.jsonReply['LevelAbs'] = level_abs 58 | 59 | def needsStartingItems(self): 60 | return len(self.items) == 0 61 | 62 | def sendStartingItems(self): 63 | itemkb = ItemKB() 64 | start_items = itemkb.getStartingItems(self.name, "Safe Lane") 65 | self.jsonReply['StartItems'] = start_items 66 | 67 | def processHero(self): 68 | self.jsonReply = {} 69 | 70 | if self.canLevelUp(): 71 | self.pickAbility() 72 | 73 | if self.needsStartingItems(): 74 | self.sendStartingItems() 75 | 76 | def getReply(self): 77 | if len(self.jsonReply) == 0: 78 | return None 79 | return self.jsonReply -------------------------------------------------------------------------------- /debug.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | local X = {} 7 | 8 | function X.myPrint(...) 9 | local args = {...} 10 | 11 | if #args > 0 then 12 | local hBot = GetBot() 13 | local botname = "" 14 | 15 | if hBot then 16 | if hBot.myBot then 17 | botname = GetBot().mybot.Name 18 | else 19 | botname = hBot:GetUnitName() 20 | end 21 | end 22 | 23 | local msg = tostring(Round(GameTime(), 5)).." [" .. botname .. "]: " 24 | for i,v in ipairs(args) do 25 | msg = msg .. tostring(v) 26 | end 27 | 28 | --uncomment to only see messages by bots mentioned underneath 29 | --if botname == "invoker" then --or botname == "viper" then 30 | if string.len(msg) > 8000 then 31 | print('[LEN]: ', string.len(msg), ' :: ', msg) 32 | else 33 | print(msg) 34 | end 35 | --end 36 | end 37 | end 38 | 39 | function X.pause(...) 40 | X.myPrint(...) 41 | DebugPause() 42 | end 43 | 44 | local last_draw_time = -500 45 | 46 | local bot_states = {} 47 | 48 | local LINE_HEIGHT = 10 49 | local TITLE_VALUE_DELTA_X = 10 50 | 51 | local BOT_STATES_MAX_LINES = 2 52 | local BOT_STATES_X = 1600 53 | local BOT_STATES_Y = 100 54 | 55 | local function SetBotState(name, line, text) 56 | if line < 1 or BOT_STATES_MAX_LINES > 2 then 57 | print("SetBotState: line out of bounds!") 58 | return 59 | end 60 | if bot_states[name] == nil then 61 | bot_states[name] = {} 62 | end 63 | bot_states[name][line] = text 64 | end 65 | 66 | local function updateBotStates() 67 | local listAllies = GetUnitList(UNIT_LIST_ALLIED_HEROES) 68 | for _, ally in pairs(listAllies) do 69 | if ally:IsBot() and not ally:IsIllusion() and ally.SelfRef then 70 | local hMyBot = ally.SelfRef 71 | local mode = hMyBot:getCurrentMode() 72 | local state = mode:GetName() 73 | SetBotState(hMyBot.Name, 1, state) 74 | end 75 | end 76 | end 77 | 78 | function X.draw() 79 | if last_draw_time > GameTime() - 0.010 then return end 80 | last_draw_time = GameTime() 81 | 82 | updateBotStates() 83 | 84 | local y = BOT_STATES_Y 85 | for name, v in pairs(bot_states) do 86 | DebugDrawText( BOT_STATES_X, y, name, 255, 0, 0 ) 87 | for line,text in pairs(v) do 88 | DebugDrawText( BOT_STATES_X + TITLE_VALUE_DELTA_X, y + line * LINE_HEIGHT, text, 255, 0, 0 ) 89 | end 90 | y = y + (BOT_STATES_MAX_LINES + 1) * LINE_HEIGHT 91 | end 92 | end 93 | 94 | function X.DebugStats() 95 | DebugDrawText(25, 50, "LH/D = "..GetBot():GetLastHits().."/"..GetBot():GetDenies(), 255, 255, 255); 96 | end 97 | 98 | return X -------------------------------------------------------------------------------- /actions/complex_actions/action_stacking.lua: -------------------------------------------------------------------------------- 1 | local next = next -- https://stackoverflow.com/a/1252776 2 | local VectorHelper = require(GetScriptDirectory().."/helper/vector_helper") 3 | local UnitHelper = require(GetScriptDirectory().."/helper/unit_helper") 4 | local Stacking = {} 5 | 6 | Stacking.Name = "Stacking" 7 | 8 | ------------------------------------------------- 9 | 10 | function Stacking:Call(bot, camp_location, camp_timing, camp_wait_at, camp_pull_to) 11 | -- Returns false if it still has future stacking actions to accomplish 12 | -- Returns true if it has finished the stack 13 | 14 | --TODO check if need to update timings based on range/projectile speed/animation speed? 15 | local current_action = bot:GetCurrentActionType() 16 | dbg.myPrint("In Stacking. Current action: ", current_action) 17 | 18 | -- treat both these options same as nil option from initial call of stack 19 | if current_action == BOT_ACTION_TYPE_NONE or current_action == BOT_ACTION_TYPE_IDLE then 20 | current_action = nil 21 | end 22 | 23 | -- action nil check necessary 24 | -- as otherwise passing over the pull_to spot whilst moving to stack will inadvertently cancel it 25 | if current_action == nil and VectorHelper:GetDistance(bot:GetLocation(), camp_pull_to) < 200 then 26 | return true 27 | end 28 | 29 | 30 | if GetSeconds() < camp_timing then -- It's not time to pull/stack yet 31 | -- If we're already at the waiting spot, nothing to do 32 | if VectorHelper:GetDistance(bot:GetLocation(), camp_wait_at) > 1 then 33 | bot:Action_MoveToLocation(camp_wait_at) 34 | end 35 | else 36 | -- take minimum distance between Attack Range and Vision Range (at night we can attack farther than we can see) 37 | local attack_range = math.min(bot:GetAttackRange(), bot:GetCurrentVisionRange()) 38 | if current_action == nil and not UnitHelper:isRanged(bot) then -- again could attach isRanged to the bot table? 39 | bot:Action_MoveToLocation(camp_location) 40 | bot:ActionQueue_MoveToLocation(camp_pull_to) 41 | else 42 | -- two options here 43 | -- 1) attack move. we track when projectile in air then move back 44 | -- 2) we check until the camp is visible whilst moving towards it (trees can obscure, especially at night) 45 | -- once neutral visible we do AttackUnit(bOnce=true), then queue a move back 46 | if current_action == nil then 47 | bot:Action_MoveLocation(camp_location) 48 | elseif current_action == BOT_ACTION_TYPE_MOVE then 49 | local first_neutral = next(bot:GetNearbyNeutralCreeps(attack_range)) 50 | if first_neutral ~= nil then 51 | bot:Action_AttackUnit(first_neutral, true) 52 | bot:ActionQueue_MoveToLocation(camp_pull_to) 53 | end 54 | end 55 | end 56 | end 57 | return false 58 | end 59 | 60 | return Stacking 61 | ------------------------------------------------- -------------------------------------------------------------------------------- /webserver/items.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from lxml import etree, html 3 | import traceback 4 | import time 5 | from web_dota_mapper import WebMapper 6 | #If Selenium/Chrome 7 | #from selenium import webdriver 8 | #from selenium.webdriver.common.by import By 9 | 10 | class ItemKB(): 11 | def __init__(self): 12 | # Get a copy of the default headers that requests would use 13 | self.headers = requests.utils.default_headers() 14 | 15 | self.headers.update({'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36'}) #Need a user agent or they 429 you 16 | #self.driver = webdriver.Chrome() #If using selenium/chrome #If Selenium/Chrome 17 | self.mapper = WebMapper() 18 | pass 19 | 20 | def getStartingItems(self,heroName,desiredLane): 21 | try: 22 | resp = requests.get("https://www.dotabuff.com/heroes/"+heroName+"/guides", headers=self.headers) 23 | resp.raise_for_status() #throw exception for bad status codes 24 | hdoc = html.fromstring(resp.content) 25 | 26 | #Get Stats for all players on page 27 | statBlocks = hdoc.xpath('//div[@class="r-stats-grid"]') 28 | 29 | #We get only the first stat block that matches our name and lane 30 | for statBlock in statBlocks: 31 | 32 | #Get lane 33 | lane = statBlock.xpath('.//i[contains(@class,"lane-icon")]/../text()')[0].strip() 34 | if lane != desiredLane: 35 | continue 36 | 37 | #Get item purchase sets for player 0 38 | itemSets = statBlock.xpath('.//div[@class="kv r-none-mobile"]') 39 | 40 | #starting items are in first itemset slot 41 | startingItems = [ href.split('/')[-1] for href in itemSets[0].xpath('.//a/@href')] 42 | 43 | #convert to internal game names 44 | startingItems = [ self.mapper.item_name(name) for name in startingItems] 45 | 46 | return startingItems 47 | 48 | return None 49 | except: 50 | print("Exception Occured:{}".format(traceback.format_exc())) 51 | 52 | 53 | #If Selenium/Chrome 54 | #A Solutoin that works using selenium/Chrome. Requires lots of setup and latency but helps abate scraping detection 55 | #THIS IS OUTDATED, BUT LEFT AS A REMINDER 56 | def __chrome_getStartingItems(self,heroName): 57 | try: 58 | self.driver.get("https://www.dotabuff.com/heroes/"+heroName+"/guides") 59 | #time.sleep(2) 60 | playerStats = self.driver.find_elements(By.XPATH,'//div[@class="r-stats-grid"]') 61 | itemSets = playerStats[0].find_elements(By.XPATH,'.//div[@class="kv r-none-mobile"]') 62 | startingItems = [ element.get_attribute('href').split('/')[-1].replace('-','_') for element in itemSets[0].find_elements(By.XPATH,'.//a')] 63 | 64 | #convert to internal game names 65 | startingItems = [ mapper.item_name(name) for name in startingItems] 66 | 67 | return startingItems 68 | except: 69 | print("Exception Occured:{}".format(traceback.format_exc())) 70 | finally: 71 | #self.driver.quit() #If Selenium/Chrome 72 | pass 73 | 74 | 75 | def getNextItem(self): 76 | pass 77 | 78 | 79 | if __name__ == "__main__": 80 | itemkb = ItemKB() 81 | print(itemkb.getStartingItems("bristleback","Off Lane")) 82 | -------------------------------------------------------------------------------- /hero_selection.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | function InTable (tab, val) 7 | if not tab then return false end 8 | for index, value in ipairs (tab) do 9 | if value == val then 10 | return true 11 | end 12 | end 13 | 14 | return false 15 | end 16 | 17 | local Pos_1_Pool = { 18 | "npc_dota_hero_antimage" 19 | } 20 | 21 | local Pos_2_Pool = { 22 | --"npc_dota_hero_invoker", 23 | "npc_dota_hero_lina" 24 | } 25 | 26 | local Pos_3_Pool = { 27 | "npc_dota_hero_viper" 28 | } 29 | 30 | local Pos_4_Pool = { 31 | } 32 | 33 | local Pos_5_Pool = { 34 | "npc_dota_hero_crystal_maiden" 35 | } 36 | 37 | -- roamers, junglers 38 | local Pos_X_Pool = { 39 | "npc_dota_hero_bloodseeker" 40 | } 41 | 42 | local BotPool = { 43 | Pos_1_Pool, -- hard carry 44 | Pos_5_Pool, -- hard support 45 | {unpack(Pos_4_Pool), unpack(Pos_X_Pool)}, -- semi-support, roamer, jungler 46 | Pos_2_Pool, -- mid 47 | Pos_3_Pool -- offlane 48 | } 49 | 50 | local BotPool2 = { 51 | Pos_1_Pool, -- hard carry 52 | Pos_5_Pool, -- hard support 53 | {unpack(Pos_4_Pool), unpack(Pos_X_Pool)}, -- semi-support, roamer, jungler 54 | Pos_2_Pool, -- mid 55 | Pos_3_Pool -- offlane 56 | } 57 | 58 | local chosenHeroes = {} 59 | 60 | local function HumansReady() 61 | local numHumans = 0 62 | local numHumansReady = 0 63 | local IDs = GetTeamPlayers(GetTeam()) 64 | for index, id in pairs(IDs) do 65 | if not IsPlayerBot(id) then 66 | local humanBotName = GetSelectedHeroName(id) 67 | numHumans = numHumans + 1 68 | 69 | -- check bot name to see if human made selection 70 | if humanBotName ~= "" then 71 | numHumansReady = numHumansReady + 1 72 | end 73 | end 74 | end 75 | 76 | return numHumansReady == numHumans 77 | end 78 | 79 | function Think() 80 | gs = GetGameState() 81 | --print( "game state: ", gs ) 82 | 83 | if ( gs == GAME_STATE_HERO_SELECTION ) then 84 | a = GetGameMode() 85 | 86 | if ( a == GAMEMODE_AP ) then 87 | --print ( "All Pick" ) 88 | 89 | if GameTime() < 45 and not HumansReady() then 90 | return 91 | end 92 | 93 | local IDs = GetTeamPlayers(GetTeam()) 94 | for index, id in pairs(IDs) do 95 | if IsPlayerBot(id) and IsPlayerInHeroSelectionControl(id) and GetSelectedHeroName(id) == "" then 96 | local pool = nil 97 | if GetTeam() == TEAM_RADIANT then 98 | pool = BotPool 99 | else 100 | pool = BotPool2 101 | end 102 | local sizeOfPool = #pool[index] 103 | for j = 1, sizeOfPool do 104 | randomHero = pool[index][RandomInt(1, sizeOfPool)] 105 | if not InTable(chosenHeroes, randomHero) then 106 | table.insert(chosenHeroes, randomHero) 107 | SelectHero(id, randomHero) 108 | break 109 | end 110 | end 111 | end 112 | end 113 | end 114 | end 115 | end 116 | 117 | function UpdateLaneAssignments() 118 | if ( GetTeam() == TEAM_RADIANT ) then 119 | return { 120 | [1] = LANE_BOT, 121 | [2] = LANE_BOT, 122 | [3] = LANE_BOT, 123 | [4] = LANE_MID, 124 | [5] = LANE_TOP, 125 | } 126 | elseif ( GetTeam() == TEAM_DIRE ) then 127 | return { 128 | [1] = LANE_TOP, 129 | [2] = LANE_TOP, 130 | [3] = LANE_TOP, 131 | [4] = LANE_MID, 132 | [5] = LANE_BOT, 133 | } 134 | end 135 | end 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dota2-WebAI 2 | 3 | ### What is this 4 | This will be my attempt at writing Dota 2 bots using a webserver backend that 5 | will provide all the decision making logic for the bots. The bots will then 6 | be instructed what they are supposed to be doing at the *macro*-level, and 7 | know how to execute those instructions inside the Dota 2 world through the 8 | exposed Bot-Scripting API. 9 | 10 | ### What's the Plan? 11 | The plan is to have the web-based Python framework control all the high-level/meta 12 | decisions for the bots. It will ingest raw data about the game provided by the 13 | Dota 2 API (i.e., hero/unit information, building status information, game 14 | progression information, etc.) and decided what each bot on the team should 15 | be doing at the high-level. Each bot will still have a LUA implementation 16 | in-game and knowledge of how to execute directives passed down from the 17 | web-based game decision-making system. 18 | 19 | For example - the web-based system might tell the bot "farm BOT_LANE". The bot 20 | will know how to do this by issuing appropriate move or teleport commands to get 21 | there, then how to properly last hit/deny as the web-based framework will not 22 | tell the bot how to last hit, when to last hit, when to deny, etc. At least not 23 | for now, maybe later if we truly move into the Reinforcement Learning AI. 24 | 25 | Eventually, I might even have a web-based GUI that will show you a replica of 26 | your game as represented by the world state of the web-server and allow you to 27 | manual issue commands (or toggle toggles) to get the bots to take certain actions. 28 | As an example, I might have a button that you can press on the GUI that will 29 | instruct the bots to go to Roshan. 30 | 31 | ### Where is the AI? 32 | It won't exist for now until all the plumbing is complete; meaning, until I 33 | have basic bot play-ability and hard-coded logic working as orchestrated by 34 | the web-based back-end framework. Once that is complete I can start leveraging 35 | available AI/Machine Learning/Reinforcement Learning Python libraries to start 36 | learning over certain aspects of the game (like formations, fight priority, 37 | etc.). The plan is to eventually get there, but it won't be in the near future. 38 | However, the whole reason and design choice of this project is TO GET THERE! 39 | 40 | For even the possibility of having future AI in this I needed a back-end server 41 | implementation of the logic so that I can in the future allow for proxy servers 42 | to feed a final aggregator server (perhaps existing in AWS). Then everyone using 43 | this bot can run Python scripts I will write which will redirect their localhost 44 | servers to the main server so that it can learn "at scale". This is because no 45 | single instance will most likely have enough test data (aka games played) to 46 | really train on and analyze. 47 | 48 | ### What about your Dota2-FullOverwrite Project? 49 | It is not going away currently, but ultimately it will become this new project. 50 | I plan to leverage a lot of the code from that project into this one. There was a 51 | reason I did a "full overwrite" and that is to allow for this project to eventually 52 | happen. The way that project was coded allows for easy transition of the control 53 | logic to occur. Much of that code will become the in-game directive execution API 54 | for the bots as instructed by the web-server. 55 | 56 | I needed to port the decision logic to Python to leverage multi-threading, existence 57 | of many research-based 3rd-party libraries, etc. in order to eventually reach the 58 | dream of Dota 2 AI. It could have been C/C++/Java, but honestly, it essentially 59 | will be anyways. I say this because most/many Python AI libraries leverage numpy 60 | or pandas which is all C++ code under-the-hood anyways. 61 | 62 | ### Can I Contribute? 63 | Sure. As with my other project, you are welcome to help. All I ask is that you drop 64 | me a note saying what you are doing and when you expect to be done (and if you 65 | happen to decide you don't have time, that's fine too, just let me know). If you 66 | have no idea how to help, just ask. 67 | 68 | This is a learning experience for me and a fun one (hopefully) as I'm passionate 69 | about AI. I tend to be very active about things I like doing so I am typically 70 | around to answer questions, discuss strategy, or just even chat about life. 71 | 72 | ### How to run 73 | Run the *main.py* file provided (code is Python 2.5+ and 3.0+ compatible) 74 | [Then follow these directions](https://github.com/Nostrademous/Dota2-FullOverwrite/wiki/Workflow-for-Debugging-Bots) -------------------------------------------------------------------------------- /game_data/item_ids.json: -------------------------------------------------------------------------------- 1 | { 2 | "1": "blink", 3 | "2": "blades_of_attack", 4 | "3": "broadsword", 5 | "4": "chainmail", 6 | "5": "claymore", 7 | "6": "helm_of_iron_will", 8 | "7": "javelin", 9 | "8": "mithril_hammer", 10 | "9": "platemail", 11 | "10": "quarterstaff", 12 | "11": "quelling_blade", 13 | "12": "ring_of_protection", 14 | "13": "gauntlets", 15 | "14": "slippers", 16 | "15": "mantle", 17 | "16": "branches", 18 | "17": "belt_of_strength", 19 | "18": "boots_of_elves", 20 | "19": "robe", 21 | "20": "circlet", 22 | "21": "ogre_axe", 23 | "22": "blade_of_alacrity", 24 | "23": "staff_of_wizardry", 25 | "24": "ultimate_orb", 26 | "25": "gloves", 27 | "26": "lifesteal", 28 | "27": "ring_of_regen", 29 | "28": "sobi_mask", 30 | "29": "boots", 31 | "30": "gem", 32 | "31": "cloak", 33 | "32": "talisman_of_evasion", 34 | "33": "cheese", 35 | "34": "magic_stick", 36 | "36": "magic_wand", 37 | "37": "ghost", 38 | "38": "clarity", 39 | "39": "flask", 40 | "40": "dust", 41 | "41": "bottle", 42 | "42": "ward_observer", 43 | "43": "ward_sentry", 44 | "44": "tango", 45 | "45": "courier", 46 | "46": "tpscroll", 47 | "48": "travel_boots", 48 | "50": "phase_boots", 49 | "51": "demon_edge", 50 | "52": "eagle", 51 | "53": "reaver", 52 | "54": "relic", 53 | "55": "hyperstone", 54 | "56": "ring_of_health", 55 | "57": "void_stone", 56 | "58": "mystic_staff", 57 | "59": "energy_booster", 58 | "60": "point_booster", 59 | "61": "vitality_booster", 60 | "63": "power_treads", 61 | "65": "hand_of_midas", 62 | "67": "oblivion_staff", 63 | "69": "pers", 64 | "71": "poor_mans_shield", 65 | "73": "bracer", 66 | "75": "wraith_band", 67 | "77": "null_talisman", 68 | "79": "mekansm", 69 | "81": "vladmir", 70 | "84": "flying_courier", 71 | "86": "buckler", 72 | "88": "ring_of_basilius", 73 | "90": "pipe", 74 | "92": "urn_of_shadows", 75 | "94": "headdress", 76 | "96": "sheepstick", 77 | "98": "orchid", 78 | "100": "cyclone", 79 | "102": "force_staff", 80 | "104": "dagon", 81 | "106": "necronomicon", 82 | "108": "ultimate_scepter", 83 | "110": "refresher", 84 | "112": "assault", 85 | "114": "heart", 86 | "116": "black_king_bar", 87 | "117": "aegis", 88 | "119": "shivas_guard", 89 | "121": "bloodstone", 90 | "123": "sphere", 91 | "125": "vanguard", 92 | "127": "blade_mail", 93 | "129": "soul_booster", 94 | "131": "hood_of_defiance", 95 | "133": "rapier", 96 | "135": "monkey_king_bar", 97 | "137": "radiance", 98 | "139": "butterfly", 99 | "141": "greater_crit", 100 | "143": "basher", 101 | "145": "bfury", 102 | "147": "manta", 103 | "149": "lesser_crit", 104 | "151": "armlet", 105 | "152": "invis_sword", 106 | "154": "sange_and_yasha", 107 | "156": "satanic", 108 | "158": "mjollnir", 109 | "160": "skadi", 110 | "162": "sange", 111 | "164": "helm_of_the_dominator", 112 | "166": "maelstrom", 113 | "168": "desolator", 114 | "170": "yasha", 115 | "172": "mask_of_madness", 116 | "174": "diffusal_blade", 117 | "176": "ethereal_blade", 118 | "178": "soul_ring", 119 | "180": "arcane_boots", 120 | "181": "orb_of_venom", 121 | "182": "stout_shield", 122 | "185": "ancient_janggo", 123 | "187": "medallion_of_courage", 124 | "188": "smoke_of_deceit", 125 | "190": "veil_of_discord", 126 | "193": "necronomicon_2", 127 | "194": "necronomicon_3", 128 | "196": "diffusal_blade_2", 129 | "201": "dagon_2", 130 | "202": "dagon_3", 131 | "203": "dagon_4", 132 | "204": "dagon_5", 133 | "206": "rod_of_atos", 134 | "208": "abyssal_blade", 135 | "210": "heavens_halberd", 136 | "212": "ring_of_aquila", 137 | "214": "tranquil_boots", 138 | "215": "shadow_amulet", 139 | "216": "enchanted_mango", 140 | "218": "ward_dispenser", 141 | "220": "travel_boots_2", 142 | "226": "lotus_orb", 143 | "229": "solar_crest", 144 | "231": "guardian_greaves", 145 | "232": "aether_lens", 146 | "235": "octarine_core", 147 | "236": "dragon_lance", 148 | "237": "faerie_fire", 149 | "239": "iron_talon", 150 | "240": "blight_stone", 151 | "241": "tango_single", 152 | "242": "crimson_guard", 153 | "244": "wind_lace", 154 | "247": "moon_shard", 155 | "249": "silver_edge", 156 | "250": "bloodthorn", 157 | "252": "echo_sabre", 158 | "254": "glimmer_cape", 159 | "257": "tome_of_knowledge", 160 | "263": "hurricane_pike", 161 | "265": "infused_raindrop", 162 | "1021": "river_painter", 163 | "1022": "river_painter2", 164 | "1023": "river_painter3", 165 | "1024": "river_painter4", 166 | "1025": "river_painter5", 167 | "1026": "river_painter6", 168 | "1027": "river_painter7" 169 | } -------------------------------------------------------------------------------- /helper/courier_helper.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHORS: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | --- IMPORTS 7 | ------------------------------------------------- 8 | 9 | local actionMoveLoc = require( GetScriptDirectory().."/actions/basic_actions/action_move_to_location" ) 10 | local actionMoveUnit = require( GetScriptDirectory().."/actions/basic_actions/action_move_to_unit" ) 11 | 12 | ------------------------------------------------- 13 | 14 | local CourierHelper = {} 15 | 16 | --- NOTE: This function is probably not necessary as the web-server will make 17 | --- all the decisions about courier availability and usage 18 | function CourierHelper:GetAvailableCourier() 19 | local num = GetNumCouriers() 20 | 21 | if num < 1 then return nil end 22 | 23 | --- NOTE: Couriers are 0-indexed 24 | for i = 0, num do 25 | local hCourier = GetCourier(i) 26 | local courierState = GetCourierState(hCourier) 27 | 28 | if courierState == COURIER_STATE_DEAD then 29 | return nil 30 | elseif courierState == COURIER_STATE_AT_BASE then 31 | return hCourier 32 | end 33 | end 34 | 35 | return nil 36 | end 37 | 38 | function CourierHelper:CourierAction( hHero, nCourierIndex, nAction ) 39 | local hCourier = GetCourier(nCourierIndex) 40 | if hCourier and not GetCourierState(hCourier) == COURIER_STATE_DEAD then 41 | hHero:ActionImmediate_Courier(hCourier, nAction) 42 | end 43 | end 44 | 45 | function CourierHelper:NumEmpyInventorySlots( nCourierIndex ) 46 | local hCourier = GetCourier(nCourierIndex) 47 | local nEmptySlots = 0 48 | for i = 0, 8 do -- 6 inv slots, 3 backpack slots 49 | local hItem = hCourier:GetItemInSlot(i) 50 | if hItem == nil then 51 | nEmptySlots = nEmptySlots + 1 52 | end 53 | end 54 | return nEmptySlots 55 | end 56 | 57 | --- THESE LEVERAGE THE ActionImmediate_Courier() API 58 | function CourierHelper:UseSpeedBurst( hHero, nCourierIndex ) 59 | local hCourier = GetCourier(nCourierIndex) 60 | 61 | if hCourier.lastBurstTime == nil then hCourier.lastBurstTime = -1000.0 end 62 | 63 | if hCourier and IsFlyingCourier(hCourier) and GameTime() > (hCourier.lastBurstTime + 90.0) then 64 | self:CourierAction(hHero, nCourierIndex, COURIER_ACTION_BURST) 65 | hCourier.lastBurstTime = GameTime() 66 | end 67 | end 68 | 69 | function CourierHelper:ReturnToBase( hHero, nCourierIndex ) 70 | self:CourierAction(hHero, nCourierIndex, COURIER_ACTION_RETURN) 71 | end 72 | 73 | function CourierHelper:TransferItems( hHero, nCourierIndex ) 74 | self:CourierAction(hHero, nCourierIndex, COURIER_ACTION_TRANSFER_ITEMS) 75 | end 76 | 77 | function CourierHelper:TakeStashItems( hHero, nCourierIndex ) 78 | self:CourierAction(hHero, nCourierIndex, COURIER_ACTION_TAKE_STASH_ITEMS) 79 | end 80 | 81 | function CourierHelper:TakeAndTransferItems( hHero, nCourierIndex ) 82 | self:CourierAction(hHero, nCourierIndex, COURIER_ACTION_TAKE_AND_TRANSFER_ITEMS) 83 | end 84 | 85 | --- NOTE: TODO - test that doing a unit-scoped:Action*_Move*() works on courier units 86 | function CourierHelper:MoveCourierToLoc( nCourierIndex, vLoc, iType ) 87 | local hCourier = GetCourier(nCourierIndex) 88 | if hCourier and not GetCourierState(hCourier) == COURIER_STATE_DEAD then 89 | actionMoveLoc:Call(hCourier, vLoc, iType) 90 | end 91 | end 92 | 93 | function CourierHelper:MoveCourierToUnit( nCourierIndex, hUnit, iType ) 94 | local hCourier = GetCourier(nCourierIndex) 95 | if hCourier and not GetCourierState(hCourier) == COURIER_STATE_DEAD then 96 | actionMoveUnit:Call(hCourier, hUnit, iType) 97 | end 98 | end 99 | 100 | function CourierHelper:MoveToTopSecretShop( nCourierIndex, iType ) 101 | self:MoveCourierToLoc(nCourierIndex, GetShopLocation(TEAM_NONE, SHOP_SECRET), iType) 102 | end 103 | 104 | function CourierHelper:MoveToTopSideShop( nCourierIndex, iType ) 105 | self:MoveCourierToLoc(nCourierIndex, GetShopLocation(TEAM_NONE, SHOP_SIDE), iType) 106 | end 107 | 108 | function CourierHelper:MoveToBotSecretShop( nCourierIndex, iType ) 109 | self:MoveCourierToLoc(nCourierIndex, GetShopLocation(TEAM_NONE, SHOP_SECRET_2), iType) 110 | end 111 | 112 | function CourierHelper:MoveToBotSideShop( nCourierIndex, iType ) 113 | self:MoveCourierToLoc(nCourierIndex, GetShopLocation(TEAM_NONE, SHOP_SIDE_2), iType) 114 | end 115 | 116 | function CourierHelper:DeathCallback( hTable ) 117 | dbg.myPrint("Courier killed: ", hTable.unit, ", Team: ", hTable.team) 118 | dbg.pause("Courier DeathCallback Called") 119 | end 120 | 121 | InstallCourierDeathCallback( CourierHelper:DeathCallback ) 122 | 123 | return CourierHelper -------------------------------------------------------------------------------- /helper/global_helper.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHORS: iSarCasm, Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | ------------------------------------------------------------------------------- 7 | --- CODE SPECIFIC 8 | ------------------------------------------------------------------------------- 9 | 10 | globalInit = false 11 | 12 | function InitializeGlobalVars() 13 | heroData = require( GetScriptDirectory().."/hero_data" ) 14 | globalInit = true 15 | end 16 | 17 | --- TABLE RELATED 18 | function InTable(tab, val) 19 | if not tab then return false end 20 | for index, value in ipairs (tab) do 21 | if value == val then 22 | return true 23 | end 24 | end 25 | 26 | return false 27 | end 28 | 29 | function PosInTable(tab, val) 30 | for index,value in ipairs(tab) do 31 | if value == val then 32 | return index 33 | end 34 | end 35 | 36 | return -1 37 | end 38 | 39 | --- USED BY: 40 | --- InventoryHelper:BuyItem() 41 | function GetTableKeyNameFromID( hTable, iIndex ) 42 | if hTable == nil or iIndex == nil then 43 | return "nil" 44 | end 45 | 46 | for key, value in pairs(hTable) do 47 | if value == iIndex then 48 | return tostring(key) 49 | end 50 | end 51 | 52 | return nil 53 | end 54 | 55 | --- USED BY: 56 | --- decision.lua - Atomic_BuyItems() 57 | function TableConcat(t1, t2) 58 | for i = 1, #t2 do 59 | t1[#t1+1] = t2[i] 60 | end 61 | return t1 62 | end 63 | 64 | ------------------------------------------------------------------------------- 65 | 66 | --- MATH & TIME RELATED 67 | -- checks if a specific bit is set in a bitmask 68 | function CheckBitmask(bitmask, bit) 69 | return ((bitmask/bit) % 2) >= 1 70 | end 71 | 72 | function GetSeconds() 73 | return math.floor(DotaTime()) % 60 74 | end 75 | 76 | function Round(num, numDecimalPlaces) 77 | local mult = 10^(numDecimalPlaces or 0) 78 | return math.floor(num * mult + 0.5) / mult 79 | end 80 | 81 | ------------------------------------------------------------------------------- 82 | --- DOTA2 SPECIFIC 83 | ------------------------------------------------------------------------------- 84 | 85 | --- TARGET RELATED 86 | function ValidTarget( hUnit ) 87 | -- handle to the unit cannot be nil and null, and unit has to be alive 88 | return hUnit ~= nil and not hUnit:IsNull() and hUnit:IsAlive() 89 | end 90 | 91 | function GetUnitName( hUnit ) 92 | local sName = hUnit:GetUnitName() 93 | return string.sub(sName, 15, string.len(sName)) 94 | end 95 | 96 | --- SHOP RELATED 97 | function GetShop() 98 | if (GetTeam() == TEAM_RADIANT) then 99 | return SHOP_RADIANT 100 | elseif (GetTeam() == TEAM_DIRE) then 101 | return SHOP_DIRE 102 | end 103 | end 104 | 105 | function ShopDistance( hUnit, iShop ) 106 | if (iShop == SHOP_DIRE or iShop == SHOP_RADIANT) then 107 | return hUnit:DistanceFromFountain() 108 | elseif (iShop == SHOP_SIDE_BOT or iShop == SHOP_SIDE_TOP) then 109 | return hUnit:DistanceFromSideShop() 110 | else 111 | return hUnit:DistanceFromSecretShop() 112 | end 113 | end 114 | 115 | --- MAP & GAME ORIENTATION RELATED 116 | function GetEnemyTeam() 117 | if (GetTeam() == TEAM_RADIANT) then 118 | return TEAM_DIRE 119 | elseif (GetTeam() == TEAM_DIRE) then 120 | return TEAM_RADIANT 121 | end 122 | end 123 | 124 | function GetFront(Team, Lane) 125 | return GetLaneFrontLocation(Team, Lane, GetLaneFrontAmount(Team, Lane, true)) 126 | end 127 | 128 | function Safelane() 129 | return ((GetTeam() == TEAM_RADIANT) and LANE_BOT or LANE_TOP) 130 | end 131 | 132 | function Hardlane() 133 | return ((GetTeam() == TEAM_RADIANT) and LANE_TOP or LANE_BOT) 134 | end 135 | 136 | function MyGetNearbyHeroes(range, bEnemies) 137 | if range <= 1600 then 138 | return GetBot():GetNearbyHeroes(1599, bEnemies, BOT_MODE_NONE) 139 | else 140 | local botInfo = GetBot().mybot.botInfo 141 | local result_heroes = {} 142 | local heroes = (bEnemies and botInfo.enemy_heroes or botInfo.ally_heroes) 143 | for i = 1, #heroes do 144 | if (GetUnitToUnitDistance(bot, heroes[i]) < range) then 145 | table.insert(result_heroes, heroes[i]) 146 | end 147 | end 148 | return result_heroes 149 | end 150 | end 151 | 152 | function MyGetNearbyCreeps(range, bEnemy) 153 | if range <= 1600 then 154 | return GetBot():GetNearbyCreeps(range, bEnemy) 155 | else 156 | local botInfo = GetBot().mybot.botInfo 157 | local result_creeps = {} 158 | local creeps = (bEnemy and botInfo.enemy_creeps or botInfo.ally_creeps) 159 | for i = 1, #creeps do 160 | if (GetUnitToUnitDistance(bot, creeps[i]) < range) then 161 | table.insert(result_creeps, creeps[i]) 162 | end 163 | end 164 | return result_creeps 165 | end 166 | end 167 | 168 | 169 | -------------------------------------------------------------------------------- /constants/jungle.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous 3 | --- CONTRIBUTOR: many people, too many to know and list them all 4 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-FullOverwrite 5 | ------------------------------------------------------------------------------- 6 | 7 | -- TODO make camps tables grouping their info? 8 | CAMP_EASY = 1 9 | CAMP_MEDIUM = 2 10 | CAMP_HARD = 3 11 | CAMP_ANCIENT = 4 12 | 13 | VECTOR = "vector" 14 | STACK_TIME = "time" 15 | PRE_STACK_VECTOR = "prestack" 16 | STACK_VECTOR = "stack" 17 | DIFFICULTY = "difficulty" 18 | 19 | --actual camp 20 | RAD_MID_MEDIUM = Vector(-1864.778076, -4351.264160, 128.000000) 21 | RAD_MID_HARD = Vector(-554.121765, -3270.541992, 256.000000) 22 | RAD_MID_ANCIENT = Vector(306.0, -2077.0, 384.0) 23 | RAD_SAFE_MEDIUM = Vector(517.355835, -4626.182129, 384.000000) 24 | RAD_SAFE_EASY = Vector(3022.693848, -4553.411621, 256.000000) 25 | RAD_SAFE_HARD = Vector(4756.009277, -4438.848145, 256.000000) 26 | RAD_OFF_ANCIENT = Vector(-2969.868164, -100.282837, 384.000000) 27 | RAD_OFF_MEDIUM = Vector(-3790.975586, 800.282837, 256.000000) 28 | RAD_OFF_HARD = Vector(-4760.342285, -375.238007, 256.000000) 29 | DIRE_MID_MEDIUM = Vector(1192.345459, 3401.774902, 384.000000) 30 | DIRE_MID_HARD = Vector(-240.170807, 3432.894531, 256.000000) 31 | DIRE_MID_ANCIENT = Vector(-846.801514, 2274.023438, 384.000000) 32 | DIRE_SAFE_MEDIUM = Vector(-1832.490112, 4078.956055, 256.000000) 33 | DIRE_SAFE_EASY = Vector(-2911.948730, 4905.840332, 384.000000) 34 | DIRE_SAFE_HARD = Vector(-4329.099609, 3678.822021, 256.000000) 35 | DIRE_OFF_ANCIENT = Vector(3765.490234, -674.424194, 256.000000) 36 | DIRE_OFF_MEDIUM = Vector(2703.443359, 107.946167, 384.000000) 37 | DIRE_OFF_HARD = Vector(4296.825684, 796.566528, 384.000000) 38 | 39 | --time to pull for a stack 40 | RAD_MID_MEDIUM_STACKTIME = 56 41 | RAD_MID_HARD_STACKTIME = 54 42 | RAD_MID_ANCIENT_STACKTIME = 54 43 | RAD_SAFE_MEDIUM_STACKTIME = 55 44 | RAD_SAFE_EASY_STACKTIME = 54 45 | RAD_SAFE_HARD_STACKTIME = 54 46 | RAD_OFF_ANCIENT_STACKTIME = 53 47 | RAD_OFF_MEDIUM_STACKTIME = 55 48 | RAD_OFF_HARD_STACKTIME = 56 49 | DIRE_MID_MEDIUM_STACKTIME = 53 50 | DIRE_MID_HARD_STACKTIME = 54 51 | DIRE_MID_ANCIENT_STACKTIME = 53 52 | DIRE_SAFE_MEDIUM_STACKTIME = 55 53 | DIRE_SAFE_EASY_STACKTIME = 55 54 | DIRE_SAFE_HARD_STACKTIME = 55 55 | DIRE_OFF_ANCIENT_STACKTIME = 54 56 | DIRE_OFF_MEDIUM_STACKTIME = 53 57 | DIRE_OFF_HARD_STACKTIME = 55 58 | 59 | --vector to wait for pull at 60 | RAD_MID_MEDIUM_PRESTACK = Vector(-1805.178076, -4183.264160, 128.000000) 61 | RAD_MID_HARD_PRESTACK = Vector(-696.121765, -3240.541992, 256.000000) 62 | RAD_MID_ANCIENT_PRESTACK = Vector(339.126160, -2069.720215, 384.000000) 63 | RAD_SAFE_MEDIUM_PRESTACK = Vector(710.355835, -4489.582129, 384.000000) 64 | RAD_SAFE_EASY_PRESTACK = Vector(3250.693848, -4553.411621, 256.000000) 65 | RAD_SAFE_HARD_PRESTACK = Vector(4590.009277, -4209.148145, 256.000000) 66 | RAD_OFF_ANCIENT_PRESTACK = Vector(-2778.268164, -88.582837, 384.000000) 67 | RAD_OFF_MEDIUM_PRESTACK = Vector(-3946.775586, 737.182837, 256.000000) 68 | RAD_OFF_HARD_PRESTACK = Vector(-4602.575586, -190.582837, 256.000000) 69 | DIRE_MID_MEDIUM_PRESTACK = Vector(1081.945459, 3529.274902, 384.000000) 70 | DIRE_MID_HARD_PRESTACK = Vector(-423.070807, 3517.694531, 256.000000) 71 | DIRE_MID_ANCIENT_PRESTACK = Vector(-569.601514, 2439.623438, 384.000000) 72 | DIRE_SAFE_MEDIUM_PRESTACK = Vector(-1605.990112, 3973.256055, 256.000000) 73 | DIRE_SAFE_EASY_PRESTACK = Vector(-3001.948730, 5061.940332, 384.000000) 74 | DIRE_SAFE_HARD_PRESTACK = Vector(-4348.299609, 3714.822021, 256.000000) 75 | DIRE_OFF_ANCIENT_PRESTACK = Vector(2877.843359, 91.246167, 384.000000) 76 | DIRE_OFF_MEDIUM_PRESTACK = Vector(3581.090234, -763.624194, 256.000000) 77 | DIRE_OFF_HARD_PRESTACK = Vector(4098.925684, 725.266528, 384.000000) 78 | 79 | --vector to run to when stacking 80 | RAD_MID_MEDIUM_STACK = Vector(-1824.378076, -3207.564160, 256.000000) 81 | RAD_MID_HARD_STACK = Vector(-425.521765, -2421.341992, 256.000000) 82 | RAD_MID_ANCIENT_STACK = Vector(1121.526160, -2940.020215, 384.000000) 83 | RAD_SAFE_MEDIUM_STACK = Vector(886.355835, -3285.482129, 384.000000) 84 | RAD_SAFE_EASY_STACK = Vector(4245.693848, -5273.411621, 384.000000) 85 | RAD_SAFE_HARD_STACK = Vector(3425.509277, -3357.848145, 256.000000) 86 | RAD_OFF_ANCIENT_STACK = Vector(-3979.668164, -815.682837, 384.000000) 87 | RAD_OFF_MEDIUM_STACK = Vector(-5200.375586, 283.682837, 256.000000) 88 | RAD_OFF_HARD_STACK = Vector(-3900.742285, -727.938007, 256.000000) 89 | DIRE_MID_MEDIUM_STACK = Vector(-142.745459, 4125.974902, 384.000000) 90 | DIRE_MID_HARD_STACK = Vector(-1122.570807, 4474.494531, 256.000000) 91 | DIRE_MID_ANCIENT_STACK = Vector(859.901514, 2333.123438, 384.000000) 92 | DIRE_SAFE_MEDIUM_STACK = Vector(-2772.090112, 3447.256055, 256.000000) 93 | DIRE_SAFE_EASY_STACK = Vector(-3605.248730, 6082.240332, 384.000000) 94 | DIRE_SAFE_HARD_STACK = Vector(-2789.299609, 3501.122021, 256.000000) 95 | DIRE_OFF_ANCIENT_STACK = Vector(4276.443359, 79.346167, 384.000000) 96 | DIRE_OFF_MEDIUM_STACK = Vector(2297.690234, -944.924194, 256.000000) 97 | DIRE_OFF_HARD_STACK = Vector(4577.625684, -349.766528, 384.000000) 98 | -------------------------------------------------------------------------------- /webserver/world.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import datetime 4 | from hero import Hero 5 | import json 6 | import constants as const 7 | 8 | startTime = datetime.datetime.now() 9 | 10 | enemies = {} 11 | allies = {} 12 | 13 | def timeSinceStart(dt): 14 | return (dt - startTime).total_seconds() 15 | 16 | class World(object): 17 | def __init__(self): 18 | self.reply = {} 19 | 20 | def __repr__(self): 21 | return 'Last Reply %s' % (self.reply) 22 | 23 | def decision(self): 24 | return self.reply 25 | 26 | def update(self, data): 27 | dataType = data['Type'] 28 | 29 | # X -- Authentication Packet 30 | if dataType == 'X': 31 | self.generateAuthenticationReply(data['Time']) 32 | # W -- World Update Packet 33 | elif dataType == 'W': 34 | self.updateCourierData(data['CourierData']) 35 | ''' 36 | self.updateEnemyHeroes(data['enemyHeroes']) 37 | self.updateAlliedHeroes(data['alliedHeroes']) 38 | 39 | self.updateOtherEnemyUnits(data['enemyHeroesOther']) 40 | self.updateOtherAlliedUnits(data['alliedHeroesOther']) 41 | 42 | self.updateEnemyCreep(data['enemyCreep']) 43 | self.updateAlliedCreep(data['alliedCreep']) 44 | self.updateNeutralCreep(data['neutralCreep']) 45 | 46 | self.updateEnemyWards(data['enemyWards']) 47 | self.updateAlliedWards(data['alliedWards']) 48 | 49 | self.updateAOEs(data['dangerousAOEs']) 50 | self.updateProjectiles(data['dangerousProjectiles']) 51 | self.updateIncomingTeleports(data['incomingTeleports']) 52 | 53 | self.updateCastCallback(data['castCallback']) 54 | ''' 55 | self.generateWorldUpdateReply(data['Time']) 56 | # E -- Enemies Update Packet 57 | elif dataType == 'E': 58 | self.updateEnemyHeroes(data['Data']) 59 | self.generateEnemiesReply(data['Time']) 60 | # A -- Allies Update Packet (for Human and not-our-bot Bots) 61 | elif dataType == 'A': 62 | self.generateAlliesReply(data['Time']) 63 | # P* -- Player Update Packet 64 | elif dataType[0] == 'P': 65 | cHero = self.updateHero(data['Data'], int(dataType[1:])) 66 | cHero.processHero() 67 | self.generatePlayerUpdateReply(data['Time'], dataType, cHero.getReply()) 68 | # ? -- Unknown 69 | else: 70 | print('Error:', 'Unknown Packet Type:', dataType) 71 | 72 | def generateAuthenticationReply(self, time): 73 | self.reply = {} 74 | self.reply["Type"] = 'X' 75 | self.reply["Time"] = time 76 | 77 | def generateWorldUpdateReply(self, time): 78 | self.reply = {} 79 | self.reply["Type"] = 'W' 80 | self.reply["Time"] = time 81 | 82 | def generateEnemiesReply(self, time): 83 | self.reply = {} 84 | self.reply["Type"] = 'E' 85 | self.reply["Time"] = time 86 | 87 | def generateAlliesReply(self, time): 88 | self.reply = {} 89 | self.reply["Type"] = 'A' 90 | self.reply["Time"] = time 91 | 92 | def generatePlayerUpdateReply(self, time, pID, reply): 93 | self.reply = {} 94 | self.reply["Type"] = pID 95 | self.reply["Time"] = time 96 | if reply != None and len(reply) > 0: 97 | print('Have Data: ', reply) 98 | self.reply["Data"] = reply 99 | 100 | def updateCourierData(self, data): 101 | print('Courier Data:\n') 102 | for entry in data: 103 | print(entry, ' ', data[entry]) 104 | 105 | def updateEnemyHeroes(self, data): 106 | print('Enemies:\n') 107 | for hero in data: 108 | print(hero, ' ', data[hero]) 109 | 110 | def updateHero(self, heroData, pID): 111 | cHero = Hero(heroData['Name'], pID, heroData['Team'], heroData) 112 | #print(cHero) 113 | return cHero 114 | 115 | def updateOtherEnemyUnits(self, data): 116 | print('Other Enemy Units:\n') 117 | for unit in data: 118 | print(unit) 119 | 120 | def updateOtherAlliedUnits(self, data): 121 | print('Other Allied Units:\n') 122 | for unit in data: 123 | print(unit) 124 | 125 | def updateEnemyCreep(self, data): 126 | print('Enemy Creep:\n') 127 | for creep in data: 128 | print(creep) 129 | 130 | def updateAlliedCreep(self, data): 131 | print('Allied Creep:\n') 132 | for creep in data: 133 | print(creep) 134 | 135 | def updateNeutralCreep(self, data): 136 | print('Neutral Creep:\n') 137 | for creep in data: 138 | print(creep) 139 | 140 | def updateEnemyWards(self, data): 141 | print('Enemy Wards:\n') 142 | for ward in data: 143 | print(ward) 144 | 145 | def updateAlliedWards(self, data): 146 | print('Allied Wards:\n') 147 | for ward in data: 148 | print(ward) 149 | 150 | def updateAOEs(self, data): 151 | print('Dangerous AOEs:\n') 152 | for aoe in data: 153 | print(aoe) 154 | 155 | def updateProjectiles(self, data): 156 | print('Dangerous Projectiles:\n') 157 | for projectile in data: 158 | print(projectile) 159 | 160 | def updateIncomingTeleports(self, data): 161 | print('Incoming Teleports:\n') 162 | for tp in data: 163 | print(tp) 164 | 165 | def updateCastCallback(self, data): 166 | print('Cast Callbacks:\n') 167 | for call in data: 168 | print(call) 169 | -------------------------------------------------------------------------------- /HeroData.py: -------------------------------------------------------------------------------- 1 | import os 2 | import string 3 | 4 | heroName = '' 5 | shiftCount = 0 6 | heroCount = 0 7 | lineCount = 0 8 | botDataProcessing = False 9 | botLaningInfo = False 10 | 11 | heroes = {} 12 | 13 | def writeHeroDataLua(obj): 14 | f = open('C:\\Program Files (x86)\\Steam\\steamapps\\common\\dota 2 beta\\game\\dota\\scripts\\vscripts\\bots\\hero_data.lua', 'w') 15 | f.write('local X = {}\n') 16 | 17 | st = '' 18 | for heroName in heroes: 19 | try: 20 | st = '\nX.%s = {}\n' % heroName 21 | try: 22 | st = st + 'X.%s.%s = "%s"\n' % (heroName, 'Type', heroes[heroName]['Type']) 23 | except KeyError as e: 24 | print('Error dumping [Type]: ', heroName) 25 | 26 | try: 27 | st = st + 'X.%s.%s = "%s"\n' % (heroName, 'Name', heroes[heroName]['Name']) 28 | except KeyError as e: 29 | print('Error dumping [Name]: ', heroName) 30 | 31 | indx = 0 32 | for ability in heroes[heroName]['Abilities']: 33 | st = st + 'X.%s.SKILL_%d = "%s"\n' % (heroName, indx, ability) 34 | indx += 1 35 | 36 | indx = 0 37 | for ability in heroes[heroName]['Talents']: 38 | st = st + 'X.%s.TALENT_%d = "%s"\n' % (heroName, indx, ability) 39 | indx += 1 40 | 41 | try: 42 | roles = heroes[heroName]['Role'].split(',') 43 | rolevals = heroes[heroName]['Rolelevels'].split(',') 44 | st = st + 'X.%s.Role = {}\n' % (heroName) 45 | for i in range(0, len(roles)): 46 | st = st + 'X.%s.Role.%s = %s\n' % (heroName, roles[i], rolevals[i]) 47 | except KeyError as e: 48 | print('Error dumping [Role]: ', heroName) 49 | 50 | try: 51 | st = st + 'X.%s.LaneInfo = {}\n' % (heroName) 52 | for key in heroes[heroName]['LaneInfo']: 53 | st = st + 'X.%s.LaneInfo.%s = %s\n' % (heroName, key, heroes[heroName]['LaneInfo'][key]) 54 | except KeyError as e: 55 | print('Error dumping [LaneInfo]: ', heroName) 56 | 57 | f.write(st) 58 | except KeyError as e: 59 | print('Generic Error: ', heroName) 60 | 61 | f.write('\nreturn X\n') 62 | f.close() 63 | 64 | badHeroNames = ['npc_dota_hero_base', 'npc_dota_hero_target_dummy'] 65 | 66 | if __name__ == "__main__": 67 | fName = open('C:\\Program Files (x86)\\Steam\\steamapps\\common\\dota 2 beta\\game\\dota\\scripts\\npc\\npc_heroes.txt', 'r') 68 | 69 | content = fName.readlines() 70 | content = [x.strip() for x in content] 71 | print(len(content)) 72 | 73 | fName.close() 74 | 75 | for line in content: 76 | lineCount += 1 77 | name = line.find("npc_dota_hero_") 78 | 79 | if name > -1 and heroName == '' and line.strip('"') not in badHeroNames and shiftCount == 1: 80 | heroName = line[name+14:-1] 81 | #print lineCount, 'Starting with', heroName 82 | heroCount += 1 83 | heroes[heroName] = {} 84 | heroes[heroName]['Abilities'] = [] 85 | heroes[heroName]['Talents'] = [] 86 | heroes[heroName]['Name'] = "" 87 | continue 88 | 89 | if line == '{': 90 | shiftCount += 1 91 | continue 92 | 93 | if line == '}': 94 | shiftCount -= 1 95 | 96 | if shiftCount == 1 and heroName != '': 97 | #print lineCount, 'Done with', heroName 98 | heroName = '' 99 | 100 | if shiftCount == 2 and heroName != '': 101 | 102 | if line[1:11] == 'Rolelevels': 103 | key, val = line.split() 104 | heroes[heroName]['Rolelevels'] = val[1:-1] 105 | elif line[1:5] == 'Role': 106 | key, val = line.split() 107 | heroes[heroName]['Role'] = val[1:-1] 108 | 109 | if line[1:4] == 'url': 110 | try: 111 | key, val = line.split() 112 | heroes[heroName]['Name'] = val.strip('"').replace("_", "-") 113 | except ValueError as e: 114 | print('Error: ', line) 115 | break 116 | 117 | if line[1:8] == 'Ability' and line[1:14] != 'AbilityLayout' and line[1:15] != 'AbilityPreview' and \ 118 | line[1:21] != 'AbilityDraftDisabled' and line[1:22] != 'AbilityDraftAbilities': 119 | try: 120 | key, val = line.split() 121 | if val.find("special_bonus_") >= 0: 122 | heroes[heroName]['Talents'].append(val.strip('"')) 123 | else: 124 | heroes[heroName]['Abilities'].append(val.strip('"')) 125 | except ValueError as e: 126 | print('Error: ', line) 127 | break 128 | 129 | if line == '"Bot"': 130 | botDataProcessing = True 131 | continue 132 | 133 | if botDataProcessing: 134 | botDataProcessing = False 135 | 136 | if shiftCount == 3 and botLaningInfo: 137 | botLaningInfo = False 138 | 139 | if shiftCount == 3 and botDataProcessing: 140 | if line[1:9] == 'HeroType': 141 | heroes[heroName]['Type'] = line.split("\t",1)[1][2:-1] 142 | 143 | if line[1:11] == 'LaningInfo': 144 | heroes[heroName]['LaneInfo'] = {} 145 | botLaningInfo = True 146 | 147 | if shiftCount == 4 and botDataProcessing and botLaningInfo: 148 | try: 149 | key, val = line.split() 150 | heroes[heroName]['LaneInfo'][key.strip('"')] = val.strip('"') 151 | except ValueError as e: 152 | print('Error: ', lineCount, line) 153 | raise e 154 | 155 | print('Processed %d heroes' % (heroCount)) 156 | 157 | writeHeroDataLua(heroes) 158 | -------------------------------------------------------------------------------- /web_data/dotabuff.json: -------------------------------------------------------------------------------- 1 | { 2 | "items":{ 3 | "aegis-of-the-immortal": "aegis", 4 | "animal-courier": "courier", 5 | "band-of-elvenskin": "boots_of_elves", 6 | "belt-of-strength": "belt_of_strength", 7 | "blade-of-alacrity": "blade_of_alacrity", 8 | "blades-of-attack": "blades_of_attack", 9 | "blight-stone": "blight_stone", 10 | "blink-dagger": "blink", 11 | "boots-of-speed": "boots", 12 | "bottle": "bottle", 13 | "broadsword": "broadsword", 14 | "chainmail": "chainmail", 15 | "cheese": "cheese", 16 | "circlet": "circlet", 17 | "clarity": "clarity", 18 | "claymore": "claymore", 19 | "cloak": "cloak", 20 | "demon-edge": "demon_edge", 21 | "dust-of-appearance": "dust", 22 | "eaglesong": "eagle", 23 | "enchanted-mango": "enchanted_mango", 24 | "energy-booster": "energy_booster", 25 | "faerie-fire": "faerie_fire", 26 | "flying-courier": "flying_courier", 27 | "gauntlets-of-strength": "gauntlets", 28 | "gem-of-true-sight": "gem", 29 | "ghost-scepter": "ghost", 30 | "gloves-of-haste": "gloves", 31 | "healing-salve": "flask", 32 | "helm-of-iron-will": "helm_of_iron_will", 33 | "hyperstone": "hyperstone", 34 | "infused-raindrop": "infused_raindrop", 35 | "iron-branch": "branches", 36 | "javelin": "javelin", 37 | "magic-stick": "magic_stick", 38 | "mantle-of-intelligence": "mantle", 39 | "mithril-hammer": "mithril_hammer", 40 | "morbid-mask": "lifesteal", 41 | "mystic-staff": "mystic_staff", 42 | "observer-ward": "ward_observer", 43 | "ogre-club": "ogre_axe", 44 | "orb-of-venom": "orb_of_venom", 45 | "platemail": "platemail", 46 | "point-booster": "point_booster", 47 | "quarterstaff": "quarterstaff", 48 | "quelling-blade": "quelling_blade", 49 | "reaver": "reaver", 50 | "ring-of-health": "ring_of_health", 51 | "ring-of-protection": "ring_of_protection", 52 | "robe-of-the-magi": "robe", 53 | "ring-of-regen": "ring_of_regen", 54 | "sacred-relic": "relic", 55 | "sentry-ward": "ward_sentry", 56 | "sages-mask": "sobi_mask", 57 | "shadow-amulet": "shadow_amulet", 58 | "slippers-of-agility": "slippers", 59 | "smoke-of-deceit": "smoke_of_deceit", 60 | "staff-of-wizardry": "staff_of_wizardry", 61 | "stout-shield": "stout_shield", 62 | "talisman-of-evasion": "talisman_of_evasion", 63 | "tango": "tango", 64 | "tango-(shared)": "tango_single", 65 | "tome-of-knowledge": "tome_of_knowledge", 66 | "town-portal-scroll": "tpscroll", 67 | "ultimate-orb": "ultimate_orb", 68 | "vitality-booster": "vitality_booster", 69 | "void-stone": "void_stone", 70 | "wind-lace": "wind_lace", 71 | "abyssal-blade": "abyssal_blade", 72 | "aether-lens": "aether_lens", 73 | "aghanims-scepter": "ultimate_scepter", 74 | "arcane-boots": "arcane_boots", 75 | "armlet-of-mordiggian": "armlet", 76 | "assault-cuirass": "assault", 77 | "battle-fury": "bfury", 78 | "black-king-bar": "black_king_bar", 79 | "blade-mail": "blade_mail", 80 | "bloodstone": "bloodstone", 81 | "bloodthorn": "bloodthorn", 82 | "boots-of-travel": "travel_boots_1", 83 | "boots-of-travel-level-2": "travel_boots_2", 84 | "bracer": "bracer", 85 | "buckler": "buckler", 86 | "butterfly": "butterfly", 87 | "crimson-guard": "crimson_guard", 88 | "crystalys": "lesser_crit", 89 | "daedalus": "greater_crit", 90 | "dagon": " dagon_1", 91 | "dagon-level-2": "dagon_2", 92 | "dagon-level-3": "dagon_3", 93 | "dagon-level-4": "dagon_4", 94 | "dagon-level-5": "dagon_5", 95 | "desolator": "desolator", 96 | "diffusal-blade": "diffusal_blade_1", 97 | "diffusal-blade-level-2": "diffusal_blade_2", 98 | "dragon-lance": "dragon_lance", 99 | "drum-of-endurance": "ancient_janggo", 100 | "echo-sabre": "echo_sabre", 101 | "ethereal-blade": "ethereal_blade", 102 | "eye-of-skadi": "skadi", 103 | "euls-scepter-of-divinity": "cyclone", 104 | "force-staff": "force_staff", 105 | "glimmer-cape": "glimmer_cape", 106 | "guardian-greaves": "guardian_greaves", 107 | "hand-of-midas": "hand_of_midas", 108 | "headdress": "headdress", 109 | "heart-of-tarrasque": "heart", 110 | "heavens-halberd": "heavens_halberd", 111 | "helm-of-the-dominator": "helm_of_the_dominator", 112 | "hood-of-defiance": "hood_of_defiance", 113 | "hurricane-pike": "hurricane_pike", 114 | "iron-talon": "iron_talon", 115 | "linkens-sphere": "sphere", 116 | "lotus-orb": "lotus_orb", 117 | "maelstrom": "maelstrom", 118 | "magic-wand": "magic_wand", 119 | "manta-style": "manta", 120 | "mask-of-madness": "mask_of_madness", 121 | "medallion-of-courage": "medallion_of_courage", 122 | "mekansm": "mekansm", 123 | "mjollnir": "mjollnir", 124 | "monkey-king-bar": "monkey_king_bar", 125 | "moon-shard": "moon_shard", 126 | "necronomicon": "necronomicon_1", 127 | "necronomicon-level-2": "necronomicon_2", 128 | "necronomicon-level-3": "necronomicon_3", 129 | "null-talisman": "null_talisman", 130 | "oblivion-staff": "oblivion_staff", 131 | "observer-and-sentry-wards": "ward_dispenser", 132 | "octarine-core": "octarine_core", 133 | "orchid-malevolence": "orchid", 134 | "perseverance": "pers", 135 | "phase-boots": "phase_boots", 136 | "pipe-of-insight": "pipe", 137 | "poor-mans-shield": "poor_mans_shield", 138 | "power-treads": "power_treads", 139 | "radiance": "radiance", 140 | "divine-rapier": "rapier", 141 | "refresher-orb": "refresher", 142 | "ring-of-aquila": "ring_of_aquila", 143 | "ring-of-basilius": "ring_of_basilius", 144 | "rod-of-atos": "rod_of_atos", 145 | "sange": "sange", 146 | "sange-and-yasha": "sange_and_yasha", 147 | "satanic": "satanic", 148 | "scythe-of-vyse": "sheepstick", 149 | "shadow-blade": "invis_sword", 150 | "shivas-guard": "shivas_guard", 151 | "silver-edge": "silver_edge", 152 | "skull-basher": "basher", 153 | "solar-crest": "solar_crest", 154 | "soul-booster": "soul_booster", 155 | "soul-ring": "soul_ring", 156 | "tranquil-boots": "tranquil_boots", 157 | "urn-of-shadows": "urn_of_shadows", 158 | "vanguard": "vanguard", 159 | "veil-of-discord": "veil_of_discord", 160 | "vladmirs-offering": "vladmir", 161 | "wraith-band": "wraith_band", 162 | "yasha": "yasha" 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /constants/item_mappings.lua: -------------------------------------------------------------------------------- 1 | local X = {} 2 | 3 | X["aegis-of-the-immortal"] = "item_aegis" 4 | X["animal-courier"] = "item_courier" 5 | X["band-of-elvenskin"] = "item_boots_of_elves" 6 | X["belt-of-strength"] = "item_belt_of_strength" 7 | X["blade-of-alacrity"] = "item_blade_of_alacrity" 8 | X["blades-of-attack"] = "item_blades_of_attack" 9 | X["blight-stone"] = "item_blight_stone" 10 | X["blink-dagger"] = "item_blink" 11 | X["boots-of-speed"] = "item_boots" 12 | X["bottle"] = "item_bottle" 13 | X["broadsword"] = "item_broadsword" 14 | X["chainmail"] = "item_chainmail" 15 | X["cheese"] = "item_cheese" 16 | X["circlet"] = "item_circlet" 17 | X["clarity"] = "item_clarity" 18 | X["claymore"] = "item_claymore" 19 | X["cloak"] = "item_cloak" 20 | X["demon-edge"] = "item_demon_edge" 21 | X["dust-of-appearance"] = "item_dust" 22 | X["eaglesong"] = "item_eagle" 23 | X["enchanted-mango"] = "item_enchanted_mango" 24 | X["energy-booster"] = "item_energy_booster" 25 | X["faerie-fire"] = "item_faerie_fire" 26 | X["flying-courier"] = "item_flying_courier" 27 | X["gauntlets-of-strength"] = "item_gauntlets" 28 | X["gem-of-true-sight"] = "item_gem" 29 | X["ghost-scepter"] = "item_ghost" 30 | X["gloves-of-haste"] = "item_gloves" 31 | X["healing-salve"] = "item_flask" 32 | X["helm-of-iron-will"] = "item_helm_of_iron_will" 33 | X["hyperstone"] = "item_hyperstone" 34 | X["infused-raindrop"] = "item_infused_raindrop" 35 | X["iron-branch"] = "item_branches" 36 | X["javelin"] = "item_javelin" 37 | X["magic-stick"] = "item_magic_stick" 38 | X["mantle-of-intelligence"] = "item_mantle" 39 | X["mithril-hammer"] = "item_mithril_hammer" 40 | X["morbid-mask"] = "item_lifesteal" 41 | X["mystic-staff"] = "item_mystic_staff" 42 | X["observer-ward"] = "item_ward_observer" 43 | X["ogre-club"] = "item_ogre_axe" 44 | X["orb-of-venom"] = "item_orb_of_venom" 45 | X["platemail"] = "item_platemail" 46 | X["point-booster"] = "item_point_booster" 47 | X["quarterstaff"] = "item_quarterstaff" 48 | X["quelling-blade"] = "item_quelling_blade" 49 | X["reaver"] = "item_reaver" 50 | X["ring-of-health"] = "item_ring_of_health" 51 | X["ring-of-protection"] = "item_ring_of_protection" 52 | X["robe-of-the-magi"] = "item_robe" 53 | X["ring-of-regen"] = "item_ring_of_regen" 54 | X["sacred-relic"] = "item_relic" 55 | X["sentry-ward"] = "item_ward_sentry" 56 | X["sages-mask"] = "item_sobi_mask" 57 | X["shadow-amulet"] = "item_shadow_amulet" 58 | X["slippers-of-agility"] = "item_slippers" 59 | X["smoke-of-deceit"] = "item_smoke_of_deceit" 60 | X["staff-of-wizardry"] = "item_staff_of_wizardry" 61 | X["stout-shield"] = "item_stout_shield" 62 | X["talisman-of-evasion"] = "item_talisman_of_evasion" 63 | X["tango"] = "item_tango" 64 | X["tango-(shared)"] = "item_tango_single" 65 | X["tome-of-knowledge"] = "item_tome_of_knowledge" 66 | X["town-portal-scroll"] = "item_tpscroll" 67 | X["ultimate-orb"] = "item_ultimate_orb" 68 | X["vitality-booster"] = "item_vitality_booster" 69 | X["void-stone"] = "item_void_stone" 70 | X["wind-lace"] = "item_wind_lace" 71 | X["abyssal-blade"] = "item_abyssal_blade" 72 | X["aether-lens"] = "item_aether_lens" 73 | X["aghanims-scepter"] = "item_ultimate_scepter" 74 | X["arcane-boots"] = "item_arcane_boots" 75 | X["armlet-of-mordiggian"] = "item_armlet" 76 | X["assault-cuirass"] = "item_assault" 77 | X["battle-fury"] = "item_bfury" 78 | X["black-king-bar"] = "item_black_king_bar" 79 | X["blade-mail"] = "item_blade_mail" 80 | X["bloodstone"] = "item_bloodstone" 81 | X["bloodthorn"] = "item_bloodthorn" 82 | X["boots-of-travel"] = "item_travel_boots_1" 83 | X["boots-of-travel-level-2"] = "item_travel_boots_2" 84 | X["bracer"] = "item_bracer" 85 | X["buckler"] = "item_buckler" 86 | X["butterfly"] = "item_butterfly" 87 | X["crimson-guard"] = "item_crimson_guard" 88 | X["crystalys"] = "item_lesser_crit" 89 | X["daedalus"] = "item_greater_crit" 90 | X["dagon"] = " item_dagon_1" 91 | X["dagon-level-2"] = "item_dagon_2" 92 | X["dagon-level-3"] = "item_dagon_3" 93 | X["dagon-level-4"] = "item_dagon_4" 94 | X["dagon-level-5"] = "item_dagon_5" 95 | X["desolator"] = "item_desolator" 96 | X["diffusal-blade"] = "item_diffusal_blade_1" 97 | X["diffusal-blade-level-2"] = "item_diffusal_blade_2" 98 | X["dragon-lance"] = "item_dragon_lance" 99 | X["drum-of-endurance"] = "item_ancient_janggo" 100 | X["echo-sabre"] = "item_echo_sabre" 101 | X["ethereal-blade"] = "item_ethereal_blade" 102 | X["eye-of-skadi"] = "item_skadi" 103 | X["euls-scepter-of-divinity"] = "item_cyclone" 104 | X["force-staff"] = "item_force_staff" 105 | X["glimmer-cape"] = "item_glimmer_cape" 106 | X["guardian-greaves"] = "item_guardian_greaves" 107 | X["hand-of-midas"] = "item_hand_of_midas" 108 | X["headdress"] = "item_headdress" 109 | X["heart-of-tarrasque"] = "item_heart" 110 | X["heavens-halberd"] = "item_heavens_halberd" 111 | X["helm-of-the-dominator"] = "item_helm_of_the_dominator" 112 | X["hood-of-defiance"] = "item_hood_of_defiance" 113 | X["hurricane-pike"] = "item_hurricane_pike" 114 | X["iron-talon"] = "item_iron_talon" 115 | X["linkens-sphere"] = "item_sphere" 116 | X["lotus-orb"] = "item_lotus_orb" 117 | X["maelstrom"] = "item_maelstrom" 118 | X["magic-wand"] = "item_magic_wand" 119 | X["manta-style"] = "item_manta" 120 | X["mask-of-madness"] = "item_mask_of_madness" 121 | X["medallion-of-courage"] = "item_medallion_of_courage" 122 | X["mekansm"] = "item_mekansm" 123 | X["mjollnir"] = "item_mjollnir" 124 | X["monkey-king-bar"] = "item_monkey_king_bar" 125 | X["moon-shard"] = "item_moon_shard" 126 | X["necronomicon"] = "item_necronomicon_1" 127 | X["necronomicon-level-2"] = "item_necronomicon_2" 128 | X["necronomicon-level-3"] = "item_necronomicon_3" 129 | X["null-talisman"] = "item_null_talisman" 130 | X["oblivion-staff"] = "item_oblivion_staff" 131 | X["observer-and-sentry-wards"] = "item_ward_dispenser" 132 | X["octarine-core"] = "item_octarine_core" 133 | X["orchid-malevolence"] = "item_orchid" 134 | X["perseverance"] = "item_pers" 135 | X["phase-boots"] = "item_phase_boots" 136 | X["pipe-of-insight"] = "item_pipe" 137 | X["poor-mans-shield"] = "item_poor_mans_shield" 138 | X["power-treads"] = "item_power_treads" 139 | X["radiance"] = "item_radiance" 140 | X["divine-rapier"] = "item_rapier" 141 | X["refresher-orb"] = "item_refresher" 142 | X["ring-of-aquila"] = "item_ring_of_aquila" 143 | X["ring-of-basilius"] = "item_ring_of_basilius" 144 | X["rod-of-atos"] = "item_rod_of_atos" 145 | X["sange"] = "item_sange" 146 | X["sange-and-yasha"] = "item_sange_and_yasha" 147 | X["satanic"] = "item_satanic" 148 | X["scythe-of-vyse"] = "item_sheepstick" 149 | X["shadow-blade"] = "item_invis_sword" 150 | X["shivas-guard"] = "item_shivas_guard" 151 | X["silver-edge"] = "item_silver_edge" 152 | X["skull-basher"] = "item_basher" 153 | X["solar-crest"] = "item_solar_crest" 154 | X["soul-booster"] = "item_soul_booster" 155 | X["soul-ring"] = "item_soul_ring" 156 | X["tranquil-boots"] = "item_tranquil_boots" 157 | X["urn-of-shadows"] = "item_urn_of_shadows" 158 | X["vanguard"] = "item_vanguard" 159 | X["veil-of-discord"] = "item_veil_of_discord" 160 | X["vladmirs-offering"] = "item_vladmir" 161 | X["wraith-band"] = "item_wraith_band" 162 | X["yasha"] = "item_yasha" 163 | 164 | return X 165 | -------------------------------------------------------------------------------- /helper/inventory_helper.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHORS: iSarCasm, Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | --- IMPORTS 7 | ------------------------------------------------- 8 | 9 | local UnitHelper = require( GetScriptDirectory().."/helper/unit_helper" ) 10 | 11 | ------------------------------------------------- 12 | 13 | local InventoryHelper = {} 14 | 15 | function InventoryHelper:HasItem(hUnit, sItemName, hasToBeActiveSlot) 16 | local slots = (hasToBeActiveSlot and 5 or 8) 17 | for i = 0, slots do 18 | local slot = hUnit:GetItemInSlot(i) 19 | if (slot and slot:GetName() == sItemName) then 20 | return true 21 | end 22 | end 23 | return false 24 | end 25 | 26 | function InventoryHelper:GetItemByName(hUnit, sItemName, hasToBeActiveSlot) 27 | local slots = (hasToBeActiveSlot and 5 or 8) 28 | for i = 0, slots do 29 | local slot = hUnit:GetItemInSlot(i) 30 | if (slot and slot:GetName() == sItemName) then 31 | return slot 32 | end 33 | end 34 | return nil 35 | end 36 | 37 | function InventoryHelper:WorthOfItemsCanBeBought(build) 38 | local gold = GetBot():GetGold() 39 | local goldLeft = gold 40 | local worth = 0 41 | for i = 1, #build do 42 | local cost = GetItemCost(build[i]) 43 | if (goldLeft >= cost) then 44 | goldLeft = goldLeft - cost 45 | worth = worth + cost 46 | else 47 | return worth 48 | end 49 | end 50 | return 0 51 | end 52 | 53 | function InventoryHelper:HasSpareSlot(hUnit, hasToBeActiveSlot) 54 | local slots = (hasToBeActiveSlot and 5 or 8) 55 | for i = 0, slots do 56 | local slot = hUnit:GetItemInSlot(i) 57 | if (not slot) then 58 | return true 59 | end 60 | end 61 | return false 62 | end 63 | 64 | function InventoryHelper:IsBackpackEmpty(hUnit) 65 | for i = 6, 8 do 66 | local slot = hUnit:GetItemInSlot(i) 67 | if (slot) then 68 | return false 69 | end 70 | end 71 | return true 72 | end 73 | 74 | function InventoryHelper:MostValuableItemSlot(hUnit, slot_from, slot_to) 75 | local most_value = VERY_LOW_INT 76 | local most = nil 77 | for i = slot_from, slot_to do 78 | local item = hUnit:GetItemInSlot(i) 79 | if (item and self:Value(item:GetName()) > most_value) then 80 | most_value = self:Value(item:GetName()) 81 | most = i 82 | end 83 | end 84 | local hash = {} 85 | hash.slot = most 86 | hash.value = most_value 87 | return hash 88 | end 89 | 90 | function InventoryHelper:LeastValuableItemSlot(hUnit, slot_from, slot_to) 91 | local least_value = VERY_HIGH_INT 92 | local least = nil 93 | for i = slot_from, slot_to do 94 | local item = hUnit:GetItemInSlot(i) 95 | if (item) then 96 | if (self:Value(item:GetName()) < least_value) then 97 | least_value = self:Value(item:GetName()) 98 | least = i 99 | end 100 | else 101 | least_value = 0 102 | least = i 103 | break 104 | end 105 | end 106 | local hash = {} 107 | hash.slot = least 108 | hash.value = least_value 109 | return hash 110 | end 111 | 112 | function InventoryHelper:Value(sItemName) 113 | --- NOTE: Blink Dagger - we "almost always" won't in main inventory 114 | if sItemName == "item_blink" then return 5000 end 115 | 116 | return GetItemCost(sItemName) 117 | end 118 | 119 | function InventoryHelper:BuyItem( hUnit, sItemName ) 120 | local buyResult = hUnit:ActionImmediate_PurchaseItem(sItemName) 121 | if buyResult ~= PURCHASE_ITEM_SUCCESS then 122 | local sError = GetTableKeyNameFromID(tableItemPurchaseResults, buyResult) 123 | if sError == nil then sError = buyResult end 124 | 125 | dbg.pause("[ITEM PURCHASE ERROR] Hero: ", hUnit:GetUnitName(), ", Item: ", sItemName, ", Result: ", sError) 126 | return 1 127 | end 128 | return 0 129 | end 130 | 131 | --- NOTE: This will only sell items we have in main inventory or backpack 132 | --- If we need ability to sell items in stash we will need to revise this function 133 | function InventoryHelper:SellItem( hUnit, sItemName ) 134 | if UnitHelper:DistanceFromNearestShop(hUnit) < SHOP_USE_DISTANCE then 135 | if self:HasItem(hUnit, sItemName, false) then 136 | hUnit:ActionImmediate_SellItem(sItemName) 137 | else 138 | dbg.pause("[ITEM SELL ERROR] Hero: ", hUnit:GetUnitName(), ", Item: ", sItemName) 139 | end 140 | end 141 | end 142 | 143 | function InventoryHelper:GetComponents( sItemName ) 144 | local variants = GetItemComponents(sItemName) 145 | 146 | -- first container is number of ways to build the items (e.g., Power Treads has 3) 147 | local comps = {} 148 | if #variants == 0 then 149 | table.insert(comps, sItemName) 150 | elseif #variants == 1 then 151 | comps = variants[1] 152 | else 153 | dbg.pause("[ITEM BREAKDOWN INTO COMPONENTS ERROR]: WE DO NOT HANDLE VARIANT BUILDUPS YET. ", #variants) 154 | comps = variants[1] 155 | end 156 | 157 | local finalComps = {} 158 | self:FlattenComponents(finalComps, comps) 159 | return finalComps 160 | end 161 | 162 | function InventoryHelper:FlattenComponents(output, input) 163 | local input_map -- has the same structure as input, but stores the 164 | -- indices to the corresponding output 165 | 166 | --print("Input Size: ", #input) 167 | for indx = 1, #input do 168 | --print("Input: ", input[indx]) 169 | local components = GetItemComponents(input[indx]) 170 | if #components > 0 then 171 | --print("Recursive...") 172 | for i = 1, #components do 173 | input_map = {} 174 | -- forward DFS order 175 | input_map[#input_map+1] = self:FlattenComponents(output, components[i]) 176 | end 177 | else 178 | --print("Non-Recursive...") 179 | input_map = #output + 1 180 | output[input_map] = input[indx] 181 | end 182 | end 183 | return input_map 184 | end 185 | 186 | function InventoryHelper:HealthConsumables( hHero ) 187 | local health = 0 188 | for i = 0, 5 do 189 | local item = hHero:GetItemInSlot(i) 190 | if (item) then 191 | if (item:GetName() == "item_tango") then 192 | health = health + 115 * item:GetCurrentCharges() 193 | elseif(item:GetName() == "item_flask") then 194 | health = health + 400 * item:GetCurrentCharges() 195 | elseif(item:GetName() == "item_faerie_fire") then 196 | health = health + 75 197 | elseif(item:GetName() == "item_bottle") then 198 | health = health + 90 * item:GetCurrentCharges() 199 | end 200 | end 201 | end 202 | return health 203 | end 204 | 205 | return InventoryHelper 206 | -------------------------------------------------------------------------------- /decision.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | --- LOAD OUR GLOBAL CONSTANTS 7 | require( GetScriptDirectory().."/constants/tables" ) 8 | require( GetScriptDirectory().."/constants/generic" ) 9 | require( GetScriptDirectory().."/constants/roles" ) 10 | require( GetScriptDirectory().."/constants/runes" ) 11 | require( GetScriptDirectory().."/constants/shops" ) 12 | require( GetScriptDirectory().."/constants/fountains" ) 13 | require( GetScriptDirectory().."/constants/jungle" ) 14 | 15 | local item_map = require( GetScriptDirectory().."/constants/item_mappings" ) 16 | 17 | --- LOAD OUR HELPERS 18 | require( GetScriptDirectory().."/helper/global_helper" ) 19 | 20 | local dp = require( GetScriptDirectory().."/data_packet" ) 21 | local InvHelp = require( GetScriptDirectory().."/helper/inventory_helper" ) 22 | 23 | --- LOAD OUR DEBUG SYSTEM 24 | dbg = require( GetScriptDirectory().."/debug" ) 25 | 26 | local think = require( GetScriptDirectory().."/think" ) 27 | 28 | local server = require( GetScriptDirectory().."/webserver_out" ) 29 | 30 | noneMode = dofile( GetScriptDirectory().."/modes/none" ) 31 | 32 | ------------------------------------------------------------------------------- 33 | -- BASE CLASS - DO NOT MODIFY THIS SECTION 34 | ------------------------------------------------------------------------------- 35 | 36 | local X = { init = false, currentMode = noneMode, currentModeValue = BOT_MODE_DESIRE_NONE, prevMode = noneMode } 37 | 38 | function X:new(myBotInfo) 39 | local mybot = {} 40 | setmetatable(mybot, self) 41 | self.__index = self 42 | mybot.botInfo = myBotInfo 43 | 44 | -- TODO - other stats we want to track? 45 | 46 | GetBot().mybot = mybot -- make mybot accessible anywhere after calling GetBot() 47 | GetBot().botInfo = mybot.botInfo -- make botInfo accessible 48 | 49 | return mybot 50 | end 51 | 52 | function X:getCurrentMode() 53 | return self.currentMode 54 | end 55 | 56 | function X:getCurrentModeValue() 57 | return self.currentModeValue 58 | end 59 | 60 | function X:getPrevMode() 61 | return self.prevMode 62 | end 63 | 64 | function X:ExecuteMode() 65 | if self.currentMode ~= self.prevMode then 66 | if self.currentMode:GetName() == nil then 67 | dbg.pause("Unimplemented Mode: ", self.currentMode) 68 | end 69 | dbg.myPrint("Mode Transition: "..self.prevMode:GetName():upper().." --> "..self.currentMode:GetName():upper()) 70 | 71 | -- call OnEnd() of previous mode 72 | self.prevMode:OnEnd() 73 | 74 | -- call OnStart() of new mode 75 | self.currentMode:OnStart() 76 | 77 | self.prevMode = self.currentMode 78 | end 79 | 80 | -- do Mode 81 | self.currentMode:Think() 82 | end 83 | 84 | function X:BeginMode(mode, value) 85 | if mode == nil then 86 | self.currentMode = noneMode 87 | self.currentModeValue = BOT_MODE_DESIRE_NONE 88 | return 89 | end 90 | 91 | if mode == self.currentMode then return end 92 | self.currentMode = mode 93 | self.currentModeValue = value 94 | end 95 | 96 | function X:ClearMode() 97 | self.currentMode = noneMode 98 | self.currentModeValue = BOT_MODE_DESIRE_NONE 99 | self:ExecuteMode() 100 | end 101 | 102 | ------------------------------------------------------------------------------- 103 | -- BASE INITIALIZATION & RESET FUNCTIONS - DO NOT OVER-LOAD 104 | ------------------------------------------------------------------------------- 105 | 106 | function X:DoInit(hBot) 107 | if not globalInit then 108 | InitializeGlobalVars() 109 | end 110 | 111 | self.Init = true 112 | hBot.mybot = self 113 | 114 | self.sNextAbility = {} 115 | self.sNextItem = {} 116 | self.lastModeThink = -1000.0 117 | 118 | self.moving_location = nil 119 | self.ability_location = nil 120 | self.ability_completed = -1000.0 121 | self.attack_target = nil 122 | self.attack_target_id = -1 123 | self.attack_completed = -1000.0 124 | 125 | local fullName = hBot:GetUnitName() 126 | self.Name = string.sub(fullName, 15, string.len(fullName)) 127 | end 128 | 129 | function X:ResetTempVars() 130 | self.moving_location = nil 131 | 132 | if self.ability_location ~= nil then 133 | if GameTime() >= self.ability_completed then 134 | self.ability_location = nil 135 | self.ability_completed = -1000.0 136 | end 137 | end 138 | 139 | if self.attack_target ~= nil then 140 | if GameTime() >= self.attack_completed then 141 | self.attack_completed = -1000.0 142 | end 143 | end 144 | end 145 | 146 | ------------------------------------------------------------------------------- 147 | -- BASE THINK - DO NOT OVER-LOAD 148 | ------------------------------------------------------------------------------- 149 | 150 | local lastReply = nil 151 | local function ServerUpdate() 152 | local hBot = GetBot() 153 | 154 | server.SendData(hBot) 155 | 156 | local worldReply = server.GetLastReply(dp.TYPE_WORLD) 157 | if worldReply ~= nil then 158 | dbg.myPrint("Need to Process new World Reply") 159 | dbg.myPrint("Packet RTT: ", RealTime() - worldReply.Time) 160 | end 161 | 162 | local enemiesReply = server.GetLastReply(dp.TYPE_ENEMIES) 163 | if enemiesReply ~= nil then 164 | dbg.myPrint("Need to Process new Enemies Reply") 165 | dbg.myPrint("Packet RTT: ", RealTime() - enemiesReply.Time) 166 | end 167 | 168 | local botReply = server.GetLastReply(dp.TYPE_PLAYER..tostring(hBot:GetPlayerID())) 169 | 170 | if botReply == nil then 171 | return nil, BOT_MODE_DESIRE_NONE 172 | else 173 | dbg.myPrint("Need to Process new Player Reply") 174 | dbg.myPrint("Packet RTT: ", RealTime() - botReply.Time) 175 | if botReply.Data then 176 | if botReply.Data.StartItems then 177 | if #hBot.mybot.sNextItem == 0 then 178 | hBot.mybot.sNextItem = botReply.Data.StartItems 179 | end 180 | end 181 | 182 | if botReply.Data.LevelAbs then 183 | if #hBot.mybot.sNextAbility == 0 then 184 | hBot.mybot.sNextAbility = botReply.Data.LevelAbs 185 | end 186 | end 187 | end 188 | end 189 | 190 | return nil, BOT_MODE_DESIRE_NONE 191 | end 192 | 193 | function X:Think(hBot) 194 | -- if we are a human player, don't bother 195 | if not hBot:IsBot() then return end 196 | 197 | if not self.Init then 198 | self:DoInit(hBot) 199 | return 200 | end 201 | 202 | if GetGameState() ~= GAME_STATE_GAME_IN_PROGRESS and GetGameState() ~= GAME_STATE_PRE_GAME then return end 203 | 204 | -- draw debug info to Game UI 205 | dbg.draw() 206 | 207 | -- do out Thinking and set our Mode 208 | if GameTime() - self.lastModeThink >= 0.1 then 209 | -- reset any frame-temporary variables 210 | self:ResetTempVars() 211 | 212 | -- try to get directives from the web-server 213 | local highestDesiredMode, highestDesiredValue = ServerUpdate() 214 | 215 | -- if we got "nil" then do local thinking 216 | if highestDesiredMode == nil then 217 | highestDesiredMode, highestDesiredValue = think.MainThink() 218 | end 219 | 220 | -- execute any atomic operations 221 | self:ExecuteAtomicOperations(hBot) 222 | 223 | -- execute our current directives 224 | self:BeginMode(highestDesiredMode, highestDesiredValue) 225 | self:ExecuteMode() 226 | 227 | self.lastModeThink = GameTime() 228 | end 229 | end 230 | 231 | ------------------------------------------------------------------------------- 232 | -- ATOMIC OPERATIONS 233 | ------------------------------------------------------------------------------- 234 | 235 | function X:ExecuteAtomicOperations(hBot) 236 | self:Atomic_LearnAbilities(hBot) 237 | self:Atomic_BuyItems(hBot) 238 | self:Atomic_SwapItems(hBot) 239 | end 240 | 241 | function X:Atomic_LearnAbilities(hBot) 242 | if #hBot.mybot.sNextAbility == 0 then return end 243 | 244 | local nAbilityPoints = hBot:GetAbilityPoints() 245 | if nAbilityPoints > 0 then 246 | for i = 1, #hBot.mybot.sNextAbility do 247 | local sNextAb = hBot.mybot.sNextAbility[1] 248 | local hAbility = hBot:GetAbilityByName(sNextAb) 249 | if hAbility and hAbility:CanAbilityBeUpgraded() then 250 | hBot:ActionImmediate_LevelAbility(sNextAb) 251 | table.remove(hBot.mybot.sNextAbility, 1) 252 | break 253 | else 254 | dbg.pause("Trying to level an ability I cannot", hBot.mybot.sNextAbility) 255 | end 256 | end 257 | end 258 | end 259 | 260 | function X:Atomic_BuyItems(hBot) 261 | if #hBot.mybot.sNextItem == 0 then return end 262 | 263 | local sComps = {} 264 | 265 | for i = 1, #hBot.mybot.sNextItem do 266 | TableConcat(sComps, InvHelp:GetComponents(item_map[hBot.mybot.sNextItem[i]])) 267 | end 268 | 269 | hBot.mybot.sNextItem = sComps 270 | for k,v in pairs(sComps) do 271 | print(k,v) 272 | end 273 | 274 | while #hBot.mybot.sNextItem > 0 do 275 | local sNextItem = hBot.mybot.sNextItem[1] 276 | 277 | if hBot:GetGold() >= GetItemCost(sNextItem) then 278 | local secret = IsItemPurchasedFromSecretShop(sNextItem) 279 | local side = IsItemPurchasedFromSideShop(sNextItem) 280 | local fountain = (not secret) 281 | 282 | local shops = {} 283 | -- Determine valid shops that sell the item 284 | if (secret and side) then 285 | shops = {SHOP_SECRET_RADIANT, SHOP_SECRET_DIRE, SHOP_SIDE_BOT, SHOP_SIDE_TOP} 286 | elseif (secret) then 287 | shops = {SHOP_SECRET_RADIANT, SHOP_SECRET_DIRE} 288 | elseif (side and fountain) then 289 | shops = {SHOP_SIDE_BOT, SHOP_SIDE_TOP, SHOP_RADIANT, SHOP_DIRE} 290 | elseif (fountain) then 291 | shops = {SHOP_RADIANT, SHOP_DIRE} 292 | end 293 | 294 | for i = 1, #shops do 295 | local shop = shops[i] 296 | if ShopDistance(hBot, shop) <= SHOP_USE_DISTANCE then 297 | local buyRet = InvHelp:BuyItem(hBot, sNextItem) 298 | if buyRet == 0 then 299 | table.remove(hBot.mybot.sNextItem, 1) 300 | end 301 | break 302 | end 303 | end 304 | else 305 | break 306 | end 307 | end 308 | end 309 | 310 | function X:Atomic_SwapItems(hBot) 311 | local index1 = nil 312 | local index2 = nil 313 | if not InvHelp:IsBackpackEmpty(hBot) then 314 | local i1 = InvHelp:MostValuableItemSlot(hBot, 6, 8) 315 | local i2 = InvHelp:LeastValuableItemSlot(hBot, 0, 5) 316 | 317 | if i1.value > i2.value then 318 | index1 = i1.slot 319 | index2 = i2.slot 320 | end 321 | end 322 | 323 | if index1 and index2 then 324 | hBot:ActionImmediate_SwapItems(index1, index2) 325 | end 326 | end 327 | 328 | return X -------------------------------------------------------------------------------- /game_data/item_groups.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "moon_shard": 1, 4 | "hyperstone": 1 5 | }, 6 | { 7 | "magic_wand": 1, 8 | "circlet": 1, 9 | "branches": 1, 10 | "magic_stick": 1 11 | }, 12 | { 13 | "ward_dispenser": 1, 14 | "ward_sentry": 1, 15 | "ward_observer": 1 16 | }, 17 | { 18 | "travel_boots": 1, 19 | "boots": 1 20 | }, 21 | { 22 | "travel_boots_2": 1, 23 | "travel_boots": 1, 24 | "boots": 1, 25 | "recipe_travel_boots": 1 26 | }, 27 | { 28 | "phase_boots": 1, 29 | "boots": 1, 30 | "blades_of_attack": 1 31 | }, 32 | { 33 | "power_treads": 1, 34 | "boots": 1, 35 | "gloves": 1, 36 | "belt_of_strength": 1 37 | }, 38 | { 39 | "hand_of_midas": 1, 40 | "gloves": 1 41 | }, 42 | { 43 | "oblivion_staff": 1, 44 | "quarterstaff": 1, 45 | "sobi_mask": 1, 46 | "robe": 1 47 | }, 48 | { 49 | "pers": 1, 50 | "ring_of_health": 1, 51 | "void_stone": 1 52 | }, 53 | { 54 | "poor_mans_shield": 1, 55 | "slippers": 1, 56 | "stout_shield": 1 57 | }, 58 | { 59 | "bracer": 1, 60 | "circlet": 1, 61 | "gauntlets": 1 62 | }, 63 | { 64 | "wraith_band": 1, 65 | "circlet": 1, 66 | "slippers": 1 67 | }, 68 | { 69 | "null_talisman": 1, 70 | "circlet": 1, 71 | "mantle": 1 72 | }, 73 | { 74 | "mekansm": 1, 75 | "headdress": 1, 76 | "ring_of_regen": 1, 77 | "branches": 1, 78 | "buckler": 1, 79 | "chainmail": 1 80 | }, 81 | { 82 | "vladmir": 1, 83 | "headdress": 1, 84 | "ring_of_regen": 1, 85 | "branches": 1, 86 | "ring_of_basilius": 1, 87 | "sobi_mask": 1, 88 | "ring_of_protection": 1, 89 | "lifesteal": 1 90 | }, 91 | { 92 | "buckler": 1, 93 | "chainmail": 1, 94 | "branches": 1 95 | }, 96 | { 97 | "ring_of_basilius": 1, 98 | "sobi_mask": 1, 99 | "ring_of_protection": 1 100 | }, 101 | { 102 | "pipe": 1, 103 | "hood_of_defiance": 1, 104 | "ring_of_health": 1, 105 | "cloak": 1, 106 | "ring_of_regen": 1, 107 | "headdress": 1, 108 | "branches": 1 109 | }, 110 | { 111 | "urn_of_shadows": 1, 112 | "infused_raindrop": 1, 113 | "circlet": 1, 114 | "ring_of_protection": 1 115 | }, 116 | { 117 | "headdress": 1, 118 | "ring_of_regen": 1, 119 | "branches": 1 120 | }, 121 | { 122 | "sheepstick": 1, 123 | "mystic_staff": 1, 124 | "ultimate_orb": 1, 125 | "void_stone": 1 126 | }, 127 | { 128 | "orchid": 1, 129 | "oblivion_staff": 1, 130 | "quarterstaff": 1, 131 | "sobi_mask": 1, 132 | "robe": 1 133 | }, 134 | { 135 | "bloodthorn": 1, 136 | "orchid": 1, 137 | "oblivion_staff": 1, 138 | "quarterstaff": 1, 139 | "sobi_mask": 1, 140 | "robe": 1, 141 | "lesser_crit": 1, 142 | "broadsword": 1, 143 | "blades_of_attack": 1 144 | }, 145 | { 146 | "echo_sabre": 1, 147 | "ogre_axe": 1, 148 | "oblivion_staff": 1, 149 | "quarterstaff": 1, 150 | "sobi_mask": 1, 151 | "robe": 1 152 | }, 153 | { 154 | "cyclone": 1, 155 | "staff_of_wizardry": 1, 156 | "void_stone": 1, 157 | "wind_lace": 1 158 | }, 159 | { 160 | "aether_lens": 1, 161 | "energy_booster": 1, 162 | "void_stone": 1 163 | }, 164 | { 165 | "force_staff": 1, 166 | "staff_of_wizardry": 1, 167 | "ring_of_health": 1 168 | }, 169 | { 170 | "hurricane_pike": 1, 171 | "force_staff": 1, 172 | "staff_of_wizardry": 1, 173 | "ring_of_health": 1, 174 | "dragon_lance": 1, 175 | "ogre_axe": 1, 176 | "boots_of_elves": 1 177 | }, 178 | { 179 | "dagon": 1, 180 | "staff_of_wizardry": 1, 181 | "null_talisman": 1, 182 | "circlet": 1, 183 | "mantle": 1 184 | }, 185 | { 186 | "dagon_2": 1, 187 | "dagon": 1, 188 | "staff_of_wizardry": 1, 189 | "null_talisman": 1, 190 | "circlet": 1, 191 | "mantle": 1, 192 | "recipe_dagon": 1 193 | }, 194 | { 195 | "dagon_3": 1, 196 | "dagon_2": 1, 197 | "dagon": 1, 198 | "staff_of_wizardry": 1, 199 | "null_talisman": 1, 200 | "circlet": 1, 201 | "mantle": 1, 202 | "recipe_dagon": 1 203 | }, 204 | { 205 | "dagon_4": 1, 206 | "dagon_3": 1, 207 | "dagon_2": 1, 208 | "dagon": 1, 209 | "staff_of_wizardry": 1, 210 | "null_talisman": 1, 211 | "circlet": 1, 212 | "mantle": 1, 213 | "recipe_dagon": 1 214 | }, 215 | { 216 | "dagon_5": 1, 217 | "dagon_4": 1, 218 | "dagon_3": 1, 219 | "dagon_2": 1, 220 | "dagon": 1, 221 | "staff_of_wizardry": 1, 222 | "null_talisman": 1, 223 | "circlet": 1, 224 | "mantle": 1, 225 | "recipe_dagon": 1 226 | }, 227 | { 228 | "necronomicon": 1, 229 | "staff_of_wizardry": 1, 230 | "belt_of_strength": 1 231 | }, 232 | { 233 | "necronomicon_2": 1, 234 | "necronomicon": 1, 235 | "staff_of_wizardry": 1, 236 | "belt_of_strength": 1, 237 | "recipe_necronomicon": 1 238 | }, 239 | { 240 | "necronomicon_3": 1, 241 | "necronomicon_2": 1, 242 | "necronomicon": 1, 243 | "staff_of_wizardry": 1, 244 | "belt_of_strength": 1, 245 | "recipe_necronomicon": 1 246 | }, 247 | { 248 | "ultimate_scepter": 1, 249 | "point_booster": 1, 250 | "staff_of_wizardry": 1, 251 | "ogre_axe": 1, 252 | "blade_of_alacrity": 1 253 | }, 254 | { 255 | "refresher": 1, 256 | "pers": 1, 257 | "ring_of_health": 1, 258 | "void_stone": 1 259 | }, 260 | { 261 | "assault": 1, 262 | "platemail": 1, 263 | "hyperstone": 1, 264 | "chainmail": 1 265 | }, 266 | { 267 | "heart": 1, 268 | "reaver": 1, 269 | "vitality_booster": 1 270 | }, 271 | { 272 | "black_king_bar": 1, 273 | "ogre_axe": 1, 274 | "mithril_hammer": 1 275 | }, 276 | { 277 | "shivas_guard": 1, 278 | "platemail": 1, 279 | "mystic_staff": 1 280 | }, 281 | { 282 | "bloodstone": 1, 283 | "soul_ring": 1, 284 | "ring_of_regen": 1, 285 | "sobi_mask": 1, 286 | "enchanted_mango": 1, 287 | "soul_booster": 1, 288 | "vitality_booster": 1, 289 | "energy_booster": 1, 290 | "point_booster": 1 291 | }, 292 | { 293 | "sphere": 1, 294 | "ultimate_orb": 1, 295 | "pers": 1, 296 | "ring_of_health": 1, 297 | "void_stone": 1 298 | }, 299 | { 300 | "lotus_orb": 1, 301 | "pers": 1, 302 | "ring_of_health": 1, 303 | "void_stone": 1, 304 | "platemail": 1, 305 | "energy_booster": 1 306 | }, 307 | { 308 | "vanguard": 1, 309 | "ring_of_health": 1, 310 | "vitality_booster": 1, 311 | "stout_shield": 1 312 | }, 313 | { 314 | "crimson_guard": 1, 315 | "vanguard": 1, 316 | "ring_of_health": 1, 317 | "vitality_booster": 1, 318 | "stout_shield": 1, 319 | "buckler": 1, 320 | "chainmail": 1, 321 | "branches": 1 322 | }, 323 | { 324 | "blade_mail": 1, 325 | "broadsword": 1, 326 | "chainmail": 1, 327 | "robe": 1 328 | }, 329 | { 330 | "soul_booster": 1, 331 | "vitality_booster": 1, 332 | "energy_booster": 1, 333 | "point_booster": 1 334 | }, 335 | { 336 | "hood_of_defiance": 1, 337 | "ring_of_health": 1, 338 | "cloak": 1, 339 | "ring_of_regen": 1 340 | }, 341 | { 342 | "rapier": 1, 343 | "relic": 1, 344 | "demon_edge": 1 345 | }, 346 | { 347 | "monkey_king_bar": 1, 348 | "demon_edge": 1, 349 | "javelin": 1 350 | }, 351 | { 352 | "radiance": 1, 353 | "relic": 1 354 | }, 355 | { 356 | "butterfly": 1, 357 | "eagle": 1, 358 | "talisman_of_evasion": 1, 359 | "quarterstaff": 1 360 | }, 361 | { 362 | "greater_crit": 1, 363 | "lesser_crit": 1, 364 | "broadsword": 1, 365 | "blades_of_attack": 1, 366 | "demon_edge": 1 367 | }, 368 | { 369 | "basher": 1, 370 | "belt_of_strength": 1, 371 | "javelin": 1 372 | }, 373 | { 374 | "bfury": 1, 375 | "broadsword": 1, 376 | "claymore": 1, 377 | "pers": 1, 378 | "ring_of_health": 1, 379 | "void_stone": 1, 380 | "quelling_blade": 1 381 | }, 382 | { 383 | "manta": 1, 384 | "yasha": 1, 385 | "blade_of_alacrity": 1, 386 | "boots_of_elves": 1, 387 | "ultimate_orb": 1 388 | }, 389 | { 390 | "lesser_crit": 1, 391 | "broadsword": 1, 392 | "blades_of_attack": 1 393 | }, 394 | { 395 | "dragon_lance": 1, 396 | "ogre_axe": 1, 397 | "boots_of_elves": 1 398 | }, 399 | { 400 | "armlet": 1, 401 | "helm_of_iron_will": 1, 402 | "gloves": 1, 403 | "blades_of_attack": 1 404 | }, 405 | { 406 | "invis_sword": 1, 407 | "shadow_amulet": 1, 408 | "claymore": 1 409 | }, 410 | { 411 | "silver_edge": 1, 412 | "invis_sword": 1, 413 | "shadow_amulet": 1, 414 | "claymore": 1, 415 | "ultimate_orb": 1 416 | }, 417 | { 418 | "sange_and_yasha": 1, 419 | "yasha": 1, 420 | "blade_of_alacrity": 1, 421 | "boots_of_elves": 1, 422 | "sange": 1, 423 | "ogre_axe": 1, 424 | "belt_of_strength": 1 425 | }, 426 | { 427 | "satanic": 1, 428 | "lifesteal": 1, 429 | "mithril_hammer": 1, 430 | "reaver": 1 431 | }, 432 | { 433 | "mjollnir": 1, 434 | "maelstrom": 1, 435 | "mithril_hammer": 1, 436 | "gloves": 1, 437 | "hyperstone": 1 438 | }, 439 | { 440 | "skadi": 1, 441 | "ultimate_orb": 1, 442 | "point_booster": 1, 443 | "orb_of_venom": 1 444 | }, 445 | { 446 | "sange": 1, 447 | "ogre_axe": 1, 448 | "belt_of_strength": 1 449 | }, 450 | { 451 | "helm_of_the_dominator": 1, 452 | "gloves": 1, 453 | "headdress": 1, 454 | "ring_of_regen": 1, 455 | "branches": 1, 456 | "ring_of_health": 1 457 | }, 458 | { 459 | "maelstrom": 1, 460 | "mithril_hammer": 1, 461 | "gloves": 1 462 | }, 463 | { 464 | "desolator": 1, 465 | "mithril_hammer": 1, 466 | "blight_stone": 1 467 | }, 468 | { 469 | "yasha": 1, 470 | "blade_of_alacrity": 1, 471 | "boots_of_elves": 1 472 | }, 473 | { 474 | "mask_of_madness": 1, 475 | "lifesteal": 1, 476 | "quarterstaff": 1 477 | }, 478 | { 479 | "diffusal_blade": 1, 480 | "blade_of_alacrity": 1, 481 | "robe": 1 482 | }, 483 | { 484 | "diffusal_blade_2": 1, 485 | "diffusal_blade": 1, 486 | "blade_of_alacrity": 1, 487 | "robe": 1, 488 | "recipe_diffusal_blade": 1 489 | }, 490 | { 491 | "ethereal_blade": 1, 492 | "eagle": 1, 493 | "ghost": 1 494 | }, 495 | { 496 | "soul_ring": 1, 497 | "ring_of_regen": 1, 498 | "sobi_mask": 1, 499 | "enchanted_mango": 1 500 | }, 501 | { 502 | "arcane_boots": 1, 503 | "boots": 1, 504 | "energy_booster": 1 505 | }, 506 | { 507 | "octarine_core": 1, 508 | "mystic_staff": 1, 509 | "soul_booster": 1, 510 | "vitality_booster": 1, 511 | "energy_booster": 1, 512 | "point_booster": 1 513 | }, 514 | { 515 | "ancient_janggo": 1, 516 | "sobi_mask": 1, 517 | "bracer": 1, 518 | "circlet": 1, 519 | "gauntlets": 1, 520 | "wind_lace": 1 521 | }, 522 | { 523 | "medallion_of_courage": 1, 524 | "chainmail": 1, 525 | "sobi_mask": 1, 526 | "blight_stone": 1 527 | }, 528 | { 529 | "solar_crest": 1, 530 | "medallion_of_courage": 1, 531 | "chainmail": 1, 532 | "sobi_mask": 1, 533 | "blight_stone": 1, 534 | "talisman_of_evasion": 1 535 | }, 536 | { 537 | "veil_of_discord": 1, 538 | "helm_of_iron_will": 1, 539 | "null_talisman": 1, 540 | "circlet": 1, 541 | "mantle": 1 542 | }, 543 | { 544 | "guardian_greaves": 1, 545 | "mekansm": 1, 546 | "headdress": 1, 547 | "ring_of_regen": 1, 548 | "branches": 1, 549 | "buckler": 1, 550 | "chainmail": 1, 551 | "arcane_boots": 1, 552 | "boots": 1, 553 | "energy_booster": 1 554 | }, 555 | { 556 | "rod_of_atos": 1, 557 | "staff_of_wizardry": 1, 558 | "bracer": 1, 559 | "circlet": 1, 560 | "gauntlets": 1 561 | }, 562 | { 563 | "iron_talon": 1, 564 | "quelling_blade": 1, 565 | "ring_of_protection": 1 566 | }, 567 | { 568 | "abyssal_blade": 1, 569 | "basher": 1, 570 | "belt_of_strength": 1, 571 | "javelin": 1, 572 | "vanguard": 1, 573 | "ring_of_health": 1, 574 | "vitality_booster": 1, 575 | "stout_shield": 1 576 | }, 577 | { 578 | "heavens_halberd": 1, 579 | "sange": 1, 580 | "ogre_axe": 1, 581 | "belt_of_strength": 1, 582 | "talisman_of_evasion": 1 583 | }, 584 | { 585 | "ring_of_aquila": 1, 586 | "wraith_band": 1, 587 | "circlet": 1, 588 | "slippers": 1, 589 | "ring_of_basilius": 1, 590 | "sobi_mask": 1, 591 | "ring_of_protection": 1 592 | }, 593 | { 594 | "tranquil_boots": 1, 595 | "boots": 1, 596 | "wind_lace": 1, 597 | "ring_of_regen": 1 598 | }, 599 | { 600 | "glimmer_cape": 1, 601 | "shadow_amulet": 1, 602 | "cloak": 1 603 | } 604 | ] -------------------------------------------------------------------------------- /webserver_out.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | --- AUTHOR: Nostrademous 3 | --- GITHUB REPO: https://github.com/Nostrademous/Dota2-WebAI 4 | ------------------------------------------------------------------------------- 5 | 6 | dkjson = require( "game/dkjson" ) 7 | 8 | local dbg = require( GetScriptDirectory().."/debug" ) 9 | local packet = require( GetScriptDirectory().."/data_packet" ) 10 | local web_config = require( GetScriptDirectory().."/web_config" ) 11 | 12 | local webserver = {} 13 | 14 | local webserverFound = false 15 | local webserverAuthTried = false 16 | 17 | webserver.startTime = -1000.0 18 | webserver.lastWorldUpdate = -1000.0 19 | webserver.lastEnemiesUpdate = -1000.0 20 | webserver.lastAlliesUpdate = -1000.0 21 | webserver.lastReply = nil 22 | 23 | local function dumpHeroInfo( hHero ) 24 | if not ValidTarget(hHero) then return "{}" end 25 | local data = {} 26 | 27 | if hHero.mybot then 28 | data.Name = heroData[hHero.mybot.Name].Name:lower() 29 | data.NextItems = hHero.mybot.sNextItem 30 | data.NextAbs = hHero.mybot.sNextAbility 31 | else 32 | data.Name = heroData[GetUnitName(hHero)].Name:lower() 33 | end 34 | data.Team = hHero:GetTeam() 35 | data.Level = hHero:GetLevel() 36 | data.Health = hHero:GetHealth() 37 | data.MaxHealth = hHero:GetMaxHealth() 38 | data.HealthReg = hHero:GetHealthRegen() 39 | data.Mana = hHero:GetMana() 40 | data.MaxMana = hHero:GetMaxMana() 41 | data.ManaReg = hHero:GetManaRegen() 42 | data.MS = hHero:GetCurrentMovementSpeed() 43 | 44 | local loc = hHero:GetLocation() 45 | data.X = loc.x 46 | data.Y = loc.y 47 | data.Z = loc.z 48 | 49 | local items = {} 50 | for iInvIndex = 0, 15, 1 do 51 | local hItem = hHero:GetItemInSlot(iInvIndex) 52 | if hItem ~= nil then 53 | local str = hItem:GetName() 54 | local numCharges = hItem:GetCurrentCharges() 55 | if numCharges > 1 then 56 | str = str .. '_' .. numCharges 57 | end 58 | table.insert(items, str) 59 | end 60 | end 61 | data.Items = items 62 | 63 | -- this is data only available to heroes on my team 64 | if hHero:GetTeam() == GetTeam() then 65 | data.Gold = hHero:GetGold() 66 | data.AP = hHero:GetAbilityPoints() 67 | end 68 | 69 | return data 70 | end 71 | 72 | local function dumpUnitInfo( hUnit ) 73 | if not ValidTarget(hUnit) then return "{}" end 74 | 75 | local str = '"' .. hUnit:GetUnitName() .. '":{' 76 | 77 | str = str .. '"Health": ' .. hUnit:GetHealth() 78 | str = str .. ', "MaxHealth": ' .. hUnit:GetMaxHealth() 79 | 80 | str = str .. ', "Mana": ' .. hUnit:GetMana() 81 | str = str .. ', "MaxMana": ' .. hUnit:GetMaxMana() 82 | 83 | local loc = hUnit:GetLocation() 84 | str = str .. ', "Loc_X": ' .. loc.x 85 | str = str .. ', "Loc_Y": ' .. loc.y 86 | str = str .. ', "Loc_Z": ' .. loc.z 87 | 88 | str = str .. ', "Team": ' .. hUnit:GetTeam() 89 | 90 | if hUnit:IsHero() then 91 | str = str .. ', "ID": ' .. hUnit:GetPlayerID() 92 | 93 | str = str .. ', "Items": [' 94 | 95 | local count = 1 96 | for iInvIndex = 0, 15, 1 do 97 | local hItem = hUnit:GetItemInSlot(iInvIndex) 98 | if hItem ~= nil then 99 | if count > 1 then str = str .. ', ' end 100 | str = str .. '"' .. hItem:GetName() 101 | local numCharges = hItem:GetCurrentCharges() 102 | if numCharges > 1 then 103 | str = str .. '_' .. numCharges 104 | end 105 | str = str .. '"' 106 | count = count + 1 107 | end 108 | end 109 | str = str .. ']' 110 | end 111 | 112 | str = str .. '}' 113 | return str 114 | end 115 | 116 | local function dumpCourierInfo() 117 | local data = {} 118 | 119 | local numCouriers = GetNumCouriers() 120 | data.Num = GetNumCouriers() 121 | 122 | if numCouriers > 0 then 123 | -- NOTE: GetCourier( iCourier ) is 0-indexed 124 | for i = 0, numCouriers-1, 1 do 125 | local hCourier = GetCourier(i) 126 | local cdata = {} 127 | 128 | cdata.Health = hCourier:GetHealth() 129 | cdata.Fly = IsFlyingCourier(hCourier) 130 | cdata.State = GetCourierState(hCourier) 131 | 132 | local loc = hCourier:GetLocation() 133 | cdata.X = loc.x 134 | cdata.Y = loc.y 135 | cdata.Z = loc.z 136 | 137 | data["C_"..tostring(i+1)] = cdata 138 | end 139 | end 140 | 141 | return data 142 | end 143 | 144 | local function dumpAlliedHeroes() 145 | local data = {} 146 | 147 | local alliedHeroes = GetUnitList(UNIT_LIST_ALLIED_HEROES) 148 | for _, ally in pairs(alliedHeroes) do 149 | if not ally:IsBot() or ally.mybot == nil then 150 | if not ally:IsIllusion() then 151 | data["Ally_"..tostring(ally:GetPlayerID())] = dumpHeroInfo(ally) 152 | end 153 | end 154 | end 155 | 156 | return data 157 | end 158 | 159 | local function dumpAlliedHeroesOther() 160 | local str = '' 161 | local count = 1 162 | str = str..'"alliedHeroesOther":{' 163 | local alliedHeroesOther = GetUnitList(UNIT_LIST_ALLIED_OTHER) 164 | for _, value in pairs(alliedHeroesOther) do 165 | if count > 1 then str = str..', ' end 166 | str = str .. dumpUnitInfo( value ) 167 | count = count + 1 168 | end 169 | str = str..'}' 170 | return str 171 | end 172 | 173 | local function dumpAlliedCreep() 174 | local str = '' 175 | local count = 1 176 | str = str..'"alliedCreep":{' 177 | local alliedCreep = GetUnitList(UNIT_LIST_ALLIED_CREEPS) 178 | for _, value in pairs(alliedCreep) do 179 | if count > 1 then str = str..', ' end 180 | 181 | str = str .. dumpUnitInfo( value ) 182 | 183 | count = count + 1 184 | end 185 | str = str..'}' 186 | return str 187 | end 188 | 189 | local function dumpAlliedWards() 190 | local str = '' 191 | local count = 1 192 | str = str..'"alliedWards":{' 193 | local alliedWards = GetUnitList(UNIT_LIST_ALLIED_WARDS) 194 | for _, value in pairs(alliedWards) do 195 | if count > 1 then str = str..', ' end 196 | 197 | str = str .. dumpUnitInfo( value ) 198 | 199 | count = count + 1 200 | end 201 | str = str..'}' 202 | return str 203 | end 204 | 205 | local function dumpNeutralCreep() 206 | local str = '' 207 | local count = 1 208 | str = str..'"neutralCreep":{' 209 | local neutralCreep = GetUnitList(UNIT_LIST_NEUTRAL_CREEPS) 210 | for _, value in pairs(neutralCreep) do 211 | if count > 1 then str = str..', ' end 212 | 213 | str = str .. dumpUnitInfo( value ) 214 | 215 | count = count + 1 216 | end 217 | str = str..'}' 218 | return str 219 | end 220 | 221 | local function dumpEnemyHeroes() 222 | local data = {} 223 | 224 | local enemyHeroes = GetUnitList(UNIT_LIST_ENEMY_HEROES) 225 | for _, enemy in pairs(enemyHeroes) do 226 | if ValidTarget(enemy) then 227 | -- we add a _# to handle illusions for enemies 228 | local count = 1 229 | local key = "E_"..tostring(enemy:GetPlayerID()).."_"..tostring(count) 230 | while InTable(data, key) do 231 | count = count + 1 232 | key = "E_"..tostring(enemy:GetPlayerID()).."_"..tostring(count) 233 | end 234 | data[key] = dumpHeroInfo(enemy) 235 | end 236 | end 237 | 238 | return data 239 | end 240 | 241 | local function dumpEnemyHeroesOther() 242 | local str = '' 243 | local count = 1 244 | str = str..'"enemyHeroesOther":{' 245 | local enemyHeroesOther = GetUnitList(UNIT_LIST_ENEMY_OTHER) 246 | for _, value in pairs(enemyHeroesOther) do 247 | if count > 1 then str = str..', ' end 248 | 249 | str = str .. dumpUnitInfo( value ) 250 | 251 | count = count + 1 252 | end 253 | str = str..'}' 254 | return str 255 | end 256 | 257 | local function dumpEnemyCreep() 258 | local str = '' 259 | local count = 1 260 | str = str..'"enemyCreep":{' 261 | local enemyCreep = GetUnitList(UNIT_LIST_ENEMY_CREEPS) 262 | for _, value in pairs(enemyCreep) do 263 | if count > 1 then str = str..', ' end 264 | 265 | str = str .. dumpUnitInfo( value ) 266 | 267 | count = count + 1 268 | end 269 | str = str..'}' 270 | return str 271 | end 272 | 273 | local function dumpEnemyWards() 274 | local str = '' 275 | local count = 1 276 | str = str..'"enemyWards":{' 277 | local enemyWards = GetUnitList(UNIT_LIST_ENEMY_WARDS) 278 | for _, value in pairs(enemyWards) do 279 | if count > 1 then str = str..', ' end 280 | 281 | str = str .. dumpUnitInfo( value ) 282 | 283 | count = count + 1 284 | end 285 | str = str..'}' 286 | return str 287 | end 288 | 289 | local function dumpAOEInfo ( hTable ) 290 | -- NOTE: an aoe will be table with { "location", "ability", "caster", "radius", "playerid" }. 291 | local str = '"' .. hTable.playerid .. '":{' 292 | 293 | str = str .. '"Ability": ' .. hTable.ability 294 | str = str .. ', "Radius": ' .. hTable.radius 295 | str = str .. ', "Caster": ' .. hTable.caster 296 | 297 | local loc = hTable.location 298 | str = str .. ', "Loc_X": ' .. loc.x 299 | str = str .. ', "Loc_Y": ' .. loc.y 300 | str = str .. ', "Loc_Z": ' .. loc.z 301 | 302 | str = str .. '}' 303 | return str 304 | end 305 | 306 | local function dumpDangerousAOEs() 307 | local str = '' 308 | local count = 1 309 | str = str..'"dangerousAOEs":{' 310 | local badAOEs = GetAvoidanceZones() 311 | for _, aoe in pairs(badAOEs) do 312 | if aoe.caster:GetTeam() ~= GetTeam() or aoe.ability == "faceless_void_chronosphere" then 313 | if count > 1 then str = str..', ' end 314 | 315 | str = str .. dumpAOEInfo( aoe ) 316 | 317 | count = count + 1 318 | end 319 | end 320 | str = str..'}' 321 | return str 322 | end 323 | 324 | local function dumpProjectileInfo( hTable ) 325 | -- NOTE: a projectile will be a table with { "location", "ability", "velocity", "radius", "playerid" } 326 | local str = '"' .. hTable.playerid .. '":{' 327 | 328 | str = str .. '"Ability": ' .. dkjson.encode(hTable.ability) 329 | str = str .. ', "Radius": ' .. hTable.radius 330 | str = str .. ', "Velocity": ' .. hTable.velocity 331 | 332 | local loc = hTable.location 333 | str = str .. ', "Loc_X": ' .. loc.x 334 | str = str .. ', "Loc_Y": ' .. loc.y 335 | str = str .. ', "Loc_Z": ' .. loc.z 336 | 337 | str = str .. '}' 338 | return str 339 | end 340 | 341 | local function dumpDangerousProjectiles() 342 | local str = '' 343 | local count = 1 344 | str = str..'"dangerousProjectiles":{' 345 | local badProjectiles = GetLinearProjectiles() 346 | for _, projectile in pairs(badProjectiles) do 347 | if projectile.playerid == nil or GetTeamForPlayer(projectile.playerid) ~= GetTeam() then 348 | if count > 1 then str = str..', ' end 349 | 350 | str = str .. dumpProjectileInfo( projectile ) 351 | 352 | count = count + 1 353 | end 354 | end 355 | str = str..'}' 356 | return str 357 | end 358 | 359 | local function dumpTeleportInfo( hTable ) 360 | local str = '"' .. hTable.playerid .. '":{' 361 | 362 | str = str .. '"TimeRemaining": ' .. hTable.time_remaining 363 | 364 | local loc = hTable.location 365 | str = str .. ', "Loc_X": ' .. loc.x 366 | str = str .. ', "Loc_Y": ' .. loc.y 367 | str = str .. ', "Loc_Z": ' .. loc.z 368 | 369 | str = str .. '}' 370 | return str 371 | end 372 | 373 | local function dumpGetIncomingTeleports() 374 | local str = '' 375 | local count = 1 376 | str = str..'"incomingTeleports":{' 377 | local teleports = GetIncomingTeleports() 378 | for _, value in pairs(teleports) do 379 | if count > 1 then str = str..', ' end 380 | 381 | str = str .. dumpTeleportInfo( value ) 382 | 383 | count = count + 1 384 | end 385 | str = str..'}' 386 | return str 387 | end 388 | 389 | local function dumpCastCallbackInfo( hTable ) 390 | local str = '"' .. hTable.playerid .. '":{' 391 | 392 | str = str .. '"TimeRemaining": ' .. hTable.time_remaining 393 | 394 | local loc = hTable.location 395 | str = str .. ', "Loc_X": ' .. loc.x 396 | str = str .. ', "Loc_Y": ' .. loc.y 397 | str = str .. ', "Loc_Z": ' .. loc.z 398 | 399 | str = str .. '}' 400 | return str 401 | end 402 | 403 | local callbackInit = false 404 | local callbackStr = "" 405 | 406 | function callbackFunc( hTable ) 407 | local str = '"' 408 | if callbackStr ~= "" then str = ', "' end 409 | 410 | str = str .. hTable.player_id .. '":{' 411 | if hTable.unit == nil then 412 | str = str .. '"CastingUnit": UNKNOWN' 413 | else 414 | str = str .. '"CastingUnit": ' .. dumpUnitInfo( hTable.unit ) 415 | end 416 | 417 | if hTable.ability == nil then 418 | str = str .. ', "Ability": UNKNOWN' 419 | else 420 | str = str .. ', "Ability": ' .. hTable.ability:GetName() 421 | end 422 | 423 | local loc = hTable.location 424 | str = str .. ', "Loc_X": ' .. loc.x 425 | str = str .. ', "Loc_Y": ' .. loc.y 426 | str = str .. ', "Loc_Z": ' .. loc.z 427 | str = str .. '}' 428 | 429 | callbackStr = callbackStr .. str 430 | end 431 | 432 | local function dumpCastCallback() 433 | local str = '' 434 | local count = 1 435 | str = str..'"castCallback":{' 436 | str = str..callbackStr 437 | str = str..'}' 438 | 439 | callbackStr = "" 440 | return str 441 | end 442 | 443 | function webserver.SendPacket( json ) 444 | local req = CreateHTTPRequest( web_config.IP_ADDR .. ":" .. web_config.IP_PORT ) 445 | req:SetHTTPRequestRawPostBody("application/json", json) 446 | req:Send( function( result ) 447 | for k,v in pairs( result ) do 448 | if k == "Body" then 449 | local jsonReply, pos, err = dkjson.decode(v, 1, nil) 450 | if err then 451 | print("JSON Decode Error: ", err) 452 | print("Sent Message: ", json) 453 | print("Msg Body: ", v) 454 | else 455 | --print( tostring(jsonReply) ) 456 | packet:ProcessPacket(jsonReply.Type, jsonReply) 457 | 458 | if jsonReply.Type == packet.TYPE_AUTH then 459 | webserverFound = true 460 | print("Connected Successfully to Backend Server") 461 | end 462 | end 463 | --break 464 | end 465 | end 466 | end ) 467 | end 468 | 469 | function webserver.SendData(hBot) 470 | -- if we have not verified the presence of a webserver yet, send authentication packet 471 | if not webserverFound and not webserverAuthTried then 472 | webserverAuthTried = true 473 | local jsonData = webserver.CreateAuthPacket() 474 | packet:CreatePacket(packet.TYPE_AUTH, jsonData) 475 | webserver.SendPacket(jsonData) 476 | end 477 | 478 | -- if we have a webserver 479 | if webserverFound then 480 | -- initialize our cast callback if we have not done so yet 481 | if not callbackInit then 482 | InstallCastCallback(-1, callbackFunc) 483 | callbackInit = true 484 | end 485 | 486 | -- check if we need to send a World Update Packet 487 | if packet.LastPacket[packet.TYPE_WORLD] == nil or packet.LastPacket[packet.TYPE_WORLD].processed 488 | or (GameTime() - webserver.lastWorldUpdate) > 0.5 then 489 | local jsonData = webserver.CreateWorldUpdate() 490 | packet:CreatePacket(packet.TYPE_WORLD, jsonData) 491 | webserver.lastWorldUpdate = GameTime() 492 | dbg.myPrint("Sending World Update: ", tostring(jsonData)) 493 | webserver.SendPacket(jsonData) 494 | end 495 | 496 | -- check if we need to send an Enemies Update Packet 497 | if packet.LastPacket[packet.TYPE_ENEMIES] == nil or packet.LastPacket[packet.TYPE_ENEMIES].processed 498 | or (GameTime() - webserver.lastEnemiesUpdate) > 0.25 then 499 | local jsonData = webserver.CreateEnemiesUpdate() 500 | packet:CreatePacket(packet.TYPE_ENEMIES, jsonData) 501 | webserver.lastEnemiesUpdate = GameTime() 502 | dbg.myPrint("Sending Enemies Update: ", tostring(jsonData)) 503 | webserver.SendPacket(jsonData) 504 | end 505 | 506 | -- check if we need to send a Player Update Packet 507 | local sPID = tostring(hBot:GetPlayerID()) 508 | local id = packet.TYPE_PLAYER .. sPID 509 | if packet.LastPacket[id] == nil or packet.LastPacket[id].processed 510 | or (webserver["lastPlayerUpdate"..sPID] and (GameTime() - webserver["lastPlayerUpdate"..sPID]) > 0.25) then 511 | local jsonData = webserver.CreatePlayerUpdate(hBot) 512 | packet:CreatePacket(id, jsonData) 513 | webserver["lastPlayerUpdate"..sPID] = GameTime() 514 | dbg.myPrint("Sending Player Update: ", tostring(jsonData)) 515 | webserver.SendPacket(jsonData) 516 | end 517 | 518 | -- check if we need to send an Enemies Update Packet 519 | if packet.LastPacket[packet.TYPE_ALLIES] == nil or (packet.LastPacket[packet.TYPE_ALLIES].processed 520 | and (GameTime() - webserver.lastAlliesUpdate) > 0.5) then 521 | local jsonData = webserver.CreateAlliesUpdate() 522 | packet:CreatePacket(packet.TYPE_ALLIES, jsonData) 523 | webserver.lastAlliesUpdate = GameTime() 524 | dbg.myPrint("Sending Allies Update: ", tostring(jsonData)) 525 | webserver.SendPacket(jsonData) 526 | end 527 | end 528 | end 529 | 530 | function webserver.CreateAuthPacket() 531 | local json = {} 532 | 533 | json.Type = packet.TYPE_AUTH 534 | json.Time = RealTime() 535 | 536 | return dkjson.encode(json) 537 | end 538 | 539 | function webserver.CreateWorldUpdate() 540 | local json = {} 541 | 542 | json.Type = packet.TYPE_WORLD 543 | json.Time = RealTime() 544 | json.DotaTime = DotaTime() 545 | 546 | -- report Lane Front info for both teams (does not ignore towers) 547 | json.RTopFront = GetLaneFrontAmount(TEAM_RADIANT, LANE_TOP, false) 548 | json.RMidFront = GetLaneFrontAmount(TEAM_RADIANT, LANE_MID, false) 549 | json.RBotFront = GetLaneFrontAmount(TEAM_RADIANT, LANE_BOT, false) 550 | json.DTopFront = GetLaneFrontAmount(TEAM_DIRE, LANE_TOP, false) 551 | json.DMidFront = GetLaneFrontAmount(TEAM_DIRE, LANE_MID, false) 552 | json.DBotFront = GetLaneFrontAmount(TEAM_DIRE, LANE_BOT, false) 553 | 554 | json.CourierData = dumpCourierInfo() 555 | 556 | --[[ 557 | json = json..", "..dumpAlliedHeroesOther() 558 | json = json..", "..dumpEnemyHeroesOther() 559 | json = json..", "..dumpAlliedCreep() 560 | json = json..", "..dumpNeutralCreep() 561 | json = json..", "..dumpEnemyCreep() 562 | json = json..", "..dumpAlliedWards() 563 | json = json..", "..dumpEnemyWards() 564 | json = json..", "..dumpDangerousAOEs() 565 | json = json..", "..dumpDangerousProjectiles() 566 | json = json..", "..dumpGetIncomingTeleports() 567 | json = json..", "..dumpCastCallback() 568 | --]] 569 | 570 | return dkjson.encode(json) 571 | end 572 | 573 | function webserver.CreatePlayerUpdate(hBot) 574 | local json = {} 575 | 576 | json.Type = packet.TYPE_PLAYER .. tostring(hBot:GetPlayerID()) 577 | json.Time = RealTime() 578 | 579 | json.Data = dumpHeroInfo(hBot) 580 | 581 | return dkjson.encode(json) 582 | end 583 | 584 | function webserver.CreateEnemiesUpdate() 585 | local json = {} 586 | 587 | json.Type = packet.TYPE_ENEMIES 588 | json.Time = RealTime() 589 | 590 | json.Data = dumpEnemyHeroes() 591 | 592 | return dkjson.encode(json) 593 | end 594 | 595 | function webserver.CreateAlliesUpdate() 596 | local json = {} 597 | 598 | json.Type = packet.TYPE_ALLIES 599 | json.Time = RealTime() 600 | json.DotaTime = DotaTime() 601 | 602 | json.AlliedHeroes = dumpAlliedHeroes() 603 | 604 | return dkjson.encode(json) 605 | end 606 | 607 | function webserver.GetLastReply(sType) 608 | return packet:GetLastReply(sType) 609 | end 610 | 611 | return webserver 612 | -------------------------------------------------------------------------------- /game_data/countries.json: -------------------------------------------------------------------------------- 1 | { 2 | "AW": { 3 | "name": { 4 | "common": "Aruba" 5 | }, 6 | "cca2": "AW" 7 | }, 8 | "AF": { 9 | "name": { 10 | "common": "Afghanistan" 11 | }, 12 | "cca2": "AF" 13 | }, 14 | "AO": { 15 | "name": { 16 | "common": "Angola" 17 | }, 18 | "cca2": "AO" 19 | }, 20 | "AI": { 21 | "name": { 22 | "common": "Anguilla" 23 | }, 24 | "cca2": "AI" 25 | }, 26 | "AX": { 27 | "name": { 28 | "common": "Åland Islands" 29 | }, 30 | "cca2": "AX" 31 | }, 32 | "AL": { 33 | "name": { 34 | "common": "Albania" 35 | }, 36 | "cca2": "AL" 37 | }, 38 | "AD": { 39 | "name": { 40 | "common": "Andorra" 41 | }, 42 | "cca2": "AD" 43 | }, 44 | "AE": { 45 | "name": { 46 | "common": "United Arab Emirates" 47 | }, 48 | "cca2": "AE" 49 | }, 50 | "AR": { 51 | "name": { 52 | "common": "Argentina" 53 | }, 54 | "cca2": "AR" 55 | }, 56 | "AM": { 57 | "name": { 58 | "common": "Armenia" 59 | }, 60 | "cca2": "AM" 61 | }, 62 | "AS": { 63 | "name": { 64 | "common": "American Samoa" 65 | }, 66 | "cca2": "AS" 67 | }, 68 | "AQ": { 69 | "name": { 70 | "common": "Antarctica" 71 | }, 72 | "cca2": "AQ" 73 | }, 74 | "TF": { 75 | "name": { 76 | "common": "French Southern and Antarctic Lands" 77 | }, 78 | "cca2": "TF" 79 | }, 80 | "AG": { 81 | "name": { 82 | "common": "Antigua and Barbuda" 83 | }, 84 | "cca2": "AG" 85 | }, 86 | "AU": { 87 | "name": { 88 | "common": "Australia" 89 | }, 90 | "cca2": "AU" 91 | }, 92 | "AT": { 93 | "name": { 94 | "common": "Austria" 95 | }, 96 | "cca2": "AT" 97 | }, 98 | "AZ": { 99 | "name": { 100 | "common": "Azerbaijan" 101 | }, 102 | "cca2": "AZ" 103 | }, 104 | "BI": { 105 | "name": { 106 | "common": "Burundi" 107 | }, 108 | "cca2": "BI" 109 | }, 110 | "BE": { 111 | "name": { 112 | "common": "Belgium" 113 | }, 114 | "cca2": "BE" 115 | }, 116 | "BJ": { 117 | "name": { 118 | "common": "Benin" 119 | }, 120 | "cca2": "BJ" 121 | }, 122 | "BF": { 123 | "name": { 124 | "common": "Burkina Faso" 125 | }, 126 | "cca2": "BF" 127 | }, 128 | "BD": { 129 | "name": { 130 | "common": "Bangladesh" 131 | }, 132 | "cca2": "BD" 133 | }, 134 | "BG": { 135 | "name": { 136 | "common": "Bulgaria" 137 | }, 138 | "cca2": "BG" 139 | }, 140 | "BH": { 141 | "name": { 142 | "common": "Bahrain" 143 | }, 144 | "cca2": "BH" 145 | }, 146 | "BS": { 147 | "name": { 148 | "common": "Bahamas" 149 | }, 150 | "cca2": "BS" 151 | }, 152 | "BA": { 153 | "name": { 154 | "common": "Bosnia and Herzegovina" 155 | }, 156 | "cca2": "BA" 157 | }, 158 | "BL": { 159 | "name": { 160 | "common": "Saint Barthélemy" 161 | }, 162 | "cca2": "BL" 163 | }, 164 | "BY": { 165 | "name": { 166 | "common": "Belarus" 167 | }, 168 | "cca2": "BY" 169 | }, 170 | "BZ": { 171 | "name": { 172 | "common": "Belize" 173 | }, 174 | "cca2": "BZ" 175 | }, 176 | "BM": { 177 | "name": { 178 | "common": "Bermuda" 179 | }, 180 | "cca2": "BM" 181 | }, 182 | "BO": { 183 | "name": { 184 | "common": "Bolivia" 185 | }, 186 | "cca2": "BO" 187 | }, 188 | "BR": { 189 | "name": { 190 | "common": "Brazil" 191 | }, 192 | "cca2": "BR" 193 | }, 194 | "BB": { 195 | "name": { 196 | "common": "Barbados" 197 | }, 198 | "cca2": "BB" 199 | }, 200 | "BN": { 201 | "name": { 202 | "common": "Brunei" 203 | }, 204 | "cca2": "BN" 205 | }, 206 | "BT": { 207 | "name": { 208 | "common": "Bhutan" 209 | }, 210 | "cca2": "BT" 211 | }, 212 | "BV": { 213 | "name": { 214 | "common": "Bouvet Island" 215 | }, 216 | "cca2": "BV" 217 | }, 218 | "BW": { 219 | "name": { 220 | "common": "Botswana" 221 | }, 222 | "cca2": "BW" 223 | }, 224 | "CF": { 225 | "name": { 226 | "common": "Central African Republic" 227 | }, 228 | "cca2": "CF" 229 | }, 230 | "CA": { 231 | "name": { 232 | "common": "Canada" 233 | }, 234 | "cca2": "CA" 235 | }, 236 | "CC": { 237 | "name": { 238 | "common": "Cocos (Keeling) Islands" 239 | }, 240 | "cca2": "CC" 241 | }, 242 | "CH": { 243 | "name": { 244 | "common": "Switzerland" 245 | }, 246 | "cca2": "CH" 247 | }, 248 | "CL": { 249 | "name": { 250 | "common": "Chile" 251 | }, 252 | "cca2": "CL" 253 | }, 254 | "CN": { 255 | "name": { 256 | "common": "China" 257 | }, 258 | "cca2": "CN" 259 | }, 260 | "CI": { 261 | "name": { 262 | "common": "Ivory Coast" 263 | }, 264 | "cca2": "CI" 265 | }, 266 | "CM": { 267 | "name": { 268 | "common": "Cameroon" 269 | }, 270 | "cca2": "CM" 271 | }, 272 | "CD": { 273 | "name": { 274 | "common": "DR Congo" 275 | }, 276 | "cca2": "CD" 277 | }, 278 | "CG": { 279 | "name": { 280 | "common": "Republic of the Congo" 281 | }, 282 | "cca2": "CG" 283 | }, 284 | "CK": { 285 | "name": { 286 | "common": "Cook Islands" 287 | }, 288 | "cca2": "CK" 289 | }, 290 | "CO": { 291 | "name": { 292 | "common": "Colombia" 293 | }, 294 | "cca2": "CO" 295 | }, 296 | "KM": { 297 | "name": { 298 | "common": "Comoros" 299 | }, 300 | "cca2": "KM" 301 | }, 302 | "CV": { 303 | "name": { 304 | "common": "Cape Verde" 305 | }, 306 | "cca2": "CV" 307 | }, 308 | "CR": { 309 | "name": { 310 | "common": "Costa Rica" 311 | }, 312 | "cca2": "CR" 313 | }, 314 | "CU": { 315 | "name": { 316 | "common": "Cuba" 317 | }, 318 | "cca2": "CU" 319 | }, 320 | "CW": { 321 | "name": { 322 | "common": "Curaçao" 323 | }, 324 | "cca2": "CW" 325 | }, 326 | "CX": { 327 | "name": { 328 | "common": "Christmas Island" 329 | }, 330 | "cca2": "CX" 331 | }, 332 | "KY": { 333 | "name": { 334 | "common": "Cayman Islands" 335 | }, 336 | "cca2": "KY" 337 | }, 338 | "CY": { 339 | "name": { 340 | "common": "Cyprus" 341 | }, 342 | "cca2": "CY" 343 | }, 344 | "CZ": { 345 | "name": { 346 | "common": "Czechia" 347 | }, 348 | "cca2": "CZ" 349 | }, 350 | "DE": { 351 | "name": { 352 | "common": "Germany" 353 | }, 354 | "cca2": "DE" 355 | }, 356 | "DJ": { 357 | "name": { 358 | "common": "Djibouti" 359 | }, 360 | "cca2": "DJ" 361 | }, 362 | "DM": { 363 | "name": { 364 | "common": "Dominica" 365 | }, 366 | "cca2": "DM" 367 | }, 368 | "DK": { 369 | "name": { 370 | "common": "Denmark" 371 | }, 372 | "cca2": "DK" 373 | }, 374 | "DO": { 375 | "name": { 376 | "common": "Dominican Republic" 377 | }, 378 | "cca2": "DO" 379 | }, 380 | "DZ": { 381 | "name": { 382 | "common": "Algeria" 383 | }, 384 | "cca2": "DZ" 385 | }, 386 | "EC": { 387 | "name": { 388 | "common": "Ecuador" 389 | }, 390 | "cca2": "EC" 391 | }, 392 | "EG": { 393 | "name": { 394 | "common": "Egypt" 395 | }, 396 | "cca2": "EG" 397 | }, 398 | "ER": { 399 | "name": { 400 | "common": "Eritrea" 401 | }, 402 | "cca2": "ER" 403 | }, 404 | "EH": { 405 | "name": { 406 | "common": "Western Sahara" 407 | }, 408 | "cca2": "EH" 409 | }, 410 | "ES": { 411 | "name": { 412 | "common": "Spain" 413 | }, 414 | "cca2": "ES" 415 | }, 416 | "EE": { 417 | "name": { 418 | "common": "Estonia" 419 | }, 420 | "cca2": "EE" 421 | }, 422 | "ET": { 423 | "name": { 424 | "common": "Ethiopia" 425 | }, 426 | "cca2": "ET" 427 | }, 428 | "FI": { 429 | "name": { 430 | "common": "Finland" 431 | }, 432 | "cca2": "FI" 433 | }, 434 | "FJ": { 435 | "name": { 436 | "common": "Fiji" 437 | }, 438 | "cca2": "FJ" 439 | }, 440 | "FK": { 441 | "name": { 442 | "common": "Falkland Islands" 443 | }, 444 | "cca2": "FK" 445 | }, 446 | "FR": { 447 | "name": { 448 | "common": "France" 449 | }, 450 | "cca2": "FR" 451 | }, 452 | "FO": { 453 | "name": { 454 | "common": "Faroe Islands" 455 | }, 456 | "cca2": "FO" 457 | }, 458 | "FM": { 459 | "name": { 460 | "common": "Micronesia" 461 | }, 462 | "cca2": "FM" 463 | }, 464 | "GA": { 465 | "name": { 466 | "common": "Gabon" 467 | }, 468 | "cca2": "GA" 469 | }, 470 | "GB": { 471 | "name": { 472 | "common": "United Kingdom" 473 | }, 474 | "cca2": "GB" 475 | }, 476 | "GE": { 477 | "name": { 478 | "common": "Georgia" 479 | }, 480 | "cca2": "GE" 481 | }, 482 | "GG": { 483 | "name": { 484 | "common": "Guernsey" 485 | }, 486 | "cca2": "GG" 487 | }, 488 | "GH": { 489 | "name": { 490 | "common": "Ghana" 491 | }, 492 | "cca2": "GH" 493 | }, 494 | "GI": { 495 | "name": { 496 | "common": "Gibraltar" 497 | }, 498 | "cca2": "GI" 499 | }, 500 | "GN": { 501 | "name": { 502 | "common": "Guinea" 503 | }, 504 | "cca2": "GN" 505 | }, 506 | "GP": { 507 | "name": { 508 | "common": "Guadeloupe" 509 | }, 510 | "cca2": "GP" 511 | }, 512 | "GM": { 513 | "name": { 514 | "common": "Gambia" 515 | }, 516 | "cca2": "GM" 517 | }, 518 | "GW": { 519 | "name": { 520 | "common": "Guinea-Bissau" 521 | }, 522 | "cca2": "GW" 523 | }, 524 | "GQ": { 525 | "name": { 526 | "common": "Equatorial Guinea" 527 | }, 528 | "cca2": "GQ" 529 | }, 530 | "GR": { 531 | "name": { 532 | "common": "Greece" 533 | }, 534 | "cca2": "GR" 535 | }, 536 | "GD": { 537 | "name": { 538 | "common": "Grenada" 539 | }, 540 | "cca2": "GD" 541 | }, 542 | "GL": { 543 | "name": { 544 | "common": "Greenland" 545 | }, 546 | "cca2": "GL" 547 | }, 548 | "GT": { 549 | "name": { 550 | "common": "Guatemala" 551 | }, 552 | "cca2": "GT" 553 | }, 554 | "GF": { 555 | "name": { 556 | "common": "French Guiana" 557 | }, 558 | "cca2": "GF" 559 | }, 560 | "GU": { 561 | "name": { 562 | "common": "Guam" 563 | }, 564 | "cca2": "GU" 565 | }, 566 | "GY": { 567 | "name": { 568 | "common": "Guyana" 569 | }, 570 | "cca2": "GY" 571 | }, 572 | "HK": { 573 | "name": { 574 | "common": "Hong Kong" 575 | }, 576 | "cca2": "HK" 577 | }, 578 | "HM": { 579 | "name": { 580 | "common": "Heard Island and McDonald Islands" 581 | }, 582 | "cca2": "HM" 583 | }, 584 | "HN": { 585 | "name": { 586 | "common": "Honduras" 587 | }, 588 | "cca2": "HN" 589 | }, 590 | "HR": { 591 | "name": { 592 | "common": "Croatia" 593 | }, 594 | "cca2": "HR" 595 | }, 596 | "HT": { 597 | "name": { 598 | "common": "Haiti" 599 | }, 600 | "cca2": "HT" 601 | }, 602 | "HU": { 603 | "name": { 604 | "common": "Hungary" 605 | }, 606 | "cca2": "HU" 607 | }, 608 | "ID": { 609 | "name": { 610 | "common": "Indonesia" 611 | }, 612 | "cca2": "ID" 613 | }, 614 | "IM": { 615 | "name": { 616 | "common": "Isle of Man" 617 | }, 618 | "cca2": "IM" 619 | }, 620 | "IN": { 621 | "name": { 622 | "common": "India" 623 | }, 624 | "cca2": "IN" 625 | }, 626 | "IO": { 627 | "name": { 628 | "common": "British Indian Ocean Territory" 629 | }, 630 | "cca2": "IO" 631 | }, 632 | "IE": { 633 | "name": { 634 | "common": "Ireland" 635 | }, 636 | "cca2": "IE" 637 | }, 638 | "IR": { 639 | "name": { 640 | "common": "Iran" 641 | }, 642 | "cca2": "IR" 643 | }, 644 | "IQ": { 645 | "name": { 646 | "common": "Iraq" 647 | }, 648 | "cca2": "IQ" 649 | }, 650 | "IS": { 651 | "name": { 652 | "common": "Iceland" 653 | }, 654 | "cca2": "IS" 655 | }, 656 | "IL": { 657 | "name": { 658 | "common": "Israel" 659 | }, 660 | "cca2": "IL" 661 | }, 662 | "IT": { 663 | "name": { 664 | "common": "Italy" 665 | }, 666 | "cca2": "IT" 667 | }, 668 | "JM": { 669 | "name": { 670 | "common": "Jamaica" 671 | }, 672 | "cca2": "JM" 673 | }, 674 | "JE": { 675 | "name": { 676 | "common": "Jersey" 677 | }, 678 | "cca2": "JE" 679 | }, 680 | "JO": { 681 | "name": { 682 | "common": "Jordan" 683 | }, 684 | "cca2": "JO" 685 | }, 686 | "JP": { 687 | "name": { 688 | "common": "Japan" 689 | }, 690 | "cca2": "JP" 691 | }, 692 | "KZ": { 693 | "name": { 694 | "common": "Kazakhstan" 695 | }, 696 | "cca2": "KZ" 697 | }, 698 | "KE": { 699 | "name": { 700 | "common": "Kenya" 701 | }, 702 | "cca2": "KE" 703 | }, 704 | "KG": { 705 | "name": { 706 | "common": "Kyrgyzstan" 707 | }, 708 | "cca2": "KG" 709 | }, 710 | "KH": { 711 | "name": { 712 | "common": "Cambodia" 713 | }, 714 | "cca2": "KH" 715 | }, 716 | "KI": { 717 | "name": { 718 | "common": "Kiribati" 719 | }, 720 | "cca2": "KI" 721 | }, 722 | "KN": { 723 | "name": { 724 | "common": "Saint Kitts and Nevis" 725 | }, 726 | "cca2": "KN" 727 | }, 728 | "KR": { 729 | "name": { 730 | "common": "South Korea" 731 | }, 732 | "cca2": "KR" 733 | }, 734 | "XK": { 735 | "name": { 736 | "common": "Kosovo" 737 | }, 738 | "cca2": "XK" 739 | }, 740 | "KW": { 741 | "name": { 742 | "common": "Kuwait" 743 | }, 744 | "cca2": "KW" 745 | }, 746 | "LA": { 747 | "name": { 748 | "common": "Laos" 749 | }, 750 | "cca2": "LA" 751 | }, 752 | "LB": { 753 | "name": { 754 | "common": "Lebanon" 755 | }, 756 | "cca2": "LB" 757 | }, 758 | "LR": { 759 | "name": { 760 | "common": "Liberia" 761 | }, 762 | "cca2": "LR" 763 | }, 764 | "LY": { 765 | "name": { 766 | "common": "Libya" 767 | }, 768 | "cca2": "LY" 769 | }, 770 | "LC": { 771 | "name": { 772 | "common": "Saint Lucia" 773 | }, 774 | "cca2": "LC" 775 | }, 776 | "LI": { 777 | "name": { 778 | "common": "Liechtenstein" 779 | }, 780 | "cca2": "LI" 781 | }, 782 | "LK": { 783 | "name": { 784 | "common": "Sri Lanka" 785 | }, 786 | "cca2": "LK" 787 | }, 788 | "LS": { 789 | "name": { 790 | "common": "Lesotho" 791 | }, 792 | "cca2": "LS" 793 | }, 794 | "LT": { 795 | "name": { 796 | "common": "Lithuania" 797 | }, 798 | "cca2": "LT" 799 | }, 800 | "LU": { 801 | "name": { 802 | "common": "Luxembourg" 803 | }, 804 | "cca2": "LU" 805 | }, 806 | "LV": { 807 | "name": { 808 | "common": "Latvia" 809 | }, 810 | "cca2": "LV" 811 | }, 812 | "MO": { 813 | "name": { 814 | "common": "Macau" 815 | }, 816 | "cca2": "MO" 817 | }, 818 | "MF": { 819 | "name": { 820 | "common": "Saint Martin" 821 | }, 822 | "cca2": "MF" 823 | }, 824 | "MA": { 825 | "name": { 826 | "common": "Morocco" 827 | }, 828 | "cca2": "MA" 829 | }, 830 | "MC": { 831 | "name": { 832 | "common": "Monaco" 833 | }, 834 | "cca2": "MC" 835 | }, 836 | "MD": { 837 | "name": { 838 | "common": "Moldova" 839 | }, 840 | "cca2": "MD" 841 | }, 842 | "MG": { 843 | "name": { 844 | "common": "Madagascar" 845 | }, 846 | "cca2": "MG" 847 | }, 848 | "MV": { 849 | "name": { 850 | "common": "Maldives" 851 | }, 852 | "cca2": "MV" 853 | }, 854 | "MX": { 855 | "name": { 856 | "common": "Mexico" 857 | }, 858 | "cca2": "MX" 859 | }, 860 | "MH": { 861 | "name": { 862 | "common": "Marshall Islands" 863 | }, 864 | "cca2": "MH" 865 | }, 866 | "MK": { 867 | "name": { 868 | "common": "Macedonia" 869 | }, 870 | "cca2": "MK" 871 | }, 872 | "ML": { 873 | "name": { 874 | "common": "Mali" 875 | }, 876 | "cca2": "ML" 877 | }, 878 | "MT": { 879 | "name": { 880 | "common": "Malta" 881 | }, 882 | "cca2": "MT" 883 | }, 884 | "MM": { 885 | "name": { 886 | "common": "Myanmar" 887 | }, 888 | "cca2": "MM" 889 | }, 890 | "ME": { 891 | "name": { 892 | "common": "Montenegro" 893 | }, 894 | "cca2": "ME" 895 | }, 896 | "MN": { 897 | "name": { 898 | "common": "Mongolia" 899 | }, 900 | "cca2": "MN" 901 | }, 902 | "MP": { 903 | "name": { 904 | "common": "Northern Mariana Islands" 905 | }, 906 | "cca2": "MP" 907 | }, 908 | "MZ": { 909 | "name": { 910 | "common": "Mozambique" 911 | }, 912 | "cca2": "MZ" 913 | }, 914 | "MR": { 915 | "name": { 916 | "common": "Mauritania" 917 | }, 918 | "cca2": "MR" 919 | }, 920 | "MS": { 921 | "name": { 922 | "common": "Montserrat" 923 | }, 924 | "cca2": "MS" 925 | }, 926 | "MQ": { 927 | "name": { 928 | "common": "Martinique" 929 | }, 930 | "cca2": "MQ" 931 | }, 932 | "MU": { 933 | "name": { 934 | "common": "Mauritius" 935 | }, 936 | "cca2": "MU" 937 | }, 938 | "MW": { 939 | "name": { 940 | "common": "Malawi" 941 | }, 942 | "cca2": "MW" 943 | }, 944 | "MY": { 945 | "name": { 946 | "common": "Malaysia" 947 | }, 948 | "cca2": "MY" 949 | }, 950 | "YT": { 951 | "name": { 952 | "common": "Mayotte" 953 | }, 954 | "cca2": "YT" 955 | }, 956 | "NA": { 957 | "name": { 958 | "common": "Namibia" 959 | }, 960 | "cca2": "NA" 961 | }, 962 | "NC": { 963 | "name": { 964 | "common": "New Caledonia" 965 | }, 966 | "cca2": "NC" 967 | }, 968 | "NE": { 969 | "name": { 970 | "common": "Niger" 971 | }, 972 | "cca2": "NE" 973 | }, 974 | "NF": { 975 | "name": { 976 | "common": "Norfolk Island" 977 | }, 978 | "cca2": "NF" 979 | }, 980 | "NG": { 981 | "name": { 982 | "common": "Nigeria" 983 | }, 984 | "cca2": "NG" 985 | }, 986 | "NI": { 987 | "name": { 988 | "common": "Nicaragua" 989 | }, 990 | "cca2": "NI" 991 | }, 992 | "NU": { 993 | "name": { 994 | "common": "Niue" 995 | }, 996 | "cca2": "NU" 997 | }, 998 | "NL": { 999 | "name": { 1000 | "common": "Netherlands" 1001 | }, 1002 | "cca2": "NL" 1003 | }, 1004 | "NO": { 1005 | "name": { 1006 | "common": "Norway" 1007 | }, 1008 | "cca2": "NO" 1009 | }, 1010 | "NP": { 1011 | "name": { 1012 | "common": "Nepal" 1013 | }, 1014 | "cca2": "NP" 1015 | }, 1016 | "NR": { 1017 | "name": { 1018 | "common": "Nauru" 1019 | }, 1020 | "cca2": "NR" 1021 | }, 1022 | "NZ": { 1023 | "name": { 1024 | "common": "New Zealand" 1025 | }, 1026 | "cca2": "NZ" 1027 | }, 1028 | "OM": { 1029 | "name": { 1030 | "common": "Oman" 1031 | }, 1032 | "cca2": "OM" 1033 | }, 1034 | "PK": { 1035 | "name": { 1036 | "common": "Pakistan" 1037 | }, 1038 | "cca2": "PK" 1039 | }, 1040 | "PA": { 1041 | "name": { 1042 | "common": "Panama" 1043 | }, 1044 | "cca2": "PA" 1045 | }, 1046 | "PN": { 1047 | "name": { 1048 | "common": "Pitcairn Islands" 1049 | }, 1050 | "cca2": "PN" 1051 | }, 1052 | "PE": { 1053 | "name": { 1054 | "common": "Peru" 1055 | }, 1056 | "cca2": "PE" 1057 | }, 1058 | "PH": { 1059 | "name": { 1060 | "common": "Philippines" 1061 | }, 1062 | "cca2": "PH" 1063 | }, 1064 | "PW": { 1065 | "name": { 1066 | "common": "Palau" 1067 | }, 1068 | "cca2": "PW" 1069 | }, 1070 | "PG": { 1071 | "name": { 1072 | "common": "Papua New Guinea" 1073 | }, 1074 | "cca2": "PG" 1075 | }, 1076 | "PL": { 1077 | "name": { 1078 | "common": "Poland" 1079 | }, 1080 | "cca2": "PL" 1081 | }, 1082 | "PR": { 1083 | "name": { 1084 | "common": "Puerto Rico" 1085 | }, 1086 | "cca2": "PR" 1087 | }, 1088 | "KP": { 1089 | "name": { 1090 | "common": "North Korea" 1091 | }, 1092 | "cca2": "KP" 1093 | }, 1094 | "PT": { 1095 | "name": { 1096 | "common": "Portugal" 1097 | }, 1098 | "cca2": "PT" 1099 | }, 1100 | "PY": { 1101 | "name": { 1102 | "common": "Paraguay" 1103 | }, 1104 | "cca2": "PY" 1105 | }, 1106 | "PS": { 1107 | "name": { 1108 | "common": "Palestine" 1109 | }, 1110 | "cca2": "PS" 1111 | }, 1112 | "PF": { 1113 | "name": { 1114 | "common": "French Polynesia" 1115 | }, 1116 | "cca2": "PF" 1117 | }, 1118 | "QA": { 1119 | "name": { 1120 | "common": "Qatar" 1121 | }, 1122 | "cca2": "QA" 1123 | }, 1124 | "RE": { 1125 | "name": { 1126 | "common": "Réunion" 1127 | }, 1128 | "cca2": "RE" 1129 | }, 1130 | "RO": { 1131 | "name": { 1132 | "common": "Romania" 1133 | }, 1134 | "cca2": "RO" 1135 | }, 1136 | "RU": { 1137 | "name": { 1138 | "common": "Russia" 1139 | }, 1140 | "cca2": "RU" 1141 | }, 1142 | "RW": { 1143 | "name": { 1144 | "common": "Rwanda" 1145 | }, 1146 | "cca2": "RW" 1147 | }, 1148 | "SA": { 1149 | "name": { 1150 | "common": "Saudi Arabia" 1151 | }, 1152 | "cca2": "SA" 1153 | }, 1154 | "SD": { 1155 | "name": { 1156 | "common": "Sudan" 1157 | }, 1158 | "cca2": "SD" 1159 | }, 1160 | "SN": { 1161 | "name": { 1162 | "common": "Senegal" 1163 | }, 1164 | "cca2": "SN" 1165 | }, 1166 | "SG": { 1167 | "name": { 1168 | "common": "Singapore" 1169 | }, 1170 | "cca2": "SG" 1171 | }, 1172 | "GS": { 1173 | "name": { 1174 | "common": "South Georgia" 1175 | }, 1176 | "cca2": "GS" 1177 | }, 1178 | "SJ": { 1179 | "name": { 1180 | "common": "Svalbard and Jan Mayen" 1181 | }, 1182 | "cca2": "SJ" 1183 | }, 1184 | "SB": { 1185 | "name": { 1186 | "common": "Solomon Islands" 1187 | }, 1188 | "cca2": "SB" 1189 | }, 1190 | "SL": { 1191 | "name": { 1192 | "common": "Sierra Leone" 1193 | }, 1194 | "cca2": "SL" 1195 | }, 1196 | "SV": { 1197 | "name": { 1198 | "common": "El Salvador" 1199 | }, 1200 | "cca2": "SV" 1201 | }, 1202 | "SM": { 1203 | "name": { 1204 | "common": "San Marino" 1205 | }, 1206 | "cca2": "SM" 1207 | }, 1208 | "SO": { 1209 | "name": { 1210 | "common": "Somalia" 1211 | }, 1212 | "cca2": "SO" 1213 | }, 1214 | "PM": { 1215 | "name": { 1216 | "common": "Saint Pierre and Miquelon" 1217 | }, 1218 | "cca2": "PM" 1219 | }, 1220 | "RS": { 1221 | "name": { 1222 | "common": "Serbia" 1223 | }, 1224 | "cca2": "RS" 1225 | }, 1226 | "SS": { 1227 | "name": { 1228 | "common": "South Sudan" 1229 | }, 1230 | "cca2": "SS" 1231 | }, 1232 | "ST": { 1233 | "name": { 1234 | "common": "São Tomé and Príncipe" 1235 | }, 1236 | "cca2": "ST" 1237 | }, 1238 | "SR": { 1239 | "name": { 1240 | "common": "Suriname" 1241 | }, 1242 | "cca2": "SR" 1243 | }, 1244 | "SK": { 1245 | "name": { 1246 | "common": "Slovakia" 1247 | }, 1248 | "cca2": "SK" 1249 | }, 1250 | "SI": { 1251 | "name": { 1252 | "common": "Slovenia" 1253 | }, 1254 | "cca2": "SI" 1255 | }, 1256 | "SE": { 1257 | "name": { 1258 | "common": "Sweden" 1259 | }, 1260 | "cca2": "SE" 1261 | }, 1262 | "SZ": { 1263 | "name": { 1264 | "common": "Swaziland" 1265 | }, 1266 | "cca2": "SZ" 1267 | }, 1268 | "SX": { 1269 | "name": { 1270 | "common": "Sint Maarten" 1271 | }, 1272 | "cca2": "SX" 1273 | }, 1274 | "SC": { 1275 | "name": { 1276 | "common": "Seychelles" 1277 | }, 1278 | "cca2": "SC" 1279 | }, 1280 | "SY": { 1281 | "name": { 1282 | "common": "Syria" 1283 | }, 1284 | "cca2": "SY" 1285 | }, 1286 | "TC": { 1287 | "name": { 1288 | "common": "Turks and Caicos Islands" 1289 | }, 1290 | "cca2": "TC" 1291 | }, 1292 | "TD": { 1293 | "name": { 1294 | "common": "Chad" 1295 | }, 1296 | "cca2": "TD" 1297 | }, 1298 | "TG": { 1299 | "name": { 1300 | "common": "Togo" 1301 | }, 1302 | "cca2": "TG" 1303 | }, 1304 | "TH": { 1305 | "name": { 1306 | "common": "Thailand" 1307 | }, 1308 | "cca2": "TH" 1309 | }, 1310 | "TJ": { 1311 | "name": { 1312 | "common": "Tajikistan" 1313 | }, 1314 | "cca2": "TJ" 1315 | }, 1316 | "TK": { 1317 | "name": { 1318 | "common": "Tokelau" 1319 | }, 1320 | "cca2": "TK" 1321 | }, 1322 | "TM": { 1323 | "name": { 1324 | "common": "Turkmenistan" 1325 | }, 1326 | "cca2": "TM" 1327 | }, 1328 | "TL": { 1329 | "name": { 1330 | "common": "Timor-Leste" 1331 | }, 1332 | "cca2": "TL" 1333 | }, 1334 | "TO": { 1335 | "name": { 1336 | "common": "Tonga" 1337 | }, 1338 | "cca2": "TO" 1339 | }, 1340 | "TT": { 1341 | "name": { 1342 | "common": "Trinidad and Tobago" 1343 | }, 1344 | "cca2": "TT" 1345 | }, 1346 | "TN": { 1347 | "name": { 1348 | "common": "Tunisia" 1349 | }, 1350 | "cca2": "TN" 1351 | }, 1352 | "TR": { 1353 | "name": { 1354 | "common": "Turkey" 1355 | }, 1356 | "cca2": "TR" 1357 | }, 1358 | "TV": { 1359 | "name": { 1360 | "common": "Tuvalu" 1361 | }, 1362 | "cca2": "TV" 1363 | }, 1364 | "TW": { 1365 | "name": { 1366 | "common": "Taiwan" 1367 | }, 1368 | "cca2": "TW" 1369 | }, 1370 | "TZ": { 1371 | "name": { 1372 | "common": "Tanzania" 1373 | }, 1374 | "cca2": "TZ" 1375 | }, 1376 | "UG": { 1377 | "name": { 1378 | "common": "Uganda" 1379 | }, 1380 | "cca2": "UG" 1381 | }, 1382 | "UA": { 1383 | "name": { 1384 | "common": "Ukraine" 1385 | }, 1386 | "cca2": "UA" 1387 | }, 1388 | "UM": { 1389 | "name": { 1390 | "common": "United States Minor Outlying Islands" 1391 | }, 1392 | "cca2": "UM" 1393 | }, 1394 | "UY": { 1395 | "name": { 1396 | "common": "Uruguay" 1397 | }, 1398 | "cca2": "UY" 1399 | }, 1400 | "US": { 1401 | "name": { 1402 | "common": "United States" 1403 | }, 1404 | "cca2": "US" 1405 | }, 1406 | "UZ": { 1407 | "name": { 1408 | "common": "Uzbekistan" 1409 | }, 1410 | "cca2": "UZ" 1411 | }, 1412 | "VA": { 1413 | "name": { 1414 | "common": "Vatican City" 1415 | }, 1416 | "cca2": "VA" 1417 | }, 1418 | "VC": { 1419 | "name": { 1420 | "common": "Saint Vincent and the Grenadines" 1421 | }, 1422 | "cca2": "VC" 1423 | }, 1424 | "VE": { 1425 | "name": { 1426 | "common": "Venezuela" 1427 | }, 1428 | "cca2": "VE" 1429 | }, 1430 | "VG": { 1431 | "name": { 1432 | "common": "British Virgin Islands" 1433 | }, 1434 | "cca2": "VG" 1435 | }, 1436 | "VI": { 1437 | "name": { 1438 | "common": "United States Virgin Islands" 1439 | }, 1440 | "cca2": "VI" 1441 | }, 1442 | "VN": { 1443 | "name": { 1444 | "common": "Vietnam" 1445 | }, 1446 | "cca2": "VN" 1447 | }, 1448 | "VU": { 1449 | "name": { 1450 | "common": "Vanuatu" 1451 | }, 1452 | "cca2": "VU" 1453 | }, 1454 | "WF": { 1455 | "name": { 1456 | "common": "Wallis and Futuna" 1457 | }, 1458 | "cca2": "WF" 1459 | }, 1460 | "WS": { 1461 | "name": { 1462 | "common": "Samoa" 1463 | }, 1464 | "cca2": "WS" 1465 | }, 1466 | "YE": { 1467 | "name": { 1468 | "common": "Yemen" 1469 | }, 1470 | "cca2": "YE" 1471 | }, 1472 | "ZA": { 1473 | "name": { 1474 | "common": "South Africa" 1475 | }, 1476 | "cca2": "ZA" 1477 | }, 1478 | "ZM": { 1479 | "name": { 1480 | "common": "Zambia" 1481 | }, 1482 | "cca2": "ZM" 1483 | }, 1484 | "ZW": { 1485 | "name": { 1486 | "common": "Zimbabwe" 1487 | }, 1488 | "cca2": "ZW" 1489 | } 1490 | } --------------------------------------------------------------------------------