- Pokémon Essentials Version
- v17.2 ➖
Triggers allow us to make self contained code, or procs as ruby refers to them, and hook them up so they can be called at a later time. There are two main types in the Essentials code that are worth editing:
Events are better when you have many procs that are all interested in one event, as Events will call each proc in turn to allow it to make its changes or observations. You may have already used it if you ever made an encounter modifier, or even when you use the shiny switch.
Meanwhile, HandlerHashs are better when you have many different effects but they are all dependent on a specific symbol. You'll have already used it if you ever defined your own item effect, hidden move handler, or even a multiple form.
Event
s and HandlerHash
s. They are set up differently and have different uses, but both can be considered triggers.Events are better when you have many procs that are all interested in one event, as Events will call each proc in turn to allow it to make its changes or observations. You may have already used it if you ever made an encounter modifier, or even when you use the shiny switch.
Meanwhile, HandlerHashs are better when you have many different effects but they are all dependent on a specific symbol. You'll have already used it if you ever defined your own item effect, hidden move handler, or even a multiple form.
Events are all stored in the Module Events (in PField_Field) as class variables. (That's what the @@ means, if you didn't know that already.) Events work on a subscription basis, where procs can add themselves to the event they are interested in, and when the trigger is activated, each of the procs are called in turn.
There are 3 steps to making a new Event. First, you will want to plan exactly the information you wish to pass to the procs. This is crucial, because once you set it, it's going to be real tough changing all those procs you created. Secondly, we need to create a new Event class and add it to the Events module. Finally, you need to add your trigger call when the event needs to be triggered. We'll go through all of these steps with our example Event, onGiveGiftPokemon, which will allow us to edit a gifted pokemon, much in the same way onWildPokemonCreate let's us edit wild pokemon.
Since we want it to be similar to onWildPokemonCreate, we will give the same arguments, specifically 1 pokemon object as our only argument.
Now we need to add it to our module Events. There are two places we need to make changes to. First, we need to put our new class variable. We can put it under the other variables.
Next, we need to add the accessing code, so we can add our procs later. You can place it with the other similar methods in the same module.
You could also put this on its own version of the Events module, as Ruby will combine classes with identical names.
Finally, the most important step, we need to find our spots that we trigger this event from. Since this is when we give a pokemon to the player, that would mean we will for sure want to edit pbAddPokemon, but that doesn't need to be our only change.
Why do I add the change here? That's because we need a pokemon object as that's what the procs will expect, and we also want to have the pokemon all changed up before saving its species, as any of our subscribed procs might change that. Last thing you want to have happen is say you gave a Charizard but a proc changed it to a Bulbasaur.
If you want to know about the first argument, the nil, that's the sender variable when it is sent to the proc. As far as I can tell only the Map related procs use it, to pass the Game_Map object in the case of onMapCreate, or the PokemonMapFactory object for the change map related Events. None of the default procs use it.
You can make similar changes to pbAddPokemonSilent, pbAddToParty, pbAddToPartySilent, and even to pbGenerateEgg if you really wanted to, but that's beyond the scope of this tutorial.
There are 3 steps to making a new Event. First, you will want to plan exactly the information you wish to pass to the procs. This is crucial, because once you set it, it's going to be real tough changing all those procs you created. Secondly, we need to create a new Event class and add it to the Events module. Finally, you need to add your trigger call when the event needs to be triggered. We'll go through all of these steps with our example Event, onGiveGiftPokemon, which will allow us to edit a gifted pokemon, much in the same way onWildPokemonCreate let's us edit wild pokemon.
Since we want it to be similar to onWildPokemonCreate, we will give the same arguments, specifically 1 pokemon object as our only argument.
Now we need to add it to our module Events. There are two places we need to make changes to. First, we need to put our new class variable. We can put it under the other variables.
Code:
@@OnGiveGiftPokemon = Event.new
Code:
# Triggers whenever a Pokémon is given to the player
# Parameters:
# e[0] - Pokémon being given
def self.onGiveGiftPokemon; @@OnGiveGiftPokemon; end
def self.onGiveGiftPokemon=(v); @@OnGiveGiftPokemon = v; end
You could also put this on its own version of the Events module, as Ruby will combine classes with identical names.
Code:
module Events
@@OnGiveGiftPokemon = Event.new
# Triggers whenever a Pokémon is given to the player
# Parameters:
# e[0] - Pokémon being given
def self.onGiveGiftPokemon; @@OnGiveGiftPokemon; end
def self.onGiveGiftPokemon=(v); @@OnGiveGiftPokemon = v; end
end
Code:
def pbAddPokemon(pokemon,level=nil,seeform=true)
return if !pokemon || !$Trainer
if pbBoxesFull?
Kernel.pbMessage(_INTL("There's no more room for Pokémon!\1"))
Kernel.pbMessage(_INTL("The Pokémon Boxes are full and can't accept any more!"))
return false
end
if pokemon.is_a?(String) || pokemon.is_a?(Symbol)
pokemon = getID(PBSpecies,pokemon)
end
if pokemon.is_a?(Integer) && level.is_a?(Integer)
pokemon = PokeBattle_Pokemon.new(pokemon,level,$Trainer)
end
Events.onGiveGiftPokemon.trigger(nil,pokemon) # This is our newly added line
speciesname = PBSpecies.getName(pokemon.species)
Kernel.pbMessage(_INTL("\\me[Pkmn get]{1} obtained {2}!\1",$Trainer.name,speciesname))
pbNicknameAndStore(pokemon)
pbSeenForm(pokemon) if seeform
return true
end
If you want to know about the first argument, the nil, that's the sender variable when it is sent to the proc. As far as I can tell only the Map related procs use it, to pass the Game_Map object in the case of onMapCreate, or the PokemonMapFactory object for the change map related Events. None of the default procs use it.
You can make similar changes to pbAddPokemonSilent, pbAddToParty, pbAddToPartySilent, and even to pbGenerateEgg if you really wanted to, but that's beyond the scope of this tutorial.
HandlerHashs are used very often through out the code in various places. They work by allowing a proc to be assigned to a symbol or constant from a certain module. Three different sets are used by default. There are MoveHandlerHashs, which are HandlerHashs that automatically use the move symbols as their keys, ItemHandlerHash, which automatically use item symbols as their keys, and the special module MultipleForms, which internally uses a HandlerHash configured for species and lots of extra code as it uses a hash map to save all of those fancy multiple forms stuff.
There are 3 steps to making a new HandlerHash as well. First, you'll want to think about what arguments your procs will take and what they will return, if anything at all. You'll also want to consider what module you are working off. Is this a Hash for items? For moves? or something completely different like PBTypes, PBAbilities, or PBNatures? Remember, these are not objects we are dealing with here, these are symbols for the constants. You are also not obligated to make a proc for every thing. Only the Masterball uses BallHandlers::IsUnconditional for example. Secondly, you'll need to create a new module to hold your new HandlerHash(s). Finally, you'll have to add the trigger call where it is appropiate.
For the purpose of example, I'll create a new HandlerHash for PBNatures, and make a move effect that returns the text from the proc, as well as change the base damage.
First we create a new HandlerHash subclass, that way we don't need to keep giving PBNatures as the argument when we create a new one.
Now we need a new module to hold the HandlerHash.
Here's the low down on how HandlerHash's trigger works. The first argument is the value of the constant from the symbol, like if you called getID(PBNatures,:HARDY), which returns 0. Any of the other arguments are passed onward to the proc, as well as that first argument. In our example, we pass the nature as well as the battler to the move text proc, while we pass the nature, the regular base damage, and the battler to the base damage proc.
We return default data if the hash does not have a corresponding key for the nature we are given.
Now we have our simplistic move class, that will display our message when the move is used, and change the base damage. All that's left is making Handlers for the natures.
HandlerHashs also support conditional registration, where instead of the symbol, we give a proc that takes the id and returns true if the handler will work. Conditionals will not activate if it already finds a specific handler.
These two Conditionals will activate if the nature is neutral, unless it's Hardy, because Hardy already has its own specifically defined proc.
Remember, you get the id as the argument for nature, not the symbol itself. So all of the nature variables in this case are numbers.
There are 3 steps to making a new HandlerHash as well. First, you'll want to think about what arguments your procs will take and what they will return, if anything at all. You'll also want to consider what module you are working off. Is this a Hash for items? For moves? or something completely different like PBTypes, PBAbilities, or PBNatures? Remember, these are not objects we are dealing with here, these are symbols for the constants. You are also not obligated to make a proc for every thing. Only the Masterball uses BallHandlers::IsUnconditional for example. Secondly, you'll need to create a new module to hold your new HandlerHash(s). Finally, you'll have to add the trigger call where it is appropiate.
For the purpose of example, I'll create a new HandlerHash for PBNatures, and make a move effect that returns the text from the proc, as well as change the base damage.
First we create a new HandlerHash subclass, that way we don't need to keep giving PBNatures as the argument when we create a new one.
Code:
class NatureHandlerHash < HandlerHash
def initialize
super(:PBNatures)
end
end
Now we need a new module to hold the HandlerHash.
Code:
module NatureHandlers
MoveText = NatureHandlerHash.new
BaseDamage = NatureHandlerHash.new
def self.moveText(nature,battler)
return _INTL("Default Text!") if !MoveText[nature]
return MoveText.trigger(nature,battler)
end
def self.baseDamage(nature,basedmg,battler)
return basedmg if !BaseDamage[nature]
return BaseDamage.trigger(nature,basedmg,battler)
end
end
We return default data if the hash does not have a corresponding key for the nature we are given.
Code:
class PokeBattle_Move_XXX < PokeBattle_Move
def pbDisplayUseMessage(attacker)
@battle.pbDisplayBrief(NatureHandlers.moveText(attacker.nature,attacker))
return super(attacker)
end
def pbBaseDamage(basedmg,attacker,opponent)
return NatureHandlers.baseDamage(attacker.nature,basedmg,attacker)
end
end
Now we have our simplistic move class, that will display our message when the move is used, and change the base damage. All that's left is making Handlers for the natures.
Code:
NatureHandlers::MoveText.add(:HARDY,proc{|nature,battler|
next _INTL("{1} goes to make a powerful blow!",battler.pbThis)
})
NatureHandlers::BaseDamage.add(:HARDY,proc{|nature,basedmg,battler|
next (basedmg*3/2).floor
})
HandlerHashs also support conditional registration, where instead of the symbol, we give a proc that takes the id and returns true if the handler will work. Conditionals will not activate if it already finds a specific handler.
Code:
NatureHandlers::MoveText.addIf(proc{|nature| return ((nature/5).floor == (nature%5).floor) },proc{|nature,battler|
next _INTL("{1} goes to make a stable attack!",battler.pbThis)
})
NatureHandlers::BaseDamage.addIf(proc{|nature| return ((nature/5).floor == (nature%5).floor)},proc{|nature,basedmg,battler|
next (basedmg*5/4).floor
})
Remember, you get the id as the argument for nature, not the symbol itself. So all of the nature variables in this case are numbers.
- Credits
- Just me