Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Build order support #18

Open
wants to merge 82 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
824bdef
Add inline condition to prevent converting of 'None' to string for no…
Jan 26, 2018
9ea30f7
Merge branch 'master' into playable
Jan 26, 2018
cf0483c
Initial implementation of build order support
Jan 26, 2018
d6e168d
Merge branch 'master' into build-order-support
reypader Jan 26, 2018
c8c0d5e
Move build order related files to its own submodule
reypader Jan 26, 2018
5c80134
Move can_afford conditions into the actions of the build order
reypader Jan 26, 2018
96ff76e
Add `expand` build order action
reypader Jan 26, 2018
4ba7ac6
Wrap cwd expression in parentheses
reypader Jan 26, 2018
13e8bcd
Merge remote-tracking branch 'remotes/upstream/master'
reypader Jan 26, 2018
f45d613
Add return resource command to units
reypader Jan 26, 2018
cb6ff95
Initialize geysers for each step
reypader Jan 26, 2018
9096d22
Conver `expansion_locations` to a dict of Point2D: Mineral Fields
reypader Jan 26, 2018
2fd008b
Implement distribution of workers
reypader Jan 26, 2018
79655e0
Add sample script to showcase worker distribution in action
reypader Jan 26, 2018
2ecb34b
Change `BotAI.already_pending` to return integers to allow counting
reypader Jan 27, 2018
f01042e
Add support for morhping instead of training for Zerg
reypader Jan 27, 2018
e68e686
rename unit_count arguments
reypader Jan 27, 2018
63505df
Introduce Intent class and make worker creation a parameter
reypader Jan 27, 2018
156fcad
Allow build order actions to be executed indefinitely as long as it's…
reypader Jan 27, 2018
f69a91d
Add supply, geyser, townhall, and worker type properties to BotAI
Jan 28, 2018
3c1beec
Move state conditions into separate submodule
Jan 28, 2018
c05f930
Add auto-add-supply feature to build order
Jan 28, 2018
78cf389
Partial fix for #8 - already_pending returns count instead of boolean
Jan 28, 2018
a919b05
Move commands into a different submodule for clear separation.
Jan 28, 2018
a34faa9
Wrap the response of `BotAI.can_afford` to return a wrapper
Jan 28, 2018
c81dffe
Add `action_result` property to CanAffordWrapper
Jan 28, 2018
95c3f62
Merge branch 'worker-distribution' into build-order-support
Jan 28, 2018
b0d7a64
Merge branch 'can_afford-wrapper' into build-order-support
Jan 28, 2018
0aadf21
Convert get_owned_expansions to owned_expansions property
Jan 28, 2018
ea1545d
Merge branch 'worker-distribution' into build-order-support
Jan 28, 2018
28850b2
Add support for rich mineral fields
Jan 28, 2018
e45bea9
Merge branch 'worker-distribution' into build-order-support
Jan 28, 2018
e0e89c9
Utilize `can_afford` wrapper
Jan 28, 2018
bd5a716
Allow prioritization of build order actions
Jan 28, 2018
5a59654
Adjust priority of build commands in build orders
Jan 28, 2018
ffb3ea8
Assign idle workers to gather before distribution
Jan 28, 2018
3fc94f6
Merge branch 'worker-distribution' into build-order-support
Jan 28, 2018
4da9af8
Optimize mineral field selection during transfer
Jan 28, 2018
1c29052
Merge branch 'worker-distribution' into build-order-support
Jan 28, 2018
63cf2af
Add unit count conditions
Jan 28, 2018
329ce67
Tweak sample scripts
Jan 28, 2018
495ace3
Queue gather command when transferring workers with resource
Jan 28, 2018
85c0581
Merge branch 'worker-distribution' into build-order-support
Jan 29, 2018
a519808
Add terran build order sample - marine rush
Jan 29, 2018
c9e8f01
Make auto-adding supply to be done when there's at least 1 supply lef…
Jan 30, 2018
e37848c
Rename commands to avoid confusion with core SC2 methods
Jan 30, 2018
e5518ac
Use the installed StarCraft 2 stableid.json for ids
Jan 30, 2018
8f705ab
Update header in id files
Jan 30, 2018
b2778d1
Use abilities with index 0
Jan 30, 2018
8be993c
Add support for getting the available abilities of a unit
Jan 30, 2018
1ac55cd
Merge branch 'can-cast-support' into build-order-support
Jan 30, 2018
e988a5f
Merge branch 'use-stableid-json' into build-order-support
Jan 30, 2018
983e52c
remove PyCharm files
Jan 30, 2018
2324062
Manually add SMART ability
Jan 30, 2018
5a64a3d
Convert usage of enums to new enum names
Jan 30, 2018
6eb9c0e
Merge branch 'use-stableid-json' into build-order-support
Jan 30, 2018
fe048c1
Tweak naming extraction to use friendly name whenever possible
Jan 30, 2018
9f0407e
Revert to using globals in examples
Jan 31, 2018
7c4f654
Adjust variable naming in zerg_rush.py
Jan 31, 2018
bea5073
Revert to using globals but add assertion for duplicate enums
Jan 31, 2018
41eaed4
Revert to some global usage in examples
Jan 31, 2018
dd1a85a
Merge remote-tracking branch 'upstream/master' into worker-distribution
Jan 31, 2018
910d2e9
Remove usage of state parameter in sample
Jan 31, 2018
e3159cc
Merge remote-tracking branch 'upstream/master' into can-cast-support
Jan 31, 2018
9471626
Merge remote-tracking branch 'upstream/master' into use-stableid-json
Jan 31, 2018
f199af1
Merge branch 'master' of github.com:Dentosal/python-sc2
Feb 1, 2018
071be7f
Merge branch 'can-cast-support'
Feb 1, 2018
0d90df9
Merge branch 'already-pending-enhancement'
Feb 1, 2018
e98cd55
Merge branch 'worker-distribution'
Feb 1, 2018
ebcf1ff
Merge branch 'use-stableid-json'
Feb 1, 2018
ad9d314
Ignore pycharm files
Feb 1, 2018
1620f16
Merge branch 'master' into build-order-support
Feb 1, 2018
9c60adc
Fix state parameters being passed
Feb 1, 2018
902b877
Update example for chronoboost cast
Feb 1, 2018
a8ceab2
Fix enum names changed from new ID generation script
Feb 1, 2018
d3d4ec9
Implement warpgate support using find_placement and available_abilities
Feb 1, 2018
e75cb65
Merge branch 'warpgate-support' into build-order-support
Feb 1, 2018
c0ba90f
Merge remote-tracking branch 'upstream/master' into use-stableid-json
Feb 1, 2018
32b422a
Rever BotAI find placement default step to 2
Feb 1, 2018
1726531
Remove local path in generated files
Feb 1, 2018
ae55f67
Merge branch 'use-stableid-json' into build-order-support
Feb 1, 2018
c5f2b26
Merge branch 'master' of github.com:Dentosal/python-sc2 into build-or…
Feb 6, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Convert usage of enums to new enum names
Reynald Pader committed Jan 30, 2018
commit 5a64a3dc4764411065a5932d7800934e42389b40
40 changes: 20 additions & 20 deletions examples/cannon_rush.py
Original file line number Diff line number Diff line change
@@ -10,42 +10,42 @@ async def on_step(self, state, iteration):
if iteration == 0:
await self.chat_send("(probe)(pylon)(cannon)(cannon)(gg)")

