• Do not use Discord to host any images you post, these links expire quickly! You can learn how to add images to your posts here.
Foreign Pokémon in the Wild

v20.1 Foreign Pokémon in the Wild 2022-12-18

This resource pertains to version 20.1 of Pokémon Essentials.
Pokémon Essentials Version
v20.1 ➖
1671366654605.png
1671366659167.png

Nobody ever talks about N's Pokémon, which is a shame, because I thought it was a really cool feature! This script lets you generate wild Pokémon with specific traits, including a different OT, then send them out into maps for the player to encounter in the wild!​

Installing this resource​

It's very nearly plug-and-play! (Technically, it's perfectly functional with just the one script, but there's a few flourishes to put in so that messages don't look goofy.

This is the main script, which you just paste as a new script section above Main, or download as a plugin for v20 here.
Ruby:
Expand Collapse Copy
module ForeignPokemon
  NPurrloin = {
    :species    => :PURRLOIN,
    :level      => 7,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :TIMID,
    :moves      => [:SCRATCH,:GROWL,:ASSIST],
    :iv         => {:HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }
 
    NPidove = {
    :species    => :PIDOVE,
    :level      => 13,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :SASSY,
    :moves      => [:GUST,:QUICKATTACK,:LEER,:GROWL],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }
 
    NTimburr = {
    :species    => :TIMBURR,
    :level      => 13,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :RASH,
    :moves      => [:LEER,:FOCUSENERGY,:BIDE,:LOWKICK],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }
 
    NTympole = {
    :species    => :TYMPOLE,
    :level      => 13,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :MODEST,
    :moves      => [:GROWL,:SUPERSONIC,:ROUND,:BUBBLEBEAM],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }

    NWoobat = {
    :species    => :WOOBAT,
    :level      => 55,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :TIMID,
    :moves      => [:AIRSLASH,:FUTURESIGHT,:PSYCHIC,:ENDEAVOR],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }

    NSandile = {
    :species    => :SANDILE,
    :level      => 22,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :DOCILE,
    :moves      => [:SANDTOMB,:ASSURANCE,:MUDSLAP,:EMBARGO],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }

    NDarumaka = {
    :species    => :DARUMAKA,
    :level      => 22,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :NAIVE,
    :moves      => [:HEADBUTT,:UPROAR,:FACADE,:FIREPUNCH],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }

    NDarmanitan = {
    :species       => :DARMANITAN,
    :level         => 35,
    :ability_index => 2,
    :shiny         => false,
    :gender        => 0,
    :ot            => :N,
    :happiness     => 255,
    :nature        => :CALM,
    :moves         => [:THRASH,:BELLYDRUM,:FLAREBLITZ,:HAMMERARM],
    :iv            => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }

    NScraggy = {
    :species    => :SCRAGGY,
    :level      => 22,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :LAX,
    :moves      => [:FAINTATTACK,:HEADBUTT,:SWAGGER,:BRICKBREAK],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }

    NSigilyph = {
    :species    => :SIGILYPH,
    :level      => 22,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :GENTLE,
    :moves      => [:TAILWIND,:WHIRLWIND,:PSYBEAM,:AIRCUTTER],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }

    NBoldore = {
    :species    => :BOLDORE,
    :level      => 28,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :NAIVE,
    :moves      => [:MUDSLAP,:IRONDEFENSE,:SMACKDOWN,:POWERGEM],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }

    NJoltik = {
    :species    => :JOLTIK,
    :level      => 28,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :DOCILE,
    :moves      => [:ELECTROWEB,:BUGBITE,:GASTROACID,:SLASH],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }

    NFerroseed = {
    :species    => :FERROSEED,
    :level      => 28,
    :shiny      => false,
    :gender     => 0,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :BASHFUL,
    :moves      => [:METALCLAW,:PINMISSILE,:GYROBALL,:IRONDEFENSE],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }

    NKlink = {
    :species    => :KLINK,
    :level      => 28,
    :shiny      => false,
    :ot         => :N,
    :happiness  => 255,
    :nature     => :RASH,
    :moves      => [:THUNDERSHOCK,:GEARGRIND,:BIND,:CHARGEBEAM],
    :iv        => { :HP => 30,
                    :ATTACK => 30,
                    :DEFENSE => 30,
                    :SPECIAL_ATTACK => 30,
                    :SPECIAL_DEFENSE => 30,
                    :SPEED => 30}
  }

end


module ForeignOTs
 
 
  N = {
    :name       => "N",
    :gender     => 0,
    :id_number  => 2
  }
 
end

module Settings
  # A set of arrays, each containing the details of a foreign Pokémon. The
  # information within each array is as follows:
  #   * Module with Pokémon's data
  #   * Game Switch; the Pokémon can be encountered while this is ON.
  #   * Map(s) the Pokémon can be encountered on.
  #   * Encounter type (0=any, 1=grass/walking in cave, 2=surfing, 3=fishing,
  #     4=surfing/fishing). See the bottom of PField_RoamingPokemon for lists.
  #   * Name of BGM to play for that encounter (optional).
  #   * Chance of being encountered. (out of 100)
  FOREIGN_SPECIES = [
    #[:NPurrloin,2,[66],1,"Battle Elite",100]
  ]

end



class PokemonGlobalMetadata
  attr_accessor :foreignEncounter
  attr_accessor :foreignPokemon
  attr_writer   :foreignPokemonCaught

  def foreignPokemonCaught
    return @foreignPokemonCaught || []
  end
end

#===============================================================================
# Encountering a foreign Pokémon in a wild battle.
#===============================================================================
class Game_Temp
  attr_accessor :foreign_index_for_encounter   # Index of foreign Pokémon to encounter next
end

EventHandlers.add(:on_wild_species_chosen, :foreign_pokemon,
  proc { |encounter|
    $game_temp.foreign_index_for_encounter = nil
    $PokemonGlobal.foreignPokemon = [] if !$PokemonGlobal.foreignPokemon
    next if !encounter
    # Give the regular encounter if encountering a foreign Pokémon isn't possible
    next if $PokemonGlobal.partner
    next if $game_temp.poke_radar_data
    # Look at each foreign Pokémon in turn and decide whether it's possible to encounter it
    currentRegion = pbGetCurrentRegion
    currentMapName = $game_map.name
    possible_foreign = []
    Settings::FOREIGN_SPECIES.each_with_index do |data, i|
      # data = [module, Game Switch, maps, encounter method, battle BGM, encounter chance]
      pokedata = ForeignPokemon.const_get(data[0])
      next if !GameData::Species.exists?(pokedata[:species])
      next if data[1] > 0 && !$game_switches[data[1]]   # Isn't active
      next if $PokemonGlobal.foreignPokemon[i] == true   # Foreign Pokémon has been caught
      # Get possible maps
      foreignMaps = data[2]
      if foreignMaps.include?($game_map.map_id)
        match = true
      else
      # If foreign mon isn't on the current map, check if it's on a map with the same
      # name and in the same region
          for i in 0...foreignMaps.length
          testMap = foreignMaps[i]
          map_metadata = GameData::MapMetadata.try_get(testMap)
            if map_metadata && map_metadata.town_map_position &&
                map_metadata.town_map_position[0] == currentRegion &&
                pbGetMapNameFromId(testMap) == currentMapName
                  match = true
            end
          end
      end
      next if !match
      # Check whether the encounter method is currently possible
      next if !pbRoamingMethodAllowed(data[3])
      next if !(rand(100) < data[5])
      # Add this foreign Pokémon to the list of possible foreign Pokémon to encounter
      possible_foreign.push([i, data[4]])   # [i, BGM]
    end
    # No encounterable foreign Pokémon were found, just have the regular encounter
    next if possible_foreign.length == 0
    # Pick a foreign Pokémon to encounter out of those available
    foreign = possible_foreign.sample
    $PokemonGlobal.foreignEncounter = foreign
    $game_temp.foreign_index_for_encounter = foreign[0]
    $PokemonGlobal.nextBattleBGM = foreign[1] if foreign[1] && !foreign[1].empty?
    $game_temp.force_single_battle = true
  }
)

EventHandlers.add(:on_calling_wild_battle, :foreign_pokemon,
  proc { |species, level, handled|
    # handled is an array: [nil]. If [true] or [false], the battle has already
    # been overridden (the boolean is its outcome), so don't do anything that
    # would override it again
    next if !handled[0].nil?
    next if !$PokemonGlobal.foreignEncounter || $game_temp.foreign_index_for_encounter.nil?
    handled[0] = pbForeignPokemonBattle
  }
)

def pbForeignPokemonBattle
  # Get the foreign Pokémon to encounter; generate it based on the species and
  # level if it doesn't already exist
  idxForeign = $game_temp.foreign_index_for_encounter
  if !$PokemonGlobal.foreignPokemon[idxForeign] ||
     !$PokemonGlobal.foreignPokemon[idxForeign].is_a?(Pokemon)
      pokedata = Settings::FOREIGN_SPECIES[idxForeign][0]
      pokedata = ForeignPokemon.const_get(pokedata)
      newpoke = pbGenerateWildPokemon(pokedata[:species],pokedata[:level])
      newpoke.shiny = pokedata[:shiny] if pokedata[:shiny]
      newpoke.name = pokedata[:name] if pokedata[:name]
      newpoke.gender = pokedata[:gender] if pokedata[:gender]
      newpoke.item = pokedata[:item] if pokedata[:item]
      newpoke.nature = pokedata[:nature] if pokedata[:nature]
      newpoke.form = pokedata[:form] if pokedata[:form]
      newpoke.happiness = pokedata[:happiness] if pokedata[:happiness]
      newpoke.sheen = pokedata[:sheen] if pokedata[:sheen]
      newpoke.cool = pokedata[:cool] if pokedata[:cool]
      newpoke.beauty = pokedata[:beauty] if pokedata[:beauty]
      newpoke.cute = pokedata[:cute] if pokedata[:cute]
      newpoke.smart = pokedata[:smart] if pokedata[:smart]
      newpoke.tough = pokedata[:tough] if pokedata[:tough]
      if pokedata[:ribbons]
        for i in 0...pokedata[:ribbons].length
          newpoke.giveRibbon(pokedata[:ribbons][i])
        end
      end
      if pokedata[:pokerus]
        if pokedata[:pokerus].is_a?(Integer)
          newpoke.givePokerus(pokedata[:pokerus])
        else
          newpoke.givePokerus
        end
      end
      newpoke.ability = pokedata[:ability] if pokedata[:ability]
      newpoke.ability_index = pokedata[:ability_index] if pokedata[:ability_index]
      if pokedata[:moves]
        moves = []
        for i in 0...pokedata[:moves].length
            moves.push(Pokemon::Move.new(pokedata[:moves][i]))
        end
        newpoke.moves = moves
      end
      if pokedata[:newmoves]
        for i in 0...pokedata[:newmoves].length
            newpoke.learn_move(pokedata[:newmoves][i])
        end
      end
      newpoke.ev = pokedata[:ev] if pokedata[:ev]
      newpoke.iv = pokedata[:iv] if pokedata[:iv]
      if pokedata[:perf_ivs]
        stats = []
        GameData::Stat.each_main { |s| stats.push(s.id)}
        stats = stats.sample(pokedata[:perf_ivs])
        stats.each do |s|
          newpoke.iv[s] = Pokemon::IV_STAT_LIMIT
        end
      end
      newpoke.memory = pokedata[:memory] if pokedata[:memory]
      if pokedata[:ot]
        trainerdata = ForeignOTs.const_get(pokedata[:ot])
        newpoke.owner.name = trainerdata[:name] if trainerdata[:name]
        newpoke.owner.gender = trainerdata[:gender] if trainerdata[:gender]
        if trainerdata[:id_number]
          id = trainerdata[:id_number]
          newpoke.owner.id = id | (id << 16)
        else
          newpoke.owner.id = $player.make_foreign_ID
        end
        newpoke.owner.language = trainerdata[:language] if trainerdata[:language]
      end
      $PokemonGlobal.foreignPokemon[idxForeign] = newpoke
  end
  if $PokemonGlobal.foreignPokemon[idxForeign].is_a?(Pokemon)
    $PokemonGlobal.foreignPokemon[idxForeign].heal if $PokemonGlobal.foreignPokemon[idxForeign].fainted?
  end
  # Set some battle rules
  setBattleRule("single")
  # Perform the battle
  decision = WildBattle.start_core($PokemonGlobal.foreignPokemon[idxForeign])
  species = $PokemonGlobal.foreignPokemon[idxForeign].species
  level = $PokemonGlobal.foreignPokemon[idxForeign].level
  # Update Foreign Pokémon data based on result of battle
  if decision == 4   # Caught
    $PokemonGlobal.foreignPokemon[idxForeign]       = true
    $PokemonGlobal.foreignPokemonCaught[idxForeign] = (decision == 4)
  end
  $PokemonGlobal.foreignEncounter = nil
  $game_temp.foreign_index_for_encounter = nil
  # Used by the Poké Radar to update/break the chain
  EventHandlers.trigger(:on_wild_battle_end, species, level, decision)
  # Return false if the player lost or drew the battle, and true if any other result
  return (decision != 2 && decision != 5)
end

Now for the flavor text!

In Battle_StartAndEnd, find this line:
Ruby:
Expand Collapse Copy
pbDisplayPaused(_INTL("Oh! A wild {1} appeared!", foeParty[0].name))
Change it to:
Ruby:
Expand Collapse Copy
        if foeParty[0].foreign?($player)
          pbDisplayPaused(_INTL("A wild {1} appeared?!", foeParty[0].name))
        else
          pbDisplayPaused(_INTL("Oh! A wild {1} appeared!", foeParty[0].name))
        end
(Of course, you can change the text to display whatever message you want it to instead of the simple ?! message.)

In Battle_CatchAndStoreMixin, find this line:
Ruby:
Expand Collapse Copy
pbDisplayPaused(_INTL("{1}'s data was added to the Pokédex.", pkmn.name))
And change it to
Ruby:
Expand Collapse Copy
pbDisplayPaused(_INTL("{1}'s data was added to the Pokédex.", pkmn.speciesName))
(This prevents it from saying "(Nickname)'s data was added to the Pokédex.")

Finally, we'll unfortunately have to make a change in the v20.1 hotfixes plugin, because it overwrites changes in the base scripts in this section. (Don't worry, if you mess up, you can just download a new copy)

