• Hi, Guest!
    Some images might be missing as we move away from using embedded images, sorry for the mess!
    From now on, you'll be required to use a third party to host images. You can learn how to add images here, and if your thread is missing images you can request them here.
    Do not use Discord to host any images you post, these links expire quickly!
Monotype Challenge

v19 Monotype Challenge 2022-03-09

This resource pertains to version 19 of Pokémon Essentials.
Pokémon Essentials Version
v19.1 ➖
1646809461894.png

ezgif-5-6c9677aa40.gif



Code
Ruby:
module Settings
  # Players can use Pokemon that are not yet the required type,
  #but can evolve into it
  PREVOS_INCLUDED = true
end

class PokemonGlobalMetadata
  attr_accessor :monotype
end


#Sets the given variable to the ID number of the type used in the run
def pbSetMonoVariables(varnum)
  $game_variables[varnum] = GameData::Type.get($PokemonGlobal.monotype).id_number
end

#Returns true if a monotype run is active
def pbMonoActive?
  return true if $PokemonGlobal.monotype
end

#Turns off monotype run
def pbEndMono
  $PokemonGlobal.monotype = nil
end

def pbChooseMono
  choices = []
  GameData::Type.each do |type|
    choices.push(type.real_name) if !type.pseudo_type
  end
  choices.push(_INTL("Cancel"))
  choice = pbMessage(_INTL("Which type would you like to use?"),choices,choices.length)
  choice = choices[choice]
  if choice != "Cancel"
    choice = choice.upcase.to_sym
    $PokemonGlobal.monotype = choice
  end
end


module GameData
  class Species
    #Adapted from Pokemon class
    # @return [Array<Symbol>] an array of this Pokémon's types
    def types
      ret = [self.type1]
      ret.push(self.type2) if self.type2 && self.type2 != self.type1
      return ret
    end
 
    # @param type [Symbol, String, Integer] type to check
    # @return [Boolean] whether this Pokémon has the specified type
    def hasType?(type)
      type = GameData::Type.get(type).id
      return self.types.include?(type)
    end
    #New utilities:
    def can_evolve_into_usable?(type,exclude_invalid = true)
      @evolutions.each do |evo|
        next if evo[3]   # Is the prevolution
        next if evo[1] == :None && exclude_invalid
        species = GameData::Species.get(evo[0])
        return true if species.usable_monotype?(type)
      end
      return false
    end
    
  
    def usable_monotype?(type=$PokemonGlobal.monotype)
      return true if type==nil
      return true if self.hasType?(type)
      return true if self.can_evolve_into_usable?(type) && Settings::PREVOS_INCLUDED
      return false
    end
  
  end
end
  

class Pokemon
 
  # The core method that performs evolution checks. Needs a block given to it,
  # which will provide either a GameData::Species ID (the species to evolve
  # into) or nil (keep checking).
  # @return [Symbol, nil] the ID of the species to evolve into
  def check_evolution_internal
    return nil if egg? || shadowPokemon?
    return nil if hasItem?(:EVERSTONE)
    return nil if hasAbility?(:BATTLEBOND)
    species_data.get_evolutions(true).each do |evo|   # [new_species, method, parameter, boolean]
      next if evo[3]   # Prevolution
      species = GameData::Species.get(evo[0])
      next if !species.usable_monotype?
      ret = yield self, evo[0], evo[1], evo[2]   # pkmn, new_species, method, parameter
      return ret if ret
    end
    return nil
  end
 
  def usable_monotype?(type=$PokemonGlobal.monotype)
    return GameData::Species.get_species_form(self.species,self.form).usable_monotype?
  end

end

#===============================================================================
# Giving Pokémon to the player (will send to storage if party is full)
#===============================================================================
def pbAddPokemon(pkmn, level = 1, see_form = true)
  return false if !pkmn
  if pbBoxesFull?
    pbMessage(_INTL("There's no more room for Pokémon!\1"))
    pbMessage(_INTL("The Pokémon Boxes are full and can't accept any more!"))
    return false
  end
  pkmn = Pokemon.new(pkmn, level) if !pkmn.is_a?(Pokemon)
  if !pkmn.usable_monotype?
    type = GameData::Type.get($PokemonGlobal.monotype).real_name
    pbMessage(_INTL("#{pkmn.speciesName} can't be used in a monotype #{type} challenge."))
    return false
  end
  species_name = pkmn.speciesName
  pbMessage(_INTL("{1} obtained {2}!\\me[Pkmn get]\\wtnp[80]\1", $Trainer.name, species_name))
  pbNicknameAndStore(pkmn)
  $Trainer.pokedex.register(pkmn) if see_form
  return true