if not self.units(NEXUS).exists:
if not self.units(UnitTypeId.NEXUS).exists:
for worker in self.workers:
await self.do(worker.attack(self.enemy_start_locations[0]))
return
else:
nexus = self.units(NEXUS).first
nexus = self.units(UnitTypeId.NEXUS).first

if self.workers.amount < 16 and nexus.noqueue:
if self.can_afford(PROBE):
await self.do(nexus.train(PROBE))
if self.can_afford(UnitTypeId.PROBE):
await self.do(nexus.train(UnitTypeId.PROBE))

elif not self.units(PYLON).exists and not self.already_pending(PYLON):
if self.can_afford(PYLON):
await self.build(PYLON, near=nexus)
elif not self.units(UnitTypeId.PYLON).exists and not self.already_pending(UnitTypeId.PYLON):
if self.can_afford(UnitTypeId.PYLON):
await self.build(UnitTypeId.PYLON, near=nexus)

elif not self.units(FORGE).exists:
pylon = self.units(PYLON).ready
elif not self.units(UnitTypeId.FORGE).exists:
pylon = self.units(UnitTypeId.PYLON).ready
if pylon.exists:
if self.can_afford(FORGE):
await self.build(FORGE, near=pylon.closest_to(nexus))
if self.can_afford(UnitTypeId.FORGE):
await self.build(UnitTypeId.FORGE, near=pylon.closest_to(nexus))

