The PokéCommunity Forums Fan Games Game Development Scripts & Tutorials
Essentials Tutorial More Multiple Form Property Changes + Conditional Registering

Scripts & Tutorials This forum is for scripts and code, as well as all kinds of tutorials, software, tools and so forth. Remember to give credit!
The thread revival limit does not apply here.


Reply
 
Thread Tools
  #1    
Old June 18th, 2017 (8:29 AM). Edited June 27th, 2017 by Vendily.
Vendily's Avatar
Vendily Vendily is offline
     
    Join Date: Aug 2015
    Gender: Female
    Nature: Calm
    Posts: 751
    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:
    Required Properties
    "Name"
    "Kind"
    "Pokedex"
    "Moves"
    "Color"
    "Type1"
    "BaseStats"
    "Rareness"
    "GenderRate"
    "Happiness"
    "GrowthRate"
    "StepsToHatch"
    "EffortPoints"
    "Compatibility"
    "Height"
    "Weight"
    "BaseEXP"
    Optional Properties
    "FormNames"
    "Abilities"
    "Habitat"
    "Type2"
    "HiddenAbility"
    "WildItemCommon"
    "WildItemUncommon"
    "WildItemRare"
    "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.

    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
    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.
    __________________
    Resources for ROM hackers has moved.

    Reply With Quote

    Relevant Advertising!

      #2    
    Old June 18th, 2017 (9:00 PM).
    GT-Baka GT-Baka is offline
       
      Join Date: Jul 2008
      Posts: 76
      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.
      Reply With Quote
        #3    
      Old June 27th, 2017 (4:17 PM).
      Vendily's Avatar
      Vendily Vendily is offline
         
        Join Date: Aug 2015
        Gender: Female
        Nature: Calm
        Posts: 751
        Updated the main post to be more organized and include changing Battler Offsets (so Player Y, Enemy Y, Enemy Altitude)
        __________________
        Resources for ROM hackers has moved.

        Reply With Quote
          #4    
        Old December 20th, 2017 (8:52 AM). Edited December 20th, 2017 by ant62.
        ant62 ant62 is offline
           
          Join Date: Sep 2015
          Gender: Male
          Posts: 8
          how would i do egg moves or evolutions
          Reply With Quote
            #5    
          Old December 21st, 2017 (3:13 PM).
          Vendily's Avatar
          Vendily Vendily is offline
             
            Join Date: Aug 2015
            Gender: Female
            Nature: Calm
            Posts: 751
            Quote:
            Originally Posted by ant62 View Post
            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.
            __________________
            Resources for ROM hackers has moved.

            Reply With Quote
              #6    
            Old December 22nd, 2017 (5:07 AM). Edited December 22nd, 2017 by ant62.
            ant62 ant62 is offline
               
              Join Date: Sep 2015
              Gender: Male
              Posts: 8
              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
              Reply With Quote
                #7    
              Old December 23rd, 2017 (2:02 AM). Edited December 23rd, 2017 by ant62.
              ant62 ant62 is offline
                 
                Join Date: Sep 2015
                Gender: Male
                Posts: 8
                for egg moves i have written but not tested this code:
                Spoiler:
                Code:
                if MultipleForms.hasFunction?(babyspecies,"getEggMoves")
                    attacks = MultipleForms.call("getEggMoves",egg,value)
                    if movefather.isMale?
                      for atk in attacks
                        moves.push(atk) if movefather.hasMove?(atk)
                      end
                    end
                    if USENEWBATTLEMECHANICS
                      for atk in attacks
                        moves.push(atk) if movemother.hasMove?(atk)
                      end
                    end
                  else
                  # NORMAL EGG MOVE CODE HERE
                  end
                MultipleForms.hasFunction? is not used anywhere in the default scripts

                the code for vulpix:
                Spoiler:
                Code:
                "getEggMoves"=>proc{|pokemon|
                list=[]
                if pokemon.form == 0
                list=[:CAPTIVATE,:DISABLE,:EXTRASENSORY,:FEINTATTACK,:FLAIL,:FLAREBLITZ,
                :HEATWAVE,:HEX,:HOWL,:HYPNOSIS,:POWERSWAP,:SECRETPOWER,:SPITE,:TAILSLAP]
                else
                list=[:AGILITY,:CHARM,:DISABLE,:ENCORE,:EXTRASENSORY,:FLAIL,:FREEZEDRY,
                :HOWL,:HYPNOSIS,:MOONBLAST,:POWERSWAP,:SPITE,:SECRETPOWER,:TAILSLAP]
                end
                #may not be needed
                   for i in 0...list.length
                     list[i]=getConst(PBMoves,list[i])
                   end
                #end of may not be needed section
                   next list
                },
                Reply With Quote
                Reply

                Quick Reply

                Join the conversation!

                Create an account to post a reply in this thread, participate in other discussions, and more!

                Create a PokéCommunity Account

                Sponsored Links
                Thread Tools

                Posting Rules
                You may not post new threads
                You may not post replies
                You may not post attachments
                You may not edit your posts

                BB code is On
                Smilies are On
                [IMG] code is On
                HTML code is Off

                Forum Jump


                All times are GMT -8. The time now is 7:06 PM.