Find this lines in Battle bug fixes -
Ruby:
Expand Collapse Copy
      if $PokemonSystem.givenicknames == 0 &&
And change it to
Ruby:
Expand Collapse Copy
      if pkmn.foreign?($player)
        pbDisplay(_INTL("It seems like #{pkmn.name} has a different OT!"))
      elsif $PokemonSystem.givenicknames == 0 &&
(This is the part that prevents players from renaming the Pokémon - you can skip it if you allow players to rename foreign Pokémon)

There is no v19 version of this script, I didn't get around to updating it in time. I currently do not have plans to offer v19 support.

The v18 version and instructions will be left here for posterity.
This is the main script, which you just paste as a new script section above Main,
Ruby:
Expand Collapse Copy
class PokemonGlobalMetadata
  attr_accessor :foreignEncounter
  attr_accessor :foreignPokemon
  attr_writer   :foreignPokemonCaught

  def foreignPokemonCaught
    return @foreignPokemonCaught || []
  end
end

# 0-5 are about encountering the Pokémon
#0-Species, 1-lvl, 2-switch, 3-encounter type, 4-battle BGM, 5-map ID
#Encounter types are the same as for Roaming Pokémon

#6-8 affect cosmetic info
#6-shiny (true/false), 7-gender (0M, 1F), 8- Nickname

