• 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,677
Posts
8
Years
  • Age 23
  • 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,677
Posts
8
Years
  • Age 23
  • 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
  • Age 24
  • Seen Aug 29, 2018
how would i do egg moves or evolutions
 
Last edited:
1,677
Posts
8
Years
  • Age 23
  • 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
  • Age 24
  • 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
  • Age 24
  • Seen Aug 29, 2018
for egg moves i have written but not tested this code:
Spoiler:

the code for vulpix:
Spoiler:
 
Last edited:
1,677
Posts
8
Years
  • Age 23
  • 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,677
Posts
8
Years
  • Age 23
  • 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