elif self.units(PYLON).amount < 2:
if self.can_afford(PYLON):
elif self.units(UnitTypeId.PYLON).amount < 2:
if self.can_afford(UnitTypeId.PYLON):
pos = self.enemy_start_locations[0].towards(self.game_info.map_center, random.randrange(8, 15))
await self.build(PYLON, near=pos)
await self.build(UnitTypeId.PYLON, near=pos)

elif not self.units(PHOTONCANNON).exists:
if self.units(PYLON).ready.amount >= 2 and self.can_afford(PHOTONCANNON):
pylon = self.units(PYLON).closer_than(20, self.enemy_start_locations[0]).random
await self.build(PHOTONCANNON, near=pylon)
elif not self.units(UnitTypeId.PHOTONCANNON).exists:
if self.units(UnitTypeId.PYLON).ready.amount >= 2 and self.can_afford(UnitTypeId.PHOTONCANNON):
pylon = self.units(UnitTypeId.PYLON).closer_than(20, self.enemy_start_locations[0]).random
await self.build(UnitTypeId.PHOTONCANNON, near=pylon)

else:
if self.can_afford(PYLON) and self.can_afford(PHOTONCANNON): # ensure "fair" decision
if self.can_afford(UnitTypeId.PYLON) and self.can_afford(UnitTypeId.PHOTONCANNON): # ensure "fair" decision
for _ in range(20):
pos = self.enemy_start_locations[0].random_on_distance(random.randrange(5, 12))
building = PHOTONCANNON if state.psionic_matrix.covers(pos) else PYLON
building = UnitTypeId.PHOTONCANNON if state.psionic_matrix.covers(pos) else UnitTypeId.PYLON
r = await self.build(building, near=pos)
if not r: # success
break
32 changes: 16 additions & 16 deletions examples/proxy_rax.py
Original file line number Diff line number Diff line change
@@ -7,38 +7,38 @@

class ProxyRaxBot(sc2.BotAI):
async def on_step(self, state, iteration):
cc = self.units(COMMANDCENTER)
cc = self.units(UnitTypeId.COMMANDCENTER)
if not cc.exists:
target = self.known_enemy_structures.random_or(self.enemy_start_locations[0]).position
for unit in self.workers | self.units(MARINE):
for unit in self.workers | self.units(UnitTypeId.MARINE):
await self.do(unit.attack(target))
return
else:
cc = cc.first

if self.units(MARINE).idle.amount > 15 and iteration % 50 == 1:
if self.units(UnitTypeId.MARINE).idle.amount > 15 and iteration % 50 == 1:
target = self.known_enemy_structures.random_or(self.enemy_start_locations[0]).position
for marine in self.units(MARINE).idle:
for marine in self.units(UnitTypeId.MARINE).idle:
await self.do(marine.attack(target))

if self.can_afford(SCV) and self.workers.amount < 16 and cc.noqueue:
await self.do(cc.train(SCV))
if self.can_afford(UnitTypeId.SCV) and self.workers.amount < 16 and cc.noqueue:
await self.do(cc.train(UnitTypeId.SCV))

elif self.supply_left < (2 if self.units(BARRACKS).amount < 3 else 4):
if self.can_afford(SUPPLYDEPOT):
await self.build(SUPPLYDEPOT, near=cc.position.towards(self.game_info.map_center, 5))
elif self.supply_left < (2 if self.units(UnitTypeId.BARRACKS).amount < 3 else 4):
if self.can_afford(UnitTypeId.SUPPLYDEPOT):
await self.build(UnitTypeId.SUPPLYDEPOT, near=cc.position.towards(self.game_info.map_center, 5))

