def move_phoenix(self, phoenix: Unit): enemy_main = self.knowledge.enemy_expansion_zones[0].center_location if phoenix.distance_to(enemy_main) <= 10: self.reached_enemy_main = True if not self.reached_enemy_main: target = self.select_target() self.do(phoenix.move(target)) if target != self.last_target: self.last_target = target self.print(f"scouting {target}, interval {self.time_interval}") else: # This else makes it scout around possible main tech positions if self.last_target is None: self.last_target = self.select_target() if self.ai.enemy_race == Race.Protoss: new_target = self.knowledge.enemy_expansion_zones[ 0].behind_mineral_position_center.towards( self.knowledge.enemy_expansion_zones[0]. mineral_line_center, 20).random_on_distance(7) if phoenix.distance_to(self.last_target) <= 3: self.do(phoenix.move(new_target)) self.last_target = new_target elif self.ai.enemy_race == Race.Terran: new_target = self.knowledge.enemy_expansion_zones[ 1].behind_mineral_position_center.towards( self.knowledge.enemy_expansion_zones[1]. mineral_line_center, 20).random_on_distance(7) if phoenix.distance_to(self.last_target) <= 3: self.do(phoenix.move(new_target)) self.last_target = new_target
async def handle_unit( self, air_threats_near_bases: Units, ground_threats_near_bases: Units, unit: Unit, th_tag: int = 0, ) -> 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 transfuse_target: Unit = self.get_transfuse_target(unit.position) self.used_transfuse_this_step: bool = False if (transfuse_target and transfuse_target is not unit and not self.used_transfuse_this_step): unit(AbilityId.TRANSFUSION_TRANSFUSION, transfuse_target) self.used_transfuse_this_step = True elif self.priority_enemy_units: await self.do_queen_micro(unit, self.priority_enemy_units) elif self.policy.attack_condition(): await self.do_queen_offensive_micro(unit, self.policy.attack_target) elif self.policy.defend_against_ground and ground_threats: await self.do_queen_micro(unit, ground_threats) elif self.policy.defend_against_air and air_threats: await self.do_queen_micro(unit, air_threats) elif unit.distance_to(self.policy.rally_point) > 12: unit.move(self.policy.rally_point)
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)
async def _control_inject_queen_near_base( self, air_threats_near_bases: Units, ground_threats_near_bases: Units, queen: Unit, townhall: Unit, grid: Optional[np.ndarray] = None, ) -> None: """ Between injects, we want the Queen to have the following behavior: - Attack any enemy that gets too close - Move the Queen back if she goes too far from the townhall - Stay out of the mineral line, incase bot has custom mineral gathering (don't block workers) """ # don't do anything else, just move the queen back if queen.distance_to(townhall) > 8: queen.move(townhall) return close_threats: Units = Units([], self.bot) # we can only have close threats if enemy are near our bases in the first place # so save calculation otherwise if air_threats_near_bases or ground_threats_near_bases: close_threats = self.bot.enemy_units.filter( lambda enemy: enemy.position.distance_to(townhall) < 12) if close_threats: await self.do_queen_micro(queen, close_threats, grid) # every now and then, check queen is not in the mineral field blocking workers elif self.bot.state.game_loop % 32 == 0: close_mfs: Units = self.bot.mineral_field.filter( lambda mf: mf.distance_to(townhall) < 8) # make a small adjustment away from the minerals if close_mfs and queen.distance_to(close_mfs.center) < 6: queen.move(queen.position.towards(close_mfs.center, -1))
async def on_unit_created(self, unit: Unit): """Send Overlords and lings to scouting spots.""" if unit.type_id == UnitTypeId.OVERLORD: if self.hidden_ol_index + 1 >= len(self.hidden_ol_spots): return else: # if self.hidden_ol_index == 0: # self.do( # unit.move( # self.knowledge.zone_manager.enemy_expansion_zones[1].center_location.towards( # self.knowledge.ai.game_info.map_center, 11 # ) # ) # ) # self.hidden_ol_index += 1 if self.hidden_ol_index == 1: self.do(unit.move(self.nydus_spot)) self.hidden_ol_index += 1 else: self.do( unit.move(self.hidden_ol_spots[self.hidden_ol_index], queue=True)) self.hidden_ol_index += 1 elif unit.type_id == UnitTypeId.ZERGLING: if self.scout_ling_count in self.ling_scout_location: self.do( unit.move(self.ling_scout_location[self.scout_ling_count])) self.knowledge.roles.set_task(UnitTask.Scouting, unit) self.scout_ling_count += 1
async def handle_unit( self, air_threats_near_bases: Units, ground_threats_near_bases: Units, priority_enemy_units: Units, unit: Unit, th_tag: int = 0, grid: Optional[np.ndarray] = None, nydus_networks: Optional[Units] = None, nydus_canals: Optional[Units] = None, ) -> None: if priority_enemy_units: await self.do_queen_micro(unit, priority_enemy_units, grid) elif self.policy.attack_condition(): await self.do_queen_offensive_micro(unit, self.policy.attack_target) elif self.policy.defend_against_ground and ground_threats_near_bases: await self.do_queen_micro(unit, ground_threats_near_bases, grid) elif self.policy.defend_against_air and air_threats_near_bases: await self.do_queen_micro(unit, air_threats_near_bases, grid) elif (self.map_data and grid is not None and not self.is_position_safe(grid, unit.position)): await self.move_towards_safe_spot(unit, grid) elif unit.distance_to(self.policy.rally_point) > 12: unit.move(self.policy.rally_point)
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))
async def move_towards_safe_spot(self, unit: Unit, grid: np.ndarray, radius: int = 7) -> None: safe_spot: Point2 = self.find_closest_safe_spot( unit.position, grid, radius) path: List[Point2] = self.map_data.pathfind(unit.position, safe_spot, grid, sensitivity=6) if path: unit.move(path[0])
async def execute_order(self, unit: Unit): assert ( unit.tag in self.knowledge.building_solver.structure_target_move_location ), f"{unit.tag}\n{self.knowledge.building_solver.structure_target_move_location}" assert isinstance(unit, Unit), f"{unit}" unit_is_busy: bool = not (unit.is_idle or unit.is_moving or unit.is_using_ability(AbilityId.LIFT) or unit.is_using_ability(AbilityId.LAND)) if unit_is_busy: return land_location: Point2 = self.knowledge.building_solver.structure_target_move_location[ unit.tag] # Structure has arrived and landed, done! if unit.position == land_location and not unit.is_flying and not unit.is_using_ability( AbilityId.LIFT): self.knowledge.building_solver.structure_target_move_location.pop( unit.tag) # Structure is landed but not in right position: lift elif unit.position != land_location and not unit.is_flying and unit.is_idle: self.do(unit(AbilityId.LIFT)) # Structure is flying but not close to land location, order move command elif (unit.is_flying and land_location.distance_to(unit) > 2 and (not unit.is_moving or isinstance(unit.order_target, Point2) and unit.order_target != land_location) and not unit.is_using_ability(AbilityId.LIFT)): self.do(unit.move(land_location)) # Structure is close to land location but flying, order land command elif unit.is_flying and land_location.distance_to( unit) < 2 and not unit.is_using_ability(AbilityId.LAND): # TODO If land location is blocked, attempt to find another land location instead self.do(unit(AbilityId.LAND, land_location))
def move_phoenix(self, phoenix: Unit): target = self.select_target() self.do(phoenix.move(target)) if target != self.last_target: self.last_target = target self.print(f"scouting {target}, interval {self.time_interval}")
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
async def on_unit_created(self, unit: Unit) -> None: # find new overlords a position to scout if self.ol_spots_index + 1 == len(self.hidden_ol_spots): return if unit.name == "Overlord": ol_spot = self.hidden_ol_spots[self.ol_spots_index] self.do(unit.move(ol_spot)) self.ol_spots_index += 1
async def on_unit_created(self, unit: Unit): # Scout with second ovie if unit.type_id == OVERLORD: if self.units(OVERLORD).amount == 2: positions = [] if self.enemy_race == Race.Protoss: # Scout for cannon rush positions.append(self.own_natural) else: # Scout for proxy rax for expansion in self.expansion_locations: if expansion == self.start_location or expansion == self.own_natural: continue if expansion.distance_to( self.start_location ) < 50 or expansion.distance_to(self.own_natural) < 50: positions.append(expansion) for position in positions: await self.do(unit.move(position, True)) elif self.units(OVERLORD).amount == 3: await self.do( unit.move( self.enemy_natural.closest( list(self.enemy_expansions_not_main_or_nat)), True)) elif self.units(OVERLORD).amount == 4: closest = self.enemy_natural.closest( list(self.enemy_expansions_not_main_or_nat)) exps: List[Point2] = list( self.enemy_expansions_not_main_or_nat) if closest in exps: exps.remove(closest) await self.do(unit.move(self.enemy_natural.closest(exps), True)) else: # Randomly spread overlords if not self.unit_manager.spread_overlords.tags_in({unit.tag }).exists: random_position = Point2( (random.randint(0, self._game_info.pathing_grid.width - 1), random.randint( 0, self._game_info.pathing_grid.height - 1))) await self.do(unit.move(random_position)) self.unit_manager.spread_overlords.append(unit)
async def on_unit_created(self, unit: Unit): if self.knowledge.ai is not None: await self.knowledge.on_unit_created(unit) if unit.type_id == UnitTypeId.OVERLORD: if self.ol_spot_index + 1 >= len(self.hidden_ol_spots): return else: self.do(unit.move(self.hidden_ol_spots[self.ol_spot_index])) self.ol_spot_index += 1
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 spread_creep(self, queen: Unit, grid: Optional[np.ndarray]) -> None: if self.creep_target_index >= len(self.creep_targets): self.creep_target_index = 0 if self.first_tumor and self.policy.first_tumor_position: queen(AbilityId.BUILD_CREEPTUMOR_QUEEN, self.policy.first_tumor_position) # retry a few times, sometimes queen gets blocked when spawning if self.first_tumor_retry_attempts > 5: self.first_tumor = False self.first_tumor_retry_attempts += 1 return should_lay_tumor: bool = True # if using map_data, creep will follow ground path to the targets if self.map_data: if grid is None: grid = self.map_data.get_pyastar_grid() pos: Point2 = self._find_closest_to_target_using_path( self.creep_targets[self.creep_target_index], self.creep_map, grid) else: pos: Point2 = self._find_closest_to_target( self.creep_targets[self.creep_target_index], self.creep_map) if (not pos or (self.policy.should_tumors_block_expansions is False and self.position_blocks_expansion(pos)) or self.position_near_enemy_townhall(pos) or self.position_near_nydus_worm(pos) or self._existing_tumors_too_close(pos)): should_lay_tumor = False if should_lay_tumor: queen(AbilityId.BUILD_CREEPTUMOR_QUEEN, pos) self.pending_positions.append((pos, self.bot.time)) # can't lay tumor right now, go back home elif queen.distance_to(self.policy.rally_point) > 7: queen.move(self.policy.rally_point) self.creep_target_index += 1
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 handle_unit( self, air_threats_near_bases: Units, ground_threats_near_bases: Units, unit: Unit, priority_enemy_units: Units, th_tag: int = 0, ) -> None: if priority_enemy_units: await self.do_queen_micro(unit, priority_enemy_units) elif self.policy.attack_condition(): await self.do_queen_offensive_micro(unit, self.policy.attack_target) elif self.policy.defend_against_ground and ground_threats_near_bases: await self.do_queen_micro(unit, ground_threats_near_bases) elif self.policy.defend_against_air and air_threats_near_bases: await self.do_queen_micro(unit, air_threats_near_bases) elif unit.distance_to(self.policy.rally_point) > 12: unit.move(self.policy.rally_point)
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 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)
async def handle_unit( self, air_threats_near_bases: Units, ground_threats_near_bases: Units, unit: Unit, priority_enemy_units: Units, th_tag: int = 0, ) -> None: should_spread_creep: bool = self._check_queen_can_spread_creep(unit) self.creep_targets = self.policy.creep_targets if priority_enemy_units: await self.do_queen_micro(unit, priority_enemy_units) elif (self.policy.defend_against_air and air_threats_near_bases and not should_spread_creep): await self.do_queen_micro(unit, air_threats_near_bases) elif (self.policy.defend_against_ground and ground_threats_near_bases and not should_spread_creep): await self.do_queen_micro(unit, ground_threats_near_bases) elif self.bot.enemy_units and self.bot.enemy_units.filter( # custom filter to replace in_attack_range_of so that it can be used with memory units lambda enemy: enemy.position.distance_to(unit) < max( unit.air_range, unit.ground_range)): unit.move(self.policy.rally_point) elif (unit.energy >= 25 and len(unit.orders) == 0 and self.creep_coverage < self.policy.target_perc_coverage): await self.spread_creep(unit) elif unit.distance_to(self.policy.rally_point) > 7: if len(unit.orders) > 0: if unit.orders[0].ability.button_name != "CreepTumor": unit.move(self.policy.rally_point) elif len(unit.orders) == 0: unit.move(self.policy.rally_point)
async def harass_command(self, dt: Unit, prism: Unit): self.knowledge.roles.set_task(UnitTask.Reserved, dt) if self.is_revealed_by_enemy(dt): if dt.distance_to( prism) <= 5 and prism.shield_health_percentage >= 0.4: self.do(dt.smart(prism)) return True else: self.do(dt.move(prism)) return True else: await self.attack_priority_targets(dt, prism) return True
async def up_and_down(self, harass_prism: Unit): self.knowledge.roles.set_task(UnitTask.Reserved, harass_prism) if harass_prism.has_buff(BuffId.LOCKON): cyclones = self.knowledge.unit_cache.enemy_in_range( harass_prism.position3d, 20).of_type(UnitTypeId.CYCLONE) if cyclones: closest_cyclone = cyclones.closest_to(harass_prism) position = harass_prism.position.towards(closest_cyclone, -18) self.do(harass_prism.move(position)) return True if harass_prism.health_percentage <= 0.2: self.do( harass_prism(AbilityId.UNLOADALLAT_WARPPRISM, harass_prism.position)) return True if harass_prism.distance_to(self.get_enemy_main_platform()) <= 12 and \ (not self.is_revealed_by_enemy(harass_prism)) and \ harass_prism.cargo_used > 0: self.do( harass_prism(AbilityId.UNLOADALLAT_WARPPRISM, harass_prism.position)) return True target = self.get_enemy_main_platform() dts = self.knowledge.unit_cache.by_tags( [self.ninja_dt_1_tag, self.ninja_dt_2_tag]) if dts.exists and harass_prism.cargo_used < 4: target = dts.center for dt in dts: if self.is_revealed_by_enemy(dt): self.do(harass_prism.move(dt.position)) return True self.prism_evasive_move_to(harass_prism, target) return True
async def attack_command(self, unit: Unit, prism: Unit): self.knowledge.roles.set_task(UnitTask.Reserved, unit) if self.is_revealed_by_enemy(unit): if unit.distance_to( prism) <= 8 and prism.shield_health_percentage >= 0.4: self.do(unit.smart(prism)) return True else: base = self.knowledge.own_main_zone.center_location self.do(unit.move(base)) return True else: await self.attack_priority_targets(unit, prism) return True
async def handle_unit( self, air_threats_near_bases: Units, ground_threats_near_bases: Units, unit: Unit, th_tag: int = 0, ) -> 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 should_spread_creep: bool = self._check_queen_can_spread_creep(unit) self.creep_targets = self.policy.creep_targets transfuse_target: Unit = self.get_transfuse_target(unit.position) # allow transfuse if energy has built up if unit.energy >= 50 and transfuse_target and transfuse_target is not unit: unit(AbilityId.TRANSFUSION_TRANSFUSION, transfuse_target) elif self.priority_enemy_units: await self.do_queen_micro(unit, self.priority_enemy_units) elif self.policy.defend_against_air and air_threats and not should_spread_creep: await self.do_queen_micro(unit, air_threats) elif ( self.policy.defend_against_ground and ground_threats and not should_spread_creep ): await self.do_queen_micro(unit, ground_threats) elif self.bot.enemy_units and self.bot.enemy_units.filter( # custom filter to replace in_attack_range_of so that it can be used with memory units lambda enemy: enemy.position.distance_to(unit) < max(unit.air_range, unit.ground_range) ): unit.move(self.policy.rally_point) elif ( unit.energy >= 25 and len(unit.orders) == 0 and self.creep_coverage < self.policy.target_perc_coverage ): await self.spread_creep(unit) elif unit.distance_to(self.policy.rally_point) > 7: if len(unit.orders) > 0: if unit.orders[0].ability.button_name != "CreepTumor": unit.move(self.policy.rally_point) elif len(unit.orders) == 0: unit.move(self.policy.rally_point)
def dodge_effects(self, unit: Unit) -> bool: """Dodge any effects""" if not self.ai.state.effects: return False for effect in self.ai.state.effects: if effect.id == GUARDIANSHIELDPERSISTENT: continue effect_data = self.ai.game_data.effects[effect.id] danger_zone = effect_data.radius + unit.radius + .1 closest_effect_position_to_unit = unit.position.closest( effect.positions) if not unit.position.distance_to_point2( closest_effect_position_to_unit) < danger_zone: continue neighbors8_of_unit = list(unit.position.neighbors8) center_of_effect = Point2.center(effect.positions) furthest_neighbor_to_effect = center_of_effect.furthest( neighbors8_of_unit) move_away = -1 * danger_zone self.ai.add_action( unit.move( furthest_neighbor_to_effect.towards( unit.position, move_away))) return True
async def handle_unit( self, air_threats_near_bases: Units, ground_threats_near_bases: Units, priority_enemy_units: Units, unit: Unit, th_tag: int = 0, grid: Optional[np.ndarray] = None, nydus_networks: Optional[Units] = None, nydus_canals: Optional[Units] = None, ) -> None: should_spread_creep: bool = self._check_queen_can_spread_creep(unit) self.creep_targets = self.policy.creep_targets if priority_enemy_units: await self.do_queen_micro(unit, priority_enemy_units, grid) elif (self.policy.defend_against_air and air_threats_near_bases and not should_spread_creep): await self.do_queen_micro(unit, air_threats_near_bases, grid) elif (self.policy.defend_against_ground and ground_threats_near_bases and not should_spread_creep): await self.do_queen_micro(unit, ground_threats_near_bases, grid) elif self.bot.enemy_units and self.bot.enemy_units.filter( # custom filter to replace in_attack_range_of so that it can be used with memory units lambda enemy: enemy.position.distance_to(unit) < max( unit.air_range, unit.ground_range)): unit.move(self.policy.rally_point) elif (unit.energy >= 25 and len(unit.orders) == 0 and self.creep_coverage < self.policy.target_perc_coverage): await self.spread_creep(unit, grid) elif (self.map_data and grid is not None and not self.is_position_safe(grid, unit.position)): await self.move_towards_safe_spot(unit, grid) elif unit.distance_to(self.policy.rally_point) > 7: if len(unit.orders) > 0: if unit.orders[0].ability.button_name != "CreepTumor": unit.move(self.policy.rally_point) elif len(unit.orders) == 0: unit.move(self.policy.rally_point) # check if tumor has been placed at a location yet self._clear_pending_positions()
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))
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)