end

def pbAddPokemonSilent(pkmn, level = 1, see_form = true)
  return false if !pkmn || pbBoxesFull?
  pkmn = Pokemon.new(pkmn, level) if !pkmn.is_a?(Pokemon)
  return false if !pkmn.usable_monotype?
  $Trainer.pokedex.register(pkmn) if see_form
  $Trainer.pokedex.set_owned(pkmn.species)
  pkmn.record_first_moves
  if $Trainer.party_full?
    $PokemonStorage.pbStoreCaught(pkmn)
  else
    $Trainer.party[$Trainer.party.length] = pkmn
  end
  return true
end

#===============================================================================
# Giving Pokémon/eggs to the player (can only add to party)
#===============================================================================
def pbAddToParty(pkmn, level = 1, see_form = true)
  return false if !pkmn || $Trainer.party_full?
  pkmn = Pokemon.new(pkmn, level) if !pkmn.is_a?(Pokemon)
  if !pkmn.usable_monotype?
    type = GameData::Type.get($PokemonGlobal.monotype).real_name
    pbMessage(_INTL("#{pkmn.speciesName} can't be used in a monotype #{type} challenge."))
    return false
  end
  species_name = pkmn.speciesName
  pbMessage(_INTL("{1} obtained {2}!\\me[Pkmn get]\\wtnp[80]\1", $Trainer.name, species_name))
  pbNicknameAndStore(pkmn)
  $Trainer.pokedex.register(pkmn) if see_form
  return true
end

def pbAddToPartySilent(pkmn, level = nil, see_form = true)
  return false if !pkmn || $Trainer.party_full?
  pkmn = Pokemon.new(pkmn, level) if !pkmn.is_a?(Pokemon)
  return false if !pkmn.usable_monotype?
  $Trainer.pokedex.register(pkmn) if see_form
  $Trainer.pokedex.set_owned(pkmn.species)
  pkmn.record_first_moves
  $Trainer.party[$Trainer.party.length] = pkmn
  return true
end

def pbAddForeignPokemon(pkmn, level = 1, owner_name = nil, nickname = nil, owner_gender = 0, see_form = true)
  return false if !pkmn || $Trainer.party_full? || !pkmn.usable_monotype?
  pkmn = Pokemon.new(pkmn, level) if !pkmn.is_a?(Pokemon)
  if !pkmn.usable_monotype?
    type = GameData::Type.get($PokemonGlobal.monotype).real_name
    pbMessage(_INTL("#{pkmn.speciesName} can't be used in a monotype #{type} challenge."))
    return false
  end
  # Set original trainer to a foreign one
  pkmn.owner = Pokemon::Owner.new_foreign(owner_name || "", owner_gender)
  # Set nickname
  pkmn.name = nickname[0, Pokemon::MAX_NAME_SIZE] if !nil_or_empty?(nickname)
  # Recalculate stats
  pkmn.calc_stats
  if owner_name
    pbMessage(_INTL("\\me[Pkmn get]{1} received a Pokémon from {2}.\1", $Trainer.name, owner_name))
  else
    pbMessage(_INTL("\\me[Pkmn get]{1} received a Pokémon.\1", $Trainer.name))
  end
  pbStorePokemon(pkmn)
  $Trainer.pokedex.register(pkmn) if see_form
  $Trainer.pokedex.set_owned(pkmn.species)
  return true
end

def pbGenerateEgg(pkmn, text = "")
  return false if !pkmn || $Trainer.party_full?
  pkmn = Pokemon.new(pkmn, Settings::EGG_LEVEL) if !pkmn.is_a?(Pokemon)
  if !pkmn.usable_monotype?
    type = GameData::Type.get($PokemonGlobal.monotype).real_name
    pbMessage(_INTL("#{pkmn.speciesName} can't be used in a monotype #{type} challenge."))
    return false
  end
  # Set egg's details
  pkmn.name           = _INTL("Egg")
  pkmn.steps_to_hatch = pkmn.species_data.hatch_steps
  pkmn.obtain_text    = text
  pkmn.calc_stats
  # Add egg to party
  $Trainer.party[$Trainer.party.length] = pkmn
  return true
end
alias pbAddEgg pbGenerateEgg
alias pbGenEgg pbGenerateEgg


