- Pokémon Essentials Version
- v16.2 ➖
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.
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. X's are already in default v16.2.
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.
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:
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.
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.
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
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:
to this:
and in def pbStartBattle in PokeBattle_SafariZone, we would change this:
to this:
and finally in def pbBugContestScore in pbBugContestScore, we would change this:
to this:
Now you can have your crazy uncatchable pokemon, or easier, whatever you please.
[/tab]
[tab=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.
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.
Battler Offsets
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
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.
All you need to do is change .species to .pokemon.
Just above it is
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.)
species is defined a bit higher, looking like this
Like before, you just need to delete .species. This has the added bonus of correcting the showShadow? just under it.
In pbChangeSpecies
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])
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?
[/tab]
[/tabs]
I think I've gone over everything, though I would love to come back to this to explain how to modify the battler offsets if and once I find it.
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. X's are already in default v16.2.
Required Properties
"Name"
"Kind" X
"Pokedex" X
"Moves" X
"Color"
"Type1" X
"BaseStats" X
"Rareness"
"GenderRate"
"Happiness"
"GrowthRate"
"StepsToHatch"
"EffortPoints" X
"Compatibility"
"Height" X
"Weight" X
"BaseEXP" X
Optional Properties
"FormNames"
"Abilities" X
"Habitat"
"Type2" X
"HiddenAbility" X
"WildItemCommon" X
"WildItemUncommon" X
"WildItemRare" X
"Incense"
Abilities and HiddenAbility are all together in "getAbilityList", while the 3 WildItem fields can changed with "wildHoldItems"
"Name"
"Kind" X
"Pokedex" X
"Moves" X
"Color"
"Type1" X
"BaseStats" X
"Rareness"
"GenderRate"
"Happiness"
"GrowthRate"
"StepsToHatch"
"EffortPoints" X
"Compatibility"
"Height" X
"Weight" X
"BaseEXP" X
Optional Properties
"FormNames"
"Abilities" X
"Habitat"
"Type2" X
"HiddenAbility" X
"WildItemCommon" X
"WildItemUncommon" X
"WildItemRare" X
"Incense"
Abilities and HiddenAbility are all together in "getAbilityList", while the 3 WildItem fields can changed with "wildHoldItems"
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
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"]
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
Code:
def rareness
v=MultipleForms.call("rareness",self)
return v if v!=nil
return self.__mf_rareness
end
Code:
MultipleForms.register(:POKEMON,{
"rareness"=>proc{|pokemon|
next if pokemon.form == 0
next 0
}
})
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
Code:
pokemon=battler.pokemon
if $DEBUG && Input.press?(Input::CTRL)
shakes=4
else
if !rareness
rareness=pokemon.rareness
end
Code:
dexdata=pbOpenDexData
pbDexDataOffset(dexdata,wildpoke.species,16)
rareness=dexdata.fgetb # Get rareness from dexdata file
dexdata.close
Code:
rareness=wildpoke.rareness
Code:
dexdata=pbOpenDexData
pbDexDataOffset(dexdata,pokemon.species,16)
rareness=dexdata.fgetb
dexdata.close
Code:
rareness=pokemon.rareness
[/tab]
[tab=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
}
})
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
})
Battler Offsets
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
Code:
@endspritey=adjustBattleSpriteY(sprite,pkmn.species,pkmn.index)
Just above it is
Code:
@endspritey=adjustBattleSpriteY(sprite,@illusionpoke.species,pkmn.index)
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)
Code:
species=@battle.party2[0].species
In pbChangeSpecies
Code:
adjustBattleSpriteY(pkmn,attacker.species,attacker.index)
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]
}
})
[/tab]
[/tabs]
- Credits
- Credits to GT-Baka on Pokecommunity, whose question made me make this tutorial.