#9-12 affect OT info
#9-name, 10-gender (0M,1F,2N), 11-ID#, 12-language
#The trainer ID# can be anywhere from 1 to 55555
#You can go higher, but it won't come out how you write it.
#(For example, 555555 doesn't display as "55555", but instead 31267)

#13-18 affect battle properties
#13-form, 14-nature, 15-IVs (an array of six numbers or one number),
#16-EVs (has to be six numbers), 17-moves (as an array), 18-ability#

#19 is currently reserved for items, but items sadly don't work yet.
#If you're using Pokémon Memories, you can uncomment part of the code
#and use 20 to set memory.


foreignSpecies = [
[:PIKACHU, 10, 8, 1, nil, 66, true, 0, "Pika", "Red", 0, 1, 1, 0, :HASTY,
[1,2,3,4,5,6], nil, [:TACKLE, :SPARK], 0],
[:BULBASAUR, 10, 8, 1, nil, 66, true, 0, "Saur", "Red", 0, 55554, 1, 0, :TIMID,
[31], nil, [:TACKLE, :FRENZYPLANT], 0]
]

#===============================================================================
# Encountering a foreign Pokémon in a wild battle.
#===============================================================================
class PokemonTemp
  attr_accessor :foreignIndex   # Index of foreign Pokémon to encounter next