elif self.units(BARRACKS).amount < 3 or (self.minerals > 400 and self.units(BARRACKS).amount < 5):
if self.can_afford(BARRACKS):
elif self.units(UnitTypeId.BARRACKS).amount < 3 or (self.minerals > 400 and self.units(UnitTypeId.BARRACKS).amount < 5):
if self.can_afford(UnitTypeId.BARRACKS):
p = self.game_info.map_center.towards(self.enemy_start_locations[0], 25)
await self.build(BARRACKS, near=p)
await self.build(UnitTypeId.BARRACKS, near=p)

for rax in self.units(BARRACKS).ready.noqueue:
if not self.can_afford(MARINE):
for rax in self.units(UnitTypeId.BARRACKS).ready.noqueue:
if not self.can_afford(UnitTypeId.MARINE):
break
await self.do(rax.train(MARINE))
await self.do(rax.train(UnitTypeId.MARINE))

for scv in self.units(SCV).idle:
for scv in self.units(UnitTypeId.SCV).idle:
await self.do(scv.gather(self.state.mineral_field.closest_to(cc)))

def main():
76 changes: 38 additions & 38 deletions examples/threebase_voidray.py
Original file line number Diff line number Diff line change
@@ -16,77 +16,77 @@ async def on_step(self, state, iteration):
if iteration == 0:
await self.chat_send("(glhf)")

if not self.units(NEXUS).ready.exists:
if not self.units(UnitTypeId.NEXUS).ready.exists:
for worker in self.workers:
await self.do(worker.attack(self.enemy_start_locations[0]))
return
else:
nexus = self.units(NEXUS).ready.random
nexus = self.units(UnitTypeId.NEXUS).ready.random

for idle_worker in self.workers.idle:
mf = state.mineral_field.closest_to(idle_worker)
await self.do(idle_worker.gather(mf))

if self.units(VOIDRAY).amount > 10 and iteration % 50 == 0:
for vr in self.units(VOIDRAY).idle:
if self.units(UnitTypeId.VOIDRAY).amount > 10 and iteration % 50 == 0:
for vr in self.units(UnitTypeId.VOIDRAY).idle:
await self.do(vr.attack(self.select_target(state)))

for a in self.units(ASSIMILATOR):
for a in self.units(UnitTypeId.ASSIMILATOR):
if a.assigned_harvesters < a.ideal_harvesters:
w = self.workers.closer_than(20, a)
if w.exists:
await self.do(w.random.gather(a))

if self.supply_left < 2 and not self.already_pending(PYLON):
if self.can_afford(PYLON):
await self.build(PYLON, near=nexus)
if self.supply_left < 2 and not self.already_pending(UnitTypeId.PYLON):
if self.can_afford(UnitTypeId.PYLON):
await self.build(UnitTypeId.PYLON, near=nexus)
return

if self.workers.amount < self.units(NEXUS).amount*15 and nexus.noqueue:
if self.can_afford(PROBE):
await self.do(nexus.train(PROBE))
if self.workers.amount < self.units(UnitTypeId.NEXUS).amount*15 and nexus.noqueue:
if self.can_afford(UnitTypeId.PROBE):
await self.do(nexus.train(UnitTypeId.PROBE))

elif not self.units(PYLON).exists and not self.already_pending(PYLON):
if self.can_afford(PYLON):
await self.build(PYLON, near=nexus)
elif not self.units(UnitTypeId.PYLON).exists and not self.already_pending(UnitTypeId.PYLON):
if self.can_afford(UnitTypeId.PYLON):
await self.build(UnitTypeId.PYLON, near=nexus)

if self.units(NEXUS).amount < 3 and not self.already_pending(NEXUS):
if self.can_afford(NEXUS):
if self.units(UnitTypeId.NEXUS).amount < 3 and not self.already_pending(UnitTypeId.NEXUS):
if self.can_afford(UnitTypeId.NEXUS):
location = await self.get_next_expansion()
await self.build(NEXUS, near=location)

