Creating new abilities may be confusing for some people since we have to go to the game codes to set them up.
Don't be afraid of codes! I don't know how to code and I managed to create new abilities!
Here we are going to learn step by step how to set up a new ability. To do this without knowing much about coding, we are going to use some of the existing codes as a reference.
As an example, I'm going to create 2 custom abilities. Hopefully, with these 2 you'll get used to how to make new ones on your own.
The first one will be "Icy Look". It will work just like Intimidate, but instead of lowing Attack, it will low Speed. I'll also add that it increases the power of Ice attacks.
Then I'll create "Fire Start". This ability will burn the target if the user attacks on its first turn.
You can create tons of more custom abilities like these by using this copy, paste & edit method. You just need the creativity and find if there is a code that does something similar to what you want to do.
Don't be afraid of codes! I don't know how to code and I managed to create new abilities!
Here we are going to learn step by step how to set up a new ability. To do this without knowing much about coding, we are going to use some of the existing codes as a reference.
As an example, I'm going to create 2 custom abilities. Hopefully, with these 2 you'll get used to how to make new ones on your own.
The first one will be "Icy Look". It will work just like Intimidate, but instead of lowing Attack, it will low Speed. I'll also add that it increases the power of Ice attacks.
Then I'll create "Fire Start". This ability will burn the target if the user attacks on its first turn.
Step 0: Create a backup for your game. It is not mandatory, but it is always recommended to have a backup in case something goes really wrong, especially if you don't know exactly what you are doing. Just copy and paste your entire game.
Step 1: Create the new ability in the PBS folder. Very easy!
note: Keep descriptions short, even if you leave some information out. There is not much space.
With this done, your ability now just exists, this means you can give it to a Pokémon, but, for now, it does nothing.
- Go to your game folder --> PBS --> open abilities.txt (I recommend using Notepad++ to edit PBS files).
- Scroll down to the last ability. I'm using the Gen 8 Project for v19.1 (which I recommend you a lot), so the current last ability I have here is the number 267 (As One - Ghost).
note: The ability name in capitals is the Internal name.
note: Keep descriptions short, even if you leave some information out. There is not much space.
Step 2: Go into your game's scripts, and search for an ability that works in a similar way.
- Open your game project and go to "Scripts"
- Press "Ctrl + Shift + F" and search for the existing ability that is similar to yours. In this case, I'm going to find Intimidate.
These are my results:
note: If you searched for another ability, you may have more or fewer results, don't worry about that.
Most abilities should be coded in the BattleHandlers_Abilities section. So there is where you should go. I'll jump into BattleHandlers_Abilities line 2538.
So here is where we are. I'm going to quickly explain a little about this section.
If you scroll up or down, you'll see there are multiple abilities coded here. Also, this section is divided into multiple ability handlers. Intimidate is in the "AbilityOnSwitchIn" part along with every ability that does something when switching in the Pokémon. For example, below Intimidate, there is Misty Surge, which also works when switching in. If you keep going down, you'll eventually find the next handler which is "AbilityOnSwitchOut", where the abilities that work when you return your Pokémon into its Pokéball are, like Natural Cure. Like these, there are many ability handlers, for abilities that multiply damage, trapping abilities, etc.
- Open your game project and go to "Scripts"
- Press "Ctrl + Shift + F" and search for the existing ability that is similar to yours. In this case, I'm going to find Intimidate.
These are my results:
note: If you searched for another ability, you may have more or fewer results, don't worry about that.
Most abilities should be coded in the BattleHandlers_Abilities section. So there is where you should go. I'll jump into BattleHandlers_Abilities line 2538.
So here is where we are. I'm going to quickly explain a little about this section.
If you scroll up or down, you'll see there are multiple abilities coded here. Also, this section is divided into multiple ability handlers. Intimidate is in the "AbilityOnSwitchIn" part along with every ability that does something when switching in the Pokémon. For example, below Intimidate, there is Misty Surge, which also works when switching in. If you keep going down, you'll eventually find the next handler which is "AbilityOnSwitchOut", where the abilities that work when you return your Pokémon into its Pokéball are, like Natural Cure. Like these, there are many ability handlers, for abilities that multiply damage, trapping abilities, etc.
Step 3: Copying and editing codes
- Copy the Intimidate code and paste it at the bottom of the AbilityOnSwitchIn handler.
It should look like this: Just above the AbilityOnSwitchOut handler section.
Now I'm going to use this code as a reference, and edit it.
But we still have some Intimidate stuff in there, like pbLowerAttackStatStageIntimidate or pbItemOnIntimidateCheck. This last one was coded for Adrenaline Orb, which has a mechanic with Intimidate, so we can delete that whole line.
pbLowerAttackStatStageIntimidate means our ability is still lowering the target's attack.
What we have to do is to find (Ctrl + Shift + F) where is pbLowerAttackStatStageIntimidate defined. So we search for the result that has "def pbLowerAttackStatStageIntimidate".
We'll find that Intimidate's code is quite long due to having lots of interactions with other abilities like Contrary or moves like Substitute. Copy the code which is from lines 266 to 320 and paste it below. Keep one line blank between codes, so it's easier to see them:
I also added a Comment so it's easier to locate my ability code. You can do this by using "#", so the game doesn't consider that line as code.
- Now that we defined what pbLowerSpeedIcyLook does, we can go back to the battle handler.
You can now use Ctrl + Shift + F and find ICYLOOK or whatever your ability is called.
- Replace pbLowerAttackStatStageIntimidate with pbLowerSpeedIcyLook
Press Apply and Ok, and we are good.
- Copy the Intimidate code and paste it at the bottom of the AbilityOnSwitchIn handler.
It should look like this: Just above the AbilityOnSwitchOut handler section.
Now I'm going to use this code as a reference, and edit it.
- I'm going to replace INTIMIDATE with ICYLOOK (remember to use the internal name).
- Now I'll change the stat ATTACK for SPEED
It should look like this:
BattleHandlers::AbilityOnSwitchIn.add(:ICYLOOK,
proc { |ability,battler,battle|
battle.pbShowAbilitySplash(battler)
battle.eachOtherSideBattler(battler.index) do |b|
next if !b.near?(battler)
b.pbLowerAttackStatStageIntimidate(battler)
check_item = true
if b.hasActiveAbility?(:CONTRARY)
check_item = false if b.statStageAtMax?(:SPEED)
else
check_item = false if b.statStageAtMin?(:SPEED)
end
b.pbItemOnIntimidatedCheck if check_item
end
battle.pbHideAbilitySplash(battler)
}
)
But we still have some Intimidate stuff in there, like pbLowerAttackStatStageIntimidate or pbItemOnIntimidateCheck. This last one was coded for Adrenaline Orb, which has a mechanic with Intimidate, so we can delete that whole line.
pbLowerAttackStatStageIntimidate means our ability is still lowering the target's attack.
What we have to do is to find (Ctrl + Shift + F) where is pbLowerAttackStatStageIntimidate defined. So we search for the result that has "def pbLowerAttackStatStageIntimidate".
We'll find that Intimidate's code is quite long due to having lots of interactions with other abilities like Contrary or moves like Substitute. Copy the code which is from lines 266 to 320 and paste it below. Keep one line blank between codes, so it's easier to see them:
I also added a Comment so it's easier to locate my ability code. You can do this by using "#", so the game doesn't consider that line as code.
- Now we have to define our code, so let's replace pbLowerAttackStatStageIntimidate with something like pbLowerSpeedIcyLook.
- Now replace the checks for ATTACK stat with SPEED.
- I'll also remove the lines where it interacts with Rattled.
- You can change "Intimidate" for "Icy Look" in the notes in green if you want, but it's not going to change anything really.
It should look like this:
def pbLowerSpeedIcyLook(user)
return false if fainted?
# NOTE: Substitute intentially blocks Icy Look even if self has Contrary.
if @effects[PBEffects::Substitute]>0
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} is protected by its substitute!",pbThis))
else
@battle.pbDisplay(_INTL("{1}'s substitute protected it from {2}'s {3}!",
pbThis,user.pbThis(true),user.abilityName))
end
return false
end
# NOTE: These checks exist to ensure appropriate messages are shown if
# Icy Look is blocked somehow (i.e. the messages should mention the
# Icy Look ability by name).
if !hasActiveAbility?(:CONTRARY)
if pbOwnSide.effects[PBEffects::Mist]>0
@battle.pbDisplay(_INTL("{1} is protected from {2}'s {3} by Mist!",
pbThis,user.pbThis(true),user.abilityName))
return false
end
if abilityActive?
if BattleHandlers.triggerStatLossImmunityAbility(self.ability,self,:SPEED,@battle,false) ||
BattleHandlers.triggerStatLossImmunityAbilityNonIgnorable(self.ability,self,:SPEED,@battle,false) ||
hasActiveAbility?(:INNERFOCUS) || hasActiveAbility?(:OWNTEMPO) ||
hasActiveAbility?(:OBLIVIOUS) || hasActiveAbility?(:SCRAPPY)
@battle.pbShowAbilitySplash(self) if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1}'s {2} prevented {3}'s {4} from working!",
pbThis,abilityName,user.pbThis(true),user.abilityName))
@battle.pbHideAbilitySplash(self) if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
return false
end
end
eachAlly do |b|
next if !b.abilityActive?
if BattleHandlers.triggerStatLossImmunityAllyAbility(b.ability,b,self,:SPEED,@battle,false)
@battle.pbShowAbilitySplash(b) if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} is protected from {2}'s {3} by {4}'s {5}!",
pbThis,user.pbThis(true),user.abilityName,b.pbThis(true),b.abilityName))
@battle.pbHideAbilitySplash(b) if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
return false
end
end
end
return false if !pbCanLowerStatStage?(:SPEED,user)
ret = false
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
ret = pbLowerStatStageByAbility(:SPEED,1,user,false)
else
ret = pbLowerStatStageByCause(:SPEED,1,user,user.abilityName)
end
return ret
end
- Now that we defined what pbLowerSpeedIcyLook does, we can go back to the battle handler.
You can now use Ctrl + Shift + F and find ICYLOOK or whatever your ability is called.
- Replace pbLowerAttackStatStageIntimidate with pbLowerSpeedIcyLook
Press Apply and Ok, and we are good.
Step 4: Setting up the damage increase in Ice attacks.
This part is much easier. You just have to find (Ctrl + Shift + F) an ability that somehow increases damage, like Transistor, which is Regieleki's ability.
Copy Transistor's code and paste it below. Then replace the internal name with your ability's and the move's type you want to increase power.
note: In this code, Ice attacks power are being multiplied by 1.5, you can change this number to whatever you want, for example, if you want to double the damage, just change it to 2.
Now Icy Looks is completely working!
Remember to push Ctrl to do a full compile when doing a Playtest.
This part is much easier. You just have to find (Ctrl + Shift + F) an ability that somehow increases damage, like Transistor, which is Regieleki's ability.
Copy Transistor's code and paste it below. Then replace the internal name with your ability's and the move's type you want to increase power.
Here is how it should look like:
BattleHandlers::DamageCalcUserAbility.add(:ICYLOOK,
proc { |ability,user,target,move,mults,baseDmg,type|
mults[:attack_multiplier] *= 1.5 if type == :ICE
}
)
Now Icy Looks is completely working!
Remember to push Ctrl to do a full compile when doing a Playtest.
Step 0:
Step 0 and 1 are the same as with Icy Look, so I'm going to skip those.
Step 0 and 1 are the same as with Icy Look, so I'm going to skip those.
Step 2:
The way we are going to do it is similar to Icy Look, but we have to find an ability that inflicts a status problem when it hits. A Pokémon with Poison Touch has a 30% chance of poisoning the target when using a contact move. We can work with that base.
So, let's find POISONTOUCH (Ctrl + Shift + F):
Copy the code and paste it below.
- Replace POISONTOUCH with the internal name of your ability, in this case, it's FIRESTART. Now our ability does something already, it does the same as Poison Touch, but we want it to burn with its attacks only on the first turn.
- On line 1816, where it says "target.pbCanPoison?" it's checking if target can get poisoned. There are codes like this defined for every status, which means that "pbBurn" and "pbCanBurn?" exists (and the same with Sleep, Paralyze, Freeze, etc).
So, we are going to replace pbCanPoison? with pbCanBurn? on line 1816, and on line 1821, replace pbPoison with pbBurn.
Where it says "next if !move.ContactMove?" the game is checking if the move that the user is using makes contact or not. We don't care if it makes contact, but we want the game to check if it's the first turn or not.
There is code that checks the battle turns: "turnCount" It is used for abilities like Speed Boost or the Timer Ball.
Replace "!move.ContactMove?" with "user.turnCount > 1".
note: "user" means it's counting the user's turn, if you instead type "target.turnCount", it will count the target's turn.
If you want you can keep the Contact Move check if it makes sense in your ability, then just write "next if user.turnCount > 1" on the next line.
The line with "battle.pbRandom(100)>=30" makes it so it has a 30% chance to burn, but I want it to be 100%, so I'll just remove that whole line.
There is only one more thing to change, and that is the in-game text. On line 1834, replace "poisoned" with "burned". It doesn't change the effect of the ability, it just changes the text that shows up when the enemy burns due to Fire Start.
Now Fire Start works completely!
The way we are going to do it is similar to Icy Look, but we have to find an ability that inflicts a status problem when it hits. A Pokémon with Poison Touch has a 30% chance of poisoning the target when using a contact move. We can work with that base.
So, let's find POISONTOUCH (Ctrl + Shift + F):
Copy the code and paste it below.
- Replace POISONTOUCH with the internal name of your ability, in this case, it's FIRESTART. Now our ability does something already, it does the same as Poison Touch, but we want it to burn with its attacks only on the first turn.
- On line 1816, where it says "target.pbCanPoison?" it's checking if target can get poisoned. There are codes like this defined for every status, which means that "pbBurn" and "pbCanBurn?" exists (and the same with Sleep, Paralyze, Freeze, etc).
So, we are going to replace pbCanPoison? with pbCanBurn? on line 1816, and on line 1821, replace pbPoison with pbBurn.
For now, the code looks like this:
BattleHandlers::UserAbilityOnHit.add(:FIRESTART,
proc { |ability,user,target,move,battle|
next if !move.contactMove?
next if battle.pbRandom(100)>=30
battle.pbShowAbilitySplash(user)
if target.hasActiveAbility?(:SHIELDDUST) && !battle.moldBreaker
battle.pbShowAbilitySplash(target)
if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis))
end
battle.pbHideAbilitySplash(target)
elsif target.pbCanBurn?(user,PokeBattle_SceneConstants::USE_ABILITY_SPLASH)
msg = nil
if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
msg = _INTL("{1}'s {2} poisoned {3}!",user.pbThis,user.abilityName,target.pbThis(true))
end
target.pbBurn(user,msg)
end
battle.pbHideAbilitySplash(user)
}
)
Where it says "next if !move.ContactMove?" the game is checking if the move that the user is using makes contact or not. We don't care if it makes contact, but we want the game to check if it's the first turn or not.
There is code that checks the battle turns: "turnCount" It is used for abilities like Speed Boost or the Timer Ball.
Replace "!move.ContactMove?" with "user.turnCount > 1".
note: "user" means it's counting the user's turn, if you instead type "target.turnCount", it will count the target's turn.
If you want you can keep the Contact Move check if it makes sense in your ability, then just write "next if user.turnCount > 1" on the next line.
The line with "battle.pbRandom(100)>=30" makes it so it has a 30% chance to burn, but I want it to be 100%, so I'll just remove that whole line.
Now my code looks like this:
BattleHandlers::UserAbilityOnHit.add(:FIRESTART,
proc { |ability,user,target,move,battle|
next if user.turnCount > 1
battle.pbShowAbilitySplash(user)
if target.hasActiveAbility?(:SHIELDDUST) && !battle.moldBreaker
battle.pbShowAbilitySplash(target)
if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis))
end
battle.pbHideAbilitySplash(target)
elsif target.pbCanBurn?(user,PokeBattle_SceneConstants::USE_ABILITY_SPLASH)
msg = nil
if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
msg = _INTL("{1}'s {2} poisoned {3}!",user.pbThis,user.abilityName,target.pbThis(true))
end
target.pbBurn(user,msg)
end
battle.pbHideAbilitySplash(user)
}
)
There is only one more thing to change, and that is the in-game text. On line 1834, replace "poisoned" with "burned". It doesn't change the effect of the ability, it just changes the text that shows up when the enemy burns due to Fire Start.
Now Fire Start works completely!
You can create tons of more custom abilities like these by using this copy, paste & edit method. You just need the creativity and find if there is a code that does something similar to what you want to do.
- Credits
- You don't have to give credits to anyone.