end



# Returns whether the given category of encounter contains the actual encounter
# method that will occur in the player's current position.
def pbForeignMethodAllowed(encType)
  encounter = $PokemonEncounters.pbEncounterType
  case encType
  when 0   # Any encounter method (except triggered ones and Bug Contest)
    return true if encounter==EncounterTypes::Land ||
                   encounter==EncounterTypes::LandMorning ||
                   encounter==EncounterTypes::LandDay ||
                   encounter==EncounterTypes::LandNight ||
                   encounter==EncounterTypes::Water ||
                   encounter==EncounterTypes::Cave
  when 1   # Grass (except Bug Contest)/walking in caves only
    return true if encounter==EncounterTypes::Land ||
                   encounter==EncounterTypes::LandMorning ||
                   encounter==EncounterTypes::LandDay ||
                   encounter==EncounterTypes::LandNight ||
                   encounter==EncounterTypes::Cave
  when 2   # Surfing only
    return true if encounter==EncounterTypes::Water
  when 3   # Fishing only
    return true if encounter==EncounterTypes::OldRod ||
                   encounter==EncounterTypes::GoodRod ||
                   encounter==EncounterTypes::SuperRod
  when 4   # Water-based only
    return true if encounter==EncounterTypes::Water ||
                   encounter==EncounterTypes::OldRod ||
                   encounter==EncounterTypes::GoodRod ||
                   encounter==EncounterTypes::SuperRod
  end
  return false
end

