Resource icon

More Multiple Form Property Changes + Conditional Registering

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.
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"
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.
[/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
}
})
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
})

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
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:
species=@battle.party2[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?
[/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.
Credits
Credits to GT-Baka on Pokecommunity, whose question made me make this tutorial.
Author
Vendily
First release
Last update
Rating
0.00 star(s) 0 ratings

More resources from Vendily

Latest updates

  1. Battler Offsets (Player Y, Enemy Y, Enemy Altitude)

    I added battler offsets to the main resource, as well as tabs to organize it.
Top