def pbStartTrade(pokemonIndex,newpoke,nickname,trainerName,trainerGender=0)
  myPokemon = $Trainer.party[pokemonIndex]
  opponent = NPCTrainer.new(trainerName,trainerGender)
  opponent.id = $Trainer.make_foreign_ID
  yourPokemon = nil
  resetmoves = true
  if newpoke.is_a?(Pokemon)
    newpoke.owner = Pokemon::Owner.new_from_trainer(opponent)
    yourPokemon = newpoke
    resetmoves = false
  else
    species_data = GameData::Species.try_get(newpoke)
    raise _INTL("Species does not exist ({1}).", newpoke) if !species_data
    yourPokemon = Pokemon.new(species_data.id, myPokemon.level, opponent)
  end
  if !yourPokemon.usable_monotype?
    pbMessage(_INTL("#{pkmn.speciesName} can't be used in a monotype #{type} challenge."))
    return false
  end
  yourPokemon.name          = nickname
  yourPokemon.obtain_method = 2   # traded
  yourPokemon.reset_moves if resetmoves
  yourPokemon.record_first_moves
  $Trainer.pokedex.register(yourPokemon)
  $Trainer.pokedex.set_owned(yourPokemon.species)
  pbFadeOutInWithMusic {
    evo = PokemonTrade_Scene.new
    evo.pbStartScreen(myPokemon,yourPokemon,$Trainer.name,opponent.name)
    evo.pbTrade
    evo.pbEndScreen
  }
  $Trainer.party[pokemonIndex] = yourPokemon
end

There is, unfortunately, one additional step to take- find this section in Item_BattleEffects -
Ruby:
    if battler.semiInvulnerable?
      scene.pbDisplay(_INTL("It's no good! It's impossible to aim at a Pokémon that's not in sight!")) if showMessages
      next false
    end
Right below that, add this:
Ruby:
    species = GameData::Species.get(battler.species)
    if !species.usable_monotype?
      type = GameData::Type.get($PokemonGlobal.monotype).real_name
      scene.pbDisplay(_INTL("You can't catch this Pokémon in a monotype #{type} challenge!"),) if showMessages
      next false
    end
(I had attempted to make this plug-and-play, but I can only seem to add another handler rather than overwrite the current one- I'll get around to fixing that later)
Using this Script
The setting PREVOS_INCLUDED at the top is to allow for Pokemon that gain or change types on evolving. For example, in a Fire run, you would still be able to use Eevee, because it can evolve into Flareon.

There's a fair number of calls here-
  • pbChooseMono has the player choose the type to use, or none at all. (The list is pulled from the Types database, you shouldn't have to do anything to adjust it for new types you add)
  • pbMonoActive? returns true if monotype rules are in place. You could use this as a global switch by naming a switch s:pbMonoActive?
  • pbSetMonoVariables(x) sets game variable x to the ID number of the type being used- 0 for Normal, 1 for Fighting, and so on. (Unfortunately, since variables are 0 by default, you'll have to either combine it with pbMonoActive?- or, if you don't think it'll be too confusing, add 1 to the variable again and just work with the values offset)
  • pbEndMono removes the challenge rules. It doesn't do anything else- be sure to give any prizes or anything before you take them off!
  • The script call $PokemonGlobal.monotype will return the internal name of the type being used.

While a challenge run is in play:
  • If a Pokemon isn't eligible for the run (has the required type or can evolve into the required type), the player can't receive it through gifts (egg or standard), through catching (will get a message like when you try to catch in doubles), or through trading.
  • Every script that gives a Pokemon will return false if it can't be used in the monotype challenge. Be sure to avoid any potential softlocks for the player!
  • Pokemon can only evolve if their evolution is eligible for the run. This should, to my knowledge, account for all stages in a chain- for example, you can obtain Caterpie in a mono Flying run, because it will evolve into Metapod, which will evolve into Butterfree.


Future Goals
  • Get a nicer UI rather than just selecting from text
  • Figure out the deal with the Poke Ball handler so I can make this completely plug-n-play
Credits
Credits to TechSkylander1518, please!
Author
TechSkylander1518
Views
1,884
First release
Last update
Rating
5.00 star(s) 2 ratings

More resources from TechSkylander1518

Latest reviews

This is actually how my brothers and me played our first playthrough of generation 2, we each choose a type and could only use that type or a pokemon of a different type if it had at least two moves of the type you had chosen. That last bit is a little different but it would be cool to see it as a possible option in the future.
I'm balancing most of the early game encounters in my game around monotype runs, so this is super cool and I'll definitely use this!
Back
Top