EncounterModifier.register(proc { |encounter|
  $PokemonTemp.foreignIndex = nil
  next nil if !encounter
  # Give the regular encounter if encountering a foreign Pokémon isn't possible
  next encounter if $PokemonGlobal.partner
  next encounter if $PokemonTemp.pokeradar
  next encounter if rand(100)<50   # 50% chance of encountering a foreign Pokémon
  # Look at each foreign Pokémon in turn and decide whether it's possible to
  # encounter it
  $PokemonGlobal.foreignPokemon = [] if !$PokemonGlobal.foreignPokemon
  foreignChoices = []
  for i in 0...foreignSpecies.length
    # [species symbol, level, Game Switch, encounter type, battle BGM, area maps hash]
    foreignData = foreignSpecies[i]
    next if $PokemonGlobal.foreignPokemon[i]==true   # Foreign Pokémon has been caught
    # Ensure species is a number rather than a string/symbol
    species = getID(PBSpecies,foreignData[0])
    next if !species || species<=0
    # Makes sure the relevant switch is on
    switch=foreignData[2]
    next if $game_switches[switch]==false
    # Get the foreign Pokémon's map
    foreignMap=foreignData[5]
    # Check if foreign Pokémon is on the current map. If not, check if foreign
    # Pokémon is on a map with the same name as the current map and both maps
    # are in the same region
    if foreignMap!=$game_map.map_id
      currentRegion = pbGetCurrentRegion
      next if pbGetMetadata(foreignMap,MetadataMapPosition)[0]!=currentRegion
      currentMapName = pbGetMessage(MessageTypes::MapNames,$game_map.map_id)
      next if pbGetMessage(MessageTypes::MapNames,foreignMap)!=currentMapName
    end
    # Check whether the roaming Pokémon's category of encounter is currently possible
    next if !pbForeignMethodAllowed(foreignData[3])
    # Add this foreign Pokémon to the list of possible foreign Pokémon to encounter
    foreignChoices.push([i,species,foreignData[1],foreignData[4],0,0,
    foreignData[6],foreignData[7],foreignData[8],foreignData[9],foreignData[10],
    foreignData[11],foreignData[12],foreignData[13],foreignData[14],foreignData[15],
    foreignData[16],foreignData[17],foreignData[18],foreignData[19]#,foreignData[20]
    ])
  end
  # No encounterable roaming Pokémon were found, just have the regular encounter
  next encounter if foreignChoices.length==0
  # Pick a roaming Pokémon to encounter out of those available
  chosenForeign = foreignChoices[rand(foreignChoices.length)]
  $PokemonGlobal.foreignEncounter = chosenForeign
  $PokemonTemp.foreignIndex     = chosenForeign[0]   # Foreign Pokémon's index
  if chosenForeign[3] && chosenForeign[3]!=""
    $PokemonGlobal.nextBattleBGM = chosenForeign[3]
  end
  $PokemonTemp.forceSingleBattle = true
  next [chosenForeign[1],chosenForeign[2]]   # Species, level
})


Events.onWildBattleOverride += proc { |_sender,e|
  species = e[0]
  level   = e[1]
  handled = e[2]
  next if handled[0]!=nil
  next if !$PokemonGlobal.foreignEncounter
  next if $PokemonTemp.foreignIndex==nil
  handled[0] = pbForeignPokemonBattle(species,level)
}


def pbForeignPokemonBattle(species, level)
  # Get the foreign Pokémon to encounter; generate it based on the species and
  # level if it doesn't already exist
  idxForeign = $PokemonTemp.foreignIndex
  if !$PokemonGlobal.foreignPokemon[idxForeign] ||
     !$PokemonGlobal.foreignPokemon[idxForeign].is_a?(PokeBattle_Pokemon)
    $PokemonGlobal.foreignPokemon[idxForeign] = pbGenerateWildPokemon(species,level,false)
  end
      #Edits to Pokemon
  chosenForeign=$PokemonGlobal.foreignEncounter
  pokemon=$PokemonGlobal.foreignPokemon[idxForeign]
  #Cosmetics
    #shiny
    case chosenForeign[6]
    when true
      pokemon.makeShiny
    when false
      pokemon.makeNotShiny
  end
    #gender
    case chosenForeign[7]
     when 0
      pokemon.makeMale
      when 1
      pokemon.makeFemale
    end
      #name
    if chosenForeign[8] && chosenForeign[8]!=""
     pokemon.name=chosenForeign[8]
    end
   #OT protperties
     #name
   if chosenForeign[9] && chosenForeign[9]!=""
      pokemon.ot=chosenForeign[9]
    end
      #gender
    if chosenForeign[10] && chosenForeign[10]!=""
      pokemon.otgender=chosenForeign[10]
    end
      #ID
    if chosenForeign[11] && chosenForeign[11]!=""
      pokemon.trainerID=chosenForeign[11]
    end
      #language
    if chosenForeign[12] && chosenForeign[12]!=""
      pokemon.language=chosenForeign[12]
    end
    #battle properties
      #form
   if chosenForeign[13] && chosenForeign[13]!=""
      pokemon.form=chosenForeign[13]
    end
    #nature
    if chosenForeign[14] && chosenForeign[14]!=""
        pokemon.setNature(chosenForeign[14])
      end
      #ivs
    if chosenForeign[15] && chosenForeign[15]!=""
      newIVs=chosenForeign[15]
      case newIVs.length
        when 1
          newIV=newIVs[0]
          pokemon.iv[PBStats::HP]=newIV
          pokemon.iv[PBStats::ATTACK]=newIV
          pokemon.iv[PBStats::DEFENSE]=newIV
          pokemon.iv[PBStats::SPATK]=newIV
          pokemon.iv[PBStats::SPDEF]=newIV
          pokemon.iv[PBStats::SPEED]=newIV
      when 6
          $PokemonGlobal.foreignPokemon[idxForeign].iv[PBStats::HP]=newIVs[0]
          $PokemonGlobal.foreignPokemon[idxForeign].iv[PBStats::ATTACK]=newIVs[1]
          $PokemonGlobal.foreignPokemon[idxForeign].iv[PBStats::DEFENSE]=newIVs[2]
          $PokemonGlobal.foreignPokemon[idxForeign].iv[PBStats::SPATK]=newIVs[3]
          $PokemonGlobal.foreignPokemon[idxForeign].iv[PBStats::SPDEF]=newIVs[4]
          $PokemonGlobal.foreignPokemon[idxForeign].iv[PBStats::SPEED]=newIVs[5]
      end
    end
    if chosenForeign[16] && chosenForeign[16]!=""
      newEVs=chosenForeign[16]
      pokemon.ev[PBStats::HP]=newEVs[0]
      pokemon.ev[PBStats::ATTACK]=newEVs[1]
      pokemon.ev[PBStats::DEFENSE]=newEVs[2]
      pokemon.ev[PBStats::SPATK]=newEVs[3]
      pokemon.ev[PBStats::SPDEF]=newEVs[4]
      pokemon.ev[PBStats::SPEED]=newEVs[5]
    end
    if chosenForeign[17] && chosenForeign[17]!=""
      newMoves=chosenForeign[17]
      for i in 0...newMoves.length
        move=newMoves[i]
        pokemon.pbLearnMove(move)
      end
    end
   if chosenForeign[18] && chosenForeign[18]!=""
     pokemon.setAbility(chosenForeign[18])
    end
  #memories
  # if chosenForeign[20] && chosenForeign[20]!=""
     # $PokemonGlobal.foreignPokemon[idxForeign].memory=chosenForeign[20]
  # end
  $PokemonGlobal.foreignPokemon[idxForeign]=pokemon
  # Set some battle rules
  setBattleRule("single")
  # Perform the battle
  decision = pbWildBattleCore($PokemonGlobal.foreignPokemon[idxForeign])
  # Update Foreign Pokémon data based on result of battle
  if decision==1 || decision==4   # Defeated or caught
    $PokemonGlobal.foreignPokemon[idxForeign]       = true
    $PokemonGlobal.foreignPokemonCaught[idxForeign] = (decision==4)
  end
  $PokemonGlobal.foreignEncounter = nil
  # Used by the Poké Radar to update/break the chain
  Events.onWildBattleEnd.trigger(nil,species,level,decision)
  # Return false if the player lost or drew the battle, and true if any other result
  return (decision!=2 && decision!=5)
