Dota 2 Game State Integrationで遊んでみよう #3 実際に製作する
何を作るかに悩んだ末、観戦を助けるツールを作ってみることにしました。
実際に動いている様子:
映像は映像はBorderless WindowにしてあるDotaクライアントをSkyWay ScreenShareでキャプチャーしたもの、音声はPlay sound in DesktopでDotaから出力されたものをそのまま聞く形で利用しています。
便利な点
- ブラウザのウィンドウで観戦ができる(リサイズなどが便利)
- プレイヤーのDotabuff/Opendotaページをすぐに開ける
- 全員のアイテムやBuyback statusを同時に表示できる
観戦時のGSIデータについて
- 観戦・リプレイ・Broadcastで同じデータ構造のデータが送られてくる
- 建造物、ヒーローの座標、Roshanのリスポーン時間などのデータは取得できない
- roshan_stateはalive, respawn_base, respawn_variableの3種類なのでrespawn_baseの段階ではリスポーンまでの残り時間を計算することができない
- これらのデータは大会の試合であればWeb APIのGetLiveLeagueGamesを使用して取得できる。更新頻度は20秒に1度
データ形式
{ "provider": { "name": "Dota 2", "appid": 570, "version": 44, "timestamp": 1479620832 }, "map": { "name": "dota", "matchid": 2790650572, "game_time": 2177, "clock_time": 1450, "daytime": true, "nightstalker_night": false, "game_state": "DOTA_GAMERULES_STATE_GAME_IN_PROGRESS", "win_team": "none", "customgamename": "", "radiant_ward_purchase_cooldown": 0, "dire_ward_purchase_cooldown": 0, "roshan_state": "respawn_base", "roshan_state_end_seconds": 88 }, "player": { "team2": { "player0": { "steamid": "76561198091503033", "name": "!", "activity": "playing", "kills": 0, "deaths": 3, "assists": 2, "last_hits": 55, "denies": 5, "kill_streak": 0, "team_name": "radiant", "gold": 125, "gold_reliable": 35, "gold_unreliable": 90, "gpm": 242, "xpm": 255, "net_worth": 3760, "hero_damage": 2898, "support_gold_spent": 1240, "wards_purchased": 14, "wards_placed": 4, "wards_destroyed": 2, "runes_activated": 1, "camps_stacked": 0 }, ... }, "team3": { ... } }, "hero": { "team2": { "player0": { "id": 64, "name": "npc_dota_hero_jakiro", "level": 11, "alive": true, "respawn_seconds": 0, "buyback_cost": 643, "buyback_cooldown": 0, "health": 1160, "max_health": 1160, "health_percent": 100, "mana": 972, "max_mana": 972, "mana_percent": 100, "silenced": false, "stunned": false, "disarmed": false, "magicimmune": false, "hexed": false, "muted": false, "break": false, "has_debuff": false, "selected_unit": false }, }, "team3": { ... } }, "abilities": { "team2": { "player0": { "ability0": { "name": "jakiro_dual_breath", "level": 2, "can_cast": true, "passive": false, "ability_active": true, "cooldown": 0, "ultimate": false }, ..., "attributes": { "level": 0 } }, "player1": { "ability0": { "name": "ember_spirit_searing_chains", "level": 4, "can_cast": true, "passive": false, "ability_active": true, "cooldown": 0, "ultimate": false }, ... }, ... }, "team3": { ... } }, "items": { "team2": { "player0": { "slot0": { "name": "item_arcane_boots", "can_cast": false, "cooldown": 37, "passive": false }, ..., "stash0": { "name": "empty" }, ... }, ... }, "team3": { ... } } }