diff --git a/configs/config.json.example b/configs/config.json.example index 1594a18f5e..db0141ebd6 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -101,6 +101,13 @@ "// Example of always catching Rattata:": {}, "// Rattata": { "always_catch" : true } }, + "catch_throw_parameters": { + "excellent_rate": 0.1, + "great_rate": 0.5, + "nice_rate": 0.3, + "normal_rate": 0.1, + "spin_success_rate" : 0.6, + }, "release": { "any": {"release_below_cp": 0, "release_below_iv": 0, "logic": "or"}, "// Example of always releasing Rattata:": {}, diff --git a/pokecli.py b/pokecli.py index 4544ccf124..2cd5553ba5 100644 --- a/pokecli.py +++ b/pokecli.py @@ -394,6 +394,51 @@ def _json_loader(filename): type=bool, default=True ) + add_config( + parser, + load, + short_flag="-cte", + long_flag="--catch_throw_parameters.excellent_rate", + help="Define the odd of performing an excellent throw", + type=float, + default=1 + ) + add_config( + parser, + load, + short_flag="-ctg", + long_flag="--catch_throw_parameters.great_rate", + help="Define the odd of performing a great throw", + type=float, + default=0 + ) + add_config( + parser, + load, + short_flag="-ctn", + long_flag="--catch_throw_parameters.nice_rate", + help="Define the odd of performing a nice throw", + type=float, + default=0 + ) + add_config( + parser, + load, + short_flag="-ctm", + long_flag="--catch_throw_parameters.normal_rate", + help="Define the odd of performing a normal throw", + type=float, + default=0 + ) + add_config( + parser, + load, + short_flag="-cts", + long_flag="--catch_throw_parameters.spin_success_rate", + help="Define the odds of performing a spin throw (Value between 0 (never) and 1 (always))", + type=float, + default=1 + ) # Start to parse other attrs config = parser.parse_args() diff --git a/pokemongo_bot/cell_workers/pokemon_catch_worker.py b/pokemongo_bot/cell_workers/pokemon_catch_worker.py index 77e29ae10b..451ca0e9df 100644 --- a/pokemongo_bot/cell_workers/pokemon_catch_worker.py +++ b/pokemongo_bot/cell_workers/pokemon_catch_worker.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- import time +from random import random from pokemongo_bot import inventory from pokemongo_bot.base_task import BaseTask -from pokemongo_bot.human_behaviour import normalized_reticle_size, sleep, spin_modifier +from pokemongo_bot.human_behaviour import sleep from pokemongo_bot.worker_result import WorkerResult CATCH_STATUS_SUCCESS = 1 @@ -69,6 +70,7 @@ def work(self, response_dict=None): # validate response if not response_dict: return WorkerResult.ERROR + try: responses = response_dict['responses'] response = responses[self.response_key] @@ -301,8 +303,18 @@ def _do_catch(self, pokemon, encounter_id, catch_rate_by_ball, is_vip=False): if catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw and berry_count > 0 and not used_berry: catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball) berry_count -= 1 + + # Randomize the quality of the throw + # Default structure + throw_parameters = {'normalized_reticle_size': 1.950, + 'spin_modifier': 1.0, + 'normalized_hit_position': 1.0, + 'throw_type_label': 'Excellent'} + self.generate_spin_parameter(throw_parameters) + self.generate_throw_quality_parameters(throw_parameters) # try to catch pokemon! + # TODO : Log which type of throw we selected items_stock[current_ball] -= 1 self.emit_event( 'threw_pokeball', @@ -314,17 +326,14 @@ def _do_catch(self, pokemon, encounter_id, catch_rate_by_ball, is_vip=False): } ) - reticle_size_parameter = normalized_reticle_size(self.config.catch_randomize_reticle_factor) - spin_modifier_parameter = spin_modifier(self.config.catch_randomize_spin_factor) - response_dict = self.api.catch_pokemon( encounter_id=encounter_id, pokeball=current_ball, - normalized_reticle_size=reticle_size_parameter, + normalized_reticle_size=throw_parameters['normalized_reticle_size'], spawn_point_id=self.spawn_point_guid, hit_pokemon=1, - spin_modifier=spin_modifier_parameter, - normalized_hit_position=1 + spin_modifier=throw_parameters['spin_modifier'], + normalized_hit_position=throw_parameters['normalized_hit_position'] ) try: @@ -392,3 +401,48 @@ def _do_catch(self, pokemon, encounter_id, catch_rate_by_ball, is_vip=False): self.bot.softban = False break + + def generate_spin_parameter(self, throw_parameters): + spin_success_rate = self.config.catch_throw_parameters_spin_success_rate + if random() <= spin_success_rate: + throw_parameters['spin_modifier'] = 0.5 + 0.5 * random() + else: + throw_parameters['spin_modifier'] = 0.499 * random() + + def generate_throw_quality_parameters(self, throw_parameters): + throw_excellent_chance = self.config.catch_throw_parameters_excellent_rate + throw_great_chance = self.config.catch_throw_parameters_great_rate + throw_nice_chance = self.config.catch_throw_parameters_nice_rate + throw_normal_throw_chance = self.config.catch_throw_parameters_normal_rate + + # Total every chance types, pick a random number in the range and check what type of throw we got + total_chances = throw_excellent_chance + throw_great_chance \ + + throw_nice_chance + throw_normal_throw_chance + + random_throw = random() * total_chances + + if random_throw <= throw_excellent_chance: + throw_parameters['normalized_reticle_size'] = 1.70 + 0.25 * random() + throw_parameters['normalized_hit_position'] = 1.0 + throw_parameters['throw_type_label'] = 'Excellent' + return + + random_throw -= throw_excellent_chance + if random_throw <= throw_great_chance: + throw_parameters['normalized_reticle_size'] = 1.30 + 0.399 * random() + throw_parameters['normalized_hit_position'] = 1.0 + throw_parameters['throw_type_label'] = 'Great' + return + + random_throw -= throw_great_chance + if random_throw <= throw_nice_chance: + throw_parameters['normalized_reticle_size'] = 1.00 + 0.299 * random() + throw_parameters['normalized_hit_position'] = 1.0 + throw_parameters['throw_type_label'] = 'Nice' + return + + # Not a any kind of special throw, let's throw a normal one + # Here the reticle size doesn't matter, we scored out of it + throw_parameters['normalized_reticle_size'] = 1.25 + 0.70 * random() + throw_parameters['normalized_hit_position'] = 0.0 + throw_parameters['throw_type_label'] = 'Normal' diff --git a/pokemongo_bot/human_behaviour.py b/pokemongo_bot/human_behaviour.py index 2a8d2d5e9f..37a95081ca 100644 --- a/pokemongo_bot/human_behaviour.py +++ b/pokemongo_bot/human_behaviour.py @@ -25,22 +25,3 @@ def random_lat_long_delta(): # should be 364,000 * .000025 = 9.1. So it returns between [-9.1, 9.1] return ((random() * 0.00001) - 0.000005) * 5 - -# Humanized `normalized_reticle_size` parameter for `catch_pokemon` API. -# 1.0 => normal, 1.950 => excellent -def normalized_reticle_size(factor): - minimum = 1.0 - maximum = 1.950 - return uniform( - minimum + (maximum - minimum) * factor, - maximum) - - -# Humanized `spin_modifier` parameter for `catch_pokemon` API. -# 0.0 => normal ball, 1.0 => super spin curve ball -def spin_modifier(factor): - minimum = 0.0 - maximum = 1.0 - return uniform( - minimum + (maximum - minimum) * factor, - maximum)