mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-03-21 17:25:10 -05:00
Fixes #6346 The `AfterDamage` event has been replaced with `DamagingHit`, which which happens for damaging moves after secondaries. The `AfterHit` event has also been moved after `DamagingHit`, to make sure Knock Off still procs after Rocky Helmet. `AfterHit` is no longer a valid event on `secondary` and `self` blocks, because it's meaningless in those blocks, anyway. All `self.onAfterHit` and `secondary.onAfterHit` handlers have been moved to `onHit`, which should have the same timing in practice.
195 lines
3.9 KiB
Plaintext
195 lines
3.9 KiB
Plaintext
This is a pseudocode overview of how Pokémon Showdown works - particularly relevant to anyone writing event code.
|
|
|
|
(A) = negated by Mold Breaker (and Turboblaze/Teravolt) on abilities only during a move
|
|
(S) = negated by Sheer Force only during a move with a secondary effect
|
|
(U) = negated by Substitute
|
|
|
|
(Air Lock and Klutz negates every event for the corresponding effect, so don't need to be marked)
|
|
|
|
=== MOVES ===
|
|
|
|
A quick overview of how moves work:
|
|
|
|
STEP 1. MOVE PRE-USAGE
|
|
external execution interruption (flinch, full paralysis)
|
|
"POKEMON used MOVE!"
|
|
STEP 2: MOVE USAGE
|
|
internal execution interruption (charge turn for SolarBeam etc)
|
|
move start of sub-move (Metronome, Sleep Talk etc)
|
|
PP deduction
|
|
STEP 3. MOVE EXECUTION (sub-moves start from here)
|
|
there is no target
|
|
internal move failure ("But it failed!")
|
|
STEP 4. MOVE HIT (happens once per target)
|
|
move misses
|
|
external move failure (Protect, Substitute blocked etc)
|
|
immunity
|
|
move animation
|
|
damage
|
|
other effects
|
|
secondary effects
|
|
|
|
=== MAIN LOOP ===
|
|
|
|
[BeforeTurn]
|
|
for every move:
|
|
[ModifyPriority]
|
|
move's [BeforeTurn]
|
|
|
|
runAction() - runs runSwitch, runAfterSwitch, and runMove in priority order, then residual at end {
|
|
runSwitch() {
|
|
[BeforeSwitch]
|
|
switch
|
|
}
|
|
runAfterSwitch() - after all pokemon are switched in {
|
|
[Switch]
|
|
ability's [Start]
|
|
item's [Start]
|
|
}
|
|
runMove() {
|
|
[BeforeMove] (A)
|
|
-> false => exit runMove
|
|
|
|
display "POKEMON used MOVE!"
|
|
|
|
useMove() {
|
|
[ModifyMove] (A)
|
|
|
|
if no targets:
|
|
display "But there was no target..." and exit useMove()
|
|
|
|
moveHit() - once for each hit of multi-hit move, and also once for secondary hits {
|
|
move's [TryHit], [TryHitSide] or [TryHitField] (A)
|
|
if hit:
|
|
[TryHit] (A)
|
|
-> 0 => skip to SelfHit (used for Substitute)
|
|
-> null => exit moveHit()
|
|
-> false => display "But it failed!" and exit moveHit()
|
|
[Immunity] (A)
|
|
-> null => exit moveHit()
|
|
-> false => display "It had no effect!" and exit moveHit()
|
|
|
|
move animation
|
|
|
|
getDamage() {
|
|
move's [BasePower]
|
|
[BasePower] (A)
|
|
move's [Damage]
|
|
if critical hit:
|
|
[CriticalHit] (A)
|
|
}
|
|
damage() {
|
|
[Damage] (A,U)
|
|
-> false => exit damage()
|
|
damage
|
|
}
|
|
heal() {
|
|
[Heal] (A)
|
|
-> false => exit heal()
|
|
heal
|
|
}
|
|
status() (U) {
|
|
[Status] (A)
|
|
-> false => exit status()
|
|
status change
|
|
status's [Start] (A)
|
|
-> false => restore previous status and exit status()
|
|
[AfterStatus]
|
|
}
|
|
effect() (U) {
|
|
if effect already exists:
|
|
effect's [Restart] (A)
|
|
otherwise:
|
|
effect change
|
|
effect's [Start] (A)
|
|
-> false => remove effect and exit effect()
|
|
}
|
|
|
|
recoil() {
|
|
call damage()
|
|
}
|
|
drain() {
|
|
call heal()
|
|
}
|
|
|
|
if hit:
|
|
[SelfHit] (A)
|
|
if secondary hit:
|
|
[SecondarySelfHit] (A)
|
|
|
|
if hit:
|
|
if secondary roll succeeds:
|
|
secondary() (S,U) {
|
|
call moveHit()
|
|
}
|
|
}
|
|
|
|
[AfterMoveSecondary] (S,U)
|
|
[AfterMoveSecondarySelf] (S)
|
|
|
|
[AfterMove]
|
|
}
|
|
pp deducted
|
|
}
|
|
runFaint() {
|
|
[Faint]
|
|
}
|
|
residual() {
|
|
for every effect:
|
|
if duration = 0:
|
|
effect's [End]
|
|
remove effect
|
|
otherwise:
|
|
effect's [Residual]
|
|
}
|
|
|
|
choose switch-ins for fainted pokemon
|
|
=> runSwitch() and runAfterSwitch() again
|
|
|
|
[Update]
|
|
}
|
|
|
|
|
|
|
|
=== ISOLATED ===
|
|
|
|
These are not part of the main loop, and are only called from inside an event.
|
|
|
|
(For instance, eatItem() is usually called from within the Update event of a berry, and weather() is called
|
|
from the Residual event of a weather condition)
|
|
|
|
eatItem() {
|
|
[UseItem] (A)
|
|
-> false => exit EatItem()
|
|
[EatItem] (A)
|
|
-> false => exit EatItem()
|
|
item's [Eat]
|
|
remove item from pokemon
|
|
}
|
|
|
|
useItem() {
|
|
[UseItem] (A)
|
|
-> false => exit UseItem()
|
|
remove item from pokemon
|
|
}
|
|
|
|
takeItem() {
|
|
[TakeItem] (A)
|
|
-> false => exit TakeItem()
|
|
remove item from pokemon
|
|
}
|
|
|
|
setItem() {
|
|
set item
|
|
item's [Start]
|
|
}
|
|
|
|
setAbility() {
|
|
set ability
|
|
ability's [Start]
|
|
}
|
|
|
|
weather() {
|
|
[Weather]
|
|
}
|