• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

[Essentials Tutorial] More Multiple Form Property Changes + Conditional Registering

1,682
Posts
8
Years
    • Seen today
    So you are adding forms to a pokemon and you think, I wish I could change more of these properties, besides types, moves, etc. Well this is a tutorial walking you through this process.
    Spoiler: More Properties
    Firstly, you'll need to decide which property you wish to change. There's a slightly limited list, as you can only use properties that are saved in the dexdata.dat or in MessageTypes. The spoiler below shows all of the fields in pokemon.txt that work with this method, though not all are recommended to use. Checkmarks () are already in default v16.2.
    Spoiler:

    I am going to add one of these using dexdata.dat, because the MessageTypes are already set up, with exception to Name and FormNames, which aren't really useful to have set up at all. I'm going to use Rareness. They're all set up nearly the same though.
    Firstly, we need to make a method that gets the rareness from the dexdata.dat file and put it in class PokeBattle_Pokemon. You can take a look at how type1 is done as it is the same way, but here is the code nonetheless.
    Code:
    def rareness
      dexdata=pbOpenDexData
      pbDexDataOffset(dexdata,species,16)
      ret=dexdata.fgetb # Get rareness from dexdata file
      dexdata.close
      return ret
    end
    Now you may be wondering, where did I get 16 from for the offset? Take a gander at the Compiler section, in def pbCompilePokemonData.
    There are two hashmaps (If you don't know what a hashmap is, you should read up on a Ruby tutorial) that follow this format:
    Code:
    "Key from PBS"=>[Offset,"various codes not relevant to this tutorial"]
    If the offset is 0, it's not saved in dexdata.dat.
    Now back to the meat of this. We need to alias this method to so we can call our Multiple Forms shenanigans. This follows the same pattern as the other things in Pokemon_MultipleForms.
    Code:
     alias __mf_rareness rareness
    Now we need to make our new method, that would call our Multiple Forms function. You can look to the others if you want to make it yourself, but here's the code.
    Code:
    def rareness
      v=MultipleForms.call("rareness",self)
      return v if v!=nil
      return self.__mf_rareness
    end
    Now we can add this to our pokemon, the way you would do any other function. Remember that you still need to fit the restrictions that are set on the PBS file. This example only lets you catch a pokemon with a form of zero, so you could make a battle with a wild pokemon that has crazy stats without the player being able to catch them
    Code:
    MultipleForms.register(:POKEMON,{
    "rareness"=>proc{|pokemon|
       next if pokemon.form == 0
       next 0
    }
    })
    Last step, we replace all instances where the dexdata is directly accessed, with our method instead. Rareness is used three times, in PokeBattle_Battle, PokeBattle_SafariZone, and PBattle_BugContest.
    In def pbThrowPokeBall in PokeBattle_Battle, we would change this:
    Code:
          pokemon=battler.pokemon
          species=pokemon.species
          if $DEBUG && Input.press?(Input::CTRL)
            shakes=4
          else
            if !rareness
              dexdata=pbOpenDexData
              pbDexDataOffset(dexdata,species,16)
              rareness=dexdata.fgetb # Get rareness from dexdata file
              dexdata.close
            end
    to this:
    Code:
          pokemon=battler.pokemon
          if $DEBUG && Input.press?(Input::CTRL)
            shakes=4
          else
            if !rareness
              rareness=pokemon.rareness
            end
    and in def pbStartBattle in PokeBattle_SafariZone, we would change this:
    Code:
          dexdata=pbOpenDexData
          pbDexDataOffset(dexdata,wildpoke.species,16)
          rareness=dexdata.fgetb # Get rareness from dexdata file
          dexdata.close
    to this:
    Code:
          rareness=wildpoke.rareness
    and finally in def pbBugContestScore in pbBugContestScore, we would change this:
    Code:
      dexdata=pbOpenDexData
      pbDexDataOffset(dexdata,pokemon.species,16)
      rareness=dexdata.fgetb
      dexdata.close
    to this:
    Code:
      rareness=pokemon.rareness
    Now you can have your crazy uncatchable pokemon, or easier, whatever you please.

    Spoiler: Conditional Registering
    But what if you have many pokemon with the same edits? Or perhaps you have many pokemon that you want to have that effect, but don't want to do the edits to all of them?
    That leads me to my next point, Conditional Registering, where we don't have just one specific species that the form change can apply to but many. You are probably already familiar with the MultipleForms.register(:POKEMON,{Functions}). Conditional Registering is almost the same, with two key differences. 1) The method is MultipleForms.registerIf, and 2) instead of the internal name of the pokemon, we need to make a procedure ( proc ) that takes the species and returns true or false.
    Using our rareness, we can make it so that say, you can't catch any pokemon if there a switch is on.
    Code:
    MultipleForms.registerIf(proc{|species| $game_switches[100]},{
    "rareness"=>proc{|pokemon|
       next 0
    }
    })
    Conditional Registering is reevaluated all the time, it's not a one time thing like how normal registering works. (PS, the proc doesn't need a return if it's just one line, because Ruby will always return the result of the last statement, in both methods and procs.) Though MultipleForms doesn't use it by default, you can find examples of it in PItem_ItemEffects, used to make all pokeballs throwable without any definitions beyond the fact that it is set up as a Pokeball in the PBS files.
    Code:
    ItemHandlers::BattleUseOnBattler.addIf(proc{|item|
                    pbIsPokeBall?(item)},proc{|item,battler,scene|  # Any Poké Ball
       battle=battler.battle
       if !battler.pbOpposing1.isFainted? && !battler.pbOpposing2.isFainted?
         if !pbIsSnagBall?(item)
           scene.pbDisplay(_INTL("It's no good! It's impossible to aim when there are two Pokémon!"))
           return false
         end
       end
       if battle.pbPlayer.party.length>=6 && $PokemonStorage.full?
         scene.pbDisplay(_INTL("There is no room left in the PC!"))
         return false
       end
       return true
    })

    Spoiler: Battler Offsets
    T40wxnQ.png
    And I'm back, with battler offsets, oh yeah!
    First, we need to make our own version of getBattleSpriteMetricOffset, which takes the pokemon, it's index, and the metrics file, which we just pass on, and our own version of showShadow?, which just checks if we need to show the shadow, and we pass on the species.
    Mind you that alias can only be used after the original has been defined, so this bit of code must either go directly under the original methods, or in any script section under Pokemon_Sprites
    Code:
    alias __mf_getBattleSpriteMetricOffset getBattleSpriteMetricOffset
    
    def getBattleSpriteMetricOffset(pkmn,index,metrics=nil)
        ret=0
        v=MultipleForms.call("getMetricOffset",pkmn)
        if v!=nil
          if index==1 || index==3   # Foe Pokémon
            ret+=(v[1] || 0)*2 # enemy Y
            ret-=(v[2] || 0)*2 # altitude
          else                      # Player's Pokémon
            ret+=(v[0] || 0)*2
          end
          return ret
        end
        return self.__mf_getBattleSpriteMetricOffset(pkmn.species,index,metrics)
    end
    
    alias __mf_showShadow? showShadow?
    def showShadow?(pkmn)
      v=MultipleForms.call("getMetricOffset",pkmn)
      return v[2]>0 if v!=nil
      return __mf_showShadow?(pkmn.species)
    end
    But the original code only took in the species, so we need to find all of the usages of the method that calls getBattleSpriteMetricOffset, adjustBattleSpriteY, and make those lines pass the pokemon itself. It's not used in very many places, relative to some other methods. There are three different ways this method is called. The first like this.
    Code:
    @endspritey=adjustBattleSpriteY(sprite,pkmn.species,pkmn.index)
    All you need to do is change .species to .pokemon.
    Just above it is
    Code:
    @endspritey=adjustBattleSpriteY(sprite,@illusionpoke.species,pkmn.index)
    We delete the .species here, because @illusionpoke is a pokemon, not a battler. We also need to scroll down until we find @shadowVisible=showShadow?(@illusionpoke.species), and do the same thing, as well and change the showShadow? just above it, and change .species to .pokemon, for the same reason (pkmn is battler, @illusionpoke is pokemon)
    The second is a bit odder, like so (this is just an example, look for all of them.)
    Code:
    @sprites["pokemon1"].y+=adjustBattleSpriteY(@sprites["pokemon1"],species,1)
    species is defined a bit higher, looking like this
    Code:
    [email protected][0].species
    Like before, you just need to delete .species. This has the added bonus of correcting the showShadow? just under it.
    In pbChangeSpecies
    Code:
    adjustBattleSpriteY(pkmn,attacker.species,attacker.index)
    We just change the .species to .pokemon and we need to look a bit below to find showShadow? and we change species there to attacker.pokemon
    Finally, in pbChangePokemon, we must change the showShadow?(pokemon.species) to just showShadow?(pokemon), everything else is fine, really.
    The reason we have to change .species to .pokemon instead of just deleting it is because these are instances of the PokeBattle_Battler class, which has different methods and slightly different variables (doesn't have EVs or Happiness for example). While the MultipleForms functions don't really care if it's a battler or a pokemon, as long as it has the same methods, it will horifically die and give you an error log if it uses a method one has but the other doesn't. (This is another one of Ruby and a couple of other languages, Duck Typing.) We can tell which is which by looking at what calls these methods that are involved here, or which instance variable are involved. Error logs help too.

    Now that we finally have all the work done, we can add it as a Form Property. Unlike last time, where we had to make a bunch of methods this is already done, as we will never need to get the battler offsets directly. This is mine, and I called the function "getMetricOffset", which you can see in the methods above. You can place this in with the other MultipleForms stuff. (My Sky Forme Shaymin from the picture uses [-50,0,20])
    Code:
    MultipleForms.register(:POKEMON,{
    "getMetricOffset"=>proc{|pokemon|
       next if pokemon.form==0
       next [PlayerY,EnemyY,EnemyAltitude]
    }
    })
    Just like last time, Conditional Registering still works here. I don't know what you'd do with it though, make sky battles look like the pokemon is flying?


    I think I've gone over everything, though I would love to come back to this to explain how to modify the battler offsets and altitude if and once I find it.

    27/06/2017: Added Battler Offsets (Sweet!)
    Credits to GT-Baka, whose question made me make this tutorial.
     
    Last edited:
    277
    Posts
    15
    Years
  • This is really amazing!
    Thank you so much for making a more in-depth tutorial. Not only does this add extra functionality to alternate forms, but gives them a bit of a personal touch as well (using Vulpix as an example, the Alolan form can be listed in the dex as white with it's own habitat).

    Though to be honest all of the credit should be all yours. All I did was make a post asking why my undefined code with syntax errors and misspelled words wasn't working.
     
    1,682
    Posts
    8
    Years
    • Seen today
    Updated the main post to be more organized and include changing Battler Offsets (so Player Y, Enemy Y, Enemy Altitude)
     
    8
    Posts
    8
    Years
    • Seen Aug 29, 2018
    how would i do egg moves or evolutions
     
    Last edited:
    1,682
    Posts
    8
    Years
    • Seen today
    how would i do egg moves or evolutions

    You are going to hate my answer but in v16.2, you can't edit either of those things using this method. (And I barely understand how both of those attributes are saved.)
    But, if you upgrade to v17+, It comes with the ability to make all of these changes right out of the box, using the pokemonforms.txt PBS file.
     
    8
    Posts
    8
    Years
    • Seen Aug 29, 2018
    one could define custom evolutions in the scripts and do this(im showing vulpix)
    PBS:
    Evolutions=NINETALES,ItemNForm,FIRESTONE,NINETALES,ItemAForm,ICESTONE
    Script:
    def pbMiniCheckEvolutionItem(pokemon,evonib,level,poke,item)
    # Checks for when an item is used on the Pokémon (e.g. an evolution stone)
    case evonib
    when PBEvolution::Item
    return poke if level==item
    when PBEvolution::ItemMale
    return poke if level==item && pokemon.isMale?
    when PBEvolution::ItemFemale
    return poke if level==item && pokemon.isFemale?
    when PBEvolution::ItemNForm
    return poke if level==item && pokemon.form==0
    when PBEvolution::ItemAForm
    return poke if level==item && pokemon.form==1
    when PBEvolution::ItemNtoA
    if level==item
    poke.form=1
    return poke
    end
    end
    return -1
    end

    this can cover evolutions
     
    Last edited:
    8
    Posts
    8
    Years
    • Seen Aug 29, 2018
    for egg moves i have written but not tested this code:
    Spoiler:

    the code for vulpix:
    Spoiler:
     
    Last edited:
    1,682
    Posts
    8
    Years
    • Seen today
    Hi! :D

    So, i want to make your Battler Offsets. Im usin v17.2 and when open Pokedex, cracks (PS im usin Advanced Pokedex by FL: ([URL="Advanced Pokedex by FL"]https://www.pokecommunity.com/showthread.php?t=315535[/URL]

    How can i fix?
    Just delete '.species' and change to '.pokemon' etc etc etc too there?

    Ty Ty :)

    This tutorial has been obsoleted by v17, which add the pokemonforms.txt PBS file, which allows you to edit almost any property of the base pokemon form, including battler offsets.
    Do not use this tutorial with v17, it was only made with v16 in mind.
     

    WolfPP

    Spriter/ Pixel Artist
    1,309
    Posts
    5
    Years
  • This tutorial has been obsoleted by v17, which add the pokemonforms.txt PBS file, which allows you to edit almost any property of the base pokemon form, including battler offsets.
    Do not use this tutorial with v17, it was only made with v16 in mind.

    Oh ok! Thanks :DD

    Soooooo, can you teach how we can resize the sprite? On Pokedex, Battle Scene and Summary Screen?

    Ty ty!
     
    1,682
    Posts
    8
    Years
    • Seen today
    Oh ok! Thanks :DD

    Soooooo, can you teach how we can resize the sprite? On Pokedex, Battle Scene and Summary Screen?

    Ty ty!

    If you need to resize the sprite for all of the areas that the sprite could possibly appear, why don't you just resize the original file.
    This is kind of outside of the scope of this tutorial, you are probably better off asking in the Essentials Question forum.
    the bitmap method stretch_blt would seem relevent though.
     
    Back
    Top