#=============================================================================== # #=============================================================================== class PokemonEncounters attr_reader :step_count def initialize @step_chances = nil @encounter_tables = [] @chance_accumulator = 0 end def setup(map_ID) @step_count = 0 @step_chances = nil @encounter_tables = [] encounter_data = GameData::Encounter.get(map_ID, $PokemonGlobal.encounter_version) if encounter_data @step_chances = encounter_data.step_chances.clone @encounter_tables = Marshal.load(Marshal.dump(encounter_data.types)) end end def reset_step_count @step_count = 0 @chance_accumulator = 0 end #============================================================================= # Returns whether encounters for the given encounter type have been defined # for the current map. def has_encounter_type?(enc_type) return false if enc_type < 0 return @encounter_tables[enc_type] && @encounter_tables[enc_type].length > 0 end # Returns whether encounters for the given encounter type have been defined # for the given map. Only called by Bug Catching Contest to see if it can use # the map's BugContest encounter type to generate caught Pokémon for the other # contestants. def map_has_encounter_type?(map_ID, enc_type) return false if enc_type < 0 encounter_data = GameData::Encounter.get(map_ID, $PokemonGlobal.encounter_version) return false if !encounter_data return encounter_data.types[enc_type] && encounter_data.types[enc_type].length > 0 end # Returns whether land-like encounters have been defined for the current map. # Applies only to encounters triggered by moving around. def has_land_encounters? return has_normal_land_encounters? || has_encounter_type?(EncounterTypes::BugContest) end # Returns whether land-like encounters have been defined for the current map # (ignoring the Bug Catching Contest one). # Applies only to encounters triggered by moving around. def has_normal_land_encounters? return has_encounter_type?(EncounterTypes::Land) || has_encounter_type?(EncounterTypes::LandDay) || has_encounter_type?(EncounterTypes::LandNight) || has_encounter_type?(EncounterTypes::LandMorning) || has_encounter_type?(EncounterTypes::LandAfternoon) || has_encounter_type?(EncounterTypes::LandEvening) end # Returns whether cave-like encounters have been defined for the current map. # Applies only to encounters triggered by moving around. def has_cave_encounters? return has_encounter_type?(EncounterTypes::Cave) || has_encounter_type?(EncounterTypes::CaveDay) || has_encounter_type?(EncounterTypes::CaveNight) || has_encounter_type?(EncounterTypes::CaveMorning) || has_encounter_type?(EncounterTypes::CaveAfternoon) || has_encounter_type?(EncounterTypes::CaveEvening) end # Returns whether water-like encounters have been defined for the current map. # Applies only to encounters triggered by moving around (i.e. not fishing). def has_water_encounters? return has_encounter_type?(EncounterTypes::Water) || has_encounter_type?(EncounterTypes::WaterDay) || has_encounter_type?(EncounterTypes::WaterNight) || has_encounter_type?(EncounterTypes::WaterMorning) || has_encounter_type?(EncounterTypes::WaterAfternoon) || has_encounter_type?(EncounterTypes::WaterEvening) end #============================================================================= # Returns whether the player's current location allows wild encounters to # trigger upon taking a step. def encounter_possible_here? return true if $PokemonGlobal.surfing terrain_tag = $game_map.terrain_tag($game_player.x, $game_player.y) return false if PBTerrain.isIce?(terrain_tag) return true if has_cave_encounters? # i.e. this map is a cave return true if PBTerrain.isGrass?(terrain_tag) && has_land_encounters? return false end # Returns whether a wild encounter should happen, based on its encounter # chance. Called when taking a step and by Rock Smash. def encounter_triggered?(enc_type, repel_active = false, triggered_by_step = true) if enc_type < 0 || enc_type > EncounterTypes::Probabilities.length raise ArgumentError.new(_INTL("Encounter type out of range")) end return false if $game_system.encounter_disabled return false if !$Trainer return false if $DEBUG && Input.press?(Input::CTRL) # Check if enc_type has a defined step chance/encounter table return false if !@step_chances[enc_type] || @step_chances[enc_type] == 0 return false if !has_encounter_type?(enc_type) # Get base encounter chance and minimum steps grace period encounter_chance = @step_chances[enc_type].to_f min_steps_needed = (8 - encounter_chance / 10).clamp(0, 8).to_f # Apply modifiers to the encounter chance and the minimum steps amount if triggered_by_step encounter_chance += @chance_accumulator / 200 encounter_chance *= 0.8 if $PokemonGlobal.bicycle end if !Settings::FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS encounter_chance /= 2 if $PokemonMap.blackFluteUsed min_steps_needed *= 2 if $PokemonMap.blackFluteUsed encounter_chance *= 1.5 if $PokemonMap.whiteFluteUsed min_steps_needed /= 2 if $PokemonMap.whiteFluteUsed end first_pkmn = $Trainer.first_pokemon if first_pkmn case first_pkmn.item_id when :CLEANSETAG encounter_chance *= 2.0 / 3 min_steps_needed *= 4 / 3.0 when :PUREINCENSE encounter_chance *= 2.0 / 3 min_steps_needed *= 4 / 3.0 else # Ignore ability effects if an item effect applies case first_pkmn.ability_id when :STENCH, :WHITESMOKE, :QUICKFEET encounter_chance /= 2 min_steps_needed *= 2 when :SNOWCLOAK if $game_screen.weather_type == PBFieldWeather::Snow || $game_screen.weather_type == PBFieldWeather::Blizzard encounter_chance /= 2 min_steps_needed *= 2 end when :SANDVEIL if $game_screen.weather_type == PBFieldWeather::Sandstorm encounter_chance /= 2 min_steps_needed *= 2 end when :SWARM encounter_chance *= 1.5 min_steps_needed /= 2 when :ILLUMINATE, :ARENATRAP, :NOGUARD encounter_chance *= 2 min_steps_needed /= 2 end end end # Wild encounters are much less likely to happen for the first few steps # after a previous wild encounter if triggered_by_step && @step_count < min_steps_needed @step_count += 1 return false if rand(100) >= encounter_chance * 5 / (@step_chances[enc_type] + @chance_accumulator / 200) end # Decide whether the wild encounter should actually happen return true if rand(100) < encounter_chance # If encounter didn't happen, make the next step more likely to produce one if triggered_by_step @chance_accumulator += @step_chances[enc_type] @chance_accumulator = 0 if repel_active end return false end # Returns whether an encounter with the given Pokémon should be allowed after # taking into account Repels and ability effects. def allow_encounter?(enc_data, repel_active = false) return false if !enc_data # Repel if repel_active && !pbPokeRadarOnShakingGrass first_pkmn = (Settings::REPEL_COUNTS_FAINTED_POKEMON) ? $Trainer.first_pokemon : $Trainer.first_able_pokemon if first_pkmn && enc_data[1] < first_pkmn.level @chance_accumulator = 0 return false end end # Some abilities make wild encounters less likely if the wild Pokémon is # sufficiently weaker than the Pokémon with the ability first_pkmn = $Trainer.first_pokemon if first_pkmn case first_pkmn.ability_id when :INTIMIDATE, :KEENEYE return false if enc_data[1] <= first_pkmn.level - 5 && rand(100) < 50 end end return true end # Returns whether a wild encounter should be turned into a double wild # encounter. def have_double_wild_battle? return false if $PokemonTemp.forceSingleBattle return false if pbInSafari? return true if $PokemonGlobal.partner return false if $Trainer.able_pokemon_count <= 1 return true if PBTerrain.isDoubleWildBattle?(pbGetTerrainTag) && rand(100) < 30 return false end # Returns the encounter method that the current encounter should be generated # from, depending on the player's current location. def encounter_type time = pbGetTimeNow ret = -1 if $PokemonGlobal.surfing ret = EncounterTypes::Water if has_encounter_type?(EncounterTypes::Water) if PBDayNight.isDay?(time) ret = EncounterTypes::WaterDay if has_encounter_type?(EncounterTypes::WaterDay) if PBDayNight.isMorning?(time) ret = EncounterTypes::WaterMorning if has_encounter_type?(EncounterTypes::WaterMorning) elsif PBDayNight.isAfternoon?(time) ret = EncounterTypes::WaterAfternoon if has_encounter_type?(EncounterTypes::WaterAfternoon) elsif PBDayNight.isEvening?(time) ret = EncounterTypes::WaterEvening if has_encounter_type?(EncounterTypes::WaterEvening) end else ret = EncounterTypes::WaterNight if has_encounter_type?(EncounterTypes::WaterNight) end else check_land = false if has_cave_encounters? check_land = PBTerrain.isGrass?($game_map.terrain_tag($game_player.x, $game_player.y)) ret = EncounterTypes::Cave if has_encounter_type?(EncounterTypes::Cave) if PBDayNight.isDay?(time) ret = EncounterTypes::CaveDay if has_encounter_type?(EncounterTypes::CaveDay) if PBDayNight.isMorning?(time) ret = EncounterTypes::CaveMorning if has_encounter_type?(EncounterTypes::CaveMorning) elsif PBDayNight.isAfternoon?(time) ret = EncounterTypes::CaveAfternoon if has_encounter_type?(EncounterTypes::CaveAfternoon) elsif PBDayNight.isEvening?(time) ret = EncounterTypes::CaveEvening if has_encounter_type?(EncounterTypes::CaveEvening) end else ret = EncounterTypes::CaveNight if has_encounter_type?(EncounterTypes::CaveNight) end end # Land if has_land_encounters? || check_land ret = EncounterTypes::Land if has_encounter_type?(EncounterTypes::Land) if PBDayNight.isDay?(time) ret = EncounterTypes::LandDay if has_encounter_type?(EncounterTypes::LandDay) if PBDayNight.isMorning?(time) ret = EncounterTypes::LandMorning if has_encounter_type?(EncounterTypes::LandMorning) elsif PBDayNight.isAfternoon?(time) ret = EncounterTypes::LandAfternoon if has_encounter_type?(EncounterTypes::LandAfternoon) elsif PBDayNight.isEvening?(time) ret = EncounterTypes::LandEvening if has_encounter_type?(EncounterTypes::LandEvening) end else ret = EncounterTypes::LandNight if has_encounter_type?(EncounterTypes::LandNight) end if pbInBugContest? && has_encounter_type?(EncounterTypes::BugContest) ret = EncounterTypes::BugContest end end end return ret end #============================================================================= # For the current map, randomly chooses a species and level from the encounter # list for the given encounter type. Returns nil if there are none defined. # A higher chance_rolls makes this method prefer rarer encounter slots. def choose_wild_pokemon(enc_type, chance_rolls = 1) if enc_type < 0 || enc_type > EncounterTypes::Probabilities.length raise ArgumentError.new(_INTL("Encounter type out of range")) end enc_list = @encounter_tables[enc_type] return nil if !enc_list || enc_list.length == 0 # Static/Magnet Pull prefer wild encounters of certain types, if possible. # If they activate, they remove all Pokémon from the encounter table that do # not have the type they favor. If none have that type, nothing is changed. first_pkmn = $Trainer.first_pokemon if first_pkmn favored_type = nil case first_pkmn.ability_id when :STATIC favored_type = :ELECTRIC if GameData::Type.exists?(:ELECTRIC) && rand(100) < 50 when :MAGNETPULL favored_type = :STEEL if GameData::Type.exists?(:STEEL) && rand(100) < 50 end if favored_type new_enc_list = [] enc_list.each do |enc| species_data = GameData::Species.get(enc[0]) t1 = species_data.type1 t2 = species_data.type2 new_enc_list.push(enc) if t1 == favored_type || t2 == favored_type end enc_list = new_enc_list if new_enc_list.length > 0 end end enc_list.sort! { |a, b| b[0] <=> a[0] } # Highest probability first # Calculate the total probability value chance_total = 0 enc_list.each { |a| chance_total += a[0] } # Choose a random entry in the encounter table based on entry probabilities rnd = 0 chance_rolls.times do r = rand(chance_total) rnd = r if r > rnd # Prefer rarer entries if rolling repeatedly end encounter = nil enc_list.each do |enc| rnd -= enc[0] next if rnd >= 0 encounter = enc break end # Get the chosen species and level level = rand(encounter[2]..encounter[3]) # Some abilities alter the level of the wild Pokémon if first_pkmn case first_pkmn.ability_id when :HUSTLE, :PRESSURE, :VITALSPIRIT level = encounter[3] if rand(100) < 50 # Highest possible level end end # Black Flute and White Flute alter the level of the wild Pokémon if Settings::FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS if $PokemonMap.blackFluteUsed level = [level + rand(1..4), GameData::GrowthRate.max_level].min elsif $PokemonMap.whiteFluteUsed level = [level - rand(1..4), 1].max end end # Return [species, level] return [encounter[1], level] end # For the given map, randomly chooses a species and level from the encounter # list for the given encounter type. Returns nil if there are none defined. # Used by the Bug Catching Contest to choose what the other participants # caught. def choose_wild_pokemon_for_map(map_ID, enc_type) if enc_type < 0 || enc_type > EncounterTypes::Probabilities.length raise ArgumentError.new(_INTL("Encounter type out of range")) end # Get the encounter table encounter_data = GameData::Encounter.get(map_ID, $PokemonGlobal.encounter_version) return nil if !encounter_data enc_list = encounter_data.types[enc_type] return nil if !enc_list || enc_list.length == 0 # Calculate the total probability value chance_total = 0 enc_list.each { |a| chance_total += a[0] } # Choose a random entry in the encounter table based on entry probabilities rnd = 0 chance_rolls.times do r = rand(chance_total) rnd = r if r > rnd # Prefer rarer entries if rolling repeatedly end encounter = nil enc_list.each do |enc| rnd -= enc[0] next if rnd >= 0 encounter = enc break end # Return [species, level] level = rand(encounter[2]..encounter[3]) return [encounter[1], level] end end #=============================================================================== # #=============================================================================== # Creates and returns a Pokémon based on the given species and level. # Applies wild Pokémon modifiers (wild held item, shiny chance modifiers, # Pokérus, gender/nature forcing because of player's lead Pokémon). def pbGenerateWildPokemon(species,level,isRoamer=false) genwildpoke = Pokemon.new(species,level) # Give the wild Pokémon a held item items = genwildpoke.wildHoldItems first_pkmn = $Trainer.first_pokemon chances = [50,5,1] chances = [60,20,5] if first_pkmn && first_pkmn.hasAbility?(:COMPOUNDEYES) itemrnd = rand(100) if (items[0]==items[1] && items[1]==items[2]) || itemrnd