if self.units(PYLON).ready.exists:
pylon = self.units(PYLON).ready.random
if self.units(GATEWAY).ready.exists:
if not self.units(CYBERNETICSCORE).exists:
if self.can_afford(CYBERNETICSCORE) and not self.already_pending(CYBERNETICSCORE):
await self.build(CYBERNETICSCORE, near=pylon)
await self.build(UnitTypeId.NEXUS, near=location)

if self.units(UnitTypeId.PYLON).ready.exists:
pylon = self.units(UnitTypeId.PYLON).ready.random
if self.units(UnitTypeId.GATEWAY).ready.exists:
if not self.units(UnitTypeId.CYBERNETICSCORE).exists:
if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE):
await self.build(UnitTypeId.CYBERNETICSCORE, near=pylon)
else:
if self.can_afford(GATEWAY) and not self.already_pending(GATEWAY):
await self.build(GATEWAY, near=pylon)
if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY):
await self.build(UnitTypeId.GATEWAY, near=pylon)

for nexus in self.units(NEXUS).ready:
for nexus in self.units(UnitTypeId.NEXUS).ready:
vgs = state.vespene_geyser.closer_than(20.0, nexus)
for vg in vgs:
if not self.can_afford(ASSIMILATOR):
if not self.can_afford(UnitTypeId.ASSIMILATOR):
break

worker = self.select_build_worker(vg.position)
if worker is None:
break

if not self.units(ASSIMILATOR).closer_than(1.0, vg).exists:
await self.do(worker.build(ASSIMILATOR, vg))
if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0, vg).exists:
await self.do(worker.build(UnitTypeId.ASSIMILATOR, vg))

if self.units(PYLON).ready.exists and self.units(CYBERNETICSCORE).ready.exists:
pylon = self.units(PYLON).ready.random
if self.units(STARGATE).amount < 3 and not self.already_pending(STARGATE):
if self.can_afford(STARGATE):
await self.build(STARGATE, near=pylon)
if self.units(UnitTypeId.PYLON).ready.exists and self.units(UnitTypeId.CYBERNETICSCORE).ready.exists:
pylon = self.units(UnitTypeId.PYLON).ready.random
if self.units(UnitTypeId.STARGATE).amount < 3 and not self.already_pending(UnitTypeId.STARGATE):
if self.can_afford(UnitTypeId.STARGATE):
await self.build(UnitTypeId.STARGATE, near=pylon)

for sg in self.units(STARGATE).ready.noqueue:
if self.can_afford(VOIDRAY):
await self.do(sg.train(VOIDRAY))
for sg in self.units(UnitTypeId.STARGATE).ready.noqueue:
if self.can_afford(UnitTypeId.VOIDRAY):
await self.do(sg.train(UnitTypeId.VOIDRAY))

