From 824bdef6d4bd2b7a34224f8bbda5870cdf6ff462 Mon Sep 17 00:00:00 2001 From: Reynald Pader Date: Fri, 26 Jan 2018 10:58:44 +0800 Subject: [PATCH 1/4] Add inline condition to prevent converting of 'None' to string for non-Windows platforms --- sc2/sc2process.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sc2/sc2process.py b/sc2/sc2process.py index 8a04960ad..eb9e9c28b 100644 --- a/sc2/sc2process.py +++ b/sc2/sc2process.py @@ -78,7 +78,7 @@ def _launch(self): "-dataDir", str(Paths.BASE), "-tempDir", self._tmp_dir ], - cwd=str(Paths.CWD), + cwd=str(Paths.CWD) if Paths.CWD else None, #, env=run_config.env ) From 4ba7ac62fc4b70826a44ee4a2aea42983811da9c Mon Sep 17 00:00:00 2001 From: Reynald Pader Date: Fri, 26 Jan 2018 23:37:44 +0800 Subject: [PATCH 2/4] Wrap cwd expression in parentheses --- sc2/sc2process.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sc2/sc2process.py b/sc2/sc2process.py index eb9e9c28b..6ce238b3f 100644 --- a/sc2/sc2process.py +++ b/sc2/sc2process.py @@ -78,7 +78,7 @@ def _launch(self): "-dataDir", str(Paths.BASE), "-tempDir", self._tmp_dir ], - cwd=str(Paths.CWD) if Paths.CWD else None, + cwd=(str(Paths.CWD) if Paths.CWD else None), #, env=run_config.env ) From 8be993cc3cc6a08d738fce149e70fd62f6158703 Mon Sep 17 00:00:00 2001 From: Reynald Pader Date: Tue, 30 Jan 2018 19:38:27 +0800 Subject: [PATCH 3/4] Add support for getting the available abilities of a unit This includes checks to cooldown, energy, and research --- examples/threebase_voidray.py | 14 ++++++++++++++ examples/zerg_rush.py | 3 ++- sc2/bot_ai.py | 4 ++++ sc2/client.py | 13 +++++++++++++ sc2/ids/ability_id.py | 2 ++ sc2/ids/buff_id.py | 1 + sc2/unit.py | 6 ++++++ 7 files changed, 42 insertions(+), 1 deletion(-) diff --git a/examples/threebase_voidray.py b/examples/threebase_voidray.py index b6612aaa5..cc2497ba0 100644 --- a/examples/threebase_voidray.py +++ b/examples/threebase_voidray.py @@ -3,6 +3,7 @@ import sc2 from sc2 import Race, Difficulty from sc2.constants import * +from sc2.ids.buff_id import BuffId from sc2.player import Bot, Computer class ThreebaseVoidrayBot(sc2.BotAI): @@ -23,6 +24,19 @@ async def on_step(self, state, iteration): else: nexus = self.units(NEXUS).ready.random + if not nexus.has_buff(BuffId.CHRONOBOOSTENERGYCOST): + abilities = await self.get_available_abilities(nexus) + if AbilityId.CHRONOBOOSTENERGYCOST in abilities: + if nexus.energy > 25: + await self.do(nexus(AbilityId.CHRONOBOOSTENERGYCOST, nexus)) + else: + await self.chat_send("Not enough energy") + else: + await self.chat_send("Can't cast") + print(abilities) + else: + await self.chat_send("Nexus is boosted") + for idle_worker in self.workers.idle: mf = state.mineral_field.closest_to(idle_worker) await self.do(idle_worker.gather(mf)) diff --git a/examples/zerg_rush.py b/examples/zerg_rush.py index 58e9a6348..2afa55b47 100644 --- a/examples/zerg_rush.py +++ b/examples/zerg_rush.py @@ -32,7 +32,8 @@ async def on_step(self, state, iteration): await self.do(zl.attack(target)) for queen in self.units(QUEEN).idle: - if queen.energy >= 25: # Hard coded, since this is not (yet) available + abilities = await self.get_available_abilities(queen) + if AbilityId.INJECTLARVA in abilities: await self.do(queen(INJECTLARVA, hatchery)) if self.vespene >= 100: diff --git a/sc2/bot_ai.py b/sc2/bot_ai.py index 7ad20792b..ae57f2975 100644 --- a/sc2/bot_ai.py +++ b/sc2/bot_ai.py @@ -69,6 +69,10 @@ def expansion_locations(self): # Not always accurate, but good enought for now. return [c.rounded for c in centers] + async def get_available_abilities(self, unit): + # right know only checks cooldown, energy cost, and whether the ability has been researched + return await self._client.query_available_abilities(unit) + async def expand_now(self, building=None, max_distance=10): if not building: building = self.townhalls.first.type_id diff --git a/sc2/client.py b/sc2/client.py index 74a02cab2..e05622420 100644 --- a/sc2/client.py +++ b/sc2/client.py @@ -6,6 +6,9 @@ ) import logging + +from sc2.ids.ability_id import AbilityId + logger = logging.getLogger(__name__) from .cache import method_cache_forever @@ -160,6 +163,16 @@ async def query_building_placement(self, ability, positions, ignore_resources=Tr )) return [ActionResult(p.result) for p in result.query.placements] + async def query_available_abilities(self, unit): + assert isinstance(unit, Unit) + result = await self._execute(query=query_pb.RequestQuery( + abilities=[query_pb.RequestQueryAvailableAbilities( + unit_tag=unit.tag + )] + )) + return [AbilityId(a.ability_id) for a in result.query.abilities[0].abilities] + + async def chat_send(self, message, team_only): ch = ChatChannel.Team if team_only else ChatChannel.Broadcast r = await self._execute(action=sc_pb.RequestAction( diff --git a/sc2/ids/ability_id.py b/sc2/ids/ability_id.py index d273e7e4d..7d01b69d7 100644 --- a/sc2/ids/ability_id.py +++ b/sc2/ids/ability_id.py @@ -4,6 +4,8 @@ import enum class AbilityId(enum.Enum): + CHRONOBOOSTENERGYCOST = 3755 # temporary > please see PR#24 + NEXUSMASSRECALL = 3757 # temporary > please see PR#24 INVALID = 0 SMART = 1 STOP_STOP = 4 diff --git a/sc2/ids/buff_id.py b/sc2/ids/buff_id.py index 0217b6a9b..7b9be8be7 100644 --- a/sc2/ids/buff_id.py +++ b/sc2/ids/buff_id.py @@ -4,6 +4,7 @@ import enum class BuffId(enum.Enum): + CHRONOBOOSTENERGYCOST = 281 # temporary > please see PR#24 INVALID = 0 GRAVITONBEAM = 5 GHOSTCLOAK = 6 diff --git a/sc2/unit.py b/sc2/unit.py index 4af6d11f0..061ed7cec 100644 --- a/sc2/unit.py +++ b/sc2/unit.py @@ -1,4 +1,5 @@ from s2clientprotocol import sc2api_pb2 as sc_pb, raw_pb2 as raw_pb +from sc2.ids.buff_id import BuffId from .position import Point3 from .data import Alliance, Attribute, DisplayType @@ -184,6 +185,11 @@ def train(self, unit, *args, **kwargs): def build(self, unit, *args, **kwargs): return self(self._game_data.units[unit.value].creation_ability.id, *args, **kwargs) + def has_buff(self, buff): + assert isinstance(buff, BuffId) + + return buff.value in self._proto.buff_ids + def attack(self, *args, **kwargs): return self(AbilityId.ATTACK, *args, **kwargs) From 902b877f54347840123f5161297b4b93371d83be Mon Sep 17 00:00:00 2001 From: Reynald Pader Date: Thu, 1 Feb 2018 14:57:56 +0800 Subject: [PATCH 4/4] Update example for chronoboost cast --- examples/threebase_voidray.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/examples/threebase_voidray.py b/examples/threebase_voidray.py index 45015dee6..40de6ce06 100644 --- a/examples/threebase_voidray.py +++ b/examples/threebase_voidray.py @@ -27,15 +27,11 @@ async def on_step(self, iteration): if not nexus.has_buff(BuffId.CHRONOBOOSTENERGYCOST): abilities = await self.get_available_abilities(nexus) if AbilityId.CHRONOBOOSTENERGYCOST in abilities: - if nexus.energy > 25: await self.do(nexus(AbilityId.CHRONOBOOSTENERGYCOST, nexus)) - else: - await self.chat_send("Not enough energy") else: - await self.chat_send("Can't cast") - print(abilities) + await self.chat_send("Can't cast chrono boost") else: - await self.chat_send("Nexus is boosted") + await self.chat_send("Nexus is already boosted") for idle_worker in self.workers.idle: mf = self.state.mineral_field.closest_to(idle_worker)