GlitcherRed

Untitled

Apr 5th, 2023
13
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 90.65 KB | None | 0 0
  1. 'use strict';
  2.  
  3. const Dex = require('./../../sim/dex');
  4.  
  5. const RandomTeams = require('../../data/random-teams');
  6.  
  7. class RandomZUTeams extends RandomTeams {
  8. /**
  9. * @param {Format | string} format
  10. * @param {?PRNG | [number, number, number, number]} [prng]
  11. */
  12. constructor(format, prng) {
  13. super(format, prng);
  14. }
  15.  
  16. /**
  17. * @param {?string[]} moves
  18. * @param {{[k: string]: boolean}} [hasType]
  19. * @param {{[k: string]: boolean}} [hasAbility]
  20. * @param {string[]} [movePool]
  21. */
  22. queryMoves(moves, hasType = {}, hasAbility = {}, movePool = []) {
  23. // This is primarily a helper function for random setbuilder functions.
  24. let counter = {
  25. Physical: 0, // Moves that depend on Attack stat
  26. Special: 0, // Moves that depend on Special Attack stat
  27. Status: 0, // Non-damaging moves
  28. damage: 0, // Fixed damage moves
  29. recovery: 0, // Recovery moves, excluding Rest
  30. stab: 0, // STAB moves, excluding moves from NoStab
  31. inaccurate: 0, // Moves with lower than 90% accuracy (for No Guard/Compound Eyes)
  32. priority: 0, // Moves with non-zero priority (for Speed setup)
  33. recoil: 0, // Moves with recoil damage (for Rock Head/Reckless/Sturdy/Focus Sash)
  34. drain: 0, // Moves that drain health from target (for Triage)
  35. adaptability: 0, // Moves that benefit from Adaptability (STAB moves)
  36. strongjaw: 0, // Moves that benefit from Strong Jaw (bite moves)
  37. contrary: 0, // Moves that benefit from Contrary (self-lowering stats)
  38. hustle: 0, // Moves that benefit from Hustle (physical moves)
  39. ironfist: 0, // Moves that benefit from Iron Fist (punch moves)
  40. serenegrace: 0, // Moves that benefit from Serene Grace (20+% secondary rate)
  41. sheerforce: 0, // Moves that benefit from Sheer Force (have secondary but not self-boosting, Fake Out excluded)
  42. skilllink: 0, // Moves that benefit from Skill Link (2-5 hits)
  43. technician: 0, // Moves that benefit from Technician (<= 60 BP, excluding Rapid Spin)
  44. physicalsetup: 0, // Moves that boost Attack
  45. specialsetup: 0, // Moves that boost Special Attack
  46. mixedsetup: 0, // Moves that boost both Attack and Special Attack
  47. speedsetup: 0, // Moves that boost Speed
  48. defensesetup: 0, // Moves that boost Defense
  49. physicalpool: 0, // Physical moves in movepool (unpicked, unrejected)
  50. specialpool: 0, // Special moves in movepool (unpicked, unrejected)
  51. trap: 0, // Moves that trap or partial-trap
  52. hazards: 0, // Hazard moves
  53. weather: 0, // Weather moves
  54. /**@type {Move[]} */
  55. damagingMoves: [], // List of moves that are used for damage
  56. setupType: '', // Physical/Special/Mixed
  57. };
  58.  
  59. for (let type in Dex.data.TypeChart) {
  60. counter[type] = 0;
  61. }
  62.  
  63. if (!moves || !moves.length) return counter;
  64.  
  65. // Moves that heal a fixed amount:
  66. let RecoveryMove = [
  67. 'healorder', 'milkdrink', 'moonlight', 'morningsun', 'recover', 'roost', 'shoreup', 'slackoff', 'softboiled', 'strengthsap', 'synthesis',
  68. ];
  69. // Moves which drop stats:
  70. let ContraryMove = [
  71. 'clangingscales', 'closecombat', 'dracometeor', 'dragonascent', 'fleurcannon', 'hammerarm', 'hyperspacefury', 'icehammer',
  72. 'leafstorm', 'overheat', 'psychoboost', 'superpower', 'vcreate',
  73. ];
  74. // Moves that boost Attack:
  75. let PhysicalSetup = [
  76. 'bellydrum', 'bulkup', 'coil', 'curse', 'dragondance', 'mirrormove', 'poweruppunch', 'shiftgear', 'swordsdance',
  77. ];
  78. // Moves which boost Special Attack:
  79. let SpecialSetup = [
  80. 'calmmind', 'chargebeam', 'fierydance', 'geomancy', 'nastyplot', 'quiverdance', 'tailglow', 'healblock',
  81. ];
  82. // Moves which boost Attack AND Special Attack:
  83. let MixedSetup = [
  84. 'celebrate', 'clangingscales', 'conversion', 'forestscurse', 'growth', 'happyhour', 'holdhands', 'lastresort', 'shellsmash', 'trickortreat', 'workup',
  85. ];
  86. // Moves which boost Speed:
  87. let SpeedSetup = [
  88. 'agility', 'autotomize', 'celebrate', 'clangingscales', 'conversion', 'dragondance', 'flamecharge', 'forestscurse', 'happyhour',
  89. 'holdhands', 'lastresort', 'mefirst', 'quiverdance', 'rockpolish', 'shellsmash', 'shiftgear', 'trickortreat',
  90. ];
  91. // Moves that shouldn't be the only STAB moves:
  92. let NoStab = [
  93. 'aquajet', 'bounce', 'dig', 'explosion', 'fakeout', 'firstimpression', 'flamecharge', 'fly', 'iceshard', 'pursuit',
  94. 'quickattack', 'selfdestruct', 'skyattack', 'suckerpunch',
  95. 'chargebeam', 'clearsmog', 'eruption', 'vacuumwave', 'waterspout',
  96. ];
  97. // Moves that are only used to trap/partial-trap opponent:
  98. let Trap = [
  99. 'block', 'meanlook', 'spiderweb', 'bind', 'clamp', 'firespin', 'infestation', 'sandtomb', 'whirlpool', 'wrap',
  100. ];
  101. // Moves which boost Defense or Special Defense (for at least 2 stages):
  102. let DefenseSetup = [
  103. 'acidarmor', 'amnesia', 'barrier', 'cosmicpower', 'cottonguard', 'defendorder', 'diamondstorm', 'irondefense', 'magneticflux', 'stockpile',
  104. ];
  105. // Hazard moves
  106. let Hazards = [
  107. 'stealthrock', 'toxicspikes', 'spikes', 'stickyweb',
  108. ];
  109. // Weather moves
  110. let Weather = [
  111. 'raindance', 'sunnyday', 'sandstorm', 'hail',
  112. ];
  113.  
  114. // Iterate through all moves we've chosen so far and keep track of what they do:
  115. for (const [k, moveId] of moves.entries()) {
  116. let move = this.getMove(moveId);
  117. if (moveId === 'naturepower') move = this.getMove('triattack');
  118. let moveid = move.id;
  119. let movetype = move.type;
  120. if (['judgment', 'multiattack', 'revelationdance'].includes(moveid)) movetype = Object.keys(hasType)[0];
  121. if (move.damage || move.damageCallback || moveid === 'foulplay') {
  122. // Moves that do a set amount of damage:
  123. counter['damage']++;
  124. if (!['counter', 'mirrorcoat', 'metalburst'].includes(moveid)) {
  125. counter.damagingMoves.push(move);
  126. }
  127. } else {
  128. // Are Physical/Special/Status moves:
  129. counter[move.category]++;
  130. }
  131. // Moves that have a low base power:
  132. if (['lowkick', 'grassknot'].includes(moveid) || (move.basePower && move.basePower <= 60 && moveid !== 'rapidspin')) counter['technician']++;
  133. if (move.multihit && Array.isArray(move.multihit) && move.multihit[1] === 5) counter['skilllink']++;
  134. if (move.recoil || move.hasCustomRecoil) counter['recoil']++;
  135. if (move.drain) counter['drain']++;
  136. // Conversion converts exactly one non-STAB into STAB
  137. if (moveid === 'conversion') {
  138. counter['stab']++;
  139. counter['adaptability']++;
  140. }
  141. // Moves which have a base power, but aren't super-weak like Rapid Spin:
  142. if ((move.basePower > 30 || move.multihit || move.basePowerCallback || moveid === 'naturepower') && !Trap.includes(moveid)) {
  143. counter[movetype]++;
  144. if (hasType[movetype] || movetype === 'Normal' && (hasAbility['Aerilate'] || hasAbility['Galvanize'] || hasAbility['Pixilate'] || hasAbility['Refrigerate'])) {
  145. counter['adaptability']++;
  146. // STAB:
  147. // Certain moves aren't acceptable as a Pokemon's only STAB attack
  148. if (!NoStab.includes(moveid) && (moveid !== 'hiddenpower' || Object.keys(hasType).length === 1)) {
  149. counter['stab']++;
  150. // Ties between Physical and Special setup should be broken in favor of STABs
  151. counter[move.category] += 0.1;
  152. }
  153. } else if (move.priority === 0 && hasAbility['Protean'] && !NoStab.includes(moveid)) {
  154. counter['stab']++;
  155. } else if (movetype === 'Steel' && hasAbility['Steelworker']) {
  156. counter['stab']++;
  157. }
  158. if (move.category === 'Physical') counter['hustle']++;
  159. if (move.flags['bite']) counter['strongjaw']++;
  160. if (move.flags['punch']) counter['ironfist']++;
  161. counter.damagingMoves.push(move);
  162. }
  163. // Moves with secondary effects:
  164. if (move.secondary) {
  165. if (!['flamecharge', 'poweruppunch', 'chargebeam', 'fakeout'].includes(moveid)) {
  166. counter['sheerforce']++;
  167. }
  168. if (move.secondary.chance && move.secondary.chance >= 20 && move.secondary.chance < 100) {
  169. counter['serenegrace']++;
  170. }
  171. }
  172. if (move.accuracy && move.accuracy !== true && move.accuracy < 90) counter['inaccurate']++;
  173. if (move.category !== 'Status' && move.priority !== 0) counter['priority']++;
  174. if (RecoveryMove.includes(moveid)) counter['recovery']++;
  175. if (ContraryMove.includes(moveid)) counter['contrary']++;
  176. if (PhysicalSetup.includes(moveid)) {
  177. counter['physicalsetup']++;
  178. counter.setupType = 'Physical';
  179. }
  180. if (SpecialSetup.includes(moveid)) {
  181. counter['specialsetup']++;
  182. counter.setupType = 'Special';
  183. }
  184. if (MixedSetup.includes(moveid)) counter['mixedsetup']++;
  185. if (SpeedSetup.includes(moveid)) counter['speedsetup']++;
  186. if (DefenseSetup.includes(moveid)) counter['defensesetup']++;
  187. if (Trap.includes(moveid)) counter['trap']++;
  188. if (Hazards.includes(moveid)) counter['hazards']++;
  189. if (Weather.includes(moveid)) counter['weather']++;
  190. }
  191.  
  192. // Keep track of the available moves
  193. for (const moveid of movePool) {
  194. let move = this.getMove(moveid);
  195. if (move.damageCallback) continue;
  196. if (move.category === 'Physical') counter['physicalpool']++;
  197. if (move.category === 'Special') counter['specialpool']++;
  198. }
  199.  
  200. // Choose a setup type:
  201. if (counter['mixedsetup']) {
  202. counter.setupType = 'Mixed';
  203. } else if (counter.setupType) {
  204. let pool = {};
  205. pool.Physical = counter.Physical + counter['physicalpool'];
  206. pool.Special = counter.Special + counter['specialpool'];
  207. if (counter['physicalsetup'] && counter['specialsetup']) {
  208. if (pool.Physical === pool.Special) {
  209. if (counter.Physical > counter.Special) counter.setupType = 'Physical';
  210. else counter.setupType = 'Special';
  211. } else {
  212. counter.setupType = pool.Physical > pool.Special ? 'Physical' : 'Special';
  213. }
  214. } else if (!pool[counter.setupType] || pool[counter.setupType] === 1 && (!moves.includes('rest') || !moves.includes('sleeptalk'))) {
  215. counter.setupType = '';
  216. }
  217. }
  218. counter['Physical'] = Math.floor(counter['Physical']);
  219. counter['Special'] = Math.floor(counter['Special']);
  220.  
  221. return counter;
  222. }
  223.  
  224. /**
  225. * @param {string | Template} template
  226. * @param {number} [slot]
  227. * @param {RandomTeamsTypes["TeamDetails"]} [teamDetails]
  228. * @return {RandomTeamsTypes["RandomSet"]}
  229. */
  230. randomSet(template, slot, teamDetails = {}) {
  231. template = this.getTemplate(template);
  232. let baseTemplate = template;
  233. let species = template.species;
  234. let isDoubles = false;
  235.  
  236. let bannedAbilities = [
  237. 'Drought', 'Drizzle', 'Arena Trap', 'Shadow Tag',
  238. ];
  239.  
  240. let zCrystals = {
  241. Bug: "Buginium Z",
  242. Dark: "Darkinium Z",
  243. Dragon: "Dragonium Z",
  244. Electric: "Electrium Z",
  245. Fairy: "Fairium Z",
  246. Fighting: "Fightinium Z",
  247. Fire: "Firium Z",
  248. Flying: "Flyinium Z",
  249. Ghost: "Ghostium Z",
  250. Grass: "Grassium Z",
  251. Ground: "Groundium Z",
  252. Ice: "Icium Z",
  253. Normal: "Normalium Z",
  254. Poison: "Poisonium Z",
  255. Psychic: "Psychium Z",
  256. Rock: "Rockium Z",
  257. Steel: "Steelium Z",
  258. Water: "Waterium Z",
  259. };
  260.  
  261. if (!template.exists || (!template.randomBattleMoves && !template.learnset)) {
  262. // GET IT? UNOWN? BECAUSE WE CAN'T TELL WHAT THE POKEMON IS
  263. template = this.getTemplate('unown');
  264.  
  265. let err = new Error('Template incompatible with random battles: ' + species);
  266. require('../../lib/crashlogger')(err, 'The randbat set generator');
  267. }
  268.  
  269. if (template.battleOnly) {
  270. // Only change the species. The template has custom moves, and may have different typing and requirements.
  271. species = template.baseSpecies;
  272. }
  273.  
  274. const randMoves = template.randomBattleMoves;
  275. let movePool = (randMoves ? randMoves.slice() : template.learnset ? Object.keys(template.learnset) : []);
  276. /**@type {string[]} */
  277. let moves = [];
  278. let ability = '';
  279. let item = '';
  280. let evs = {
  281. hp: 85,
  282. atk: 85,
  283. def: 85,
  284. spa: 85,
  285. spd: 85,
  286. spe: 85,
  287. };
  288. let ivs = {
  289. hp: 31,
  290. atk: 31,
  291. def: 31,
  292. spa: 31,
  293. spd: 31,
  294. spe: 31,
  295. };
  296. let hasType = {};
  297. hasType[template.types[0]] = true;
  298. if (template.types[1]) {
  299. hasType[template.types[1]] = true;
  300. }
  301. let hasAbility = {};
  302. if (!bannedAbilities.includes(template.abilities[0])) {
  303. hasAbility[template.abilities[0]] = true;
  304. }
  305. if (template.abilities[1] && !bannedAbilities.includes(template.abilities[1])) {
  306. // @ts-ignore
  307. hasAbility[template.abilities[1]] = true;
  308. }
  309. if (template.abilities['H'] && !bannedAbilities.includes(template.abilities['H'])) {
  310. // @ts-ignore
  311. hasAbility[template.abilities['H']] = true;
  312. }
  313. let availableHP = 0;
  314. for (const moveid of movePool) {
  315. if (moveid.startsWith('hiddenpower')) availableHP++;
  316. }
  317.  
  318. // These moves can be used even if we aren't setting up to use them:
  319. let SetupException = [
  320. 'closecombat', 'extremespeed', 'suckerpunch', 'superpower',
  321. 'clangingscales', 'dracometeor', 'leafstorm', 'overheat',
  322. ];
  323. let weatherMoves = [
  324. 'raindance', 'sunnyday', 'sandstorm', 'hail',
  325. ];
  326. let counterAbilities = [
  327. 'Adaptability', 'Contrary', 'Hustle', 'Iron Fist', 'Skill Link', 'Strong Jaw', 'Serene Grace',
  328. ];
  329. let ateAbilities = [
  330. 'Aerilate', 'Galvanize', 'Pixilate', 'Refrigerate',
  331. ];
  332.  
  333. /**@type {{[k: string]: boolean}} */
  334. let hasMove = {};
  335. let counter;
  336.  
  337. // for retrying move selection
  338. let tempMovePool = movePool.slice();
  339. let tryCount = 0;
  340. let availableHPCount = availableHP;
  341.  
  342. do {
  343. // Keep track of all moves we have:
  344. hasMove = {};
  345. for (const moveid of moves) {
  346. if (moveid.startsWith('hiddenpower')) {
  347. hasMove['hiddenpower'] = true;
  348. } else {
  349. hasMove[moveid] = true;
  350. }
  351. }
  352.  
  353. // Choose next 4 moves from learnset/viable moves and add them to moves list:
  354. while (moves.length < 4 && tempMovePool.length) {
  355. let moveid = this.sampleNoReplace(tempMovePool);
  356. if (moveid.startsWith('hiddenpower')) {
  357. availableHPCount--;
  358. if (hasMove['hiddenpower']) continue;
  359. hasMove['hiddenpower'] = true;
  360. } else {
  361. hasMove[moveid] = true;
  362. }
  363. moves.push(moveid);
  364. }
  365.  
  366. counter = this.queryMoves(moves, hasType, hasAbility, tempMovePool);
  367.  
  368. let hasZ = false;
  369.  
  370. // Iterate through the moves again, this time to cull them:
  371. for (const [k, moveId] of moves.entries()) {
  372. let move = this.getMove(moveId);
  373. let moveid = move.id;
  374. let rejected = false;
  375. let isSetup = false;
  376.  
  377. switch (moveid) {
  378.  
  379. // Physical setup
  380. case 'bellydrum': case 'bulkup': case 'coil': case 'curse': case 'dragondance': case 'honeclaws': case 'swordsdance': case 'poweruppunch':
  381. if (counter.setupType !== 'Physical' || counter['physicalsetup'] > 1) rejected = true;
  382. if (counter.Physical < 2 && !(hasMove['rest'] && hasMove['sleeptalk']) && !counter['recovery']) rejected = true;
  383. if (moveid === 'bellydrum' && counter.Physical + counter['recovery'] < 3) rejected = true;
  384. if (moveid === 'dragondance' && teamDetails.trickroom && template.baseStats.spe <= 50) rejected = true;
  385. if (!rejected) isSetup = true;
  386. break;
  387. case 'mirrormove':
  388. if (hasZ || teamDetails.zMove) rejected = true;
  389. if (counter.setupType !== 'Physical' || counter['physicalsetup'] > 1) rejected = true;
  390. if (counter.Physical < 2) rejected = true;
  391. if (!rejected) {
  392. isSetup = true;
  393. hasZ = true;
  394. }
  395. break;
  396.  
  397. // Special setup
  398. case 'calmmind': case 'nastyplot': case 'quiverdance': case 'chargebeam': case 'geomancy':
  399. if (counter.setupType !== 'Special' || counter['specialsetup'] > 1) rejected = true;
  400. if (counter.Special < 2 && !(hasMove['rest'] && hasMove['sleeptalk']) && !counter['recovery']) rejected = true;
  401. if (moveid === 'quiverdance' && teamDetails.trickroom && template.baseStats.spe <= 50) rejected = true;
  402. if (!rejected) isSetup = true;
  403. break;
  404. case 'healblock':
  405. if (hasZ || teamDetails.zMove) rejected = true;
  406. if (counter.setupType !== 'Special' || counter['specialsetup'] > 1) rejected = true;
  407. if (counter.Special < 2) rejected = true;
  408. if (!rejected) {
  409. isSetup = true;
  410. hasZ = true;
  411. }
  412. break;
  413.  
  414. // Mixed setup
  415. case 'growth': case 'shellsmash': case 'workup':
  416. if (moveid === 'shellsmash' && template.species === 'Shuckle' && hasMove['rest'] && (hasMove['infestation'] || hasMove['toxic'])) break;
  417. if (counter.setupType !== 'Mixed' || counter['mixedsetup'] > 1) rejected = true;
  418. if (moveid === 'workup' && counter.damagingMoves.length + counter['recovery'] + counter['speedsetup'] < 3) rejected = true;
  419. if (moveid === 'growth' && !hasMove['sunnyday'] && teamDetails.weather !== 'sun') rejected = true;
  420. if (moveid === 'shellsmash' && teamDetails.trickroom && template.baseStats.spe <= 50) rejected = true;
  421. if (!rejected) isSetup = true;
  422. break;
  423. case 'celebrate': case 'happyhour': case 'trickortreat': case 'forestscurse': case 'conversion': case 'clangingscales': case 'lastresort':
  424. if (hasZ || teamDetails.zMove || isSetup) rejected = true;
  425. if (moveid === 'conversion' && hasMove['triattack']) rejected = true;
  426. if (teamDetails.trickroom && template.baseStats.spe <= 50) rejected = true;
  427. if (!rejected) {
  428. isSetup = true;
  429. hasZ = true;
  430. }
  431. break;
  432.  
  433. // Defense setup
  434. case 'acidarmor': case 'amnesia': case 'barrier': case 'cosmicpower': case 'cottonguard':
  435. case 'defendorder': case 'irondefense': case 'stockpile': case 'magneticflux':
  436. if (!counter['recovery'] && !hasMove['rest']) rejected = true;
  437. if (!rejected) isSetup = true;
  438. break;
  439.  
  440. // Speed setup
  441. case 'agility': case 'autotomize': case 'rockpolish': case 'shiftgear':
  442. if (counter.damagingMoves.length + counter['recovery'] < 2) rejected = true;
  443. if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  444. if (counter['weather']) rejected = true;
  445. if (counter['priority']) rejected = true;
  446. if (hasMove['trickroom'] || (teamDetails.trickroom && template.baseStats.spe <= 50)) rejected = true;
  447. if (!rejected) isSetup = true;
  448. break;
  449. case 'flamecharge':
  450. if (counter.damagingMoves.length + counter['recovery'] < 3 && !counter.setupType) rejected = true;
  451. if (counter['priority']) rejected = true;
  452. if (hasMove['trickroom'] || (teamDetails.trickroom && template.baseStats.spe <= 50)) rejected = true;
  453. if (!rejected) isSetup = true;
  454. break;
  455. case 'mefirst': case 'snatch':
  456. if (hasZ || teamDetails.zMove || counter.damagingMoves.length + counter['recovery'] < 3) rejected = true;
  457. if (counter['priority']) rejected = true;
  458. if (hasMove['trickroom'] || (teamDetails.trickroom && template.baseStats.spe <= 50)) rejected = true;
  459. if (!rejected) {
  460. isSetup = true;
  461. hasZ = true;
  462. }
  463. break;
  464.  
  465. // Trapping moves
  466. case 'bind': case 'clamp': case 'firespin': case 'infestation': case 'sandtomb': case 'whirlpool': case 'wrap':
  467. if (counter['trap'] > 1) rejected = true;
  468. if (counter.setupType && !counter['recovery'] && !hasMove['rest']) rejected = true;
  469. break;
  470. case 'block': case 'meanlook': case 'spiderweb':
  471. if (counter['trap'] > 1) rejected = true;
  472. if (!hasMove['perishsong'] && !hasMove['toxic'] && !hasMove['spite']) rejected = true;
  473. break;
  474.  
  475. // Hazards
  476. case 'stealthrock': case 'toxicspikes': case 'spikes': case 'stickyweb':
  477. let setterMax = (moveid === 'spikes') ? 2 : 1;
  478. if (counter.setupType || hasMove['rest'] && hasMove['sleeptalk'] || teamDetails[moveid] >= setterMax) rejected = true;
  479. break;
  480. case 'defog':
  481. if (counter.setupType || counter['hazards'] || hasMove['rest'] && hasMove['sleeptalk'] || teamDetails.hazardClear > 1) rejected = true;
  482. break;
  483. case 'rapidspin':
  484. if (counter.setupType || hasMove['rest'] && hasMove['sleeptalk'] || teamDetails.hazardClear > 1) rejected = true;
  485. break;
  486.  
  487. // Screens
  488. case 'reflect': case 'lightscreen':
  489. break;
  490. case 'auroraveil':
  491. if (!hasAbility['Snow Warning'] && !hasMove['hail'] && teamDetails.weather !== 'hail') rejected = true;
  492. break;
  493. case 'safeguard':
  494. if (hasMove['destinybond']) rejected = true;
  495. break;
  496.  
  497. // Weather
  498. case 'raindance':
  499. if (hasZ || teamDetails.zMove || counter.Physical + counter.Special < 2 || hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  500. if (!hasAbility['Swift Swim'] && !hasAbility['Dry Skin'] && !hasAbility['Rain Dish'] && !hasMove['thunder']) rejected = true;
  501. if (teamDetails.weather === 'rain' || (hasAbility['Hydration'] && hasMove['rest'] && !hasMove['sleeptalk']) || template.baseSpecies === 'Castform') rejected = false;
  502. if (teamDetails.weather && teamDetails.weather !== 'rain') rejected = true;
  503. break;
  504. case 'sunnyday':
  505. if (hasZ || teamDetails.zMove || counter.Physical + counter.Special < 2 || hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  506. if (!hasAbility['Chlorophyll'] && !hasAbility['Flower Gift'] && !hasMove['solarbeam']) rejected = true;
  507. if (teamDetails.weather === 'sun' || template.baseSpecies === 'Castform') rejected = false;
  508. if (teamDetails.weather && teamDetails.weather !== 'sun') rejected = true;
  509. break;
  510. case 'hail':
  511. if (hasZ || teamDetails.zMove || counter.Physical + counter.Special < 2 || hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  512. if (!hasAbility['Slush Rush'] && !hasAbility['Ice Body'] && !(hasType['Ice'] && hasMove['blizzard'])) rejected = true;
  513. if (teamDetails.weather === 'hail' || template.baseSpecies === 'Castform') rejected = false;
  514. if (teamDetails.weather && teamDetails.weather !== 'hail') rejected = true;
  515. break;
  516. case 'sandstorm':
  517. if (hasZ || teamDetails.zMove || counter.Physical + counter.Special < 2 || hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  518. if (!hasAbility['Sand Rush'] && !hasAbility['Sand Force']) rejected = true;
  519. if (teamDetails.weather === 'sand') rejected = false;
  520. if (teamDetails.weather && teamDetails.weather !== 'sand') rejected = true;
  521. break;
  522.  
  523. // Trick Room
  524. case 'trickroom':
  525. if (hasMove['lightscreen'] || hasMove['reflect']) rejected = true;
  526. if (counter.setupType || !!counter['speedsetup'] || counter.damagingMoves.length < 2) rejected = true;
  527. if (teamDetails.trickroom) rejected = false;
  528. break;
  529.  
  530. // Terrain
  531. case 'electricterrain':
  532. if (!counter['Electric'] || counter.weather) rejected = true;
  533. break;
  534. case 'psychicterrain':
  535. if (!counter['Psychic'] || hasMove['counter'] || counter.weather) rejected = true;
  536. break;
  537.  
  538. // Status-inducing moves (accurate sleep > burn > toxic > para > inaccurate sleep)
  539. case 'darkvoid': case 'grasswhistle': case 'hypnosis': case 'sing':
  540. if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  541. if (hasMove['stunspore'] || hasMove['thunderwave'] || hasMove['glare'] || hasMove['nuzzle'] || hasMove['toxic'] || hasMove['willowisp']) rejected = true;
  542. break;
  543. case 'stunspore': case 'thunderwave': case 'glare': case 'nuzzle':
  544. if (counter.setupType || counter['speedsetup']) rejected = true;
  545. if (hasMove['discharge'] || hasMove['bodyslam'] || hasMove['gyroball'] || hasMove['trickroom']) rejected = true;
  546. if (hasMove['toxic'] || hasMove['willowisp'] || hasMove['spore'] || hasMove['sleeppowder'] || hasMove['lovelykiss']) rejected = true;
  547. break;
  548. case 'toxic':
  549. if (counter.setupType) rejected = true;
  550. if (hasMove['willowisp'] || hasMove['poisonfang'] || hasMove['spore'] || hasMove['sleeppowder'] || hasMove['lovelykiss'] || hasMove['toxicspikes']) rejected = true;
  551. break;
  552. case 'willowisp':
  553. if (counter.setupType) rejected = true;
  554. if (hasMove['scald'] || hasMove['sacredfire'] || hasMove['steameruption'] || hasMove['spore'] || hasMove['sleeppowder'] || hasMove['lovelykiss']) rejected = true;
  555. break;
  556. case 'spore': case 'sleeppowder': case 'lovelykiss':
  557. break;
  558.  
  559. // Confusion-inducing moves
  560. // only reason to confuse is stall
  561. case 'confuseray': case 'sweetkiss':
  562. rejected = true;
  563. if (template.species === 'regigigas') rejected = false;
  564. if (hasMove['toxic'] || hasMove['glare'] || hasMove['thunderwave'] || hasMove['nuzzle'] || hasMove['substitute']) rejected = false;
  565. break;
  566.  
  567. // Recovery (wish > instant recovery > rest)
  568. case 'rest':
  569. if (tempMovePool.includes('sleeptalk') && !hasAbility['Hydration']) rejected = true;
  570. if (hasAbility['Hydration'] && !hasMove['raindance']) rejected = true;
  571. if (!hasMove['sleeptalk'] && template.nfe && template.baseStats.hp + template.baseStats.def + template.baseStats.spd < 200) rejected = true;
  572. if (hasMove['perishsong'] || hasMove['forestscurse']) rejected = false;
  573. if (hasMove['sleeptalk'] && counter.priority) rejected = true;
  574. if (hasMove['sleeptalk'] && !counter.setupType && !counter['defensesetup'] && counter.damagingMoves.length < 2) rejected = true;
  575. if (!hasMove['sleeptalk'] && template.nfe && template.baseStats.hp + template.baseStats.def + template.baseStats.spd >= 200 && !hasAbility['Guts']) rejected = false;
  576. if (counter.recovery || hasMove['painsplit'] || hasMove['wish']) rejected = true;
  577. break;
  578. case 'healorder': case 'milkdrink': case 'moonlight': case 'morningsun': case 'shoreup': case 'slackoff': case 'recover': case 'roost': case 'softboiled': case 'synthesis':
  579. case 'painsplit': case 'strengthsap':
  580. if (hasMove['leechseed'] || hasMove['wish']) rejected = true;
  581. if (['moonlight', 'morningsun', 'synthesis'].includes(moveid) && teamDetails.weather && teamDetails.weather !== 'sun') rejected = true;
  582. break;
  583. case 'psychup':
  584. if (teamDetails.zMove || hasZ || hasMove['rest'] || hasMove['wish'] || counter.recovery) rejected = true;
  585. if (!rejected) hasZ = true;
  586. break;
  587. case 'wish':
  588. break;
  589.  
  590. // Status-curing moves
  591. case 'healbell': case 'aromatherapy':
  592. if (hasAbility['Hydration'] && hasMove['raindance'] && hasMove['rest']) rejected = true;
  593. break;
  594.  
  595. // Sleep Talk (only be used with Rest + 2 suitable moves OR as Z-Sleep Talk on Komala)
  596. case 'sleeptalk':
  597. if (!hasMove['rest']) rejected = true;
  598. if (hasAbility['Comatose'] && !teamDetails.zMove && !hasZ && counter.damagingMoves.length >= 3) rejected = false;
  599. if (hasMove['cottonguard']) rejected = true;
  600. if (counter.priority) rejected = true;
  601. if (!rejected && hasAbility['Comatose']) hasZ = true;
  602. break;
  603.  
  604. // Phazing moves / haze
  605. case 'roar': case 'whirlwind':
  606. if (counter.setupType || counter['speedsetup']) rejected = true;
  607. if (hasMove['dragontail'] || hasMove['circlethrow'] || hasMove['leechseed'] || hasMove['encore'] || hasMove['perishsong'] || counter['trap']) rejected = true;
  608. break;
  609. case 'circlethrow': case 'dragontail':
  610. if (counter.setupType && !(hasMove['rest'] && hasMove['sleeptalk'])) rejected = true;
  611. if (counter['speedsetup'] || hasMove['encore'] || hasMove['perishsong'] || counter['trap']) rejected = true;
  612. break;
  613. case 'haze':
  614. if (hasMove['foulplay']) rejected = true;
  615. if (counter.setupType || counter['speedsetup'] || counter['defensesetup'] || hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  616. if (hasMove['stickyweb']) rejected = true;
  617. break;
  618.  
  619. // Moves that need other moves/items/abilities to be useful at all
  620. case 'endeavor':
  621. if (slot > 0) rejected = true;
  622. if (!counter.setupType && counter.damagingMoves.length <= 2) rejected = false;
  623. if (counter['recovery'] || hasMove['wish'] || hasMove['rest']) rejected = true;
  624. break;
  625. case 'endure':
  626. if (!hasMove['reversal'] || hasMove['substitute']) rejected = true;
  627. break;
  628. case 'focuspunch':
  629. if (!hasMove['substitute'] || counter.damagingMoves.length < 2) rejected = true;
  630. break;
  631. case 'foresight': case 'odorsleuth':
  632. if (!hasMove['rapidspin']) rejected = true;
  633. break;
  634. case 'perishsong':
  635. if (!hasMove['protect'] && !counter.recovery && !hasMove['rest']) rejected = true;
  636. if (!counter['trap']) rejected = true;
  637. break;
  638. case 'reversal':
  639. if (!hasMove['substitute']) rejected = true;
  640. if (!teamDetails.zMove && !hasZ) {
  641. rejected = false;
  642. hasZ = true;
  643. }
  644. break;
  645. case 'storedpower':
  646. if (!counter.setupType && !counter['defensesetup']) rejected = true;
  647. break;
  648. case 'weatherball':
  649. if (!counter.weather) rejected = true;
  650. break;
  651.  
  652. // Z-moves only
  653. case 'bounce': case 'dig': case 'fly':
  654. if (teamDetails.zMove || hasZ || counter.setupType !== 'Physical') rejected = true;
  655. if (!rejected) hasZ = true;
  656. break;
  657. case 'gigaimpact': case 'hyperbeam':
  658. if (teamDetails.zMove || hasZ || !counter.setupType) rejected = true;
  659. if (hasAbility['Truant']) rejected = false;
  660. if (!rejected && !hasAbility['Truant']) hasZ = true;
  661. break;
  662. case 'solarbeam':
  663. if (!hasAbility['Drought'] && !hasMove['sunnyday']) rejected = true;
  664. if (!teamDetails.zMove && !hasZ && !counter['Grass'] && !hasType['Grass']) {
  665. rejected = false;
  666. hasZ = true;
  667. }
  668. break;
  669. case 'zapcannon':
  670. if (teamDetails.zMove || hasZ) rejected = true;
  671. if (hasMove['facade']) rejected = true; // for Flareon
  672. if (!rejected) hasZ = true;
  673. break;
  674.  
  675. // Volt turn (U-turn > Volt Switch > Parting Shot)
  676. case 'partingshot':
  677. if (counter.setupType || counter['speedsetup']) rejected = true;
  678. if (hasMove['uturn'] || hasMove['voltswitch']) rejected = true;
  679. break;
  680. case 'voltswitch':
  681. if (counter.setupType || counter['speedsetup']) rejected = true;
  682. if (hasMove['uturn']) rejected = true;
  683. break;
  684. case 'uturn':
  685. if (counter.setupType || counter['speedsetup']) rejected = true;
  686. break;
  687.  
  688. // Attack moves that don't take attack stats into account
  689. case 'foulplay':
  690. if (counter.setupType || counter['speedsetup']) rejected = true;
  691. if (counter.damagingMoves.length > 2) rejected = true;
  692. if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  693. if (counter.damagingMoves.length - 1 === counter['priority']) rejected = true;
  694. break;
  695. case 'nightshade': case 'seismictoss': case 'superfang': case 'psywave':
  696. if (counter.setupType || counter['speedsetup']) rejected = true;
  697. if (counter.damagingMoves.length > 2) rejected = true;
  698. if ((moveid === 'seismictoss' || moveid === 'superfang') && (counter['Fighting'] + counter['Normal'] > 0 || hasMove['nightshade'])) rejected = true;
  699. if (moveid === 'superfang' && hasMove['seismictoss']) rejected = true;
  700. if (moveid === 'nightshade' && counter['Ghost'] > 0) rejected = true;
  701. if (moveid === 'psywave' && (counter['Psychic'] > 0 || hasMove['seismictoss'])) rejected = true;
  702. break;
  703.  
  704. // Protect
  705. case 'protect':
  706. if (counter.setupType && !hasMove['wish']) rejected = true;
  707. if (counter.damagingMoves.length > 2) rejected = true;
  708. if (hasMove['rest'] || hasMove['lightscreen'] && hasMove['reflect']) rejected = true;
  709. break;
  710. case 'banefulbunker':
  711. if (hasMove['spikyshield']) rejected = true;
  712. break;
  713. case 'spikyshield':
  714. break;
  715.  
  716. // Other status moves
  717. case 'leechseed':
  718. if (counter.setupType || counter['speedsetup']) rejected = true;
  719. if (hasMove['dragontail'] || hasMove['roar'] || hasMove['whirlwind']) rejected = true;
  720. break;
  721. case 'substitute':
  722. if (hasMove['dracometeor'] || (hasMove['leafstorm'] && !hasAbility['Contrary'])) rejected = true;
  723. if (hasMove['rest'] || hasMove['taunt']) rejected = true;
  724. if (hasMove['uturn'] || hasMove['voltswitch'] || hasMove['partingshot']) rejected = true;
  725. if (hasMove['counter'] || hasMove['mirrorcoat'] || hasMove['metalburst']) rejected = true;
  726. break;
  727. case 'switcheroo': case 'trick':
  728. if (counter.Physical + counter.Special < 3 || counter.setupType || counter['speedsetup']) rejected = true;
  729. if (hasMove['acrobatics'] || hasMove['suckerpunch']) rejected = true;
  730. if (hasAbility['Klutz']) rejected = false; // for Lopunny
  731. break;
  732. case 'taunt': case 'encore': case 'disable':
  733. if (hasMove['sleeptalk']) rejected = true;
  734. break;
  735. case 'yawn':
  736. if (hasMove['toxic'] || hasMove['willowisp'] || hasMove['thunderwave']) rejected = true;
  737. break;
  738. case 'tailwind':
  739. if (counter['speedsetup']) rejected = true;
  740. break;
  741. case 'healingwish': case 'memento':
  742. if (counter.setupType || counter['recovery'] || hasMove['wish'] || hasMove['substitute'] || hasMove['destinybond']) rejected = true;
  743. break;
  744. case 'destinybond':
  745. if (hasMove['substitute']) rejected = true;
  746. break;
  747. case 'magiccoat':
  748. if (hasMove['facade'] || hasMove['rest']) rejected = true;
  749. break;
  750. case 'soak':
  751. if (!hasMove['toxic']) rejected = true;
  752. break;
  753. case 'screech':
  754. if (counter.Physical < 3) rejected = true;
  755. break;
  756. case 'magnetrise':
  757. if (hasMove['voltswitch']) rejected = true;
  758. break;
  759. case 'assist':
  760. break;
  761. case 'spite':
  762. if ((hasMove['soak'] && hasMove['toxic']) || !hasMove['block']) rejected = true;
  763. break;
  764.  
  765. // Bad after setup
  766. case 'fakeout':
  767. if (counter.setupType || hasMove['substitute'] || hasMove['switcheroo'] || hasMove['trick']) rejected = true;
  768. break;
  769. case 'waterspout':
  770. if (counter.setupType || !!counter['speedsetup'] || (hasMove['rest'] && hasMove['sleeptalk'])) rejected = true;
  771. break;
  772. case 'pursuit':
  773. if (counter.setupType || (hasMove['rest'] && hasMove['sleeptalk']) || counter['Dark'] > 2 || (hasMove['knockoff'] && !hasType['Dark'])) rejected = true;
  774. break;
  775.  
  776. // Bit redundant to have both
  777. // Attacks:
  778. case 'bugbite': case 'bugbuzz': case 'signalbeam':
  779. if (hasMove['uturn'] && !counter.setupType) rejected = true;
  780. if (hasMove['xscissor']) rejected = true;
  781. break;
  782. case 'lunge':
  783. if (hasMove['leechlife']) rejected = true;
  784. break;
  785. case 'darkestlariat': case 'nightslash':
  786. if (hasMove['knockoff'] || hasMove['pursuit'] || hasMove['throatchop']) rejected = true;
  787. break;
  788. case 'darkpulse':
  789. if (hasMove['shadowball']) rejected = true;
  790. if ((hasMove['crunch'] || hasMove['hyperspacefury']) && counter.setupType !== 'Special') rejected = true;
  791. break;
  792. case 'suckerpunch':
  793. if (counter['Dark'] > 1 && !hasType['Dark']) rejected = true;
  794. if (counter.damagingMoves.length < 2 || hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  795. break;
  796. case 'thief':
  797. if ((hasMove['darkpulse'] || hasMove['foulplay'] || hasMove['pursuit']) && counter.setupType !== 'Physical') rejected = true;
  798. if (hasMove['suckerpunch']) rejected = true;
  799. break;
  800. case 'dualchop':
  801. if (hasMove['dragonclaw'] || hasMove['dragontail'] || hasMove['outrage']) rejected = true;
  802. break;
  803. case 'dragonclaw':
  804. if (hasMove['dragontail'] || hasMove['outrage']) rejected = true;
  805. break;
  806. case 'dracometeor':
  807. if (hasMove['swordsdance'] || counter.setupType === 'Physical' && hasMove['outrage']) rejected = true;
  808. break;
  809. case 'dragonpulse': case 'spacialrend':
  810. if (hasMove['dracometeor'] || hasMove['outrage']) rejected = true;
  811. break;
  812. case 'outrage':
  813. if (hasMove['dracometeor'] && counter.damagingMoves.length < 3) rejected = true;
  814. if (hasMove['clangingscales'] && !teamDetails.zMove) rejected = true;
  815. break;
  816. case 'discharge':
  817. if (hasMove['thunderbolt'] && !isDoubles) rejected = true;
  818. break;
  819. case 'thunder':
  820. if ((hasMove['thunderbolt'] || tempMovePool.includes('thunderbolt')) && !hasMove['raindance']) rejected = true;
  821. if (hasMove['sunnyday']) rejected = true;
  822. break;
  823. case 'thunderbolt':
  824. if ((hasMove['discharge'] && isDoubles) || (hasMove['raindance'] && hasMove['thunder']) || (hasMove['voltswitch'] && hasMove['wildcharge'])) rejected = true;
  825. if (hasMove['thunderfang'] && counter.setupType === 'Physical') rejected = true;
  826. break;
  827. case 'thunderfang':
  828. if ((hasMove['thunderbolt'] || tempMovePool.includes('thunderbolt')) && counter.setupType !== 'Physical') rejected = true;
  829. break;
  830. case 'thunderpunch':
  831. if (hasAbility['Galvanize'] && !!counter['Normal']) rejected = true;
  832. if (hasMove['thunder'] && counter.setupType !== 'Physical') rejected = true;
  833. break;
  834. case 'wildcharge':
  835. if (hasMove['thunderbolt'] && !hasMove['voltswitch']) rejected = true;
  836. break;
  837. case 'dazzlinggleam':
  838. if (hasMove['playrough'] && counter.setupType !== 'Special') rejected = true;
  839. break;
  840. case 'drainingkiss':
  841. if (hasMove['dazzlinggleam'] || counter.setupType !== 'Special' && !hasAbility['Triage']) rejected = true;
  842. break;
  843. case 'aurasphere': case 'focusblast':
  844. if ((hasMove['closecombat'] || hasMove['superpower']) && counter.setupType !== 'Special') rejected = true;
  845. if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  846. break;
  847. case 'drainpunch':
  848. if (!hasMove['bulkup'] && (hasMove['closecombat'] || hasMove['highjumpkick'])) rejected = true;
  849. if ((hasMove['focusblast'] || hasMove['superpower']) && counter.setupType !== 'Physical') rejected = true;
  850. break;
  851. case 'closecombat': case 'highjumpkick':
  852. if ((hasMove['aurasphere'] || hasMove['focusblast'] || tempMovePool.includes('aurasphere')) && counter.setupType === 'Special') rejected = true;
  853. if (hasMove['bulkup'] && hasMove['drainpunch']) rejected = true;
  854. break;
  855. case 'machpunch':
  856. if (hasType['Fighting'] && counter.stab < 2 && !hasAbility['Technician']) rejected = true;
  857. break;
  858. case 'stormthrow':
  859. if (hasMove['circlethrow'] && hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  860. break;
  861. case 'superpower':
  862. if (counter['Fighting'] > 1 && counter.setupType) rejected = true;
  863. if (hasMove['rest'] && hasMove['sleeptalk'] && !hasAbility['Contrary']) rejected = true;
  864. if (hasAbility['Contrary']) isSetup = true;
  865. break;
  866. case 'vacuumwave':
  867. if ((hasMove['closecombat'] || hasMove['machpunch']) && counter.setupType !== 'Special') rejected = true;
  868. break;
  869. case 'lowkick':
  870. if (hasMove['closecombat'] || hasMove['machpunch']) rejected = true;
  871. break;
  872. case 'fierydance': case 'firepunch': case 'flamethrower': case 'flareblitz':
  873. if (hasMove['blazekick'] || hasMove['heatwave'] || hasMove['overheat'] || hasMove['sacredfire']) rejected = true;
  874. if (hasMove['fireblast'] && counter.setupType !== 'Physical' && !hasAbility['Reckless']) rejected = true;
  875. if (moveid === 'firepunch' && counter.setupType !== 'Physical' && hasMove['flamethrower']) rejected = true;
  876. break;
  877. case 'fireblast': case 'magmastorm':
  878. if (hasMove['lavaplume'] && !counter.setupType && !counter['speedsetup']) rejected = true;
  879. if (hasMove['mindblown'] && counter.setupType) rejected = true;
  880. if (hasMove['flareblitz'] && hasAbility['Reckless']) rejected = true;
  881. break;
  882. case 'firefang':
  883. if (counter['Fire'] > 1) rejected = true;
  884. break;
  885. case 'lavaplume':
  886. if (hasMove['firepunch'] || hasMove['fireblast'] && (counter.setupType || !!counter['speedsetup'])) rejected = true;
  887. break;
  888. case 'overheat':
  889. if (hasMove['fireblast'] || hasMove['lavaplume'] || counter.setupType === 'Special') rejected = true;
  890. break;
  891. case 'acrobatics':
  892. if (hasMove['memento']) rejected = true;
  893. break;
  894. case 'airslash':
  895. if (hasMove['acrobatics'] || hasMove['bravebird']) rejected = true;
  896. if (hasMove['hurricane'] && !tempMovePool.includes('raindance') && !hasMove['sunnyday']) rejected = true;
  897. break;
  898. case 'bravebird':
  899. if (hasMove['hurricane'] && hasMove['raindance']) rejected = true;
  900. break;
  901. case 'hurricane':
  902. if (tempMovePool.includes('raindance')) rejected = true;
  903. if ((hasMove['bravebird'] || hasMove['acrobatics']) && !hasMove['raindance']) rejected = true;
  904. if (hasMove['sunnyday']) rejected = true;
  905. break;
  906. case 'hex':
  907. if (hasMove['shadowball'] && !hasMove['willowisp']) rejected = true;
  908. break;
  909. case 'shadowball':
  910. if (hasMove['hex'] && hasMove['willowisp']) rejected = true;
  911. break;
  912. case 'shadowclaw':
  913. if (hasMove['phantomforce'] || hasMove['shadowforce'] || hasMove['shadowsneak']) rejected = true;
  914. if (hasMove['shadowball'] && counter.setupType !== 'Physical') rejected = true;
  915. break;
  916. case 'shadowsneak':
  917. if (hasType['Ghost'] && template.types.length > 1 && counter.stab < 2) rejected = true;
  918. if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  919. if (hasMove['shadowpunch']) rejected = true;
  920. break;
  921. case 'bulletseed':
  922. if (counter['Grass'] > 1 && !hasAbility['Technician'] && !hasAbility['Skill Link']) rejected = true;
  923. break;
  924. case 'energyball':
  925. if (hasMove['gigadrain'] || hasMove['solarbeam'] && (hasAbility['Drought'] || hasMove['sunnyday'])) rejected = true;
  926. if ((hasMove['leafblade'] || hasMove['seedbomb']) && counter.setupType !== 'Special') rejected = true;
  927. break;
  928. case 'gigadrain':
  929. if (hasMove['petaldance'] || hasMove['powerwhip'] || (hasMove['seedbomb'] && !isDoubles)) rejected = true;
  930. if (counter.Special < 4 && !counter.setupType && hasMove['leafstorm']) rejected = true;
  931. break;
  932. case 'leafblade': case 'woodhammer':
  933. if (hasMove['gigadrain'] && counter.setupType !== 'Physical') rejected = true;
  934. if (hasMove['leafstorm'] && hasAbility['Contrary']) rejected = true;
  935. break;
  936. case 'leafstorm':
  937. if (counter['Grass'] > 1 && counter.setupType) rejected = true;
  938. break;
  939. case 'seedbomb':
  940. if (hasMove['woodhammer']) rejected = true;
  941. break;
  942. case 'powerwhip':
  943. if ((hasAbility['Drought'] || hasMove['sunnyday']) && hasMove['solarbeam']) rejected = true;
  944. break;
  945. case 'bonemerang': case 'precipiceblades':
  946. if (hasMove['earthquake']) rejected = true;
  947. break;
  948. case 'earthpower':
  949. if (hasMove['earthquake'] && counter.setupType !== 'Special') rejected = true;
  950. break;
  951. case 'avalanche':
  952. if (counter['Ice'] > 1) rejected = true;
  953. break;
  954. case 'blizzard':
  955. if (hasMove['icebeam'] && !teamDetails['hail']) rejected = true;
  956. break;
  957. case 'icebeam':
  958. if (hasMove['blizzard'] && teamDetails['hail'] || hasMove['freezedry']) rejected = true;
  959. break;
  960. case 'iceshard':
  961. if (hasMove['freezedry']) rejected = true;
  962. break;
  963. case 'icywind':
  964. if (hasMove['icebeam']) rejected = true;
  965. break;
  966. case 'frostbreath': // added z-move check for Glaceon
  967. if ((hasMove['icebeam'] || hasMove['blizzard'] || hasMove['freezedry']) && !teamDetails.zMove) rejected = true;
  968. break;
  969. case 'bodyslam':
  970. if (hasMove['glare'] && hasMove['headbutt'] || hasMove['doubleedge']) rejected = true;
  971. break;
  972. case 'explosion':
  973. if (counter.setupType || (hasAbility['Refrigerate'] && hasMove['freezedry']) || hasMove['wish']) rejected = true;
  974. break;
  975. case 'extremespeed':
  976. if (counter.setupType !== 'Physical' && hasMove['vacuumwave']) rejected = true;
  977. break;
  978. case 'facade':
  979. if (hasMove['rest'] && hasMove['sleeptalk']) rejected = true;
  980. if (hasMove['dynamicpunch']) rejected = true; // for Machoke
  981. if (counter.damagingMoves.length < 2) rejected = true;
  982. break;
  983. case 'gigaimpact':
  984. if (hasMove['doubleedge'] || hasMove['return']) rejected = true;
  985. break;
  986. case 'hiddenpower':
  987. if (hasMove['rest'] || (!counter.stab && counter.damagingMoves.length < 2) || counter[move.type] > 1) rejected = true;
  988. break;
  989. case 'hypervoice':
  990. if (hasMove['blizzard'] || hasMove['naturepower'] || hasMove['return']) rejected = true;
  991. break;
  992. case 'judgment':
  993. if (counter.setupType !== 'Special' && counter.stab > 1) rejected = true;
  994. break;
  995. case 'quickattack':
  996. if (hasType['Normal'] && (!counter.stab || counter['Normal'] > 2)) rejected = true;
  997. if (hasMove['feint']) rejected = true;
  998. break;
  999. case 'return': case 'rockclimb':
  1000. if (hasMove['bodyslam'] || hasMove['doubleedge'] || hasMove['headcharge'] || hasMove['headbutt']) rejected = true;
  1001. break;
  1002. case 'acidspray':
  1003. if (hasMove['sludgebomb'] || counter.Special < 2) rejected = true;
  1004. break;
  1005. case 'poisonjab':
  1006. if (hasMove['gunkshot']) rejected = true;
  1007. if ((hasMove['sludgewave'] || hasMove['sludgebomb']) && counter.setupType !== 'Physical') rejected = true;
  1008. break;
  1009. case 'sludgewave':
  1010. if (hasMove['poisonjab']) rejected = true;
  1011. break;
  1012. case 'sludgebomb':
  1013. if (hasMove['sludgewave']) rejected = true;
  1014. break;
  1015. case 'photongeyser': case 'psychic':
  1016. if (hasMove['psyshock'] || counter.setupType === 'Special' && hasMove['storedpower']) rejected = true;
  1017. break;
  1018. case 'psychocut': case 'zenheadbutt':
  1019. if ((hasMove['psychic'] || hasMove['psyshock']) && counter.setupType !== 'Physical') rejected = true;
  1020. if (hasAbility['Contrary'] && !counter.setupType && !!counter['physicalpool']) rejected = true;
  1021. break;
  1022. case 'psyshock':
  1023. if (tempMovePool.length > 1) {
  1024. let psychic = tempMovePool.indexOf('psychic');
  1025. if (psychic >= 0) this.fastPop(tempMovePool, psychic);
  1026. }
  1027. break;
  1028. case 'ancientpower':
  1029. if (hasMove['powergem'] || hasMove['rockslide']) rejected = true;
  1030. break;
  1031. case 'headsmash': case 'powergem':
  1032. if (hasMove['stoneedge']) rejected = true;
  1033. break;
  1034. case 'rockblast': case 'rockslide':
  1035. if (hasMove['headsmash'] || hasMove['stoneedge'] || hasMove['powergem']) rejected = true;
  1036. break;
  1037. case 'smackdown':
  1038. if (!counter['Ground'] || !!counter['Rock']) rejected = true;
  1039. break;
  1040. case 'rocktomb':
  1041. if (!!counter['Rock'] || !!counter['speedsetup']) rejected = true;
  1042. break;
  1043. case 'bulletpunch':
  1044. if (hasType['Steel'] && counter.stab < 2 && !hasAbility['Adaptability'] && !hasAbility['Technician']) rejected = true;
  1045. break;
  1046. case 'flashcannon':
  1047. if ((hasMove['ironhead'] || hasMove['meteormash'] || hasMove['gyroball']) && counter.setupType !== 'Special') rejected = true;
  1048. break;
  1049. case 'gyroball':
  1050. if ((hasMove['ironhead'] || hasMove['meteormash']) && template.baseStats.spe > 30) rejected = true;
  1051. if (hasMove['flashcannon'] && counter.setupType === 'Special') rejected = true;
  1052. if (counter['speedsetup']) rejected = true;
  1053. break;
  1054. case 'ironhead': case 'meteormash':
  1055. if (hasMove['gyroball'] && template.baseStats.spe <= 30) rejected = true;
  1056. if (hasMove['flashcannon'] && counter.setupType === 'Special') rejected = true;
  1057. break;
  1058. case 'hydropump':
  1059. if (hasMove['liquidation'] || hasMove['razorshell'] || hasMove['waterfall'] || (hasMove['rest'] && hasMove['sleeptalk'])) rejected = true;
  1060. if (hasMove['scald'] && (counter.Special < 4 || template.types.length > 1 && counter.stab < 3)) rejected = true;
  1061. break;
  1062. case 'originpulse': case 'surf':
  1063. if (hasMove['hydropump'] || hasMove['scald'] || hasMove['waterfall'] || hasMove['liquidation']) rejected = true;
  1064. break;
  1065. case 'scald':
  1066. if (hasMove['liquidation'] || hasMove['waterfall'] || hasMove['waterpulse']) rejected = true;
  1067. break;
  1068. case 'waterfall':
  1069. if (hasMove['liquidation']) rejected = true;
  1070. break;
  1071.  
  1072. // Status:
  1073. case 'electroweb':
  1074. if (counter.setupType || !!counter['speedsetup'] || (hasMove['rest'] && hasMove['sleeptalk'])) rejected = true;
  1075. if (hasMove['discharge'] || hasMove['gyroball'] || hasMove['spore'] || hasMove['toxic'] || hasMove['trickroom'] || hasMove['yawn']) rejected = true;
  1076. break;
  1077. case 'powersplit':
  1078. if (hasMove['guardsplit']) rejected = true;
  1079. break;
  1080. case 'featherdance':
  1081. if (hasMove['foulplay'] || hasMove['haze']) rejected = true;
  1082. break;
  1083. }
  1084.  
  1085. // Increased/decreased priority moves are unneeded with moves that boost only speed
  1086. if (move.priority !== 0 && (!!counter['speedsetup'] || hasMove['copycat'])) {
  1087. rejected = true;
  1088. }
  1089.  
  1090. // Certain Pokemon should always have a recovery move
  1091. if (!counter.recovery && template.baseStats.hp >= 125 && tempMovePool.includes('wish')) {
  1092. if (move.category === 'Status' || !hasType[move.type] && !move.damage) rejected = true;
  1093. }
  1094.  
  1095. // Force Pyukumuku to have Toxic
  1096. if (template.id === 'pyukumuku' && !hasMove['toxic']) {
  1097. rejected = true;
  1098. }
  1099.  
  1100. // Give slightly more chance to have hazard removal (around 1/3 chance to reroll for it if exists in movepool)
  1101. if ((tempMovePool.includes('defog') || tempMovePool.includes('rapidspin')) && !teamDetails.hazardClear && this.randomChance(1, 10)) {
  1102. rejected = true;
  1103. }
  1104.  
  1105. // Weather/movetype incompatibility
  1106. if (move.type === 'Fire' && hasMove['raindance'] || move.type === 'Water' && hasMove['sunnyday']) {
  1107. rejected = true;
  1108. }
  1109.  
  1110. // No more than one weather move
  1111. if (weatherMoves.includes(moveid) && counter['weather'] > 1) {
  1112. rejected = true;
  1113. }
  1114.  
  1115. // Force weather move if team needs it
  1116. if (weatherMoves.filter(move => tempMovePool.includes(move)).length && this.randomChance(1, 5)) {
  1117. rejected = true;
  1118. }
  1119.  
  1120. // This move doesn't satisfy our setup requirements:
  1121. if ((move.category === 'Physical' && counter.setupType === 'Special') || (move.category === 'Special' && counter.setupType === 'Physical')) {
  1122. // Reject STABs last in case the setup type changes later on
  1123. if (!SetupException.includes(moveid) && (!hasType[move.type] || counter.stab > 1 || counter[move.category] < 2)) rejected = true;
  1124. }
  1125. if (counter.setupType && !isSetup && counter.setupType !== 'Mixed' && move.category !== counter.setupType && counter[counter.setupType] < 2 && moveid !== 'rest' && moveid !== 'sleeptalk') {
  1126. // Mono-attacking with setup and RestTalk is allowed
  1127. // Reject Status moves only if there is nothing else to reject
  1128. if (move.category !== 'Status' || counter[counter.setupType] + counter.Status > 3 && counter['physicalsetup'] + counter['specialsetup'] < 2) rejected = true;
  1129. }
  1130. if (counter.setupType === 'Special' && moveid === 'hiddenpower' && template.types.length > 1 && counter['Special'] <= 2 && !hasType[move.type] && !counter['Physical'] && counter['specialpool']) {
  1131. // Hidden Power isn't good enough
  1132. rejected = true;
  1133. }
  1134.  
  1135. // Pokemon should have moves that benefit their Type/Ability/Weather, as well as moves required by its forme
  1136. if (!rejected && template.species !== 'Pyukumuku' && template.species !== 'Smeargle' &&
  1137. (counter['physicalsetup'] + counter['specialsetup'] < 2 && (!counter.setupType || counter.setupType === 'Mixed' || (move.category !== counter.setupType && move.category !== 'Status') || counter[counter.setupType] + counter.Status > 3)) &&
  1138. ((counter.damagingMoves.length === 0 && !hasMove['metalburst']) ||
  1139. (!counter.stab && (template.types.length > 1 || (template.types[0] !== 'Normal' && template.types[0] !== 'Psychic') || !hasMove['icebeam'] || template.baseStats.spa >= template.baseStats.spd) && (!!counter['physicalpool'] || !!counter['specialpool']) && !hasMove['foulplay']) ||
  1140. (hasType['Bug'] && (tempMovePool.includes('megahorn') || tempMovePool.includes('pinmissile') || (hasType['Flying'] && !hasMove['hurricane'] && tempMovePool.includes('bugbuzz')))) ||
  1141. ((hasType['Dark'] && !counter['Dark']) || hasMove['suckerpunch'] && !hasAbility['Contrary'] && counter.stab < template.types.length) ||
  1142. (hasType['Dragon'] && !counter['Dragon'] && !hasAbility['Aerilate'] && !hasAbility['Pixilate'] && !hasMove['rest'] && !hasMove['sleeptalk']) ||
  1143. (hasType['Electric'] && !counter['Electric'] && !hasAbility['Galvanize']) ||
  1144. (hasType['Fairy'] && !counter['Fairy'] && (!!counter['speedsetup'] || !counter['Status'])) ||
  1145. (hasType['Fighting'] && !counter['Fighting'] && (counter.setupType || !counter['Status'])) ||
  1146. (hasType['Fire'] && !counter['Fire']) ||
  1147. (hasType['Ghost'] && !hasType['Dark'] && !counter['Ghost'] && !hasAbility['Steelworker'] && !hasMove['foulplay']) ||
  1148. (hasType['Grass'] && !hasType['Fairy'] && !hasType['Poison'] && !hasType['Steel'] && !counter['Grass']) ||
  1149. (hasType['Ground'] && !counter['Ground'] && !hasMove['rest'] && !hasMove['sleeptalk']) ||
  1150. (hasType['Ice'] && !counter['Ice'] && !hasAbility['Refrigerate']) ||
  1151. (hasType['Psychic'] && !!counter['Psychic'] && !hasType['Flying'] && !hasAbility['Pixilate'] && template.types.length > 1 && counter.stab < 2) ||
  1152. (hasType['Rock'] && !counter['Rock'] && counter.setupType === 'Physical') ||
  1153. (((hasType['Steel'] && hasAbility['Technician']) || hasAbility['Steelworker']) && !counter['Steel']) ||
  1154. (hasType['Water'] && (!counter['Water'] || !counter.stab) && !hasAbility['Protean'] && !hasMove['willowisp']) ||
  1155. ((hasAbility['Adaptability'] && !counter.setupType && template.types.length > 1 && (!counter[template.types[0]] || !counter[template.types[1]])) ||
  1156. ((hasAbility['Aerilate'] || (hasAbility['Galvanize'] && !counter['Electric']) || hasAbility['Pixilate'] || (hasAbility['Refrigerate'] && !hasMove['blizzard'])) && !counter['Normal']) ||
  1157. (hasAbility['Contrary'] && !counter['contrary'] && template.species !== 'Shuckle') ||
  1158. (hasAbility['Gale Wings'] && !counter['Flying']) ||
  1159. (hasAbility['Guts'] && hasType['Normal'] && tempMovePool.includes('facade')) ||
  1160. (hasAbility['Psychic Surge'] && !counter['Psychic']) ||
  1161. (hasAbility['Slow Start'] && tempMovePool.includes('substitute')) ||
  1162. (hasAbility['Stance Change'] && !counter.setupType && tempMovePool.includes('kingsshield')) ||
  1163. (template.requiredMove && tempMovePool.includes(toId(template.requiredMove)))))) {
  1164. // Reject Status or non-STAB
  1165. if (!isSetup && !move.weather && moveid !== 'judgment' && moveid !== 'rest' && moveid !== 'sleeptalk' && !hasMove['perishsong']) {
  1166. if (move.category === 'Status' || !hasType[move.type] || move.selfSwitch || move.basePower && move.basePower < 40 && !move.multihit) rejected = true;
  1167. }
  1168. }
  1169.  
  1170. // Sleep Talk shouldn't be selected without Rest
  1171. if (moveid === 'rest' && rejected) {
  1172. let sleeptalk = tempMovePool.indexOf('sleeptalk');
  1173. if (sleeptalk >= 0) {
  1174. if (tempMovePool.length < 2) {
  1175. rejected = false;
  1176. } else {
  1177. this.fastPop(tempMovePool, sleeptalk);
  1178. }
  1179. }
  1180. }
  1181.  
  1182. // Remove rejected moves from the move list
  1183. if (rejected) {
  1184. if (tempMovePool.length - availableHPCount || availableHPCount && (moveid === 'hiddenpower' || !hasMove['hiddenpower'])) {
  1185. // There are still moves left in move pool
  1186. moves.splice(k, 1);
  1187. } else if (tryCount < 10) {
  1188. // No more moves in move pool, reset everything and increment try count
  1189. moves = [];
  1190. tempMovePool = movePool.slice();
  1191. availableHPCount = availableHP;
  1192. tryCount++;
  1193. }
  1194. break;
  1195. }
  1196. }
  1197. } while (moves.length < 4 && tempMovePool.length);
  1198.  
  1199. // Moveset modifications
  1200. if (hasMove['autotomize'] && hasMove['heavyslam']) {
  1201. if (template.id === 'celesteela') {
  1202. moves[moves.indexOf('heavyslam')] = 'flashcannon';
  1203. } else {
  1204. moves[moves.indexOf('autotomize')] = 'rockpolish';
  1205. }
  1206. }
  1207. if (moves[0] === 'conversion') {
  1208. moves[0] = moves[3];
  1209. moves[3] = 'conversion';
  1210. }
  1211.  
  1212. /**@type {[string, string | undefined, string | undefined]} */
  1213. // @ts-ignore
  1214. let abilities = Object.values(baseTemplate.abilities);
  1215. abilities.sort((a, b) => this.getAbility(b).rating - this.getAbility(a).rating);
  1216. let ability0 = this.getAbility(abilities[0]);
  1217. let ability1 = this.getAbility(abilities[1]);
  1218. let ability2 = this.getAbility(abilities[2]);
  1219. if (abilities[1]) {
  1220. if (abilities[2] && ability1.rating <= ability2.rating && this.randomChance(1, 2)) {
  1221. [ability1, ability2] = [ability2, ability1];
  1222. }
  1223. if (ability0.rating <= ability1.rating && this.randomChance(1, 2)) {
  1224. [ability0, ability1] = [ability1, ability0];
  1225. } else if (ability0.rating - 0.6 <= ability1.rating && this.randomChance(1, 3)) {
  1226. [ability0, ability1] = [ability1, ability0];
  1227. }
  1228. ability = ability0.name;
  1229.  
  1230. let rejectAbility;
  1231. do {
  1232. rejectAbility = this.getAbility(ability).rating <= 0;
  1233. if (bannedAbilities.includes(ability)) {
  1234. rejectAbility = true;
  1235. } else if (counterAbilities.includes(ability)) {
  1236. // Adaptability, Contrary, Hustle, Iron Fist, Skill Link, Strong Jaw, Serene Grace
  1237. rejectAbility = !counter[toId(ability)];
  1238. } else if (ateAbilities.includes(ability)) {
  1239. rejectAbility = !counter['Normal'];
  1240. } else if (ability === 'Blaze') {
  1241. rejectAbility = !counter['Fire'];
  1242. } else if (ability === 'Chlorophyll') {
  1243. rejectAbility = !hasMove['sunnyday'] && teamDetails.weather !== 'sun';
  1244. } else if (ability === 'Competitive') {
  1245. rejectAbility = !counter['Special'];
  1246. } else if (ability === 'Compound Eyes' || ability === 'No Guard') {
  1247. rejectAbility = !counter['inaccurate'];
  1248. } else if (ability === 'Defiant' || ability === 'Moxie') {
  1249. rejectAbility = !counter['Physical'];
  1250. } else if (ability === 'Flare Boost' || ability === 'Moody' || ability === 'Innards Out') {
  1251. rejectAbility = true;
  1252. } else if (ability === 'Gluttony') {
  1253. rejectAbility = !hasMove['bellydrum'];
  1254. } else if (ability === 'Hydration' || ability === 'Rain Dish' || ability === 'Swift Swim') {
  1255. rejectAbility = !hasMove['raindance'] && teamDetails.weather !== 'rain';
  1256. } else if (ability === 'Ice Body' || ability === 'Slush Rush' || ability === 'Snow Cloak') {
  1257. rejectAbility = !hasMove['hail'] && teamDetails.weather !== 'hail';
  1258. } else if (ability === 'Leaf Guard') {
  1259. rejectAbility = teamDetails.weather !== 'sun';
  1260. } else if (ability === 'Lightning Rod') {
  1261. rejectAbility = template.types.includes('Ground');
  1262. } else if (ability === 'Limber') {
  1263. rejectAbility = template.types.includes('Electric');
  1264. } else if (ability === 'Liquid Voice') {
  1265. rejectAbility = !hasMove['hypervoice'];
  1266. } else if (ability === 'Oblivious') {
  1267. rejectAbility = !counter.Status;
  1268. } else if (ability === 'Overcoat') {
  1269. rejectAbility = abilities.includes('Sturdy');
  1270. } else if (ability === 'Overgrow') {
  1271. rejectAbility = !counter['Grass'];
  1272. } else if (ability === 'Poison Heal') {
  1273. rejectAbility = abilities.includes('Technician') && !!counter['technician'];
  1274. } else if (ability === 'Power Construct') {
  1275. rejectAbility = template.forme === '10%' && !hasMove['substitute'];
  1276. } else if (ability === 'Prankster') {
  1277. rejectAbility = !counter['Status'];
  1278. } else if (ability === 'Pressure' || ability === 'Synchronize') {
  1279. rejectAbility = counter.Status < 2;
  1280. } else if (ability === 'Regenerator') {
  1281. rejectAbility = abilities.includes('Magic Guard');
  1282. } else if (ability === 'Quick Feet') {
  1283. rejectAbility = hasMove['bellydrum'];
  1284. } else if (ability === 'Reckless' || ability === 'Rock Head') {
  1285. rejectAbility = !counter['recoil'];
  1286. } else if (ability === 'Sand Force' || ability === 'Sand Veil') {
  1287. rejectAbility = teamDetails.weather !== 'sand';
  1288. } else if (ability === 'Sand Rush') {
  1289. rejectAbility = !hasMove['sandstorm'] && teamDetails.weather !== 'sand';
  1290. } else if (ability === 'Scrappy') {
  1291. rejectAbility = !template.types.includes('Normal');
  1292. } else if (ability === 'Sheer Force') {
  1293. rejectAbility = !counter['sheerforce'] || template.isMega || (abilities.includes('Iron Fist') && counter['ironfist'] > counter['sheerforce']) || hasMove['flamecharge'] || hasMove['poweruppunch'] || hasMove['chargebeam'];
  1294. } else if (ability === 'Simple') {
  1295. rejectAbility = !counter.setupType && !hasMove['cosmicpower'] && !hasMove['flamecharge'];
  1296. } else if (ability === 'Snow Warning') {
  1297. rejectAbility = hasMove['hypervoice'];
  1298. } else if (ability === 'Solar Power') {
  1299. rejectAbility = !counter['Special'] || template.isMega;
  1300. } else if (ability === 'Sturdy') {
  1301. rejectAbility = !!counter['recoil'] && !counter['recovery'];
  1302. } else if (ability === 'Swarm') {
  1303. rejectAbility = !counter['Bug'];
  1304. } else if (ability === 'Technician') {
  1305. rejectAbility = !counter['technician'] || (abilities.includes('Skill Link') && counter['skilllink'] >= counter['technician']);
  1306. } else if (ability === 'Tinted Lens') {
  1307. rejectAbility = counter['damage'] >= counter.damagingMoves.length || (counter.Status > 2 && !counter.setupType);
  1308. } else if (ability === 'Torrent') {
  1309. rejectAbility = !counter['Water'];
  1310. } else if (ability === 'Triage') {
  1311. rejectAbility = !counter['recovery'] && !counter['drain'];
  1312. } else if (ability === 'Unburden') {
  1313. rejectAbility = template.isMega || (!counter.setupType && !hasMove['acrobatics']);
  1314. } else if (ability === 'Water Absorb') {
  1315. rejectAbility = abilities.includes('Volt Absorb') || (abilities.includes('Water Bubble') && !!counter['Water']);
  1316. }
  1317.  
  1318. if (rejectAbility) {
  1319. if (ability === ability0.name) {
  1320. ability = ability1.name;
  1321. } else if (ability === ability1.name && abilities[2]) {
  1322. ability = ability2.name;
  1323. } else {
  1324. // Default to the highest rated non-banned ability if all are rejected
  1325. // Assume no Pokemon has two banned abilities
  1326. ability = !bannedAbilities.includes(abilities[0]) ? abilities[0] : abilities[1];
  1327. rejectAbility = false;
  1328. }
  1329. }
  1330. } while (rejectAbility);
  1331.  
  1332. if (abilities.includes('Chlorophyll') && (hasMove['sunnyday'] || teamDetails.weather === 'sun')) {
  1333. ability = 'Chlorophyll';
  1334. }
  1335. if (abilities.includes('Galvanize') && !!counter['Normal']) {
  1336. ability = 'Galvanize';
  1337. }
  1338. if (abilities.includes('Guts') && ability !== 'Quick Feet' && (hasMove['facade'] || hasMove['protect'] || (hasMove['rest'] && hasMove['sleeptalk']))) {
  1339. ability = 'Guts';
  1340. }
  1341. if (abilities.includes('Hydration') && !hasMove['sleeptalk'] && (hasMove['raindance'] || teamDetails.weather === 'rain')) {
  1342. ability = 'Hydration';
  1343. }
  1344. if (abilities.includes('Insomnia') && hasMove['skillswap']) {
  1345. ability = 'Insomnia';
  1346. }
  1347. if (abilities.includes('Marvel Scale') && hasMove['rest'] && hasMove['sleeptalk']) {
  1348. ability = 'Marvel Scale';
  1349. }
  1350. if (abilities.includes('No Guard') && hasMove['dynamicpunch']) {
  1351. ability = 'No Guard';
  1352. }
  1353. if (abilities.includes('Plus') && hasMove['magneticflux']) {
  1354. ability = 'Plus';
  1355. }
  1356. if (abilities.includes('Prankster') && counter.Status > 1) {
  1357. ability = 'Prankster';
  1358. }
  1359. if (abilities.includes('Sand Rush') && (hasMove['sandstorm'] || teamDetails.weather === 'sand')) {
  1360. ability = 'Sand Rush';
  1361. }
  1362. if (abilities.includes('Slush Rush') && (hasMove['hail'] || teamDetails.weather === 'hail')) {
  1363. ability = 'Slush Rush';
  1364. }
  1365. if (abilities.includes('Swift Swim') && (hasMove['raindance'] || teamDetails.weather === 'rain')) {
  1366. ability = 'Swift Swim';
  1367. }
  1368. if (abilities.includes('Triage') && !!counter['drain']) {
  1369. ability = 'Triage';
  1370. }
  1371. if (abilities.includes('Unburden') && hasMove['acrobatics']) {
  1372. ability = 'Unburden';
  1373. }
  1374. if (template.species === 'Ambipom' && !counter['technician']) {
  1375. // If it doesn't qualify for Technician, Skill Link is useless on it
  1376. ability = 'Pickup';
  1377. } else if (template.baseSpecies === 'Basculin') {
  1378. ability = 'Adaptability';
  1379. } else if (template.species === 'Lopunny' && hasMove['switcheroo'] && this.randomChance(2, 3)) {
  1380. ability = 'Klutz';
  1381. } else if ((template.species === 'Rampardos' && !hasMove['headsmash']) || hasMove['rockclimb']) {
  1382. ability = 'Sheer Force';
  1383. } else if (template.species === 'Torterra' && !counter['Grass']) {
  1384. ability = 'Shell Armor';
  1385. } else if (template.species === 'Umbreon') {
  1386. ability = 'Synchronize';
  1387. } else if (template.species === 'Unfezant') {
  1388. ability = 'Super Luck';
  1389. } else if (template.id === 'dugtrioalola' && hasMove['sandstorm']) {
  1390. ability = 'Sand Force';
  1391. } else if (template.species === 'Shuckle' && hasMove['shellsmash']) {
  1392. ability = 'Contrary';
  1393. }
  1394. } else {
  1395. ability = ability0.name;
  1396. }
  1397.  
  1398. item = 'Leftovers';
  1399. if (template.requiredItems) {
  1400. if (template.baseSpecies === 'Arceus' && (hasMove['judgment'] || !counter[template.types[0]] || teamDetails.zMove)) {
  1401. // Judgment doesn't change type with Z-Crystals
  1402. item = template.requiredItems[0];
  1403. } else {
  1404. item = this.sample(template.requiredItems);
  1405. }
  1406. } else if (hasMove['magikarpsrevenge']) {
  1407. // PoTD Magikarp
  1408. item = 'Choice Band';
  1409.  
  1410. // First, the extra high-priority items
  1411. } else if (template.species === 'Clamperl' && !hasMove['shellsmash']) {
  1412. item = 'Deep Sea Tooth';
  1413. } else if (template.species === 'Cubone' || template.baseSpecies === 'Marowak') {
  1414. item = 'Thick Club';
  1415. } else if (template.species === 'Decidueye' && hasMove['spiritshackle'] && counter.setupType && !teamDetails.zMove) {
  1416. item = 'Decidium Z';
  1417. } else if (template.species === 'Dedenne') {
  1418. item = 'Petaya Berry';
  1419. } else if (template.species === 'Deoxys-Attack') {
  1420. item = (slot === 0 && hasMove['stealthrock']) ? 'Focus Sash' : 'Life Orb';
  1421. } else if (template.species === 'Farfetch\'d') {
  1422. item = 'Stick';
  1423. } else if (template.species === 'Genesect' && hasMove['technoblast']) {
  1424. item = 'Douse Drive';
  1425. species = 'Genesect-Douse';
  1426. } else if (template.species === 'Kadabra') {
  1427. item = (hasMove['counter']) ? 'Focus Sash' : 'Life Orb';
  1428. } else if (template.species === 'Komala' && hasMove['sleeptalk']) {
  1429. item = this.randomChance(3, 5) ? 'Choice Scarf' : 'Choice Band';
  1430. } else if (template.species === 'Kommo-o' && !teamDetails.zMove) {
  1431. item = hasMove['clangingscales'] ? 'Kommonium Z' : 'Dragonium Z';
  1432. } else if (template.baseSpecies === 'Lycanroc' && hasMove['stoneedge'] && counter.setupType && !teamDetails.zMove && !hasMove['endeavor']) {
  1433. item = 'Lycanium Z';
  1434. } else if (template.species === 'Marshadow' && hasMove['spectralthief'] && counter.setupType && !teamDetails.zMove) {
  1435. item = 'Marshadium Z';
  1436. } else if (template.species === 'Mimikyu' && hasMove['playrough'] && counter.setupType && !teamDetails.zMove) {
  1437. item = 'Mimikium Z';
  1438. } else if (['Necrozma-Dusk-Mane', 'Necrozma-Dawn-Wings'].includes(template.species) && !teamDetails.zMove) {
  1439. if (hasMove['autotomize'] && hasMove['sunsteelstrike']) {
  1440. item = 'Solganium Z';
  1441. } else if (hasMove['trickroom'] && hasMove['moongeistbeam']) {
  1442. item = 'Lunalium Z';
  1443. } else {
  1444. item = 'Ultranecrozium Z';
  1445. if (!hasMove['photongeyser']) {
  1446. for (const moveid of moves) {
  1447. let move = this.getMove(moveid);
  1448. if (move.category === 'Status' || hasType[move.type]) continue;
  1449. moves[moves.indexOf(moveid)] = 'photongeyser';
  1450. break;
  1451. }
  1452. }
  1453. }
  1454. } else if (template.baseSpecies === 'Pikachu') {
  1455. item = 'Light Ball';
  1456. } else if (template.species === 'Raichu-Alola' && hasMove['thunderbolt'] && !teamDetails.zMove && this.randomChance(1, 4)) {
  1457. item = 'Aloraichium Z';
  1458. } else if (template.species === 'Shedinja') {
  1459. item = this.randomChance(2, 3) ? 'Focus Sash' : 'Lum Berry';
  1460. } else if (template.species === 'Slaking') {
  1461. item = counter.Physical >= 4 ? 'Choice Band' : 'Life Orb';
  1462. } else if (template.species === 'Smeargle') {
  1463. item = 'Focus Sash';
  1464. } else if (template.species === 'Unfezant' && counter['Physical'] >= 2) {
  1465. item = 'Scope Lens';
  1466. } else if (template.species === 'Unown') {
  1467. item = 'Choice Specs';
  1468. } else if (template.species === 'Wishiwashi') {
  1469. item = this.sample(['Aguav', 'Figy', 'Iapapa', 'Mago', 'Wiki']) + ' Berry';
  1470. } else if (template.species === 'Wobbuffet') {
  1471. if (hasMove['destinybond']) {
  1472. item = 'Custap Berry';
  1473. } else {
  1474. item = isDoubles || this.randomChance(1, 2) ? 'Sitrus Berry' : 'Leftovers';
  1475. }
  1476. } else if (template.species === 'Zygarde-10%' && hasMove['substitute'] && !teamDetails.zMove) {
  1477. item = hasMove['outrage'] ? 'Dragonium Z' : 'Groundium Z';
  1478. } else if (template.species === 'Eevee' && !teamDetails.zMove) {
  1479. item = 'Eevium Z';
  1480. } else if (ability === 'Imposter') {
  1481. item = 'Choice Scarf';
  1482. } else if (hasMove['geomancy']) {
  1483. item = 'Power Herb';
  1484. } else if (ability === 'Klutz' && hasMove['switcheroo']) {
  1485. // To perma-taunt a Pokemon by giving it Assault Vest
  1486. item = 'Assault Vest';
  1487. } else if (hasMove['switcheroo'] || hasMove['trick']) {
  1488. if (template.baseStats.spe >= 60 && template.baseStats.spe <= 98) {
  1489. item = 'Choice Scarf';
  1490. } else {
  1491. item = (counter.Physical > counter.Special) ? 'Choice Band' : 'Choice Specs';
  1492. }
  1493. } else if ((hasMove['conversion'] || hasMove['mefirst'] || hasMove['celebrate'] || hasMove['happyhour'] || hasMove['psychup']) && !teamDetails.zMove) {
  1494. item = 'Normalium Z';
  1495. } else if (hasMove['forestscurse'] && !teamDetails.zMove) {
  1496. item = 'Grassium Z';
  1497. } else if (hasMove['trickortreat'] && !teamDetails.zMove) {
  1498. item = 'Ghostium Z';
  1499. } else if ((hasMove['snatch'] || hasMove['memento']) && !teamDetails.zMove) {
  1500. item = 'Darkinium Z';
  1501. } else if (hasMove['zapcannon'] && !teamDetails.zMove) {
  1502. item = 'Electrium Z';
  1503. } else if (hasMove['healblock'] && !teamDetails.zMove) {
  1504. item = 'Psychium Z';
  1505. } else if (['Latias', 'Latios'].includes(template.species) && counter['Psychic'] + counter['Dragon'] >= 2) {
  1506. item = 'Soul Dew';
  1507. } else if (hasMove['bellydrum']) {
  1508. if (ability === 'Gluttony') {
  1509. item = this.sample(['Aguav', 'Figy', 'Iapapa', 'Mago', 'Wiki']) + ' Berry';
  1510. } else if (template.baseStats.spe <= 50 && !teamDetails.zMove && this.randomChance(2, 3)) {
  1511. item = 'Normalium Z';
  1512. } else {
  1513. item = 'Sitrus Berry';
  1514. }
  1515. } else if (hasMove['dig'] && !teamDetails.zMove) {
  1516. item = 'Groundium Z';
  1517. } else if (hasMove['fleurcannon'] && !!counter['speedsetup'] && !teamDetails.zMove) {
  1518. item = 'Fairium Z';
  1519. } else if (hasMove['electricterrain']) {
  1520. item = !teamDetails.zMove ? 'Electrium Z' : (this.randomChance(1, 2) ? 'Electric Seed' : 'Terrain Extender');
  1521. } else if (hasMove['psychicterrain']) {
  1522. item = !teamDetails.zMove ? 'Psychium Z' : (this.randomChance(1, 2) ? 'Psychic Seed' : 'Terrain Extender');
  1523. } else if ((hasMove['gigaimpact'] || hasMove['hyperbeam']) && ability !== 'Truant' && !teamDetails.zMove) {
  1524. item = 'Normalium Z';
  1525. } else if ((hasMove['magmastorm'] || hasMove['mindblown'] && !!counter['Status']) && !teamDetails.zMove) {
  1526. item = 'Firium Z';
  1527. } else if ((hasMove['fly'] || (hasMove['hurricane'] && template.baseStats.spa >= 95) || ((hasMove['bounce'] || (hasAbility['Gale Wings'] && hasMove['bravebird'])) && counter.setupType) || hasMove['mirrormove']) && !teamDetails.zMove) {
  1528. item = 'Flyinium Z';
  1529. } else if (hasMove['skyattack']) {
  1530. item = (ability === 'Unburden' || teamDetails.zMove) ? 'Power Herb' : 'Flyinium Z';
  1531. } else if (hasMove['tailwind'] && ['Sniper', 'Super Luck'].includes(ability) && !teamDetails.zMove) {
  1532. item = 'Flyinium Z';
  1533. } else if (hasMove['solarbeam'] && !hasAbility['Drought'] && !hasMove['sunnyday'] && teamDetails['sun']) {
  1534. item = !teamDetails.zMove ? 'Grassium Z' : 'Power Herb';
  1535. } else if (hasMove['shellsmash'] && ability !== 'Contrary') {
  1536. item = (ability === 'Solid Rock' && counter['priority']) ? 'Weakness Policy' : 'White Herb';
  1537. } else if (ability === 'Harvest') {
  1538. item = hasMove['rest'] ? 'Lum Berry' : 'Sitrus Berry';
  1539. } else if (ability === 'Slow Start') {
  1540. item = 'Leftovers';
  1541. } else if (['Poison Heal', 'Toxic Boost'].includes(ability)) {
  1542. item = 'Toxic Orb';
  1543. } else if (hasMove['rest'] && !hasMove['sleeptalk'] && !hasMove['perishsong'] && !['Natural Cure', 'Shed Skin'].includes(ability)) {
  1544. item = (hasMove['raindance'] && ability === 'Hydration') ? 'Damp Rock' : (template.evos.length && template.baseStats.hp + template.baseStats.def + template.baseStats.spd >= 200) ? 'Eviolite' : 'Chesto Berry';
  1545. } else if (hasMove['psychoshift'] || (['Guts', 'Quick Feet'].includes(ability) && !hasMove['sleeptalk'])) {
  1546. item = (hasType['Fire'] || ability === 'Quick Feet') ? 'Toxic Orb' : 'Flame Orb';
  1547. } else if (ability === 'Unburden') {
  1548. if (hasMove['fakeout']) {
  1549. item = 'Normal Gem';
  1550. } else {
  1551. item = 'Sitrus Berry';
  1552. }
  1553. } else if (hasMove['acrobatics']) {
  1554. item = '';
  1555.  
  1556. // Medium priority
  1557. } else if (template.evos.length && ability !== 'Speed Boost' && (template.baseStats.spe < 80 || (template.baseStats.atk < 80 && template.baseStats.spa < 80) ||
  1558. counter.setupType)) {
  1559. item = 'Eviolite';
  1560. } else if (['Magic Guard', 'Sheer Force'].includes(ability) && counter.damagingMoves.length > 1) {
  1561. item = 'Life Orb';
  1562. } else if (hasMove['raindance'] && (!hasMove['thunder'] || template.baseSpecies === 'Castform')) {
  1563. if (template.baseSpecies === 'Castform' && !teamDetails.zMove) {
  1564. item = 'Waterium Z';
  1565. } else {
  1566. item = (ability === 'Swift Swim' && counter.Status < 2) ? 'Life Orb' : 'Damp Rock';
  1567. }
  1568. } else if (hasMove['sunnyday'] && (!hasMove['solarbeam'] || template.baseSpecies === 'Castform')) {
  1569. if (template.baseSpecies === 'Castform' && !teamDetails.zMove) {
  1570. item = 'Firium Z';
  1571. } else {
  1572. item = (ability === 'Chlorophyll' && counter.Status < 2) ? 'Life Orb' : 'Heat Rock';
  1573. }
  1574. } else if (hasMove['hail'] && (!hasMove['blizzard'] || template.baseSpecies === 'Castform')) {
  1575. if (template.baseSpecies === 'Castform' && !teamDetails.zMove) {
  1576. item = 'Icium Z';
  1577. } else {
  1578. item = (ability === 'Slush Rush' && counter.Status < 2) ? 'Life Orb' : 'Icy Rock';
  1579. }
  1580. } else if (hasMove['sandstorm']) {
  1581. item = (['Sand Rush', 'Sand Force'].includes(ability) && counter.Status < 2) ? 'Life Orb' : 'Smooth Rock';
  1582. } else if (hasMove['auroraveil'] || hasMove['lightscreen'] && hasMove['reflect']) {
  1583. item = 'Light Clay';
  1584. } else if (((ability === 'Speed Boost' && !hasMove['substitute']) || (ability === 'Stance Change')) && counter.Physical + counter.Special > 2) {
  1585. item = 'Life Orb';
  1586. } else if (hasType['Grass'] && template.baseStats.spe <= 60 && hasMove['sleeppowder'] && counter.setupType && !teamDetails.zMove) {
  1587. item = 'Grassium Z';
  1588. } else if (counter.Physical >= 4 && !hasMove['bodyslam'] && !hasMove['dragontail'] && !hasMove['fakeout'] && !hasMove['flamecharge'] && !hasMove['rapidspin'] && !hasMove['suckerpunch'] && !hasMove['poweruppunch'] && !counter['trap'] && !isDoubles) {
  1589. item = (template.baseStats.atk >= 80 || ability === 'Huge Power') && template.baseStats.spe >= 60 && template.baseStats.spe <= 98 && !counter['priority'] && this.randomChance(3, 5) ? 'Choice Scarf' : 'Choice Band';
  1590. } else if (counter.Special >= 4 && !hasMove['acidspray'] && !hasMove['chargebeam'] && !hasMove['clearsmog'] && !hasMove['fierydance'] && !hasMove['icywind'] && !counter['trap'] && !isDoubles) {
  1591. item = template.baseStats.spa >= 80 && template.baseStats.spe >= 60 && template.baseStats.spe <= 98 && !counter['priority'] && this.randomChance(3, 5) ? 'Choice Scarf' : 'Choice Specs';
  1592. } else if (((counter.Physical >= 3 && hasMove['defog']) || (counter.Special >= 3 && hasMove['uturn'])) && template.baseStats.spe >= 60 && template.baseStats.spe <= 98 && !counter['priority'] && !hasMove['foulplay'] && !hasMove['icywind'] && this.randomChance(3, 5) && !counter['trap'] && !isDoubles) {
  1593. item = 'Choice Scarf';
  1594. } else if (ability === 'Defeatist' || hasMove['eruption'] || hasMove['waterspout']) {
  1595. item = counter.Status <= 1 ? 'Expert Belt' : 'Leftovers';
  1596. } else if (isDoubles && counter.damagingMoves.length >= 4 && template.baseStats.spe >= 60 && !hasMove['fakeout'] && !hasMove['flamecharge'] && !hasMove['suckerpunch'] && ability !== 'Multiscale' && ability !== 'Sturdy') {
  1597. item = 'Life Orb';
  1598. } else if (hasMove['reversal'] && !teamDetails.zMove) {
  1599. item = 'Fightinium Z';
  1600. } else if ((hasMove['endeavor'] || hasMove['flail'] || hasMove['reversal']) && ability !== 'Sturdy') {
  1601. item = 'Focus Sash';
  1602. } else if (template.evos.length) {
  1603. item = "Eviolite";
  1604. } else if (counter.Status >= 3 && template.baseStats.spe < 80 && !hasMove['sleeptalk'] && !hasMove['protect'] && !hasMove['magiccoat']) {
  1605. item = "Mental Herb";
  1606. } else if (hasMove['outrage'] && (counter.setupType || ability === 'Multiscale')) {
  1607. item = 'Lum Berry';
  1608. } else if (isDoubles && this.getEffectiveness('Ice', template) >= 2) {
  1609. item = 'Yache Berry';
  1610. } else if (isDoubles && this.getEffectiveness('Rock', template) >= 2) {
  1611. item = 'Charti Berry';
  1612. } else if (isDoubles && this.getEffectiveness('Fire', template) >= 2) {
  1613. item = 'Occa Berry';
  1614. } else if (isDoubles && this.getImmunity('Fighting', template) && this.getEffectiveness('Fighting', template) >= 2) {
  1615. item = 'Chople Berry';
  1616. } else if ((ability === 'Slow Start' || hasMove['clearsmog'] || hasMove['curse'] || hasMove['detect'] || hasMove['protect'] || hasMove['sleeptalk']) && !isDoubles) {
  1617. item = 'Leftovers';
  1618. } else if (hasMove['substitute']) {
  1619. item = counter.damagingMoves.length > 2 && !!counter['drain'] && !counter['damage'] ? 'Life Orb' : 'Leftovers';
  1620. } else if (this.getEffectiveness('Ground', template) >= 2 && ability !== 'Levitate' && !hasMove['magnetrise']) {
  1621. item = 'Air Balloon';
  1622. } else if (['Iron Barbs', 'Rough Skin'].includes(ability) && this.randomChance(1, 2)) {
  1623. item = 'Rocky Helmet';
  1624. } else if (counter.Physical + counter.Special >= 4 && template.baseStats.spd >= 60 && template.baseStats.hp + template.baseStats.def + template.baseStats.spd >= 185 && !hasMove['naturepower']) {
  1625. item = 'Assault Vest';
  1626. } else if (counter.damagingMoves.length >= 4 && !counter['damage']) {
  1627. item = (!!counter['Dragon'] || !!counter['Normal'] || (hasMove['suckerpunch'] && !hasType['Dark'])) ? 'Life Orb' : 'Expert Belt';
  1628. } else if (template.species === 'Palkia' && (hasMove['dracometeor'] || hasMove['spacialrend']) && hasMove['hydropump']) {
  1629. item = 'Lustrous Orb';
  1630. } else if (counter.damagingMoves.length >= 3 && !!counter['speedsetup'] && template.baseStats.hp + template.baseStats.def + template.baseStats.spd >= 250) {
  1631. item = 'Weakness Policy';
  1632. } else if (slot === 0 && !['Regenerator', 'Sturdy'].includes(ability) && !counter['recoil'] && !counter['recovery'] && template.baseStats.hp + template.baseStats.def + template.baseStats.spd < 185) {
  1633. item = 'Focus Sash';
  1634.  
  1635. // This is the "REALLY can't think of a good item" cutoff
  1636. } else if (counter.damagingMoves.length >= 3 && ability !== 'Sturdy' && !hasMove['acidspray'] && !hasMove['dragontail'] && !hasMove['foulplay'] && !hasMove['rapidspin'] && !hasMove['superfang'] && !counter['damage']) {
  1637. item = (template.baseStats.hp + template.baseStats.def + template.baseStats.spd < 185 || !!counter['speedsetup'] || hasMove['trickroom']) ? 'Life Orb' : 'Leftovers';
  1638. } else if (ability === 'Sturdy' && hasMove['explosion'] && !counter['speedsetup']) {
  1639. item = 'Custap Berry';
  1640. } else if (ability === 'Super Luck') {
  1641. item = 'Scope Lens';
  1642. } else if (hasMove['partingshot'] && !teamDetails.zMove) {
  1643. item = 'Darkinium Z';
  1644.  
  1645. // 30% chance to get a Z-Crystal for attacking moves
  1646. } else if (this.randomChance(3, 10) && ability !== 'Contrary' && (template.baseStats.atk >= 90 || template.baseStats.spa >= 90 || counter.setupType) && !teamDetails.zMove) {
  1647. let moveTypes = [];
  1648. let category = {
  1649. Physical: template.baseStats.atk >= 90 || counter.setupType === 'Physical' || counter.setupType === 'Mixed',
  1650. Special: template.baseStats.spa >= 90 || counter.setupType === 'Special' || counter.setupType === 'Mixed',
  1651. };
  1652. for (let moveId of moves) {
  1653. if (moveId === 'foulplay') continue;
  1654. let move = this.getMove(moveId);
  1655. if ((move.zMovePower >= 160) && !(move.type === 'Normal' && !hasType['Normal']) && category[move.category]) {
  1656. moveTypes.push(move.type);
  1657. }
  1658. }
  1659. let zType = this.sampleNoReplace(moveTypes);
  1660. item = zType ? zCrystals[zType] : 'Leftovers';
  1661. }
  1662.  
  1663. // For Trick / Switcheroo
  1664. if (item === 'Leftovers' && hasType['Poison']) {
  1665. item = 'Black Sludge';
  1666. }
  1667.  
  1668. let level;
  1669.  
  1670. let levelScale = {
  1671. LC: 100,
  1672. 'LC Uber': 100,
  1673. NFE: 100,
  1674. ZU: 100,
  1675. };
  1676. let customScale = {
  1677. // S Rank, new additions
  1678. Swanna: 89,
  1679.  
  1680. // A+ Rank
  1681. Combusken: 90, Electivire: 90, Golem: 90, Komala: 90, "Silvally-Dragon": 90,
  1682.  
  1683. // A Rank
  1684. Bouffalant: 91, Bronzor: 91, Crustle: 91, Exeggutor: 91, "Gourgeist-Super": 91,
  1685. Mareanie: 91, Pinsir: 91, Rapidash: 91, "Rotom-Fan": 91, "Silvally-Fighting": 91, Torterra: 91,
  1686.  
  1687. // A- Rank
  1688. Altaria: 92, Beheeyem: 92, Floatzel: 92, Kadabra: 92, Kecleon: 92, Leafeon: 92, Lickilicky: 92, Mawile: 92, Muk: 92, Pyukumuku: 92,
  1689. "Silvally-Ghost": 92, "Silvally-Water": 92, Simisear: 92,
  1690.  
  1691. // B+ Rank
  1692. Dusclops: 93, Granbull: 93, Marowak: 93, Monferno: 93, "Mr. Mime": 93, Pawniard: 93, Poliwrath: 93,
  1693. Raichu: 93, Shiinotic: 93, Silvally: 93, "Silvally-Dark": 93, Simipour: 93, Simisage: 93, Toucannon: 93, Vigoroth: 93,
  1694.  
  1695. // B Rank
  1696. Avalugg: 94, Bellossom: 94, Camerupt: 94, Chatot: 94, "Golem-Alola": 94, Machoke: 94,
  1697. "Oricorio-Pom-Pom": 94, Purugly: 94, Sandslash: 94, Sawsbuck: 94, Servine: 94, "Silvally-Ground": 94,
  1698.  
  1699. // B- Rank
  1700. Butterfree: 95, Carbink: 95, Cradily: 95, Ditto: 95, Dugtrio: 95, Electrode: 95, Fraxure: 95, Golduck: 95, "Hakamo-o": 95, Jumpluff: 95,
  1701. Oricorio: 95, Metang: 95, Rampardos: 95, Raticate: 95, "Silvally-Grass": 95, "Silvally-Poison": 95,
  1702.  
  1703. // C+ Rank
  1704. Armaldo: 96, Basculin: 96, "Basculin-Blue-Striped": 96, Bibarel: 96, Cacturne: 96, Corsola: 96, Drifblim: 96, Dusknoir: 96,
  1705. Furfrou: 96, Glaceon: 96, Grumpig: 96, Huntail: 96, "Lycanroc-Midnight": 96, Misdreavus: 96, Ninjask: 96, Probopass: 96,
  1706. Regice: 96, Regigigas: 96, Relicanth: 96, Shuckle: 96, "Silvally-Electric": 96, Swoobat: 96,
  1707. Volbeat: 96, Wishiwashi: 96, Zebstrika: 96,
  1708.  
  1709. // C Rank
  1710. Arbok: 97, Beartic: 97, Duosion: 97, Frogadier: 97, Gabite: 97, "Gourgeist-Small": 97, Hippopotas: 97,
  1711. Lapras: 97, Meowstic: 97, Munchlax: 97, Murkrow: 97, Natu: 97, Noctowl: 97, Quilladin: 97, Smeargle: 97, Stunfisk: 97, Vibrava: 97, Vullaby: 97,
  1712.  
  1713. // C- Rank
  1714. Chimecho: 98, Flareon: 98, Gogoat: 98, Trevenant: 98, "Wormadam-Trash": 98,
  1715.  
  1716. // Usually Useless
  1717. Ampharos: 99, Gothitelle: 99, Leavanny: 99, Masquerain: 99, "Meowstic-F": 99, Politoed: 99, Shedinja: 99,
  1718. "Silvally-Fire": 99, "Silvally-Ice": 99, "Silvally-Psychic": 99, Slaking: 99, Togetic: 99, Whirlipede: 99, Zweilous: 99,
  1719.  
  1720. // Holistic judgement
  1721. Unown: 115, Wobbuffet: 115, Luvdisc: 110, Spinda: 105, 'Castform-Rainy': 105, 'Castform-Snowy': 105, 'Castform-Sunny': 105,
  1722. };
  1723. let tier = template.tier;
  1724. if (tier.includes('Unreleased') && baseTemplate.tier === 'Uber') {
  1725. tier = 'Uber';
  1726. }
  1727. if (tier.charAt(0) === '(') {
  1728. tier = tier.slice(1, -1);
  1729. }
  1730. level = levelScale[tier] || 88;
  1731. if (customScale[template.name]) level = customScale[template.name];
  1732.  
  1733. // Prepare optimal HP
  1734. let srWeakness = this.getEffectiveness('Rock', template);
  1735. while (evs.hp > 1) {
  1736. let hp = Math.floor(Math.floor(2 * template.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10);
  1737. if (hasMove['substitute'] && hasMove['reversal']) {
  1738. // Reversal users should be able to use four Substitutes
  1739. if (hp % 4 > 0) break;
  1740. } else if (hasMove['substitute'] && (item === 'Petaya Berry' || item === 'Sitrus Berry' || ability === 'Power Construct' && item !== 'Leftovers')) {
  1741. // Three Substitutes should activate Petaya Berry for Dedenne
  1742. // Two Substitutes should activate Sitrus Berry or Power Construct
  1743. if (hp % 4 === 0) break;
  1744. } else if (hasMove['bellydrum'] && (item === 'Sitrus Berry' || ability === 'Gluttony')) {
  1745. // Belly Drum should activate Sitrus Berry
  1746. if (hp % 2 === 0) break;
  1747. } else {
  1748. // Maximize number of Stealth Rock switch-ins
  1749. if (srWeakness <= 0 || hp % (4 / srWeakness) > 0) break;
  1750. }
  1751. evs.hp -= 4;
  1752. }
  1753.  
  1754. // Minimize confusion damage
  1755. if (!counter.Physical && !hasMove['copycat'] && !hasMove['transform']) {
  1756. evs.atk = 0;
  1757. ivs.atk = 0;
  1758. }
  1759.  
  1760. if (ability === 'Beast Boost' && counter.Special < 1) {
  1761. evs.spa = 0;
  1762. ivs.spa = 0;
  1763. }
  1764.  
  1765. if (hasMove['gyroball'] || hasMove['trickroom'] || (teamDetails.trickroom && template.baseStats.spe <= 50)) {
  1766. evs.spe = 0;
  1767. ivs.spe = 0;
  1768. }
  1769.  
  1770. // For debug only
  1771. // if (tryCount) evs.spe = tryCount;
  1772. // if (teamDetails.weather === 'sun') evs.spe = 1;
  1773. // if (teamDetails.weather === 'rain') evs.spe = 2;
  1774. // if (teamDetails.weather === 'sand') evs.spe = 3;
  1775. // if (teamDetails.weather === 'hail') evs.spe = 4;
  1776. // if (teamDetails.trickroom) evs.spe = 5;
  1777. // evs.spa = teamDetails.cantrickroom;
  1778. // evs.spd = teamDetails.needtrickroom;
  1779.  
  1780. return {
  1781. name: template.baseSpecies,
  1782. species: species,
  1783. gender: template.gender,
  1784. moves: moves,
  1785. ability: ability,
  1786. evs: evs,
  1787. ivs: ivs,
  1788. item: item,
  1789. level: level,
  1790. shiny: this.randomChance(1, 256), // upped shiny chance because
  1791. };
  1792. }
  1793.  
  1794. randomTeam() {
  1795. let pokemon = [];
  1796. let sets = [];
  1797.  
  1798. const excludedTiers = ['Unreleased', 'Uber', 'OU', 'UUBL', 'UU', 'RUBL', 'RU', 'NUBL', 'NU', 'PUBL', 'PU', 'ZUBL'];
  1799. const allowedNFE = [
  1800. 'Kadabra', 'Vigoroth', 'Combusken', 'Misdreavus', 'Monferno',
  1801. 'Bronzor', 'Machoke', 'Metang', 'Pawniard', 'Fraxure', 'Hakamo-o', 'Servine', 'Togetic',
  1802. 'Dusclops', 'Gabite', 'Mareanie', 'Murkrow', 'Quilladin', 'Vullaby', 'Duosion', 'Munchlax',
  1803. 'Natu', 'Vibrava', 'Frogadier',
  1804. ];
  1805.  
  1806. let pokemonPool = [];
  1807. for (let id in this.data.FormatsData) {
  1808. if (id === "silvallyghost") continue;
  1809. let template = this.getTemplate(id);
  1810. if (template.tier.charAt(0) === '(') {
  1811. template.tier = template.tier.slice(1, -1);
  1812. }
  1813. if (template.gen <= this.gen && !excludedTiers.includes(template.tier) && !template.isNonstandard && template.randomBattleMoves || id === 'exeggutor') {
  1814. pokemonPool.push(id);
  1815. }
  1816. }
  1817.  
  1818. let typeCount = {};
  1819. let typeComboCount = {};
  1820. let baseFormes = {};
  1821. /**@type {RandomTeamsTypes["TeamDetails"]} */
  1822. let teamDetails = {
  1823. zMove: 0,
  1824. sun: 0, rain: 0, sand: 0, hail: 0,
  1825. stealthrock: 0, toxicspikes: 0, stickyweb: 0, spikes: 0, hazardClear: 0,
  1826. cansun: 0, canrain: 0, cansand: 0, canhail: 0,
  1827. needsun: 0, needrain: 0, needsand: 0, needhail: 0,
  1828. cantrickroom: 0, needtrickroom: 0,
  1829. };
  1830.  
  1831. while (pokemonPool.length && pokemon.length < 6) {
  1832. let template = this.getTemplate(this.sampleNoReplace(pokemonPool));
  1833. if (!template.exists) continue;
  1834.  
  1835. // Limit to one of each species (Species Clause)
  1836. if (baseFormes[template.baseSpecies]) continue;
  1837.  
  1838. // Only certain NFE Pokemon are allowed
  1839. if (template.nfe && !allowedNFE.includes(template.species)) continue;
  1840.  
  1841. // Adjust rate for species with multiple formes
  1842. switch (template.baseSpecies) {
  1843. case 'Silvally':
  1844. if (this.randomChance(15, 16)) continue;
  1845. break;
  1846. case 'Gourgeist':
  1847. if (this.randomChance(3, 4)) continue;
  1848. break;
  1849. case 'Castform': case 'Oricorio': case 'Wormadam':
  1850. if (this.randomChance(2, 3)) continue;
  1851. break;
  1852. case 'Basculin': case 'Meowstic': case 'Golem':
  1853. if (this.randomChance(1, 2)) continue;
  1854. break;
  1855. }
  1856.  
  1857. let types = template.types;
  1858.  
  1859. // Limit 2 of any type
  1860. let skip = false;
  1861. for (const type of types) {
  1862. if (typeCount[type] > 1) {
  1863. skip = true;
  1864. break;
  1865. }
  1866. }
  1867. if (skip) continue;
  1868.  
  1869. // Limit 1 of any type combination
  1870. let typeCombo = types.slice().sort().join();
  1871.  
  1872. // Sand Stream and Snow Warning don't count towards the type combo limit
  1873. if (Object.values(template.abilities).includes('Sand Stream')) {
  1874. typeCombo = 'Sand Stream';
  1875. if (typeCombo in typeComboCount) continue;
  1876. } else if (Object.values(template.abilities).includes('Snow Warning')) {
  1877. typeCombo = 'Snow Warning';
  1878. if (typeCombo in typeComboCount) continue;
  1879. } else {
  1880. if (typeComboCount[typeCombo] >= 1) continue;
  1881. }
  1882.  
  1883. // Check if template can summon weather
  1884. if (template.randomBattleMoves.includes('sunnyday')) teamDetails['cansun']++;
  1885. if (template.randomBattleMoves.includes('raindance')) teamDetails['canrain']++;
  1886. if (template.randomBattleMoves.includes('sandstorm') || Object.values(template.abilities).includes('Sand Stream')) teamDetails['cansand']++;
  1887. if (template.randomBattleMoves.includes('hail') || Object.values(template.abilities).includes('Snow Warning')) teamDetails['canhail']++;
  1888.  
  1889. // Check if template may need weather
  1890. if (Object.values(template.abilities).includes('Chlorophyll')) teamDetails['needsun']++;
  1891. if (Object.values(template.abilities).includes('Swift Swim')) teamDetails['needrain']++;
  1892. if (Object.values(template.abilities).includes('Sand Rush')) teamDetails['needsand']++;
  1893. if (Object.values(template.abilities).includes('Slush Rush') || template.randomBattleMoves.includes('auroraveil')) teamDetails['needhail']++;
  1894.  
  1895. // Check if template can use trick room
  1896. if (template.randomBattleMoves.includes('trickroom')) teamDetails['cantrickroom']++;
  1897.  
  1898. // Check if template may want trick room
  1899. if (template.baseStats.spe <= 70) teamDetails['needtrickroom']++;
  1900.  
  1901. pokemon.push(template);
  1902.  
  1903. // Now that our Pokemon has passed all checks, we can increment our counters
  1904. baseFormes[template.baseSpecies] = 1;
  1905.  
  1906. // Increment type counters
  1907. for (const type of types) {
  1908. if (type in typeCount) {
  1909. typeCount[type]++;
  1910. } else {
  1911. typeCount[type] = 1;
  1912. }
  1913. }
  1914. typeComboCount[typeCombo] = 1;
  1915. }
  1916.  
  1917. // Pick a weather for the team
  1918. let weatherSetup = 0;
  1919. for (let weather of ['rain', 'sun', 'sand', 'hail']) {
  1920. if (teamDetails['can' + weather] && teamDetails['need' + weather] &&
  1921. teamDetails['can' + weather] + teamDetails['need' + weather] > weatherSetup) {
  1922. weatherSetup = teamDetails['can' + weather] + teamDetails['need' + weather];
  1923. teamDetails.weather = weather;
  1924. }
  1925. }
  1926.  
  1927. // If no weather picked, check if team is suitable for trick room
  1928. if (!teamDetails.weather && teamDetails['cantrickroom'] && (teamDetails['needtrickroom'] >= 3)) {
  1929. teamDetails.trickroom = true;
  1930. }
  1931.  
  1932. for (let template of pokemon) {
  1933. let set = this.randomSet(template, sets.length, teamDetails);
  1934.  
  1935. // Okay, the set passes, add it to our team
  1936. sets.push(set);
  1937.  
  1938. let item = this.getItem(set.item);
  1939.  
  1940. // Team has weather/hazards/hazard removal
  1941. if (item.zMove) teamDetails['zMove']++;
  1942. if (set.moves.includes('sunnyday')) teamDetails['sun']++;
  1943. if (set.moves.includes('raindance')) teamDetails['rain']++;
  1944. if (set.ability === 'Snow Warning' || set.moves.includes('hail')) teamDetails['hail']++;
  1945. if (set.ability === 'Sand Stream' || set.moves.includes('sandstorm')) teamDetails['sand']++;
  1946. if (set.moves.includes('stealthrock')) teamDetails['stealthrock']++;
  1947. if (set.moves.includes('toxicspikes')) teamDetails['toxicspikes']++;
  1948. if (set.moves.includes('stickyweb')) teamDetails['stickyweb']++;
  1949. if (set.moves.includes('spikes')) teamDetails['spikes']++;
  1950. if (set.moves.includes('defog') || set.moves.includes('rapidspin')) teamDetails['hazardClear']++;
  1951. }
  1952. return sets;
  1953. }
  1954. };
  1955.  
  1956. module.exports = RandomZUTeams;
  1957.  
Add Comment
Please, Sign In to add comment