From abcc0f87394afc4c489fcfeed354fedba1d60ca1 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Sun, 9 Jul 2017 12:54:45 +0200 Subject: [PATCH 1/2] Pokemon Hunter changes --- pokemongo_bot/__init__.py | 7 + pokemongo_bot/cell_workers/pokemon_hunter.py | 397 +++++++++++++++++-- pokemongo_bot/event_manager.py | 2 +- pokemongo_bot/walkers/step_walker.py | 3 + 4 files changed, 364 insertions(+), 45 deletions(-) diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index b19ac28cf0..e6cd68b5e6 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -352,6 +352,13 @@ def _register_events(self): 'longitude' ) ) + self.event_manager.register_event( + 'moving_to_hunter_target', + parameters=( + 'target_name', + 'distance' + ) + ) self.event_manager.register_event( 'moving_to_fort', parameters=( diff --git a/pokemongo_bot/cell_workers/pokemon_hunter.py b/pokemongo_bot/cell_workers/pokemon_hunter.py index 16abfeea48..8d3ccff6cc 100644 --- a/pokemongo_bot/cell_workers/pokemon_hunter.py +++ b/pokemongo_bot/cell_workers/pokemon_hunter.py @@ -14,13 +14,14 @@ from pokemongo_bot.walkers.polyline_walker import PolylineWalker from pokemongo_bot.walkers.step_walker import StepWalker from pokemongo_bot.worker_result import WorkerResult -from .utils import fort_details +from .utils import fort_details, format_dist, distance import random from random import uniform class PokemonHunter(BaseTask): SUPPORTED_TASK_API_VERSION = 1 + LOOK_AROUND_TIME = 20 def __init__(self, bot, config): super(PokemonHunter, self).__init__(bot, config) @@ -29,15 +30,19 @@ def initialize(self): self.max_pokemon_storage = inventory.get_pokemon_inventory_size() self.notified_second_gen = [] self.destination = None + self.previous_destination = None self.walker = None self.search_cell_id = None self.search_points = [] self.lost_counter = 0 + self.lost_map_counter = 0 self.no_log_until = 0 self.distance_to_target = 0 self.distance_counter = 0 self.recent_tries = [] - self.no_hunt_until = None + # No hunting from the start; give sightings a few secs to load! + self.no_hunt_until = time.time() + 10 + self.no_look_around_until = time.time() + 20 self.hunt_started_at = None self.config_max_distance = self.config.get("max_distance", 2000) @@ -62,6 +67,9 @@ def initialize(self): self.config_hunt_for_trash = self.config.get("hunt_for_trash_to_fill_bag", False) self.config_trash_hunt_open_slots = self.config.get("trash_hunt_open_slots", 25) self.hunting_trash = False + # Allow the bot to run to a VIP? + self.config_run_to_vip = self.config.get("run_to_vip", False) + self.runs_to_vips = 0 def work(self): if not self.enabled: @@ -115,12 +123,22 @@ def work(self): self.hunting_trash = False return WorkerResult.SUCCESS + if hasattr(self.bot,"hunter_locked_target"): + if self.destination is not None and self.bot.hunter_locked_target is not None: + if self.destination is not self.bot.hunter_locked_target: + self.logger.info("Locked on to a different target than destination??") + self.bot.hunter_locked_target = None + if self.destination is not None: if self.destination_caught(): self.logger.info("We found a %(name)s while hunting.", self.destination) - self.recent_tries.append(self.destination['pokemon_id']) + # self.recent_tries.append(self.destination['pokemon_id']) + self.previous_destination = self.destination self.destination = None self.hunting_trash = False + self.bot.hunter_locked_target = None + self.lost_counter = 0 + self.lost_map_counter = 0 if self.config_enable_cooldown: wait = uniform(120, 600) self.no_hunt_until = time.time() + wait @@ -131,9 +149,13 @@ def work(self): if self.destination_vanished(): self.logger.info("Darn, target got away!") - self.recent_tries.append(self.destination['pokemon_id']) + # self.recent_tries.append(self.destination['pokemon_id']) + self.previous_destination = self.destination self.destination = None self.hunting_trash = False + self.bot.hunter_locked_target = None + self.lost_counter = 0 + self.lost_map_counter = 0 if self.config_enable_cooldown: wait = uniform(120, 600) self.no_hunt_until = time.time() + wait @@ -144,30 +166,92 @@ def work(self): now = time.time() pokemons = self.get_nearby_pokemons() + pokemons = filter(lambda x: x["pokemon_id"] not in self.recent_tries, pokemons) + trash_mons = ["Caterpie", "Weedle", "Pidgey", "Pidgeotto", "Pidgeot", "Kakuna", "Beedrill", "Metapod", "Butterfree"] - if self.config_hunt_for_trash and self.hunting_trash is False and (self.destination is None or self._is_vip_pokemon(self.destination) is False ): + if self.destination is not None: + target_mons = filter(lambda x: x["name"] is self.destination["name"], pokemons) + if self.no_log_until < now: + # self.logger.info("Targets on sightings: %s" % len(target_mons)) + if len(pokemons) > 0: + if len(target_mons) < 1: + # Target off sightings; must be getting close + # Drops of at about 120 meters to target... + distance = great_circle(self.bot.position, (self.walker.dest_lat, self.walker.dest_lng)).meters + if (distance > 125 and self.lost_map_counter > 4) or self.lost_map_counter > 10: + # If > 120 meter => must be gone? + # Searching for 10 times, give up... + self.logger.info("It seems %(name)s is no longer there!", self.destination) + self.destination = None + self.hunting_trash = False + self.bot.hunter_locked_target = None + self.lost_map_counter = 0 + self.lost_counter = 0 + if self.config_enable_cooldown: + wait = uniform(120, 600) + self.no_hunt_until = time.time() + wait + self.logger.info("Hunting on cooldown until {}.".format((datetime.now() + timedelta(seconds=wait)).strftime("%H:%M:%S"))) + return WorkerResult.SUCCESS + else: + self.logger.info("Electing new target....") + else: + self.lost_map_counter += 1 + else: + self.lost_map_counter = 0 + else: + self.logger.info("No sightings available at the moment...") + + if self.config_hunt_for_trash and self.hunting_trash is False and (self.destination is None or not self._is_vip_pokemon(self.destination) ): # Okay, we should hunt for trash if the bag is almost full - trash_mons = ["Caterpie", "Weedle", "Pidgey", "Pidgeotto", "Pidgeot", "Kakuna", "Beedrill", "Metapod", "Butterfree"] + pokemons.sort(key=lambda p: p["distance"]) + possible_targets = filter(lambda x: x["name"] in trash_mons, pokemons) if self.pokemon_slots_left() <= self.config_trash_hunt_open_slots: if self.no_log_until < now: - self.logger.info("Less than %s slots left to fill, starting hunt for trash" % elf.config_trash_hunt_open_slots) - for pokemon in pokemons: - if pokemon["name"] in trash_mons: - self.hunting_trash = True - self.destination = pokemon - self.lost_counter = 0 - self.hunt_started_at = datetime.now() - self.logger.info("Hunting for trash at %(distance).2f meters: %(name)s", self.destination) - self.set_target() - # We have a target - return WorkerResult.SUCCESS + self.logger.info("Less than %s slots left to fill, starting hunt for trash" % self.config_trash_hunt_open_slots) + if len(possible_targets) is 0: + self.logger.info("No trash pokemon around...") + for pokemon in possible_targets: + if self.destination is not None: + self.logger.info("Trash hunt takes priority! Changing target...") + self.hunting_trash = True + self.destination = pokemon + self.lost_counter = 0 + self.hunt_started_at = datetime.now() + self.logger.info("Hunting for trash at %(distance).2f meters: %(name)s", self.destination) + self.set_target() + # We have a target + return WorkerResult.RUNNING if self.config_hunt_for_trash and self.hunting_trash: - if self.pokemon_slots_left() > 20: + if self.pokemon_slots_left() > self.config_trash_hunt_open_slots: self.logger.info("No longer trying to fill the bag. Electing new target....") self.hunting_trash = False self.destination = None + # Closer target? + if self.no_log_until < now: + # Don't check every tick! + if self.destination is not None and len(pokemons) > 0: + pokemons.sort(key=lambda p: p["distance"]) + possible_targets = filter(lambda x: x["name"] in trash_mons, pokemons) + # Check for a closer target + self.destination["distance"] = self.get_distance(self.bot.position, self.destination) + for pokemon in possible_targets: + if pokemon is not self.destination: + if round(pokemon["distance"], 2) >= round(self.destination["distance"], 2): + # further away! + break + self.logger.info("Found a closer target: %s < %s" % (pokemon["distance"], self.destination["distance"])) + if self.destination is not None: + self.logger.info("Closer trash hunt takes priority! Changing target...") + self.hunting_trash = True + self.destination = pokemon + self.lost_counter = 0 + self.hunt_started_at = datetime.now() + self.logger.info("New target at %(distance).2f meters: %(name)s", self.destination) + self.set_target() + # We have a target + return WorkerResult.RUNNING if self.destination is None: worth_pokemons = self.get_worth_pokemons(pokemons, self.config_hunt_closest_first) @@ -175,19 +259,48 @@ def work(self): if len(worth_pokemons) > 0: # Pick a random target from the list # random.shuffle(worth_pokemons) - # Priotize closer pokemon - worth_pokemons.sort(key=lambda p: p["distance"]) + if self.config_hunt_closest_first: + # Priotize closer pokemon + worth_pokemons.sort(key=lambda p: p["distance"]) + else: + random.shuffle(worth_pokemons) # Prevents the bot from looping the same Pokemon self.destination = worth_pokemons[0] + + if self.previous_destination is not None: + # Check if we are hunting the same target again... + if self.previous_destination["pokemon_id"] == self.destination["pokemon_id"]: + # Hunting the same pokemon again? + if "fort_id" in self.previous_destination and "fort_id" in self.destination and \ + self.previous_destination["fort_id"] == self.destination["fort_id"]: + # Hunting the same target again? + if len(worth_pokemons) > 1: + self.destination = worth_pokemons[1] + else: + # Checking if it's the same distance... + self.previous_destination["distance"] = self.get_distance(self.bot.start_position, self.previous_destination) + self.destination["distance"] = self.get_distance(self.bot.start_position, self.destination) + if round(self.previous_destination["distance"], 2) == round(self.destination["distance"], 2): + self.logger.info("Likely we are trying the same Pokemon again") + if len(worth_pokemons) > 1: + self.destination = worth_pokemons[1] + + if self.previous_destination == self.destination: + # We already caught that Pokemon! + if len(worth_pokemons) > 1: + self.destination = worth_pokemons[1] + self.set_target() self.lost_counter = 0 self.hunt_started_at = datetime.now() self.logger.info("New destination at %(distance).2f meters: %(name)s", self.destination) - if self._is_vip_pokemon(self.destination): - self.logger.info("This is a VIP Pokemon! Starting hunt.") - if self.config_lock_on_target: - self.bot.hunter_locked_target = self.destination + if self._is_vip_pokemon(self.destination) and self.config_lock_on_target: + self.logger.info("This is a VIP Pokemon! Locking on to target!") + self.bot.hunter_locked_target = self.destination + elif self._is_family_of_vip(self.destination) and self.config_treat_family_of_vip_as_vip and self.config_lock_on_target: + self.logger.info("This Pokemon is family of a VIP! Locking target!") + self.bot.hunter_locked_target = self.destination elif self._is_needed_pokedex(self.destination): self.logger.info("I need a %(name)s to complete the Pokedex! I have %(candies)s candies.", self.destination) if self.config_lock_on_target and not self.config_lock_vip_only: @@ -203,8 +316,10 @@ def work(self): # Show like "Pidgey (12), Zubat(2)" names = Counter((p["name"] for p in pokemons)) sorted(names) # unicode object, no lower? , key=str.lower) - - self.logger.info("There is no nearby pokemon worth hunting down [%s]", ", ".join('{}({})'.format(key, val) for key, val in names.items())) + if len(names) > 0: + self.logger.info("There is no nearby pokemon worth hunting down [%s]", ", ".join('{}({})'.format(key, val) for key, val in names.items())) + else: + self.logger.info("No sightings available at the moment...") self.no_log_until = now + 120 self.destination = None if self.config_enable_cooldown: @@ -217,9 +332,79 @@ def work(self): return WorkerResult.SUCCESS # Make sure a VIP is treated that way - if self.config_lock_on_target and self.destination is not None: - if self._is_vip_pokemon(self.destination) and self.bot.hunter_locked_target is None: + if self.config_lock_on_target and self.bot.hunter_locked_target is None and self.destination is not None: + if self._is_vip_pokemon(self.destination): self.bot.hunter_locked_target = self.destination + #Check if we are treating Family of VIP as VIP + if self.config_treat_family_of_vip_as_vip and self.destination is not None: + if self._is_family_of_vip(self.destination): + # We're hunting for family, so we need to check if we find a VIP + if self.no_log_until < now: + # Not every tick please + possible_targets = filter(lambda p: self._is_vip_pokemon(p), pokemons) + # Update the distance to targets + for p in possible_targets: + p["distance"] = self.get_distance(self.bot.position, p) + possible_targets.sort(key=lambda p: p["distance"]) + if len(possible_targets) > 0: + # Check if it's not the same mon... + if possible_targets[0]["name"] != self.destination["name"]: + self.logger.info("We found a real VIP while hunting for %(name)s", self.destination) + self.destination = possible_targets[0] + self.bot.hunter_locked_target = self.destination + self.lost_counter = 0 + self.hunt_started_at = datetime.now() + self.logger.info("New VIP target at %(distance).2f meters: %(name)s", self.destination) + self.set_target() + # We have a target + return WorkerResult.RUNNING + + # Now we check if there is a VIP target closer by + if self.destination is not None and self.bot.hunter_locked_target is self.destination: + # Hunting a VIP, checking for closer VIP target + if self.no_log_until < now: + # Not every tick please + possible_targets = filter(lambda p: self._is_vip_pokemon(p), pokemons) + # Update the distance to targets + for p in possible_targets: + p["distance"] = self.get_distance(self.bot.position, p) + possible_targets.sort(key=lambda p: p["distance"]) + # Check for a closer target + self.destination["distance"] = self.get_distance(self.bot.position, self.destination) + for pokemon in possible_targets: + if pokemon is not self.destination: + if round(pokemon["distance"], 2) >= round(self.destination["distance"], 2): + # further away! + break + with self.bot.database as conn: + c = conn.cursor() + c.execute( + "SELECT COUNT(pokemon) FROM catch_log where pokemon = '{}' and datetime(dated, 'localtime') > Datetime('{}')".format(pokemon["name"], self.hunt_started_at.strftime("%Y-%m-%d %H:%M:%S"))) + # Now check if there is 1 or more caught + amount = c.fetchone()[0] + if amount > 0: + # We caught this pokemon recently, skip it + continue + if self.config_treat_family_of_vip_as_vip and self._is_family_of_vip(pokemon): + if self._is_vip_pokemon(self.destination): + self.logger.info("Seeing a familymember of a VIP at %(distance).2f meters: %(name)s", pokemon) + self.logger.info("Not hunting down because we are locked to a real VIP: %(name)s", self.destination) + continue + else: + self.logger.info("Closer (is distance) familymember of VIP found!") + + self.logger.info("Found a closer VIP target: %s < %s" % (pokemon["distance"], self.destination["distance"])) + if self.destination is not None: + self.logger.info("Closer VIP hunt takes priority! Changing target...") + self.destination = pokemon + self.bot.hunter_locked_target = self.destination + self.lost_counter = 0 + self.hunt_started_at = datetime.now() + self.logger.info("New VIP target at %(distance).2f meters: %(name)s", self.destination) + self.set_target() + # We have a target + return WorkerResult.RUNNING + # Check if there is a VIP around to hunt if (self.destination is not None and @@ -238,9 +423,8 @@ def work(self): self.set_target() if self.config_lock_on_target: self.bot.hunter_locked_target = self.destination - self.logger.info("Found a VIP Pokemon! Looking for a %(name)s at %(distance).2f.", self.destination) + self.logger.info("Spotted a VIP Pokemon! Looking for a %(name)s at %(distance).2f.", self.destination) return WorkerResult.SUCCESS - pass if self.destination is None: if self.no_log_until < now: @@ -250,6 +434,7 @@ def work(self): if self.config_lock_on_target and not self.config_lock_vip_only: if self.bot.hunter_locked_target == None: self.logger.info("We found a %(name)s while hunting. Aborting the current search.", self.destination) + self.previous_destination = self.destination self.destination = None self.hunting_trash = False if self.config_enable_cooldown: @@ -258,10 +443,31 @@ def work(self): self.logger.info("Hunting on cooldown until {}.".format((datetime.now() + timedelta(seconds=wait)).strftime("%H:%M:%S"))) return WorkerResult.SUCCESS + # Determin if we are allowed to run to a VIP + different_target = False + if self.destination is not None: + if self.previous_destination is None: + self.previous_destination = self.destination + elif self.previous_destination is not self.destination: + different_target = True + self.previous_destination = self.destination + + if self.config_run_to_vip and self._is_vip_pokemon(self.destination): + if self.runs_to_vips > 3: + self.logger.info("Ran to 3 Pokemon in a row. Cooling down...") + self.runs_to_vips = 0 + speed = None + else: + speed = self.bot.config.walk_max + if different_target: + self.runs_to_vips += 1 + else: + speed = None + if any(self.destination["encounter_id"] == p["encounter_id"] for p in self.bot.cell["catchable_pokemons"] + self.bot.cell["wild_pokemons"]): self.destination = None self.hunting_trash = False - elif self.walker.step(): + elif self.walker.step(speed): if not any(self.destination["encounter_id"] == p["encounter_id"] for p in pokemons): self.lost_counter += 1 else: @@ -278,9 +484,11 @@ def work(self): self.logger.info("Hunting on cooldown until {}.".format((datetime.now() + timedelta(seconds=wait)).strftime("%H:%M:%S"))) else: self.logger.info("Now searching for %(name)s", self.destination) - - self.walker = StepWalker(self.bot, self.search_points[0][0], self.search_points[0][1]) - self.search_points = self.search_points[1:] + self.search_points[:1] + if self.search_points == []: + self.walker = StepWalker(self.bot, self.destination['latitude'], self.destination['longitude']) + else: + self.walker = StepWalker(self.bot, self.search_points[0][0], self.search_points[0][1]) + self.search_points = self.search_points[1:] + self.search_points[:1] elif self.no_log_until < now: distance = great_circle(self.bot.position, (self.walker.dest_lat, self.walker.dest_lng)).meters if round(distance, 2) == self.distance_to_target: @@ -289,7 +497,13 @@ def work(self): else: self.distance_counter = 0 - if self.distance_counter >= 3: + if self.distance_counter is 3: + # Try another walker + self.logger.info("Having difficulty walking to target, changing walker!") + self.walker = StepWalker(self.bot, self.search_points[0][0], self.search_points[0][1]) + self.distance_counter += 1 + + if self.distance_counter >= 6: # Ignore last 3 if len(self.recent_tries) > 3: self.recent_tries.pop() @@ -305,13 +519,32 @@ def work(self): self.logger.info("Hunting on cooldown until {}.".format((datetime.now() + timedelta(seconds=wait)).strftime("%H:%M:%S"))) return WorkerResult.ERROR else: - self.logger.info("Moving to destination at %s meters: %s", round(distance, 2), self.destination["name"]) + unit = self.bot.config.distance_unit # Unit to use when printing formatted distance + if speed is not None: + self.emit_event( + 'moving_to_hunter_target', + formatted="Running towards VIP target {target_name} - {distance}", + data={ + 'target_name': u"{}".format(self.destination["name"]), + 'distance': format_dist(distance, unit), + } + ) + else: + self.emit_event( + 'moving_to_hunter_target', + formatted="Moving towards target {target_name} - {distance}", + data={ + 'target_name': u"{}".format(self.destination["name"]), + 'distance': format_dist(distance, unit), + } + ) + # self.logger.info("Moving to destination at %s meters: %s", round(distance, 2), self.destination["name"]) # record the new distance... self.distance_to_target = round(distance, 2) if self.config_lock_on_target and not self.config_lock_vip_only: # Just to ensure we stay on target self.bot.hunter_locked_target = self.destination - self.no_log_until = now + 30 + self.no_log_until = now + 5 return WorkerResult.RUNNING @@ -319,10 +552,20 @@ def get_pokeball_count(self): return sum([inventory.items().get(ball.value).count for ball in [Item.ITEM_POKE_BALL, Item.ITEM_GREAT_BALL, Item.ITEM_ULTRA_BALL]]) def set_target(self): - self.search_points = self.get_search_points(self.destination["s2_cell_id"]) - self.walker = PolylineWalker(self.bot, self.search_points[0][0], self.search_points[0][1]) - self.search_cell_id = self.destination["s2_cell_id"] - self.search_points = self.search_points[1:] + self.search_points[:1] + if not 's2_cell_id' in self.destination: + # This Pokemon has coords + self.search_points = [] + self.walker = PolylineWalker(self.bot, self.destination["latitude"], self.destination["longitude"]) + self.logger.info("Target must be close by...") + # self.logger.info("destination: %s" % self.destination) + # self.search_points = self.get_search_points(self.bot.cell["s2_cell_id"]) + # self.search_cell_id = self.bot.cell["s2_cell_id"] + # self.search_points = self.search_points[1:] + self.search_points[:1] + else: + self.search_points = self.get_search_points(self.destination["s2_cell_id"]) + self.walker = PolylineWalker(self.bot, self.search_points[0][0], self.search_points[0][1]) + self.search_cell_id = self.destination["s2_cell_id"] + self.search_points = self.search_points[1:] + self.search_points[:1] if "fort_id" in self.destination: # The Pokemon is hding at a POkestop, so move to that Pokestop! @@ -337,6 +580,15 @@ def set_target(self): fort_name = details.get('name', 'Unknown') self.logger.info("%s is hiding at %s, going there first!" % (self.destination["name"], fort_name)) self.walker = PolylineWalker(self.bot, lat, lng) + else: + nearest_fort = self.get_nearest_fort_on_the_way() + if nearest_fort is not None: + lat = nearest_fort['latitude'] + lng = nearest_fort['longitude'] + details = fort_details(self.bot, nearest_fort['id'], lat, lng) + fort_name = details.get('name', 'Unknown') + self.logger.info("Moving to %s via %s." % (self.destination["name"], fort_name)) + self.walker = PolylineWalker(self.bot, lat, lng) def pokemon_slots_left(self): left = self.max_pokemon_storage - inventory.Pokemons.get_space_used() @@ -347,9 +599,39 @@ def get_nearby_pokemons(self): pokemons = [p for p in self.bot.cell["nearby_pokemons"] if self.get_distance(self.bot.start_position, p) <= radius] + if 'wild_pokemons' in self.bot.cell: + for pokemon in self.bot.cell['wild_pokemons']: + if pokemon['encounter_id'] in map(lambda pokemon: pokemon['encounter_id'], pokemons): + # Already added this Pokemon + continue + if self.get_distance(self.bot.start_position, pokemon) <= radius: + pokemons.append(pokemon) + + if 'catchable_pokemons' in self.bot.cell: + for pokemon in self.bot.cell['catchable_pokemons']: + if pokemon['encounter_id'] in map(lambda pokemon: pokemon['encounter_id'], pokemons): + # Already added this Pokemon + continue + if self.get_distance(self.bot.start_position, pokemon) <= radius: + pokemons.append(pokemon) + for pokemon in pokemons: + if "pokemon_data" in pokemon: + pokemon["pokemon_id"] = pokemon["pokemon_data"]["pokemon_id"] + pokemon["name"] = inventory.pokemons().name_for(pokemon["pokemon_id"]) + + if "name" not in pokemon and "pokemon_id" not in pokemon: + self.logger.warning("Strange result? %s" % pokemon) + # Skip this one! + continue + pokemon["distance"] = self.get_distance(self.bot.position, pokemon) - pokemon["name"] = inventory.pokemons().name_for(pokemon["pokemon_id"]) + + if "name" not in pokemon: + pokemon["name"] = inventory.pokemons().name_for(pokemon["pokemon_id"]) + if "pokemon_id" not in pokemon: + pokemon["pokemon_id"] = inventory.pokemons().id_for(pokemon["name"]) + pokemon["candies"] = inventory.candies().get(pokemon["pokemon_id"]).quantity # Pokemon also has a fort_id of the PokeStop the Pokemon is hiding at. # We should set our first destination at that Pokestop. @@ -364,8 +646,8 @@ def _is_vip_pokemon(self, pokemon): if self.bot.config.vips.get(pokemon["name"]) == {} or (self.config_treat_unseen_as_vip and not inventory.pokedex().seen(pokemon["pokemon_id"])): return True # If we must treat the family of the Pokemon as a VIP, also return true! - if self.config_treat_family_of_vip_as_vip and self._is_family_of_vip(pokemon): - return True + # if self.config_treat_family_of_vip_as_vip and self._is_family_of_vip(pokemon): + # return True def _is_family_of_vip(self, pokemon): for fid in self.get_family_ids(pokemon): @@ -404,6 +686,8 @@ def get_worth_pokemons(self, pokemons, closest_first=False): else: worth_pokemons = [] + worth_pokemons += [p for p in pokemons if not inventory.pokedex().seen(p["pokemon_id"])] + if self.config_hunt_vip: worth_pokemons += [p for p in pokemons if p["name"] in self.bot.config.vips] @@ -483,3 +767,28 @@ def destination_vanished(self): self.logger.info("We lost {} {}(s) since {}".format(amount, self.destination["name"], self.hunt_started_at.strftime("%Y-%m-%d %H:%M:%S"))) return vanished + + def get_nearest_fort_on_the_way(self): + forts = self.bot.get_forts(order_by_distance=True) + + # Remove stops that are still on timeout + forts = filter(lambda x: x["id"] not in self.bot.fort_timeouts, forts) + i = 0 + while i < len(forts): + ratio = float(self.config.get('max_extra_dist_fort', 20)) + dist_self_to_fort = distance(self.bot.position[0], self.bot.position[1], forts[i]['latitude'], + forts[i]['longitude']) + # self.search_points[0][0], self.search_points[0][1] + dist_fort_to_pokemon = distance(self.search_points[0][0], self.search_points[0][1], forts[i]['latitude'], + forts[i]['longitude']) + total_dist = dist_self_to_fort + dist_fort_to_pokemon + dist_self_to_pokemon = distance(self.bot.position[0], self.bot.position[1], self.search_points[0][0], self.search_points[0][1]) + if total_dist < (1 + (ratio / 100)) * dist_self_to_pokemon: + i += 1 + else: + del forts[i] + # Return nearest fort if there are remaining + if len(forts): + return forts[0] + else: + return None diff --git a/pokemongo_bot/event_manager.py b/pokemongo_bot/event_manager.py index ae929dd48f..2be9f02f66 100644 --- a/pokemongo_bot/event_manager.py +++ b/pokemongo_bot/event_manager.py @@ -56,7 +56,7 @@ def emit(self, event, sender=None, level='info', formatted='', data={}): raise EventNotRegisteredException("Event %s not registered..." % event) if self._limit_output: - if (event == self._last_event) and (event in ["moving_to_fort", "moving_to_lured_fort", "position_update"]): + if (event == self._last_event) and (event in ["moving_to_fort", "moving_to_lured_fort", "position_update", "moving_to_hunter_target"]): stdout.write("\033[1A\033[0K\r") stdout.flush() diff --git a/pokemongo_bot/walkers/step_walker.py b/pokemongo_bot/walkers/step_walker.py index b558507011..8181a2fcfd 100644 --- a/pokemongo_bot/walkers/step_walker.py +++ b/pokemongo_bot/walkers/step_walker.py @@ -34,6 +34,9 @@ def step(self, speed=None): if speed is None: speed = uniform(self.bot.config.walk_min, self.bot.config.walk_max) + elif speed == self.bot.config.walk_max: + # Keep it more Human like... + speed = uniform(speed - 0.5, speed + 0.5) origin_lat, origin_lng, origin_alt = self.bot.position From 68d1a70f2c9a885ff2c68fd5a0100edbeca80776 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Sun, 9 Jul 2017 13:08:22 +0200 Subject: [PATCH 2/2] Updated docs and added catch changes --- docs/configuration_files.md | 7 ++ pokemongo_bot/cell_workers/catch_pokemon.py | 76 +++++++++++++++------ 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/docs/configuration_files.md b/docs/configuration_files.md index 48d798bcfd..97231e2a84 100644 --- a/docs/configuration_files.md +++ b/docs/configuration_files.md @@ -172,6 +172,8 @@ The behaviors of the bot are configured via the `tasks` key in the `config.json` [[back to top](#table-of-contents)] * CatchPokemon * `enabled`: Default "true" | Enable/Disable the task. + * `always_catch_family_of_vip`: Default "false" | Always catch family members of a VIP, even if locked on a target. + * `always_catch_trash`: Default "false" | Always catch trash Pokemon (12 candy evolve), even if locked on a target. * `treat_unseen_as_vip`: Default `"true"` | If true, treat new to dex as VIP * `catch_visible_pokemon`: Default "true" | If enabled, attempts to catch "visible" pokemon that are reachable * `catch_lured_pokemon`: Default "true" | If enabled, attempts to catch "lured" pokemon that are reachable @@ -1421,6 +1423,11 @@ Hunts down nearby Pokemon. Searches for Pokemon to complete the Pokedex, or if a * `treat_family_of_vip_as_vip`: `Default: false`. Should we see family of an VIP as a VIP (locking onto it if enabled) * `hunt_for_trash_to_fill_bag`: `Default: false`. Should we try to fill the bag with trash if a set amount of slots is left? * `trash_hunt_open_slots`: `Default: 25`. The amount of slots for the previous setting +* `run_to_vip`: `Default: false`. Run to a VIP Pokemon? Running sets the speed of the walker to the walk_max value! + +### Hunting family members of VIPs +If enabled (`target_family_of_vip` = true), the hunter will also hunt down family members of a VIP. For example, if you marked Gyarados as a VIP Pokemon then the hunter will now also hunt down Magikarps. +When on the hunt for a family member of a VIP, and `treat_family_of_vip_as_vip` is false, the hunter will keep a look out for "real" VIPs. So when hunting for a Magikarp, if a Snorlax shows up in the sightings, the hunter will target the Snorlax. ### Hunting for trash If enabled the hunter will start hunting down Pidgeys, Weedles and Caterpies when a set amount of slots (defaults to 25) are left in the bag to fill. The idea is simple; we are about to start evolving Pokemon. So the priority of the hunter shiftes. BUT when a VIP Pokemon is nearby, the Hunter will always start chasing that VIP first. diff --git a/pokemongo_bot/cell_workers/catch_pokemon.py b/pokemongo_bot/cell_workers/catch_pokemon.py index 7f6f2d9cf2..4c49db8154 100644 --- a/pokemongo_bot/cell_workers/catch_pokemon.py +++ b/pokemongo_bot/cell_workers/catch_pokemon.py @@ -71,36 +71,54 @@ def work(self): if hasattr(self.bot, "skipped_pokemon"): # Skip pokemon the catcher told us to ignore - self.pokemon = [ p for p in self.pokemon if p not in self.bot.skipped_pokemon ] + self.pokemon = [p for p in self.pokemon if p not in self.bot.skipped_pokemon] num_pokemon = len(self.pokemon) + always_catch_family_of_vip = self.config.get('always_catch_family_of_vip', False) + always_catch_trash = self.config.get('always_catch_trash', False) + trash_pokemon = ["Caterpie", "Weedle", "Pidgey", "Pidgeotto", "Pidgeot", "Kakuna", "Beedrill", "Metapod", "Butterfree"] if num_pokemon > 0: # try catching mon_to_catch = self.pokemon.pop() - is_vip = hasattr(mon_to_catch, "pokemon_id") and self._is_vip_pokemon(mon_to_catch['pokemon_id']) + is_vip = self._is_vip_pokemon(mon_to_catch) # hasattr(mon_to_catch, "pokemon_id") and self._is_vip_pokemon(mon_to_catch['pokemon_id']) # Always catch VIP Pokemons! if hasattr(self.bot,"hunter_locked_target") and self.bot.hunter_locked_target != None: bounty = self.bot.hunter_locked_target mon_name = Pokemons.name_for(mon_to_catch['pokemon_id']) bounty_name = Pokemons.name_for(bounty['pokemon_id']) - if mon_name != bounty_name and is_vip == False: - # This is not the Pokémon you are looking for... - self.logger.info("[Hunter locked a {}] Ignoring a {}".format(bounty_name, mon_name)) - self.ignored_while_looking.append(mon_to_catch['pokemon_id']) - - if num_pokemon > 1: - return WorkerResult.RUNNING - else: - return WorkerResult.SUCCESS - else: - # We have found a vip or our target... - if bounty_name == mon_name: - self.bot.hunter_locked_target = None - self.logger.info("Found my target {}!".format(bounty_name)) + family_catch = False + trash_catch = False + + if always_catch_trash: + if mon_name in trash_pokemon: + self.logger.info("While on the hunt for {}, I found a {}! I want that Pokemon! Will try to catch...".format(bounty_name, mon_name)) + trash_catch = True + + if always_catch_family_of_vip: + if mon_name != bounty_name: + if self._is_family_of_vip(mon_to_catch['pokemon_id']): + self.logger.info("While on the hunt for {}, I found a {}! I want that Pokemon! Will try to catch...".format(bounty_name, mon_name)) + family_catch = True + + if not family_catch and not trash_catch: + if (mon_name != bounty_name and is_vip is False): + # This is not the Pokémon you are looking for... + self.logger.info("[Hunter locked a {}] Ignoring a {}".format(bounty_name, mon_name)) + self.ignored_while_looking.append(mon_to_catch['pokemon_id']) + + if num_pokemon > 1: + return WorkerResult.RUNNING + else: + return WorkerResult.SUCCESS else: - self.logger.info("While on the hunt for {}, I found a {}! I need that Pokemon! Will try to catch...".format(bounty_name, mon_name)) + # We have found a vip or our target... + if bounty_name == mon_name: + self.bot.hunter_locked_target = None + self.logger.info("Found my target {}!".format(bounty_name)) + else: + self.logger.info("While on the hunt for {}, I found a {}! I want that Pokemon! Will try to catch...".format(bounty_name, mon_name)) try: if self.catch_pokemon(mon_to_catch) == WorkerResult.ERROR: # give up incase something went wrong in our catch worker (ran out of balls, etc) @@ -114,15 +132,23 @@ def work(self): # all pokemon have been processed return WorkerResult.SUCCESS - def _is_vip_pokemon(self, pokemon_id): + def _is_vip_pokemon(self, pokemon): + if 'pokemon_id' not in pokemon: + if not 'name' in pokemon: + return False + pokemon['pokemon_id'] = Pokemons.id_for(pokemon['name']) # having just a name present in the list makes them vip # Not seen pokemons also will become vip if it's not disabled in config - if self.bot.config.vips.get(Pokemons.name_for(pokemon_id)) == {}: + if self.bot.config.vips.get(Pokemons.name_for(pokemon['pokemon_id'])) == {}: return True - if (not inventory.pokedex().seen(pokemon_id)): + if (not inventory.pokedex().seen(pokemon['pokemon_id'])): return True + # If we must treat family of VIP as VIP + if self.config.get('treat_family_of_vip_as_vip', False): + if self._is_family_of_vip(pokemon['pokemon_id']): + return True # If we need the Pokemon for an evolution, catch it. - if any(not inventory.pokedex().seen(fid) for fid in self.get_family_ids(pokemon_id)): + if any(not inventory.pokedex().seen(fid) for fid in self.get_family_ids(pokemon['pokemon_id'])): # self.logger.info('Found a Pokemon whoes family is not yet complete in Pokedex!') return True @@ -267,6 +293,14 @@ def _have_applied_incense(self): return False return False + def _is_family_of_vip(self, pokemon_id): + for fid in self.get_family_ids(pokemon_id): + name = inventory.pokemons().name_for(fid) + if self.bot.config.vips.get(name) == {}: + return True + # No, not a family member of the VIP + return False + def get_family_ids(self, pokemon_id): family_id = inventory.pokemons().data_for(pokemon_id).first_evolution_id ids = [family_id]