doshidak / showdex

Pokémon Showdown extension that harnesses the power of parabolic calculus to strategically extract your opponents' Elo.
GNU Affero General Public License v3.0
104 stars 18 forks source link

incorrect Psyshock/Psystrike damage again rip #53

Closed doshidak closed 2 years ago

doshidak commented 2 years ago

Also affects Psystrike.

Since in v1.0.3, the passed-in Generation object is now fully supplied by the global Dex object available in Showdown, which is missing some key properties, most notably overrideDefensiveStat, which is responsible for making sure Psyshock and Psystrike hits the defending Pokémon's DEF, not SPD.

Note
This SmogonMove is based off of the latest efa6fe7 commit on @smogon/calc, not its v0.6.0 release on npm, which does not include the overrideDefensiveStat, but rather defensiveCategory. The current v0.6.0 release is known to not properly account for Psyshock, hence why this project includes a patch for @smogon/calc.

import { Move as SmogonMove } from '@smogon/calc';

const move = new SmogonMove(8, 'Psyshock');

SmogonMove {
  ability: undefined,
  bp: 80,
  breaksProtect: false,
  category: 'Special',
  drain: undefined,
  flags: {},
  gen: Generation { num: 8, ... },
  hasCrashDamage: false,
  hits: 1,
  ignoreDefensive: false,
  isCrit: false,
  isMax: false,
  isZ: false,
  item: undefined,
  mindBlownRecoil: false,
  name: 'Psyshock',
  originalName: 'Psyshock',
  overrideDefensivePokemon: undefined,
  overrideDefensiveStat: 'def',
  overrideOffensivePokemon: undefined,
  overrideOffensiveStat: undefined,
  overrides: undefined,
  priority: 0,
  recoil: undefined,
  secondaries: undefined,
  species: undefined,
  struggleRecoil: false,
  target: 'any',
  timesUsed: 1,
  timesUsedWithMetronome: undefined,
  type: 'Psychic',
  useMax: undefined,
  useZ: undefined,
}
Dex.forGen(8).moves.get('Psyshock');

Showdown.Move {
  accuracy: 100,
  basePower: 80,
  category: 'Special',
  critRatio: 1,
  desc: 'Deals damage to the target based on its Defense instead of Special Defense.',
  effectType: 'Move',
  exists: true,
  flags: {
    protect: 1,
    mirror: 1,
  },
  gen: 5,
  hasCrashDamage: false,
  heal: null,
  id: 'psyshock',
  isMax: false,
  isNonstandard: null,
  isZ: '',
  maxMove: {
    basePower: 130,
  },
  multihit: null,
  name: 'Psyshock',
  noPPBoosts: false,
  noSketch: false,
  num: 473,
  ohko: null,
  pp: 10,
  pressureTarget: 'normal',
  priority: 0,
  recoil: null,
  secondaries: null,
  shortDesc: 'Damages target based on Defense, not Sp. Def.',
  target: 'normal',
  type: 'Psychic',
  zMove: {
    basePower: 160,
  },
}

Using the global Dex object results in the following SmogonMove:

SmogonMove {
  ability: null,
  bp: 80,
  breaksProtect: false,
  category: 'Special',
  drain: undefined,
  flags: {
    protect: 1,
    mirror: 1,
  },
  gen: Showdown.Dex { gen: 8, modid: 'gen8', ... },
  hasCrashDamage: false,
  hits: 1,
  ignoreDefensive: false,
  isCrit: false,
  isMax: false,
  isZ: false,
  item: undefined,
  mindBlownRecoil: false,
  name: 'Psyshock',
  originalName: 'Psyshock',
  overrideDefensivePokemon: undefined,
  overrideDefensiveStat: undefined,
  overrideOffensivePokemon: undefined,
  overrideOffensiveStat: undefined,
  overrides: undefined,
  priority: 0,
  recoil: 0,
  secondaries: null,
  species: undefined,
  struggleRecoil: false,
  target: 'normal',
  timesUsed: 1,
  timesUsedWithMetronome: undefined,
  type: 'Psychic',
  useMax: false,
  useZ: false,
}

Since @smogon/calc includes its own Generation data in its own data directory, the fix is to supply these missing properties from @smogon/calc's internal data to the data already supplied by Showdown's global Dex object.

Additionally, unlike @pkmn/dex, passing an invalid name for the constructor parameter of SmogonMove won't return undefined, but rather, an instantiated SmogonMove with invalid properties:

// 'Psyshock' is purposefully mistyped here for demonstration purposes
const move = new SmogonMove(8, 'Psyshck');

SmogonMove {
  ability: undefined,
  bp: undefined,
  breaksProtect: false,
  category: 'Status',
  drain: undefined,
  flags: undefined,
  gen: Generation { num: 8, ... },
  hasCrashDamage: false,
  hits: 1,
  ignoreDefensive: false,
  isCrit: false,
  isMax: false,
  isZ: false,
  item: undefined,
  mindBlownRecoil: false,
  name: 'Psyshck',
  originalName: 'Psyshck',
  overrideDefensivePokemon: undefined,
  overrideDefensiveStat: undefined,
  overrideOffensivePokemon: undefined,
  overrideOffensiveStat: undefined,
  overrides: undefined,
  priority: 0,
  recoil: undefined,
  secondaries: undefined,
  struggleRecoil: false,
  target: 'any',
  timesUsed: 1,
  timesUsedWithMetronome: undefined,
  type: undefined,
  useMax: undefined,
  useZ: undefined,
}

Thankfully, Photon Geyser is still working properly, using the corresponding defensive stat (either DEF or SPD) of the defending Pokémon determined by the higher attacking stat (either ATK or SPA) of the attacking Pokémon.

doshidak commented 2 years ago

Fixed by performing a "lookup" by instantiating a temporary SmogonMove with the gen number as the first constructor parameter (instead of the global Dex object) to pass along the following fields:

Will close this via reference on the next PR.