end

EncounterModifier.registerEncounterEnd(proc {
  $PokemonTemp.foreignIndex = nil
})

In PokeBattle_BattleCommon, find
Ruby:
Expand Collapse Copy
  def pbStorePokemon(pkmn)
    # Nickname the Pokémon (unless it's a Shadow Pokémon)
    if !pkmn.shadowPokemon?
Below that, add
Ruby:
Expand Collapse Copy
      if pkmn.isForeign?
        pbDisplayPaused(_INTL("It seems like {1} has a different OT!.",pkmn.name))
      end
Alternatively, if your game allows renaming Pokémon that aren't yours, you could change it to:
Ruby:
Expand Collapse Copy
  def pbStorePokemon(pkmn)
    # Nickname the Pokémon (unless it's a Shadow or foreign Pokémon)
    if !pkmn.shadowPokemon? && !pkmn.isForeign?
To get around the awkwardness of "Give a nickname to this Pokémon that clearly has a nickname?"

Below that, find
Ruby:
Expand Collapse Copy
pbDisplayPaused(_INTL("{1}'s data was added to the Pokédex.",pkmn.name))
Change it to:
Ruby:
Expand Collapse Copy
          pbDisplayPaused(_INTL("{1}'s data was added to the Pokédex.",PBSpecies.getName(pkmn.species)))
This makes sure the game says "Added (species)'s data to the PokeDex", rather than saying "Added (nickname)'s data to the PokeDex".

In Battle_StartAndEnd, find
Ruby:
Expand Collapse Copy
    if wildBattle?
      foeParty = pbParty(1)
      case foeParty.length
      when 1
          pbDisplayPaused(_INTL("Oh! A wild {1} appeared!",foeParty[0].name))
Change "pbDisplayPaused(_INTL("Oh! A wild {1} appeared!",foeParty[0].name))" to
Ruby:
Expand Collapse Copy
        if $PokemonTemp.foreignIndex
          pbDisplayPaused(_INTL("Huh? {1} appeared!",foeParty[0].name))
        else
          pbDisplayPaused(_INTL("Oh! A wild {1} appeared!",foeParty[0].name))
        end
This will make battles display "Huh? (Pokémon) appeared!" for Pokémon generated with this script, while regular encounters still say "Oh! A wild (Pokémon) appeared!"

Using this resource​

You'll be setting up your data at the top of the script, or in 001_ForeignData.rb if you took the plugin.

