class ScoutBaseAction(ActBase): _units: Units def __init__(self, only_once: bool) -> None: super().__init__() self.current_target = Point2((0, 0)) self.ended = False self.only_once = only_once async def start(self, knowledge: "Knowledge"): await super().start(knowledge) self._units = Units([], self.ai) def set_scouts(self, scouts: List[Unit]): self._units.clear() self._units.extend(scouts)
class Scout(SubActs): units: Units def __init__(self, unit_types: Union[UnitTypeId, Set[UnitTypeId]], unit_count: int, *args: ScoutBaseAction): """ Scout act for all races, loops the given scout actions @param unit_types: Types of units accepted as scouts @param unit_count: Units required to be used in scouting, scouting will only start after all are available @param args: Scout actions, cen be to scout a certain location, or to move around in certain way. Defaults to scouting enemy main """ if isinstance(unit_types, UnitTypeId): self.unit_types = set() self.unit_types.add(unit_types) else: self.unit_types = unit_types self.unit_count = unit_count if len(args) > 0: super().__init__(*args) else: super().__init__(ScoutLocation.scout_main()) self.scout_tags: List[int] = [] self.started = False self.ended = False self.index = 0 async def start(self, knowledge: "Knowledge"): await super().start(knowledge) self.units = Units([], self.ai) async def execute(self) -> bool: if self.ended: return True self.units.clear() if self.find_units(): return True if self.units: self.roles.set_tasks(UnitTask.Scouting, self.units) await self.micro_units() # Ignore if the scouting has finished return True async def micro_units(self) -> bool: """ Micros units @return: True when finished """ count = len(self.orders) self.index = self.index % count for looped in range(0, count + 1): if looped == count: self.ended = True return True # noinspection PyTypeChecker action: ScoutBaseAction = self.orders[self.index] action.set_scouts(self.units) result = await action.execute() if not result: # Not finished return False self.index = (self.index + 1) % count return False def find_units(self) -> bool: if not self.started: if UnitTypeId.OVERLORD in self.unit_types: free_units = self.roles.get_types_from( self.unit_types, UnitTask.Idle, UnitTask.Moving, UnitTask.Gathering, UnitTask.Reserved ) else: free_units = self.roles.get_types_from( self.unit_types, UnitTask.Idle, UnitTask.Moving, UnitTask.Gathering ) if len(free_units) >= self.unit_count: # TODO: Better selection? new_scouts = free_units.random_group_of(self.unit_count) self.units.extend(new_scouts) self.scout_tags = new_scouts.tags self.started = True else: scouts = self.roles.get_types_from(self.unit_types, UnitTask.Scouting) self.units.extend(scouts.tags_in(self.scout_tags)) if not self.units: # Scouts are dead, end the scout act self.ended = True return True
class NydusMain(ActBase): """Set up Nydus in their main.""" units: Units def __init__(self, unit_type: UnitTypeId, amount: int): super().__init__() self.micro = MicroRules() self.micro.load_default_methods() self.micro.unit_micros[UnitTypeId.ZERGLING] = SuicideLingMicro() self.started = False self.ended = False self.unit_type = unit_type self.amount = amount self.lair_tech = MorphLair() self.network = ActBuilding(UnitTypeId.NYDUSNETWORK, to_count=1) self.zerg_build = ZergUnit(unit_type) self.tags: List[int] = [] async def start(self, knowledge: "Knowledge"): await super(NydusMain, self).start(knowledge) await self.lair_tech.start(knowledge) await self.network.start(knowledge) await self.zerg_build.start(knowledge) await self.micro.start(knowledge) self.units = Units([], self.ai) async def execute(self) -> bool: if self.cache.own(UnitTypeId.NYDUSCANAL).ready: for canal in self.cache.own(UnitTypeId.NYDUSCANAL).ready: self.do(canal(AbilityId.UNLOADALL_NYDUSWORM)) if self.knowledge.lost_units_manager.own_lost_type(UnitTypeId.NYDUSCANAL): self.ended = True if self.ended: return True if self.cache.own(self.unit_type).amount < self.amount: self.zerg_build.to_count = self.amount await self.zerg_build.execute() self.units.clear() if not self.get_count(UnitTypeId.LAIR, include_pending=True, include_not_ready=True) + self.get_count( UnitTypeId.HIVE ): await self.lair_tech.execute() return True if not self.get_count(UnitTypeId.NYDUSNETWORK, include_pending=True, include_not_ready=True): await self.network.execute() return True # build the nydus worm if not self.get_count(UnitTypeId.NYDUSCANAL) and self.get_count( UnitTypeId.NYDUSNETWORK, include_not_ready=False, include_pending=False ): closest_overlord = self.cache.own(UnitTypeId.OVERLORD).closest_to(self.knowledge.enemy_start_location) nydus_network = self.cache.own(UnitTypeId.NYDUSNETWORK).first for i in range(11): pos = closest_overlord.position.towards(self.knowledge.enemy_start_location, i) if self.ai.get_terrain_z_height(pos) != self.ai.get_terrain_z_height( self.knowledge.enemy_start_location ): continue if self.ai.is_visible(pos) and await self.ai.can_place(UnitTypeId.NYDUSCANAL, pos): self.do(nydus_network(AbilityId.BUILD_NYDUSWORM, pos)) if i == 10 and not nydus_network.orders: self.do(closest_overlord.move(closest_overlord.position.towards(pos, 1))) # put units into the Nydus if self.get_count(UnitTypeId.NYDUSCANAL, include_pending=True, include_not_ready=True) and self.get_count( UnitTypeId.NYDUSNETWORK, include_pending=False, include_not_ready=False ): network = self.cache.own(UnitTypeId.NYDUSNETWORK).first if not self.started: free_units = self.roles.get_types_from( {self.unit_type}, UnitTask.Idle, UnitTask.Moving, UnitTask.Gathering ) if free_units.amount < self.amount: return True self.units.extend(free_units.random_group_of(self.amount)) self.tags = self.units.tags self.started = True else: unit_grouping = self.roles.get_types_from({self.unit_type}, UnitTask.Reserved) self.units.extend(unit_grouping.tags_in(self.tags)) if not self.units: self.ended = True return True if self.units: network = None canal = None if self.get_count(UnitTypeId.NYDUSNETWORK): network = self.cache.own(UnitTypeId.NYDUSNETWORK).first if self.get_count(UnitTypeId.NYDUSCANAL): canal = self.cache.own(UnitTypeId.NYDUSCANAL).first if not canal or not network: return True self.roles.set_tasks(UnitTask.Reserved, self.units) for unit in self.units: if unit.distance_to(canal) <= unit.distance_to(network): self.combat.add_unit(unit) else: self.do(network(AbilityId.LOAD_NYDUSNETWORK, unit)) self.combat.execute( self.knowledge.enemy_expansion_zones[0].center_location, MoveType.Assault, rules=self.micro ) return True # never block