async def handle_unit( self, air_threats_near_bases: Units, ground_threats_near_bases: Units, unit: Unit, th_tag: int, ) -> None: if self.policy.pass_own_threats: air_threats: Units = air_threats_near_bases ground_threats: Units = ground_threats_near_bases else: air_threats: Units = self.enemy_air_threats ground_threats: Units = self.enemy_ground_threats ths: Units = self.bot.townhalls.ready.tags_in([th_tag]) if ths: th: Unit = ths.first if self.priority_enemy_units: await self.do_queen_micro(unit, self.priority_enemy_units) elif self.policy.defend_against_air and air_threats: await self.do_queen_micro(unit, air_threats) elif self.policy.defend_against_ground and ground_threats: await self.do_queen_micro(unit, ground_threats) else: if unit.energy >= 25: unit(AbilityId.EFFECT_INJECTLARVA, th) # regardless of policy, chase away enemy close to th # but if queen gets too far away, walk back to th elif unit.distance_to(th) > 7: unit.move(th.position) elif self.bot.enemy_units.filter( lambda enemy: enemy.position.distance_to(unit) < 10): unit.attack( self.find_closest_enemy(unit, self.bot.enemy_units))
def attack_prime(self, unit: Unit, target: Unit) -> bool: if ( self.WORKERS ): # probably should change this name as it's referring to closest enemy workers to the reaper workers = self.WORKERS.filter( lambda unit: unit.distance_to(unit.position) < 20) if len(workers) > 0: prime_target = min(self.WORKERS, key=lambda x: x.health_percentage) self.ai.do(unit.attack(prime_target)) return True self.ai.do(unit.attack(target)) return True
async def do_queen_micro(self, queen: Unit, enemy: Units, grid: Optional[np.ndarray] = None) -> None: if not queen: return in_range_enemies: Units = self.bot.enemy_units.in_attack_range_of( queen) in_range_enemies = in_range_enemies.exclude_type( {UnitID.EGG, UnitID.LARVA}) if in_range_enemies: target: Unit = self._get_target_from_in_range_enemies( in_range_enemies) if target: if self.attack_ready(queen, target): queen.attack(target) elif self.map_data and grid is not None: await self.move_towards_safe_spot(queen, grid) else: distance: float = queen.ground_range + queen.radius + target.radius move_to: Point2 = target.position.towards(queen, distance) if self.bot.in_pathing_grid(move_to): queen.move(move_to) else: queen.attack(in_range_enemies.center) elif enemy: target = enemy.closest_to(queen) queen.attack(target) elif self.bot.all_enemy_units: target = self.bot.all_enemy_units.closest_to(queen) queen.attack(target)
def to_commmand(self, unit: Unit) -> UnitCommand: if self.ability is not None: action = unit(self.ability, self.target) elif self.is_attack: action = unit.attack(self.target) else: action = unit.move(self.target) return action
def execute_command(self, unit: Unit, command: CombatAction): if command.ability is not None: action = unit(command.ability, command.target) elif command.is_attack: action = unit.attack(command.target) else: action = unit.move(command.target) if self.prevent_double_actions(action): self.ai.do(action)
async def attack_priority_targets(self, dt: Unit, prism: Unit): self.knowledge.roles.set_task(UnitTask.Reserved, dt) enemy_ground_ACs = self.knowledge.unit_cache.enemy_in_range( dt.position, 15).of_type([UnitTypeId.SPORECRAWLER, UnitTypeId.MISSILETURRET]) if enemy_ground_ACs: target = enemy_ground_ACs.closest_to(dt) self.do(dt.attack(target)) return True enemy_bad_AAs = self.knowledge.unit_cache.enemy_in_range(prism.position, 15) \ .filter(lambda u: u.can_attack_air and not u.is_flying and u.distance_to(prism) <= 10) if enemy_bad_AAs: target = enemy_bad_AAs.closest_to(dt) self.do(dt.attack(target)) return True enemy_workers = self.knowledge.unit_cache.enemy_in_range( dt.position, 13).of_type([ UnitTypeId.SCV, UnitTypeId.PROBE, UnitTypeId.DRONE, UnitTypeId.MULE ]) if enemy_workers: target = enemy_workers.closest_to(dt) self.do(dt.attack(target)) return True enemy_add_ons = self.knowledge.unit_cache.enemy_in_range( dt.position, 7).of_type([UnitTypeId.STARPORTTECHLAB]) if enemy_add_ons: target = enemy_add_ons.closest_to(dt) self.do(dt.attack(target)) return True enemy_add_ons = self.knowledge.unit_cache.enemy_in_range( dt.position, 7).of_type([ UnitTypeId.FACTORYTECHLAB, UnitTypeId.BARRACKSTECHLAB, UnitTypeId.STARPORTREACTOR ]) if enemy_add_ons: target = enemy_add_ons.closest_to(dt) self.do(dt.attack(target)) return True enemy_ground_AAs = self.knowledge.unit_cache.enemy_in_range(dt.position, 7) \ .filter(lambda u: u.can_attack_air and not u.is_flying) if enemy_ground_AAs: target = enemy_ground_AAs.closest_to(dt) self.do(dt.attack(target)) return True self.do(dt.attack(self.knowledge.enemy_start_location)) return True
async def do_queen_micro(self, queen: Unit, enemy: Units) -> None: if not queen or not enemy: return in_range_enemies: Units = self.in_attack_range_of(queen, enemy) if in_range_enemies: if queen.weapon_cooldown == 0: target: Unit = self._get_target_from_in_range_enemies( in_range_enemies) queen.attack(target) else: closest_enemy: Unit = self.find_closest_enemy( queen, in_range_enemies) distance: float = (queen.ground_range + queen.radius + closest_enemy.radius) queen.move(closest_enemy.position.towards(queen, distance)) else: target = self.find_closest_enemy(queen, enemy) queen.attack(target)
async def do_queen_micro(self, queen: Unit, enemy: Units) -> None: if not queen or not enemy: return in_range_enemies: Units = enemy.in_attack_range_of(queen) if in_range_enemies: if queen.weapon_cooldown == 0: lowest_hp: Unit = min( in_range_enemies, key=lambda e: (e.health + e.shield, e.tag) ) queen.attack(lowest_hp) else: closest_enemy: Unit = in_range_enemies.closest_to(queen) distance: float = ( queen.ground_range + queen.radius + closest_enemy.radius ) queen.move(closest_enemy.position.towards(queen, distance)) else: queen.attack(enemy.center)
async def do_queen_offensive_micro(self, queen: Unit, offensive_pos: Point2) -> None: if not queen or not offensive_pos: return enemy: Units = self.bot.enemy_units.exclude_type( {UnitID.MULE, UnitID.EGG, UnitID.LARVA}) enemy_structures: Units = self.bot.enemy_structures queens: Units = self.bot.units(UnitID.QUEEN) own_close_queens: Units = queens.filter( lambda u: u.distance_to(queen) < 5) if enemy: in_range_enemies: Units = self.in_attack_range_of(queen, enemy) in_range_structures: Units = self.in_attack_range_of( queen, enemy_structures) if queen.weapon_cooldown == 0: if in_range_enemies: target: Unit = self._get_target_from_in_range_enemies( in_range_enemies) queen.attack(target) elif in_range_structures: queen.attack( self.find_closest_enemy(queen, in_range_structures)) else: queen.move(offensive_pos) else: if own_close_queens.amount <= 3: queen.move(queens.center) else: queen.move(offensive_pos) else: queen.attack(offensive_pos)
def follow_path(self, unit: Unit) -> None: """ Follow the path set or set a new one if none exists. Args: unit (Unit): the unit moving Returns: None """ if unit.tag not in self.pathing_dict: self.add_to_path_dict(unit, self.target) else: advance_factor = int(unit.movement_speed) + 2 self.pathing_dict[unit.tag]["step"] += advance_factor curr_step = self.pathing_dict[unit.tag]["step"] if curr_step >= len(self.pathing_dict[unit.tag]["path"]): curr_step = len(self.pathing_dict[unit.tag]["path"]) - 1 self.do( unit.attack(Point2( self.pathing_dict[unit.tag]["path"][curr_step]))) if curr_step == len(self.pathing_dict[unit.tag]["path"]) - 1: del self.pathing_dict[unit.tag]
async def do_queen_offensive_micro(self, queen: Unit, offensive_pos: Point2) -> None: if not queen or not offensive_pos: return enemy: Units = self.bot.enemy_units.exclude_type( {UnitID.MULE, UnitID.EGG, UnitID.LARVA}) enemy_structures: Units = self.bot.enemy_structures queens: Units = self.bot.units(UnitID.QUEEN) own_close_queens: Units = queens.filter( lambda u: u.distance_to(queen) < 5) if enemy: in_range_enemies: Units = enemy.in_attack_range_of(queen) in_range_structures: Units = enemy_structures.in_attack_range_of( queen) if in_range_enemies: target: Unit = self._get_target_from_in_range_enemies( in_range_enemies) if self.attack_ready(queen, target): queen.attack(target) else: # loose queen_control should try to rejoin the queen pack if own_close_queens.amount <= 3: queen.move(queens.center) # otherwise move forward between attacks, since Queen is slow and can get stuck behind each other else: queen.move(offensive_pos) elif in_range_structures: target: Unit = self._get_target_from_in_range_enemies( in_range_structures) if self.attack_ready(queen, target): queen.attack(target) else: if own_close_queens.amount <= 3: queen.move(queens.center) else: queen.move(offensive_pos) else: queen.move(offensive_pos) else: queen.attack(offensive_pos)
async def _manage_nydus_attack( self, canal: Unit, network: Unit, unit: Unit, unit_distance_to_target: float, grid: Optional[np.ndarray] = None, ) -> None: """ Get a Queen through the nydus and out the other side! @param canal: The canal is the worm placed on the map @param network: This is built at home @param unit: In this case, the queen we want to move through @param unit_distance_to_target: @return: """ # user does not have some predefined nydus logic, so we unload the proxy canal for them if len(canal.passengers_tags) > 0 and not self.policy.nydus_move_function: canal(AbilityId.UNLOADALL_NYDUSWORM) # worm has popped somewhere, but we are waiting for it to finish, move next to network ready to go # usually we want queens last in anyway, so this gives a chance for other units to enter the nydus if not canal.is_ready and unit.distance_to(canal) > 30: unit.move(network.position) # both canal and network must be ready else: # unit needs to go through the nydus if unit_distance_to_target > 45 and unit.distance_to(network) < 70: # user has some custom code for moving units through nydus if self.policy.nydus_move_function: self.policy.nydus_move_function(unit, self.policy.nydus_target) # manage this ourselves else: network(AbilityId.LOAD_NYDUSNETWORK, unit) # else queen should micro on the other side # remember that all queens already have transfuse code baked in else: # queen has enough energy for a transfuse and a tumor, so put a tumor down where she currently is if unit.energy >= 75 and self.bot.has_creep(unit.position): # check if there are too many tumors already tumors: Units = self.bot.structures.filter( lambda s: s.type_id in {UnitID.CREEPTUMORBURROWED, UnitID.CREEPTUMORQUEEN} and s.distance_to(unit) < 15 ) if tumors.amount < 7: unit(AbilityId.BUILD_CREEPTUMOR_QUEEN, unit.position) if unit.is_using_ability(AbilityId.BUILD_CREEPTUMOR_QUEEN): return # get priority target, ie: target the flying enemies first target: Optional[Unit] = self._get_target_from_close_enemies(unit) if target: if self.attack_ready(unit, target): unit.attack(target) elif self.map_data and grid is not None: await self.move_towards_safe_spot(unit, grid) else: distance: float = ( unit.ground_range + unit.radius + target.radius ) move_to: Point2 = target.position.towards(unit, distance) if self.bot.in_pathing_grid(move_to): unit.move(move_to) # check if there is anything around here to attack, # if not then we attack the general attack target the user has passed in # TODO: In the future, this is where we would want the queens to come home # At the moment a nydus queen is on a one way trip elif ( self.enemy_ground_units_near_nydus_target.amount == 0 and self.enemy_flying_units_near_nydus_target.amount == 0 ): await self.do_queen_offensive_micro(unit, self.policy.attack_target) # there are targets, but nothing in range so move towards the nydus target else: unit.move(self.policy.nydus_target)
async def execute_command(self, unit: Unit, command: CombatAction): if command.is_attack: self.do(unit.attack(command.target)) else: self.do(unit.move(command.target))