module ForeignPokemon is where you'll be setting up all the data with the Pokémon's attributes. Data will be organized in a hash. (Look at the examples if you're not familiar) You can change pretty much any attribute here. The only required values are species and level, everything else is optional. (Most of this follows the wiki article on Editing a Pokémon)
  • species - Should be a symbol. (:NAME)
  • level - Should be an integer.
  • ot - The name of a hash in module ForeignOTs. Should be a symbol. (:Name - capitalization can be what you want here, as long as it matches what you put in ForeignOTs)
  • name - Should be a string. ("Name")
  • gender - 0 male, 1 female. Leave blank to randomize.
  • shiny - true for guaranteed shiny, false for shiny-lock the Pokémon. Leave blank for standard odds.
  • happiness, cool, beauty, cute, smart, tough, sheen - Sets the stat directly. Should be an integer.
  • item - Should be a symbol
  • pokerus - Can be true to guarantee Pokérus or a number 1-16 to guarantee a particular strain. No option to Pokérus-lock.
  • nature - should be a symbol
  • form - should be an integer
  • ability_index - should be an integer.
  • ability - should be a symbol. Abilities set by changing the ability value directly only last until a species/form change, according to the wiki, so you should only do this for a Pokémon that can't evolve or change form. (The main appeal is that it could be any ability you want, not just the usual species abilities)
  • new_moves - Should be an array. ([:MOVE,:MOVE]) The Pokémon will learn these moves in addition to any level-up moves it would normally know.
  • moves - Should be an array. ([:MOVE,:MOVE]) The Pokémon will only know these moves - if you give an array with only two moves, the Pokémon will only have two moves.
  • perf_ivs - A number 1 to 6. Guarantees at least that many perfect IVs for the Pokémon, but not in any particular stat.
  • ivs - Used to set all IVs directly. Should be a hash like this:
    Ruby:
    Expand Collapse Copy
    {:HP => 30,
    :ATTACK => 30,
    :DEFENSE => 30,
    :SPECIAL_ATTACK => 30,
    :SPECIAL_DEFENSE => 30,
    :SPEED => 30}
  • evs - Used to set all EVs directly. Should be a hash just like IVs.
  • ribbons - Should be an array. ([:RIBBON,:RIBBON]). The Pokémon will have these ribbons attached in the same order.
  • memory is to be used with my Pokémon Memories script. Should be a string. (In quotes)
Once the Pokémon is generated, the same Pokémon has a chance of being encountered until it's been caught. Damage and status inflicted will carry over between battles. If the Pokémon is knocked out, it'll be revived. (IVs, shininess, etc. will be carried over.)

module ForeignOTs has hashes for the OT's information.
  • name - Should be a string. ("name")
  • gender - 0 male, 1 female, 2 neutral
  • id_number - Should be an integer. Limit is 65535. Don't worry about converting it so it's displayed right, the script takes care of that.
  • language - Based on the values in pbGetLanguage. See Editing a Pokémon to see what they usually are.
The main reason for this being a separate module is just to make it easier to have multiple Pokémon with the same OT.

The array FOREIGN_SPECIES is where the actual encounter info will be set up. Unlike the others, it's in an array, similar to how roaming Pokémon are set up. Everything has to be in the right order, and you can't skip values. (But you can put nil in some cases.)
[poke,switch,maps,enctype,theme,chance]
Where...
  • poke is the name of a Pokémon in module ForeignPokemon. Should be a symbol. (:Name)
  • switch is the number of a Global Switch that must be turned on for the Pokémon to be encountered. Should be an integer. (Don't put 0s in front of it - switch 1 is just 1, not 001)
  • maps is an array of map IDs the Pokémon can be encountered on. Should be an array. ([1,2,3])
  • enctype is a number corresponding to an encounter type. It's the same list that's used for Roaming Pokemon.
    • 0=any
    • 1=grass/walking in cave
    • 2=surfing
    • 3=fishing
    • 4=surfing/fishing
  • theme is the filename of a song in Audio/BGM to be played during the encounter. Should be a string. ("name") Can be left as nil to just play the standard theme.
  • chance is the % chance that this encounter overrides a normal encounter. Should be an integer between 1 and 100, inclusive.

I've got examples of all of N's Pokémon from canon in here, as well as N himself and an array in FOREIGN_SPECIES I was using to playtest. (N's Purrloin appearing in the Safari Zone-outside map when switch 2 was on)

To add a Pokémon to this, just find this section at the top:
Ruby:
Expand Collapse Copy
foreignSpecies = [
[:PIKACHU, 10, 8, 1, nil, 66, true, 0, "Pika", "Red", 0, 1, 1, 0, :HASTY,
[1,2,3,4,5,6], nil, [:TACKLE, :SPARK], 0],
[:BULBASAUR, 10, 8, 1, nil, 66, true, 0, "Saur", "Red", 0, 55554, 1, 0, :TIMID,
[31], nil, [:TACKLE, :FRENZYPLANT], 0]
]
You're going to create a new entry similar to what I've put here! There's a lot to these examples, but you actually only need the first six entries, the rest are all optional! Set any of them to nil, and it'll be generated the same as any other wild Pokémon!

0-5- Encountering the Pokémon
  • 0 is the species, written as :SPECIES
  • 1 is the level the Pokémon will be when encountered
  • 2 is the number of the Global Switch that needs to be turned on for this encounter to happen
  • 3 is the encounter type the Pokémon will spawn in. It's the same as it is for Roaming Pokémon :
    • 0 - Walking in grass, walking in caves or surfing.
    • 1 - Walking in grass or walking in caves.
    • 2 - Surfing.
    • 3 - Fishing.
    • 4 - Surfing or fishing.
  • 4 is a filename for BGM for this encounter, formatted as "name", if you'd like to change it from the standard encounters on the map
  • 5 is the ID number of the map you can encounter this Pokémon on
6-8- Cosmetic info
  • 6 is true if the Pokemon is a guaranteed Shiny and false if it's Shiny-locked. Set it to nil if you want the regular shiny chance.
  • 7 is the gender of the Pokémon - 0 for male, 1 for female. You can leave it as nil to have the regular gender chance, and genderless Pokémon should also have it set to nil, because it might cause an error otherwise.
  • 8 is the nickname of the Pokemon, formatted as "Name". Leave as nil to have the regular species name.
9-12- OT's info

  • 9 is the name of the OT, written as "Name". Leave as nil to let the player be defined as the OT for this Pokémon. If you do, make sure anything else you use in this section is nil, too.
  • 10 is the gender of the OT- 0 for male, 1 for female, 2 for other.
  • 11 is the ID number of the OT. As far as I can tell, you can write in any number from 1 to 55555 and have it display those characters. Any higher and I think it calculates it differently somehow, so the digits won't display what you typed in.
  • 12 is the OT's language.
    • 0 = Unknown
    • 1 = Japanese
    • 2 = English
    • 3 = French
    • 4 = Italian
    • 5 = German
    • 7 = Spanish
    • 8 = Korean

13-18 Battle Properties
  • 13 is the number of this Pokémon's form.
  • 14 is the nature of the Pokémon, written as :NATURE
  • 15 is this Pokémon's IVs. You can either make this an array of six numbers, like [1,2,3,4,5,6], or an array of one number, like [1]. If it's just one number, then all six IVs will be set to that one number.
  • 16 is this Pokémon's EVs. This one has to be an array of six numbers, because I didn't see the point in setting EVs to the same thing when you could only go up to 42 doing it that way.
  • 17 is this Pokémon's moves. You have to set this an array, but it can be as long or short as you like- [:MOVE] to [:MOVE,:MOVE,:MOVE,:MOVE]. This script uses pbLearnMove to add these in, so it'll bump off the top move if the Pokémon has four moves, but the Pokémon will still have any remaining natural moves in its learnset.
  • 18- The number of this Pokémon's ability slot.

Other
19 is currently reserved for items, but I unfortunately don't have it working yet.

20 is designed to work with my Pokémon Memories script! It's commented out so this resource doesn't cause any crashes on its own, but if you'd like to use them together, just remove the #s you see blocking it, and it should be pretty simply to use! Just use "text" to write the memory!

Probability
I had considered making probability an element of the Pokémon, but I couldn't get that to run smoothly. Right now, probability is defined by this line here:
Ruby:
Expand Collapse Copy
  next encounter if rand(100)<50   # 50% chance of encountering a foreign Pokémon

That means that the game has a 50% chance of checking for a foreign Pokémon with every encounter.

If you'd like this to change depending on what Pokémon is being found, just add some conditionals around it! For example, if I want one specific Pokémon to be rarer than the others, I'd just change this to:

Ruby:
Expand Collapse Copy
if $game_switches[switch]==true && $game_map.map_id==number
  next encounter if rand(100)<90   # 10% chance of encountering a foreign Pokémon
else
  next encounter if rand(100)<50   # 50% chance of encountering a foreign Pokémon
end

And just make "switch" and "number" match up with the ones for my Pokémon!

Ideas for this resource​

  • Naturally, reformed villain's Pokémon out in the wild would be fun to catch! But it doesn't have to be villains- why not give some iconic characters a released Pokémon for the player to find? (The TCG's Owner's Pokémon could be a good source of inspiration!)
  • But then, who says it has to be a character the player's met at all? There's a lot of scenarios where a player could find a long-gone trainer's Pokémon- what about an ancient king's team, a mad scientist's experiments gone rogue, or a fallen adventurer's Pokémon?
  • You don't even have to have an OT for these Pokémon, though, you could just make some special Pokémon for the player to find! A guaranteed shiny, high IVs, egg moves, maybe even a special species or form you can only catch once!
  • This can also be used to replicate Ultra Beast encounters from the postgame Looker missions!

Future Goals​

  • Create the animation that plays for N's Pokémon
  • Double-check and make sure nothing tricky can go on with setting EVs.
  • Create a limit to the length of memories.
  • Figure out a way to combine this with phenomena, so it's more clear to the player that there's special Pokémon here.
Credits
Credits to TechSkylander1518, please! And credit to the original devs of Pokémon Essentials, because the code for Roaming Pokémon was used as a base for a lot of this, and ThatWelshOne_, because the hashes were based on how he organized his quest system!
Author
TechSkylander1518
Downloads
526
Views
5,275
First release
Last update

Ratings

0.00 star(s) 0 ratings

More resources from TechSkylander1518

Latest updates

  1. v20 Update

    Got a nice overhaul with this update! Data is now organized in hashes, so more values are...
Back
Top