def main():
sc2.run_game(sc2.maps.get("Abyssal Reef LE"), [
58 changes: 29 additions & 29 deletions examples/zerg_rush.py
Original file line number Diff line number Diff line change
@@ -19,26 +19,26 @@ async def on_step(self, state, iteration):
if iteration == 0:
await self.chat_send("(glhf)")

if not self.units(HATCHERY).ready.exists:
for unit in self.workers | self.units(ZERGLING) | self.units(QUEEN):
if not self.units(UnitTypeId.HATCHERY).ready.exists:
for unit in self.workers | self.units(UnitTypeId.ZERGLING) | self.units(UnitTypeId.QUEEN):
await self.do(unit.attack(self.enemy_start_locations[0]))
return

hatchery = self.units(HATCHERY).ready.first
larvae = self.units(LARVA)
hatchery = self.units(UnitTypeId.HATCHERY).ready.first
larvae = self.units(UnitTypeId.LARVA)

target = self.known_enemy_structures.random_or(self.enemy_start_locations[0]).position
for zl in self.units(ZERGLING).idle:
for zl in self.units(UnitTypeId.ZERGLING).idle:
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
await self.do(queen(INJECTLARVA, hatchery))
for UnitTypeId.QUEEN in self.units(UnitTypeId.QUEEN).idle:
if UnitTypeId.QUEEN.energy >= 25: # Hard coded, since this is not (yet) available
await self.do(UnitTypeId.QUEEN(AbilityId.INJECTLARVA, hatchery))

if self.vespene >= 100:
sp = self.units(SPAWNINGPOOL).ready
sp = self.units(UnitTypeId.SPAWNINGPOOL).ready
if sp.exists and self.minerals >= 100 and not self.mboost_started:
await self.do(sp.first(RESEARCH_ZERGLINGMETABOLICBOOST))
await self.do(sp.first(AbilityId.ZERGLINGMOVEMENTSPEED))
self.mboost_started = True

if not self.moved_workers_from_gas:
@@ -48,54 +48,54 @@ async def on_step(self, state, iteration):
await self.do(drone.gather(m.random, queue=True))

if self.supply_left < 2:
if self.can_afford(OVERLORD) and larvae.exists:
await self.do(larvae.random.train(OVERLORD))
if self.can_afford(UnitTypeId.OVERLORD) and larvae.exists:
await self.do(larvae.random.train(UnitTypeId.OVERLORD))

if self.units(SPAWNINGPOOL).ready.exists:
if larvae.exists and self.can_afford(ZERGLING):
await self.do(larvae.random.train(ZERGLING))
if self.units(UnitTypeId.SPAWNINGPOOL).ready.exists:
if larvae.exists and self.can_afford(UnitTypeId.ZERGLING):
await self.do(larvae.random.train(UnitTypeId.ZERGLING))

if self.units(EXTRACTOR).ready.exists and not self.moved_workers_to_gas:
if self.units(UnitTypeId.EXTRACTOR).ready.exists and not self.moved_workers_to_gas:
self.moved_workers_to_gas = True
extractor = self.units(EXTRACTOR).first
extractor = self.units(UnitTypeId.EXTRACTOR).first
for drone in self.workers.random_group_of(3):
await self.do(drone.gather(extractor))

if self.minerals > 500:
for d in range(4, 15):
pos = hatchery.position.to2.towards(self.game_info.map_center, d)
if await self.can_place(HATCHERY, pos):
if await self.can_place(UnitTypeId.HATCHERY, pos):
self.spawning_pool_started = True
await self.do(self.workers.random.build(HATCHERY, pos))
await self.do(self.workers.random.build(UnitTypeId.HATCHERY, pos))
break

if self.drone_counter < 3:
if self.can_afford(DRONE):
if self.can_afford(UnitTypeId.DRONE):
self.drone_counter += 1
await self.do(larvae.random.train(DRONE))
await self.do(larvae.random.train(UnitTypeId.DRONE))

if not self.extractor_started:
if self.can_afford(EXTRACTOR):
if self.can_afford(UnitTypeId.EXTRACTOR):
drone = self.workers.random
target = state.vespene_geyser.closest_to(drone.position)
err = await self.do(drone.build(EXTRACTOR, target))
err = await self.do(drone.build(UnitTypeId.EXTRACTOR, target))
if not err:
self.extractor_started = True

elif not self.spawning_pool_started:
if self.can_afford(SPAWNINGPOOL):
if self.can_afford(UnitTypeId.SPAWNINGPOOL):
for d in range(4, 15):
pos = hatchery.position.to2.towards(self.game_info.map_center, d)
if await self.can_place(SPAWNINGPOOL, pos):
if await self.can_place(UnitTypeId.SPAWNINGPOOL, pos):
drone = self.workers.closest_to(pos)
err = await self.do(drone.build(SPAWNINGPOOL, pos))
err = await self.do(drone.build(UnitTypeId.SPAWNINGPOOL, pos))
if not err:
self.spawning_pool_started = True
break

elif not self.queeen_started and self.units(SPAWNINGPOOL).ready.exists:
if self.can_afford(QUEEN):
r = await self.do(hatchery.train(QUEEN))
elif not self.queeen_started and self.units(UnitTypeId.SPAWNINGPOOL).ready.exists:
if self.can_afford(UnitTypeId.QUEEN):
r = await self.do(hatchery.train(UnitTypeId.QUEEN))
if not r:
self.queeen_started = True

Loading