From f3dbfbe685190b71f94c8fdf44a421904b9465c4 Mon Sep 17 00:00:00 2001 From: Guangcong Luo Date: Sat, 2 Dec 2017 11:30:36 -0600 Subject: [PATCH] Refactor Decision -> Action "Decision" and "Choice" were always kind of unclear, so Decision is now Action. It should now be a lot clearer. Actions are also now strongly typed. --- PROTOCOL.md | 2 +- data/abilities.js | 6 +- data/moves.js | 74 ++--- data/scripts.js | 2 +- mods/gen2/moves.js | 2 +- mods/gen2/scripts.js | 2 +- mods/gen2/statuses.js | 2 +- mods/gen3/moves.js | 2 +- mods/gen4/items.js | 28 +- mods/gen4/moves.js | 6 +- mods/gen6/moves.js | 2 +- sim/battle.js | 389 +++++++++++++++------------ sim/pokemon.js | 11 +- sim/side.js | 40 +-- simulator-doc.txt | 2 +- test/common.js | 3 - test/simulator/misc/choice-parser.js | 104 +++---- test/simulator/misc/decisions.js | 24 +- 18 files changed, 375 insertions(+), 326 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index 8a9960e9adb3..8a9d28c248a3 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -303,7 +303,7 @@ In addition to room messages, battles have their own messages. `|request|REQUEST` -> Gives a JSON object containing a request for a decision (to move or +> Gives a JSON object containing a request for a choice (to move or > switch). To assist in your decision, `REQUEST.active` has information > about your active Pokémon, and `REQUEST.side` has information about your > your team as a whole. diff --git a/data/abilities.js b/data/abilities.js index 00ee27e4de44..fa4942651153 100644 --- a/data/abilities.js +++ b/data/abilities.js @@ -420,9 +420,9 @@ exports.BattleAbilities = { if (target.side.active.length === 2 && target.position === 1) { // Curse Glitch - const decision = this.willMove(target); - if (decision && decision.move.id === 'curse') { - decision.targetLoc = -1; + const action = this.willMove(target); + if (action && action.move.id === 'curse') { + action.targetLoc = -1; } } } diff --git a/data/moves.js b/data/moves.js index fd72d3810e8a..c837a9cd32a5 100644 --- a/data/moves.js +++ b/data/moves.js @@ -285,10 +285,10 @@ exports.BattleMovedex = { flags: {authentic: 1, mystery: 1}, onHit: function (target) { if (target.side.active.length < 2) return false; // fails in singles - let decision = this.willMove(target); - if (decision) { + let action = this.willMove(target); + if (action) { this.cancelMove(target); - this.queue.unshift(decision); + this.queue.unshift(action); this.add('-activate', target, 'move: After You'); } else { return false; @@ -4415,7 +4415,7 @@ exports.BattleMovedex = { this.effectData.duration++; } }, - onOverrideDecision: function (pokemon, target, move) { + onOverrideAction: function (pokemon, target, move) { if (move.id !== this.effectData.move) return this.effectData.move; }, onResidualOrder: 13, @@ -5045,11 +5045,11 @@ exports.BattleMovedex = { flags: {protect: 1, mirror: 1, nonsky: 1}, onPrepareHit: function (target, source, move) { for (let i = 0; i < this.queue.length; i++) { - let decision = this.queue[i]; - if (!decision.move || !decision.pokemon || !decision.pokemon.isActive || decision.pokemon.fainted) continue; - if (decision.pokemon.side === source.side && ['grasspledge', 'waterpledge'].includes(decision.move.id)) { - this.prioritizeQueue(decision); - this.add('-waiting', source, decision.pokemon); + let action = this.queue[i]; + if (!action.move || !action.pokemon || !action.pokemon.isActive || action.pokemon.fainted) continue; + if (action.pokemon.side === source.side && ['grasspledge', 'waterpledge'].includes(action.move.id)) { + this.prioritizeAction(action); + this.add('-waiting', source, action.pokemon); return null; } } @@ -6461,11 +6461,11 @@ exports.BattleMovedex = { flags: {protect: 1, mirror: 1, nonsky: 1}, onPrepareHit: function (target, source, move) { for (let i = 0; i < this.queue.length; i++) { - let decision = this.queue[i]; - if (!decision.move || !decision.pokemon || !decision.pokemon.isActive || decision.pokemon.fainted) continue; - if (decision.pokemon.side === source.side && ['waterpledge', 'firepledge'].includes(decision.move.id)) { - this.prioritizeQueue(decision); - this.add('-waiting', source, decision.pokemon); + let action = this.queue[i]; + if (!action.move || !action.pokemon || !action.pokemon.isActive || action.pokemon.fainted) continue; + if (action.pokemon.side === source.side && ['waterpledge', 'firepledge'].includes(action.move.id)) { + this.prioritizeAction(action); + this.add('-waiting', source, action.pokemon); return null; } } @@ -9926,12 +9926,12 @@ exports.BattleMovedex = { priority: 0, flags: {protect: 1, authentic: 1}, onTryHit: function (target, pokemon) { - let decision = this.willMove(target); - if (decision) { + let action = this.willMove(target); + if (action) { let noMeFirst = [ 'chatter', 'counter', 'covet', 'focuspunch', 'mefirst', 'metalburst', 'mirrorcoat', 'struggle', 'thief', ]; - let move = this.getMoveCopy(decision.move.id); + let move = this.getMoveCopy(action.move.id); if (move.category !== 'Status' && !noMeFirst.includes(move)) { pokemon.addVolatile('mefirst'); this.useMove(move, pokemon, target); @@ -12749,7 +12749,7 @@ exports.BattleMovedex = { this.add('-activate', pokemon, 'move: Pursuit'); alreadyAdded = true; } - // Run through each decision in queue to check if the Pursuit user is supposed to Mega Evolve this turn. + // Run through each action in queue to check if the Pursuit user is supposed to Mega Evolve this turn. // If it is, then Mega Evolve before moving. if (source.canMegaEvo || source.canUltraBurst) { for (const [actionIndex, action] of this.queue.entries()) { @@ -12784,13 +12784,13 @@ exports.BattleMovedex = { flags: {protect: 1, mirror: 1}, onHit: function (target) { if (target.side.active.length < 2) return false; // fails in singles - let decision = this.willMove(target); - if (decision) { - decision.priority = -7.1; + let action = this.willMove(target); + if (action) { + action.priority = -7.1; this.cancelMove(target); for (let i = this.queue.length - 1; i >= 0; i--) { if (this.queue[i].choice === 'residual') { - this.queue.splice(i, 0, decision); + this.queue.splice(i, 0, action); break; } } @@ -13842,10 +13842,10 @@ exports.BattleMovedex = { flags: {protect: 1, mirror: 1, sound: 1, authentic: 1}, onTry: function () { for (let i = 0; i < this.queue.length; i++) { - let decision = this.queue[i]; - if (!decision.pokemon || !decision.move) continue; - if (decision.move.id === 'round') { - this.prioritizeQueue(decision); + let action = this.queue[i]; + if (!action.pokemon || !action.move) continue; + if (action.move.id === 'round') { + this.prioritizeAction(action); return; } } @@ -16573,8 +16573,8 @@ exports.BattleMovedex = { priority: 1, flags: {contact: 1, protect: 1, mirror: 1}, onTry: function (source, target) { - let decision = this.willMove(target); - if (!decision || decision.choice !== 'move' || (decision.move.category === 'Status' && decision.move.id !== 'mefirst') || target.volatiles.mustrecharge) { + let action = this.willMove(target); + if (!action || action.choice !== 'move' || (action.move.category === 'Status' && action.move.id !== 'mefirst') || target.volatiles.mustrecharge) { this.attrLastMove('[still]'); this.add('-fail', source); return null; @@ -17884,9 +17884,9 @@ exports.BattleMovedex = { if (target.side.active.length === 2 && target.position === 1) { // Curse Glitch - const decision = this.willMove(target); - if (decision && decision.move.id === 'curse') { - decision.targetLoc = -1; + const action = this.willMove(target); + if (action && action.move.id === 'curse') { + action.targetLoc = -1; } } }, @@ -17926,7 +17926,7 @@ exports.BattleMovedex = { onStart: function (target, source) { this.add('-fieldstart', 'move: Trick Room', '[of] ' + source); }, - // Speed modification is changed in Pokemon.getDecisionSpeed() in sim/pokemon.js + // Speed modification is changed in Pokemon.getActionSpeed() in sim/pokemon.js onResidualOrder: 23, onEnd: function () { this.add('-fieldend', 'move: Trick Room'); @@ -18413,11 +18413,11 @@ exports.BattleMovedex = { flags: {protect: 1, mirror: 1, nonsky: 1}, onPrepareHit: function (target, source, move) { for (let i = 0; i < this.queue.length; i++) { - let decision = this.queue[i]; - if (!decision.move || !decision.pokemon || !decision.pokemon.isActive || decision.pokemon.fainted) continue; - if (decision.pokemon.side === source.side && ['firepledge', 'grasspledge'].includes(decision.move.id)) { - this.prioritizeQueue(decision); - this.add('-waiting', source, decision.pokemon); + let action = this.queue[i]; + if (!action.move || !action.pokemon || !action.pokemon.isActive || action.pokemon.fainted) continue; + if (action.pokemon.side === source.side && ['firepledge', 'grasspledge'].includes(action.move.id)) { + this.prioritizeAction(action); + this.add('-waiting', source, action.pokemon); return null; } } diff --git a/data/scripts.js b/data/scripts.js index adb72288fb46..82c13cb09d41 100644 --- a/data/scripts.js +++ b/data/scripts.js @@ -18,7 +18,7 @@ exports.BattleScripts = { runMove: function (move, pokemon, targetLoc, sourceEffect, zMove, externalMove) { let target = this.getTarget(pokemon, zMove || move, targetLoc); if (!sourceEffect && toId(move) !== 'struggle' && !zMove) { - let changedMove = this.runEvent('OverrideDecision', pokemon, target, move); + let changedMove = this.runEvent('OverrideAction', pokemon, target, move); if (changedMove && changedMove !== true) { move = changedMove; target = null; diff --git a/mods/gen2/moves.js b/mods/gen2/moves.js index 03ab0e26e5ac..f8ff928457dc 100644 --- a/mods/gen2/moves.js +++ b/mods/gen2/moves.js @@ -180,7 +180,7 @@ exports.BattleMovedex = { this.effectData.duration++; } }, - onOverrideDecision: function (pokemon) { + onOverrideAction: function (pokemon) { return this.effectData.move; }, onResidualOrder: 13, diff --git a/mods/gen2/scripts.js b/mods/gen2/scripts.js index 10451c007892..74f1eb696dd3 100644 --- a/mods/gen2/scripts.js +++ b/mods/gen2/scripts.js @@ -65,7 +65,7 @@ exports.BattleScripts = { runMove: function (move, pokemon, targetLoc, sourceEffect) { let target = this.getTarget(pokemon, move, targetLoc); if (!sourceEffect && toId(move) !== 'struggle') { - let changedMove = this.runEvent('OverrideDecision', pokemon, target, move); + let changedMove = this.runEvent('OverrideAction', pokemon, target, move); if (changedMove && changedMove !== true) { move = changedMove; target = null; diff --git a/mods/gen2/statuses.js b/mods/gen2/statuses.js index b795dcfe41e7..a928062332f1 100644 --- a/mods/gen2/statuses.js +++ b/mods/gen2/statuses.js @@ -165,7 +165,7 @@ exports.BattleStatuses = { let move = this.getMove(this.effectData.move); if (move.id) { this.debug('Forcing into ' + move.id); - this.changeDecision(pokemon, {move: move.id}); + this.changeAction(pokemon, {move: move.id}); } }, }, diff --git a/mods/gen3/moves.js b/mods/gen3/moves.js index fd5cb26fcc97..2fe7e1a3547d 100644 --- a/mods/gen3/moves.js +++ b/mods/gen3/moves.js @@ -238,7 +238,7 @@ exports.BattleMovedex = { this.effectData.duration++; } }, - onOverrideDecision: function (pokemon) { + onOverrideAction: function (pokemon) { return this.effectData.move; }, onResidualOrder: 13, diff --git a/mods/gen4/items.js b/mods/gen4/items.js index 1654d250c5a0..7fef0f0a1d72 100644 --- a/mods/gen4/items.js +++ b/mods/gen4/items.js @@ -39,32 +39,32 @@ exports.BattleItems = { onModifyPriority: function () {}, onBeforeTurn: function (pokemon) { if (pokemon.hp <= pokemon.maxhp / 4 || (pokemon.hp <= pokemon.maxhp / 2 && pokemon.ability === 'gluttony')) { - let decision = this.willMove(pokemon); - if (!decision) return; + let action = this.willMove(pokemon); + if (!action) return; this.insertQueue({ choice: 'event', event: 'Custap', - priority: decision.priority + 0.1, - pokemon: decision.pokemon, - move: decision.move, - target: decision.target, + priority: action.priority + 0.1, + pokemon: action.pokemon, + move: action.move, + target: action.target, }); } }, onCustap: function (pokemon) { - let decision = this.willMove(pokemon); - this.debug('custap decision: ' + decision); - if (decision) { + let action = this.willMove(pokemon); + this.debug('custap action: ' + action); + if (action) { pokemon.eatItem(); } }, onEat: function (pokemon) { - let decision = this.willMove(pokemon); - this.debug('custap eaten: ' + decision); - if (decision) { - this.cancelDecision(pokemon); + let action = this.willMove(pokemon); + this.debug('custap eaten: ' + action); + if (action) { + this.cancelAction(pokemon); this.add('-message', "Custap Berry activated."); - this.runDecision(decision); + this.runAction(action); } }, }, diff --git a/mods/gen4/moves.js b/mods/gen4/moves.js index 4e6ea4144db0..002ff615f079 100644 --- a/mods/gen4/moves.js +++ b/mods/gen4/moves.js @@ -413,7 +413,7 @@ exports.BattleMovedex = { this.effectData.duration++; } }, - onOverrideDecision: function (pokemon) { + onOverrideAction: function (pokemon) { return this.effectData.move; }, onResidualOrder: 13, @@ -1094,8 +1094,8 @@ exports.BattleMovedex = { inherit: true, desc: "Fails if the target did not select a physical or special attack for use this turn, or if the target moves before the user.", onTry: function (source, target) { - let decision = this.willMove(target); - if (!decision || decision.choice !== 'move' || decision.move.category === 'Status' || target.volatiles.mustrecharge) { + let action = this.willMove(target); + if (!action || action.choice !== 'move' || action.move.category === 'Status' || target.volatiles.mustrecharge) { this.add('-fail', source); return null; } diff --git a/mods/gen6/moves.js b/mods/gen6/moves.js index d50ae5354a13..34eb32a4b573 100644 --- a/mods/gen6/moves.js +++ b/mods/gen6/moves.js @@ -73,7 +73,7 @@ exports.BattleMovedex = { this.effectData.duration++; } }, - onOverrideDecision: function (pokemon, target, move) { + onOverrideAction: function (pokemon, target, move) { if (move.id !== this.effectData.move) return this.effectData.move; }, onResidualOrder: 13, diff --git a/sim/battle.js b/sim/battle.js index 403ac0b1f3a0..de8be60fcfb0 100644 --- a/sim/battle.js +++ b/sim/battle.js @@ -22,20 +22,59 @@ const Pokemon = require('./pokemon'); */ /** - * An object representing a single action that can be chosen. + * A move action * - * @typedef {Object} Action - * @property {string} choice - a choice - * @property {Pokemon} [pokemon] - the pokemon making the choice - * @property {number} [targetLoc] - location of the target, relative to pokemon's side - * @property {string} [move] - a move to use - * @property {Pokemon} [target] - the target of the choice - * @property {number} [index] - the chosen index in team preview - * @property {number} [priority] - priority of the chosen index - * @property {Side} [side] - the pokemon's side - * @property {?boolean} [mega] - true if megaing or ultra bursting - * @property {?boolean} [zmove] - true if zmoving + * @typedef {Object} MoveAction + * @property {'move' | 'beforeTurnMove'} choice - action type + * @property {number} priority - priority of the action (lower first) + * @property {number} speed - speed of pokemon using move (higher first if priority tie) + * @property {Pokemon} pokemon - the pokemon doing the move + * @property {number} targetLoc - location of the target, relative to pokemon's side + * @property {string} moveid - a move to use (move action only) + * @property {Move} move - a move to use (move action only) + * @property {boolean | 'done'} mega - true if megaing or ultra bursting + * @property {boolean} zmove - true if zmoving + * @property {Effect?} sourceEffect - effect that did the action */ +/** + * A switch action + * + * @typedef {Object} SwitchAction + * @property {'switch' | 'instaswitch'} choice - action type + * @property {number} priority - priority of the action (lower first) + * @property {number} speed - speed of pokemon switching (higher first if priority tie) + * @property {Pokemon} pokemon - the pokemon doing the switch + * @property {Pokemon} target - pokemon to switch to + */ +/** + * A Team Preview choice action + * + * @typedef {Object} TeamAction + * @property {'team'} choice - action type + * @property {number} priority - priority of the action (lower first) + * @property {1} speed - unused for this action type + * @property {Pokemon} pokemon - the pokemon switching + * @property {number} index - new index + */ +/** + * A generic action not done by a pokemon + * + * @typedef {Object} FieldAction + * @property {'start' | 'residual' | 'pass' | 'beforeTurn'} choice - action type + * @property {number} priority - priority of the action (lower first) + * @property {1} speed - unused for this action type + * @property {null} pokemon - unused for this action type + */ +/** + * A generic action done by a single pokemon + * + * @typedef {Object} PokemonAction + * @property {'megaEvo' | 'shift' | 'runPrimal' | 'runSwitch' | 'event' | 'runUnnerve'} choice - action type + * @property {number} priority - priority of the action (lower first) + * @property {number} speed - speed of pokemon doing action (higher first if priority tie) + * @property {Pokemon} pokemon - the pokemon doing action + */ +/** @typedef {MoveAction | SwitchAction | TeamAction | FieldAction | PokemonAction} Action */ class Battle extends Dex.ModdedDex { /** @@ -77,7 +116,7 @@ class Battle extends Dex.ModdedDex { this.reportExactHP = !!format.debug; this.replayExactHP = !format.team; - /**@type {AnyObject[]} */ + /**@type {Action[]} */ this.queue = []; /**@type {FaintedPokemon[]} */ this.faintQueue = []; @@ -1730,7 +1769,7 @@ class Battle extends Dex.ModdedDex { this.residualEvent('TeamPreview'); - this.addQueue({choice: 'start'}); + this.addToQueue({choice: 'start'}); this.midTurn = true; if (!this.currentRequest) this.go(); } @@ -2432,7 +2471,7 @@ class Battle extends Dex.ModdedDex { this.cancelMove(pokemon); } else { // in gen 3, fainting skips all moves and switches - this.cancelDecision(pokemon); + this.cancelAction(pokemon); } } } @@ -2454,15 +2493,20 @@ class Battle extends Dex.ModdedDex { } /** - * @param {AnyObject} decision + * Takes an object describing an action, and fills it out into a full + * Action object. + * + * @param {AnyObject} action * @param {boolean} [midTurn] + * @return {Action} */ - resolvePriority(decision, midTurn = false) { - if (!decision) return; + resolveAction(action, midTurn = false) { + if (!action) throw new Error(`Action not passed to resolveAction`); - if (!decision.side && decision.pokemon) decision.side = decision.pokemon.side; - if (!decision.choice && decision.move) decision.choice = 'move'; - if (!decision.priority && decision.priority !== 0) { + if (!action.side && action.pokemon) action.side = action.pokemon.side; + if (!action.move && action.moveid) action.move = this.getMoveCopy(action.moveid); + if (!action.choice && action.move) action.choice = 'move'; + if (!action.priority && action.priority !== 0) { let priorities = { 'beforeTurn': 100, 'beforeTurnMove': 99, @@ -2476,77 +2520,85 @@ class Battle extends Dex.ModdedDex { 'team': 102, 'start': 101, }; - if (decision.choice in priorities) { - decision.priority = priorities[decision.choice]; + if (action.choice in priorities) { + action.priority = priorities[action.choice]; } } if (!midTurn) { - if (decision.choice === 'move') { - if (!decision.zmove && this.getMove(decision.move).beforeTurnCallback) { - this.addQueue({choice: 'beforeTurnMove', pokemon: decision.pokemon, move: decision.move, targetLoc: decision.targetLoc}); + if (action.choice === 'move') { + if (!action.zmove && action.move.beforeTurnCallback) { + this.addToQueue({choice: 'beforeTurnMove', pokemon: action.pokemon, move: action.move, targetLoc: action.targetLoc}); } - if (decision.mega) { + if (action.mega) { // TODO: Check that the Pokémon is not affected by Sky Drop. // (This is currently being done in `runMegaEvo`). - this.addQueue({ + this.addToQueue({ choice: 'megaEvo', - pokemon: decision.pokemon, + pokemon: action.pokemon, }); } - } else if (decision.choice === 'switch' || decision.choice === 'instaswitch') { - if (decision.pokemon.switchFlag && decision.pokemon.switchFlag !== true) { - decision.pokemon.switchCopyFlag = decision.pokemon.switchFlag; + } else if (action.choice === 'switch' || action.choice === 'instaswitch') { + if (action.pokemon.switchFlag && action.pokemon.switchFlag !== true) { + action.pokemon.switchCopyFlag = action.pokemon.switchFlag; } - decision.pokemon.switchFlag = false; - if (!decision.speed) decision.speed = decision.pokemon.getDecisionSpeed(); + action.pokemon.switchFlag = false; + if (!action.speed) action.speed = action.pokemon.getActionSpeed(); } } - let deferPriority = this.gen >= 7 && decision.mega && decision.mega !== 'done'; - if (decision.move) { + let deferPriority = this.gen >= 7 && action.mega && action.mega !== 'done'; + if (action.move) { let target = null; + action.move = this.getMoveCopy(action.move); - if (!decision.targetLoc) { - target = this.resolveTarget(decision.pokemon, decision.move); - decision.targetLoc = this.getTargetLoc(target, decision.pokemon); + if (!action.targetLoc) { + target = this.resolveTarget(action.pokemon, action.move); + action.targetLoc = this.getTargetLoc(target, action.pokemon); } - decision.move = this.getMoveCopy(decision.move); - if (!decision.priority && !deferPriority) { - let move = decision.move; - if (decision.zmove) { + if (!action.priority && !deferPriority) { + let move = action.move; + if (action.zmove) { // @ts-ignore - let zMoveName = this.getZMove(decision.move, decision.pokemon, true); + let zMoveName = this.getZMove(action.move, action.pokemon, true); let zMove = this.getMove(zMoveName); if (zMove.exists) { move = zMove; } } - let priority = this.runEvent('ModifyPriority', decision.pokemon, target, move, move.priority); - decision.priority = priority; + let priority = this.runEvent('ModifyPriority', action.pokemon, target, move, move.priority); + action.priority = priority; // In Gen 6, Quick Guard blocks moves with artificially enhanced priority. - if (this.gen > 5) decision.move.priority = priority; + if (this.gen > 5) action.move.priority = priority; + } + } + if (!action.speed) { + if ((action.choice === 'switch' || action.choice === 'instaswitch') && action.target) { + action.speed = action.target.getActionSpeed(); + } else if (!action.pokemon) { + action.speed = 1; + } else if (!deferPriority) { + action.speed = action.pokemon.getActionSpeed(); } } - if (!decision.pokemon && !decision.speed) decision.speed = 1; - if (!decision.speed && (decision.choice === 'switch' || decision.choice === 'instaswitch') && decision.target) decision.speed = decision.target.getDecisionSpeed(); - if (!decision.speed && !deferPriority) decision.speed = decision.pokemon.getDecisionSpeed(); + return /** @type {any} */ (action); } /** + * Adds the action last in the queue. Mostly used before sortQueue. + * * @param {AnyObject | AnyObject[]} action */ - addQueue(action) { + addToQueue(action) { if (Array.isArray(action)) { for (let i = 0; i < action.length; i++) { - this.addQueue(action[i]); + this.addToQueue(action[i]); } return; } if (action.choice === 'pass') return; - this.resolvePriority(action); - this.queue.push(action); + this.queue.push(this.resolveAction(action)); } sortQueue() { @@ -2554,52 +2606,58 @@ class Battle extends Dex.ModdedDex { } /** - * @param {AnyObject | AnyObject[]} decision + * Inserts the passed action into the action queue when it normally + * would have happened (sorting by priority/speed), without + * re-sorting the existing actions. + * + * @param {AnyObject | AnyObject[]} chosenAction * @param {boolean} [midTurn] */ - insertQueue(decision, midTurn = false) { - if (Array.isArray(decision)) { - for (let i = 0; i < decision.length; i++) { - this.insertQueue(decision[i]); + insertQueue(chosenAction, midTurn = false) { + if (Array.isArray(chosenAction)) { + for (const subAction of chosenAction) { + this.insertQueue(subAction); } return; } - if (decision.pokemon) decision.pokemon.updateSpeed(); - this.resolvePriority(decision, midTurn); + if (chosenAction.pokemon) chosenAction.pokemon.updateSpeed(); + const action = this.resolveAction(chosenAction, midTurn); for (let i = 0; i < this.queue.length; i++) { - if (Battle.comparePriority(decision, this.queue[i]) < 0) { - this.queue.splice(i, 0, decision); + if (Battle.comparePriority(action, this.queue[i]) < 0) { + this.queue.splice(i, 0, action); return; } } - this.queue.push(decision); + this.queue.push(action); } /** - * @param {AnyObject} decision + * Makes the passed move action happen next (skipping speed order). + * + * @param {MoveAction} action * @param {Pokemon} [source] * @param {Effect} [sourceEffect] */ - prioritizeQueue(decision, source, sourceEffect) { + prioritizeAction(action, source, sourceEffect) { if (this.event) { if (!source) source = this.event.source; if (!sourceEffect) sourceEffect = this.effect; } - for (let i = 0; i < this.queue.length; i++) { - if (this.queue[i] === decision) { + for (const [i, curAction] of this.queue.entries()) { + if (curAction === action) { this.queue.splice(i, 1); break; } } - decision.sourceEffect = sourceEffect; - this.queue.unshift(decision); + action.sourceEffect = sourceEffect; + this.queue.unshift(action); } willAct() { - for (let i = 0; i < this.queue.length; i++) { - if (this.queue[i].choice === 'move' || this.queue[i].choice === 'switch' || this.queue[i].choice === 'instaswitch' || this.queue[i].choice === 'shift') { - return this.queue[i]; + for (const action of this.queue) { + if (action.choice === 'move' || action.choice === 'switch' || action.choice === 'instaswitch' || action.choice === 'shift') { + return action; } } return null; @@ -2610,9 +2668,9 @@ class Battle extends Dex.ModdedDex { */ willMove(pokemon) { if (pokemon.fainted) return false; - for (let i = 0; i < this.queue.length; i++) { - if (this.queue[i].choice === 'move' && this.queue[i].pokemon === pokemon) { - return this.queue[i]; + for (const action of this.queue) { + if (action.choice === 'move' && action.pokemon === pokemon) { + return action; } } return null; @@ -2621,7 +2679,7 @@ class Battle extends Dex.ModdedDex { /** * @param {Pokemon} pokemon */ - cancelDecision(pokemon) { + cancelAction(pokemon) { let success = false; this.queue = this.queue.filter(action => { if (action.pokemon === pokemon && action.priority >= -100) { @@ -2637,8 +2695,8 @@ class Battle extends Dex.ModdedDex { * @param {Pokemon} pokemon */ cancelMove(pokemon) { - for (let i = 0; i < this.queue.length; i++) { - if (this.queue[i].choice === 'move' && this.queue[i].pokemon === pokemon) { + for (const [i, action] of this.queue.entries()) { + if (action.choice === 'move' && action.pokemon === pokemon) { this.queue.splice(i, 1); return true; } @@ -2650,20 +2708,20 @@ class Battle extends Dex.ModdedDex { * @param {Pokemon} pokemon */ willSwitch(pokemon) { - for (let i = 0; i < this.queue.length; i++) { - if ((this.queue[i].choice === 'switch' || this.queue[i].choice === 'instaswitch') && this.queue[i].pokemon === pokemon) { - return this.queue[i]; + for (const action of this.queue) { + if ((action.choice === 'switch' || action.choice === 'instaswitch') && action.pokemon === pokemon) { + return action; } } return false; } /** - * @param {AnyObject} decision + * @param {Action} action */ - runDecision(decision) { + runAction(action) { // returns whether or not we ended in a callback - switch (decision.choice) { + switch (action.choice) { case 'start': { // I GIVE UP, WILL WRESTLE WITH EVENT SYSTEM LATER let format = this.getFormat(); @@ -2700,56 +2758,54 @@ class Battle extends Dex.ModdedDex { } case 'move': - if (!decision.pokemon.isActive) return false; - if (decision.pokemon.fainted) return false; + if (!action.pokemon.isActive) return false; + if (action.pokemon.fainted) return false; // @ts-ignore - this.runMove(decision.move, decision.pokemon, decision.targetLoc, decision.sourceEffect, decision.zmove); + this.runMove(action.move, action.pokemon, action.targetLoc, action.sourceEffect, action.zmove); break; case 'megaEvo': // @ts-ignore - this.runMegaEvo(decision.pokemon); + this.runMegaEvo(action.pokemon); break; case 'beforeTurnMove': { - if (!decision.pokemon.isActive) return false; - if (decision.pokemon.fainted) return false; - this.debug('before turn callback: ' + decision.move.id); - let target = this.getTarget(decision.pokemon, decision.move, decision.targetLoc); + if (!action.pokemon.isActive) return false; + if (action.pokemon.fainted) return false; + this.debug('before turn callback: ' + action.move.id); + let target = this.getTarget(action.pokemon, action.move, action.targetLoc); if (!target) return false; - decision.move.beforeTurnCallback.call(this, decision.pokemon, target); + if (!action.move.beforeTurnCallback) throw new Error(`beforeTurnMove has no beforeTurnCallback`); + action.move.beforeTurnCallback.call(this, action.pokemon, target); break; } case 'event': - this.runEvent(decision.event, decision.pokemon); + // @ts-ignore Easier than defining a custom event attribute tbh + this.runEvent(action.event, action.pokemon); break; case 'team': { - decision.side.pokemon.splice(decision.index, 0, decision.pokemon); - decision.pokemon.position = decision.index; + action.pokemon.side.pokemon.splice(action.index, 0, action.pokemon); + action.pokemon.position = action.index; // we return here because the update event would crash since there are no active pokemon yet return; } case 'pass': - if (!decision.priority || decision.priority <= 101) return; - if (decision.pokemon) { - decision.pokemon.switchFlag = false; - } - break; + return; case 'instaswitch': case 'switch': - if (decision.choice === 'switch' && decision.pokemon.status && this.data.Abilities.naturalcure) { - this.singleEvent('CheckShow', this.getAbility('naturalcure'), null, decision.pokemon); + if (action.choice === 'switch' && action.pokemon.status && this.data.Abilities.naturalcure) { + this.singleEvent('CheckShow', this.getAbility('naturalcure'), null, action.pokemon); } - if (decision.pokemon.hp) { - decision.pokemon.beingCalledBack = true; - let lastMove = this.getMove(decision.pokemon.lastMove); + if (action.pokemon.hp) { + action.pokemon.beingCalledBack = true; + let lastMove = this.getMove(action.pokemon.lastMove); if (lastMove.selfSwitch !== 'copyvolatile') { - this.runEvent('BeforeSwitchOut', decision.pokemon); + this.runEvent('BeforeSwitchOut', action.pokemon); if (this.gen >= 5) { this.eachEvent('Update'); } } - if (!this.runEvent('SwitchOut', decision.pokemon)) { + if (!this.runEvent('SwitchOut', action.pokemon)) { // Warning: DO NOT interrupt a switch-out // if you just want to trap a pokemon. // To trap a pokemon and prevent it from switching out, @@ -2761,14 +2817,14 @@ class Battle extends Dex.ModdedDex { break; } } - decision.pokemon.illusion = null; - this.singleEvent('End', this.getAbility(decision.pokemon.ability), decision.pokemon.abilityData, decision.pokemon); - if (!decision.pokemon.hp && !decision.pokemon.fainted) { + action.pokemon.illusion = null; + this.singleEvent('End', this.getAbility(action.pokemon.ability), action.pokemon.abilityData, action.pokemon); + if (!action.pokemon.hp && !action.pokemon.fainted) { // a pokemon fainted from Pursuit before it could switch if (this.gen <= 4) { // in gen 2-4, the switch still happens - decision.priority = -101; - this.queue.unshift(decision); + action.priority = -101; + this.queue.unshift(action); this.add('-hint', 'Pursuit target fainted, switch continues in gen 2-4'); break; } @@ -2776,51 +2832,51 @@ class Battle extends Dex.ModdedDex { this.debug('A Pokemon can\'t switch between when it runs out of HP and when it faints'); break; } - if (decision.target.isActive) { + if (action.target.isActive) { this.add('-hint', 'Switch failed; switch target is already active'); break; } - if (decision.choice === 'switch' && decision.pokemon.activeTurns === 1) { - let foeActive = decision.pokemon.side.foe.active; + if (action.choice === 'switch' && action.pokemon.activeTurns === 1) { + let foeActive = action.pokemon.side.foe.active; for (let i = 0; i < foeActive.length; i++) { if (foeActive[i].isStale >= 2) { - decision.pokemon.isStaleCon++; - decision.pokemon.isStaleSource = 'switch'; + action.pokemon.isStaleCon++; + action.pokemon.isStaleSource = 'switch'; break; } } } - this.switchIn(decision.target, decision.pokemon.position); + this.switchIn(action.target, action.pokemon.position); break; case 'runUnnerve': - this.singleEvent('PreStart', decision.pokemon.getAbility(), decision.pokemon.abilityData, decision.pokemon); + this.singleEvent('PreStart', action.pokemon.getAbility(), action.pokemon.abilityData, action.pokemon); break; case 'runSwitch': - this.runEvent('SwitchIn', decision.pokemon); - if (this.gen <= 2 && !decision.pokemon.side.faintedThisTurn && decision.pokemon.draggedIn !== this.turn) this.runEvent('AfterSwitchInSelf', decision.pokemon); - if (!decision.pokemon.hp) break; - decision.pokemon.isStarted = true; - if (!decision.pokemon.fainted) { - this.singleEvent('Start', decision.pokemon.getAbility(), decision.pokemon.abilityData, decision.pokemon); - decision.pokemon.abilityOrder = this.abilityOrder++; - this.singleEvent('Start', decision.pokemon.getItem(), decision.pokemon.itemData, decision.pokemon); - } - delete decision.pokemon.draggedIn; + this.runEvent('SwitchIn', action.pokemon); + if (this.gen <= 2 && !action.pokemon.side.faintedThisTurn && action.pokemon.draggedIn !== this.turn) this.runEvent('AfterSwitchInSelf', action.pokemon); + if (!action.pokemon.hp) break; + action.pokemon.isStarted = true; + if (!action.pokemon.fainted) { + this.singleEvent('Start', action.pokemon.getAbility(), action.pokemon.abilityData, action.pokemon); + action.pokemon.abilityOrder = this.abilityOrder++; + this.singleEvent('Start', action.pokemon.getItem(), action.pokemon.itemData, action.pokemon); + } + delete action.pokemon.draggedIn; break; case 'runPrimal': - if (!decision.pokemon.transformed) this.singleEvent('Primal', decision.pokemon.getItem(), decision.pokemon.itemData, decision.pokemon); + if (!action.pokemon.transformed) this.singleEvent('Primal', action.pokemon.getItem(), action.pokemon.itemData, action.pokemon); break; case 'shift': { - if (!decision.pokemon.isActive) return false; - if (decision.pokemon.fainted) return false; - decision.pokemon.activeTurns--; - this.swapPosition(decision.pokemon, 1); - let foeActive = decision.pokemon.side.foe.active; + if (!action.pokemon.isActive) return false; + if (action.pokemon.fainted) return false; + action.pokemon.activeTurns--; + this.swapPosition(action.pokemon, 1); + let foeActive = action.pokemon.side.foe.active; for (let i = 0; i < foeActive.length; i++) { if (foeActive[i].isStale >= 2) { - decision.pokemon.isStaleCon++; - decision.pokemon.isStaleSource = 'switch'; + action.pokemon.isStaleCon++; + action.pokemon.isStaleSource = 'switch'; break; } } @@ -2837,9 +2893,6 @@ class Battle extends Dex.ModdedDex { this.residualEvent('Residual'); this.add('upkeep'); break; - - case 'skip': - throw new Error("Decision illegally skipped!"); } // phazing (Roar, etc) @@ -2871,17 +2924,14 @@ class Battle extends Dex.ModdedDex { // in gen 3 or earlier, switching in fainted pokemon is done after // every move, rather than only at the end of the turn. this.checkFainted(); - } else if (decision.choice === 'pass') { - this.eachEvent('Update'); - return false; - } else if (decision.choice === 'megaEvo' && this.gen >= 7) { + } else if (action.choice === 'megaEvo' && this.gen >= 7) { this.eachEvent('Update'); - // In Gen 7, the decision order is recalculated for a Pokémon that mega evolves. - const moveIndex = this.queue.findIndex(queuedDecision => queuedDecision.pokemon === decision.pokemon && queuedDecision.choice === 'move'); + // In Gen 7, the action order is recalculated for a Pokémon that mega evolves. + const moveIndex = this.queue.findIndex(queuedAction => queuedAction.pokemon === action.pokemon && queuedAction.choice === 'move'); if (moveIndex >= 0) { - const moveDecision = this.queue.splice(moveIndex, 1)[0]; - moveDecision.mega = 'done'; - this.insertQueue(moveDecision, true); + const moveAction = /** @type {MoveAction} */ (this.queue.splice(moveIndex, 1)[0]); + moveAction.mega = 'done'; + this.insertQueue(moveAction, true); } return false; } else if (this.queue.length && this.queue[0].choice === 'instaswitch') { @@ -2924,16 +2974,16 @@ class Battle extends Dex.ModdedDex { } if (!this.midTurn) { - this.queue.push({choice: 'residual', priority: -100}); - this.queue.unshift({choice: 'beforeTurn', priority: 100}); + this.queue.push(this.resolveAction({choice: 'residual'})); + this.queue.unshift(this.resolveAction({choice: 'beforeTurn'})); this.midTurn = true; } while (this.queue.length) { - let decision = this.queue[0]; + let action = this.queue[0]; this.queue.shift(); - this.runDecision(decision); + this.runAction(action); if (this.currentRequest) { return; @@ -2948,19 +2998,19 @@ class Battle extends Dex.ModdedDex { } /** - * Changes a pokemon's decision, and inserts its new decision + * Changes a pokemon's action, and inserts its new action * in priority order. * - * You'd normally want the OverrideDecision event (which doesn't + * You'd normally want the OverrideAction event (which doesn't * change priority order). * * @param {Pokemon} pokemon - * @param {AnyObject} decision + * @param {AnyObject} action */ - changeDecision(pokemon, decision) { - this.cancelDecision(pokemon); - if (!decision.pokemon) decision.pokemon = pokemon; - this.insertQueue(decision); + changeAction(pokemon, action) { + this.cancelAction(pokemon); + if (!action.pokemon) action.pokemon = pokemon; + this.insertQueue(action); } /** @@ -2977,7 +3027,7 @@ class Battle extends Dex.ModdedDex { if (!side.choose(input)) return false; - this.checkDecisions(); + this.checkActions(); return true; } @@ -3009,7 +3059,7 @@ class Battle extends Dex.ModdedDex { this.LEGACY_API_DO_NOT_USE = oldFlag; this.add('choice', this.p1.getChoice, this.p2.getChoice); for (const side of this.sides) { - this.addQueue(side.choice.actions); + this.addToQueue(side.choice.actions); } this.sortQueue(); @@ -3042,17 +3092,17 @@ class Battle extends Dex.ModdedDex { /** * returns true if both decisions are complete */ - checkDecisions() { - let totalDecisions = 0; + checkActions() { + let totalActions = 0; if (this.p1.isChoiceDone()) { if (!this.supportCancel) this.p1.choice.cantUndo = true; - totalDecisions++; + totalActions++; } if (this.p2.isChoiceDone()) { if (!this.supportCancel) this.p2.choice.cantUndo = true; - totalDecisions++; + totalActions++; } - if (totalDecisions >= this.sides.length) { + if (totalActions >= this.sides.length) { this.commitDecisions(); return true; } @@ -3277,7 +3327,6 @@ class Battle extends Dex.ModdedDex { this.p2 = null; for (let i = 0; i < this.queue.length; i++) { delete this.queue[i].pokemon; - delete this.queue[i].side; } this.queue = []; diff --git a/sim/pokemon.js b/sim/pokemon.js index e14497e9ff1f..a6440c7868ae 100644 --- a/sim/pokemon.js +++ b/sim/pokemon.js @@ -243,7 +243,7 @@ class Pokemon { } updateSpeed() { - this.speed = this.getDecisionSpeed(); + this.speed = this.getActionSpeed(); } /** @@ -346,7 +346,7 @@ class Pokemon { return stat; } - getDecisionSpeed() { + getActionSpeed() { let speed = this.getStat('spe', false, false); if (speed > 10000) speed = 10000; if (this.battle.getPseudoWeather('trickroom')) { @@ -514,14 +514,17 @@ class Pokemon { }; } + /** + * @return {string | null} + */ getLockedMove() { let lockedMove = this.battle.runEvent('LockMove', this); - if (lockedMove === true) lockedMove = false; + if (lockedMove === true) lockedMove = null; return lockedMove; } /** - * @param {string} [lockedMove] + * @param {string?} [lockedMove] * @param {boolean} [restrictData] */ getMoves(lockedMove, restrictData) { diff --git a/sim/side.js b/sim/side.js index fc22e694601a..b3f56d2dcc3f 100644 --- a/sim/side.js +++ b/sim/side.js @@ -11,17 +11,17 @@ const Pokemon = require('./pokemon'); /** * An object representing a single action that can be chosen. * - * @typedef {Object} Action - * @property {string} choice - a choice - * @property {Pokemon} [pokemon] - the pokemon making the choice - * @property {number} [targetLoc] - location of the target, relative to pokemon's side - * @property {string} [move] - a move to use - * @property {Pokemon} [target] - the target of the choice - * @property {number} [index] - the chosen index in team preview - * @property {number} [priority] - priority of the chosen index - * @property {Side} [side] - the pokemon's side + * @typedef {Object} ChosenAction + * @property {'move' | 'switch' | 'instaswitch' | 'team' | 'shift' | 'pass'} choice - action type + * @property {Pokemon} [pokemon] - the pokemon doing the action + * @property {number} [targetLoc] - relative location of the target to pokemon (move action only) + * @property {string} [moveid] - a move to use (move action only) + * @property {Pokemon} [target] - the target of the action + * @property {number} [index] - the chosen index in Team Preview + * @property {Side} [side] - the action's side * @property {?boolean} [mega] - true if megaing or ultra bursting * @property {?boolean} [zmove] - true if zmoving + * @property {number} [priority] - priority of the action */ /** @@ -30,7 +30,7 @@ const Pokemon = require('./pokemon'); * @typedef {Object} Choice * @property {boolean} cantUndo - true if the choice can't be cancelled because of the maybeTrapped issue * @property {string} error - contains error text in the case of a choice error - * @property {Action[]} actions - array of chosen actions + * @property {ChosenAction[]} actions - array of chosen actions * @property {number} forcedSwitchesLeft - number of switches left that need to be performed * @property {number} forcedPassesLeft - number of passes left that need to be performed * @property {Set} switchIns - indexes of pokemon chosen to switch in @@ -128,7 +128,7 @@ class Side { if (action.targetLoc && this.active.length > 1) details += ` ${action.targetLoc}`; if (action.mega) details += ` mega`; if (action.zmove) details += ` zmove`; - return `move ${toId(action.move)}${details}`; + return `move ${action.moveid}${details}`; case 'switch': case 'instaswitch': return `switch ${action.target.position + 1}`; @@ -307,7 +307,7 @@ class Side { if (!targetLoc) targetLoc = 0; // Parse moveText (name or index) - // If the move is not found, the decision is invalid without requiring further inspection. + // If the move is not found, the action is invalid without requiring further inspection. const requestMoves = pokemon.getRequestData().moves; let moveid = ''; @@ -390,11 +390,11 @@ class Side { choice: 'move', pokemon: pokemon, targetLoc: lockedMoveTarget || 0, - move: lockedMove, + moveid: toId(lockedMove), }); return true; } else if (!moves.length && !zMove) { - // Override decision and use Struggle if there are no enabled moves with PP + // Override action and use Struggle if there are no enabled moves with PP // Gen 4 and earlier announce a Pokemon has no moves left before the turn begins, and only to that player's side. if (this.battle.gen <= 4) this.send('-activate', pokemon, 'move: Struggle'); moveid = 'struggle'; @@ -445,7 +445,7 @@ class Side { choice: 'move', pokemon: pokemon, targetLoc: targetLoc, - move: moveid, + moveid: moveid, mega: mega || ultra, zmove: zMove, }); @@ -458,7 +458,7 @@ class Side { if (ultra) this.choice.ultra = true; if (zMove) this.choice.zMove = true; - if (this.battle.LEGACY_API_DO_NOT_USE && !this.battle.checkDecisions()) return this; + if (this.battle.LEGACY_API_DO_NOT_USE && !this.battle.checkActions()) return this; return true; } @@ -534,7 +534,7 @@ class Side { target: targetPokemon, }); - if (this.battle.LEGACY_API_DO_NOT_USE && !this.battle.checkDecisions()) return this; + if (this.battle.LEGACY_API_DO_NOT_USE && !this.battle.checkActions()) return this; return true; } @@ -586,7 +586,7 @@ class Side { }); } - if (this.battle.LEGACY_API_DO_NOT_USE && !this.battle.checkDecisions()) return this; + if (this.battle.LEGACY_API_DO_NOT_USE && !this.battle.checkActions()) return this; return true; } @@ -610,7 +610,7 @@ class Side { pokemon: pokemon, }); - if (this.battle.LEGACY_API_DO_NOT_USE && !this.battle.checkDecisions()) return this; + if (this.battle.LEGACY_API_DO_NOT_USE && !this.battle.checkActions()) return this; return true; } @@ -765,7 +765,7 @@ class Side { this.choice.actions.push({ choice: 'pass', }); - if (this.battle.LEGACY_API_DO_NOT_USE && !this.battle.checkDecisions()) return this; + if (this.battle.LEGACY_API_DO_NOT_USE && !this.battle.checkActions()) return this; return true; } diff --git a/simulator-doc.txt b/simulator-doc.txt index 0329b9d6d767..a406305e2d7c 100644 --- a/simulator-doc.txt +++ b/simulator-doc.txt @@ -36,7 +36,7 @@ for every move: [ModifyPriority] move's [BeforeTurn] -runDecision() - runs runSwitch, runAfterSwitch, and runMove in priority order, then residual at end { +runAction() - runs runSwitch, runAfterSwitch, and runMove in priority order, then residual at end { runSwitch() { [BeforeSwitch] switch diff --git a/test/common.js b/test/common.js index ff12a367d0d3..c828cb341b2a 100644 --- a/test/common.js +++ b/test/common.js @@ -13,7 +13,6 @@ const RULE_FLAGS = { preview: 4, sleepClause: 8, cancel: 16, - partialDecisions: 32, }; function capitalize(word) { @@ -86,7 +85,6 @@ class TestTools { if (options.preview) format.ruleset.push('Team Preview'); if (options.sleepClause) format.ruleset.push('Sleep Clause Mod'); if (options.cancel) format.ruleset.push('Cancel Mod'); - // if (options.partialDecisions) format.ruleset.push('Partial Decisions'); this.dex.installFormat(formatId, format); return format; @@ -115,7 +113,6 @@ class TestTools { prng ); battle.LEGACY_API_DO_NOT_USE = true; - if (options && options.partialDecisions) battle.supportPartialDecisions = true; if (teams) { for (let i = 0; i < teams.length; i++) { assert(Array.isArray(teams[i]), "Team provided is not an array"); diff --git a/test/simulator/misc/choice-parser.js b/test/simulator/misc/choice-parser.js index b2bd386d49ed..84e5f66627fd 100644 --- a/test/simulator/misc/choice-parser.js +++ b/test/simulator/misc/choice-parser.js @@ -14,15 +14,15 @@ describe('Choice parser', function () { [{species: "Rhydon", ability: 'prankster', moves: ['splash']}], ]); - const validDecision = 'team 1'; - assert(battle.choose('p1', validDecision)); + const validChoice = 'team 1'; + assert(battle.choose('p1', validChoice)); battle.p1.clearChoice(); - assert(battle.choose('p2', validDecision)); + assert(battle.choose('p2', validChoice)); battle.p1.clearChoice(); - const badDecisions = ['move 1', 'move 2 mega', 'switch 1', 'pass', 'shift']; - for (const badDecision of badDecisions) { - assert.false(battle.choose('p1', badDecision), `Decision '${badDecision}' should be rejected`); + const badChoices = ['move 1', 'move 2 mega', 'switch 1', 'pass', 'shift']; + for (const badChoice of badChoices) { + assert.false(battle.choose('p1', badChoice), `Choice '${badChoice}' should be rejected`); } }); @@ -68,13 +68,13 @@ describe('Choice parser', function () { battle.commitDecisions(); - const badDecisions = ['move 1', 'move 2 mega', 'team 1', 'pass', 'shift']; - for (const badDecision of badDecisions) { - assert.false(battle.choose('p1', badDecision), `Decision '${badDecision}' should be rejected`); + const badChoices = ['move 1', 'move 2 mega', 'team 1', 'pass', 'shift']; + for (const badChoice of badChoices) { + assert.false(battle.choose('p1', badChoice), `Choice '${badChoice}' should be rejected`); } - const validDecision = 'switch Bulbasaur'; - assert(battle.choose('p1', validDecision)); + const validChoice = 'switch Bulbasaur'; + assert(battle.choose('p1', validChoice)); battle.p1.clearChoice(); }); }); @@ -94,12 +94,12 @@ describe('Choice parser', function () { ]); battle.commitDecisions(); // Both p1 active Pokémon faint - const badDecisions = ['move 1', 'move 2 mega', 'team 1', 'shift']; - for (const badDecision of badDecisions) { - assert.false(battle.choose('p1', badDecision), `Decision '${badDecision}' should be rejected`); + const badChoices = ['move 1', 'move 2 mega', 'team 1', 'shift']; + for (const badChoice of badChoices) { + assert.false(battle.choose('p1', badChoice), `Choice '${badChoice}' should be rejected`); } - assert(battle.choose('p1', `pass, switch 3`), `Decision 'pass, switch 3' should be valid`); + assert(battle.choose('p1', `pass, switch 3`), `Choice 'pass, switch 3' should be valid`); }); it('should reject choice details for `pass` choices', function () { @@ -150,15 +150,15 @@ describe('Choice parser', function () { {species: "Charmander", ability: 'blaze', moves: ['tackle', 'growl']}, ]); - const validDecisions = ['move 1', 'move 2', 'switch 2']; - for (const decision of validDecisions) { - assert(battle.choose('p1', decision), `Decision '${decision}' should be valid`); + const validChoices = ['move 1', 'move 2', 'switch 2']; + for (const action of validChoices) { + assert(battle.choose('p1', action), `Choice '${action}' should be valid`); battle.p1.clearChoice(); } - const badDecisions = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass', 'shift']; - for (const badDecision of badDecisions) { - assert.false(battle.choose('p1', badDecision), `Decision '${badDecision}' should be rejected`); + const badChoices = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass', 'shift']; + for (const badChoice of badChoices) { + assert.false(battle.choose('p1', badChoice), `Choice '${badChoice}' should be rejected`); } }); }); @@ -183,7 +183,7 @@ describe('Choice parser', function () { assert.false.fainted(p1.active[1]); assert(battle.choose('p1', 'move smog 2')); - assert.strictEqual(battle.p1.getChoice(true), `pass, move smog 2`, `Decision mismatch`); + assert.strictEqual(battle.p1.getChoice(true), `pass, move smog 2`, `Choice mismatch`); }); }); @@ -202,18 +202,18 @@ describe('Choice parser', function () { {species: "Golem", ability: 'sturdy', moves: ['defensecurl']}, ]); - const validDecisions = ['move 1', 'switch 4']; + const validChoices = ['move 1', 'switch 4']; - validDecisions.forEach(decision => { - const choiceString = `move 1, ${decision}, move 1 1`; - assert(battle.choose('p1', choiceString), `Decision '${choiceString}' should be valid`); + validChoices.forEach(action => { + const choiceString = `move 1, ${action}, move 1 1`; + assert(battle.choose('p1', choiceString), `Choice '${choiceString}' should be valid`); battle.p1.clearChoice(); }); - const badDecisions = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass', 'shift']; - for (const badDecision of badDecisions) { - const choiceString = `move 1, ${badDecision}, move 1 1`; - assert.false(battle.choose('p1', choiceString), `Decision '${choiceString}' should be rejected`); + const badChoices = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass', 'shift']; + for (const badChoice of badChoices) { + const choiceString = `move 1, ${badChoice}, move 1 1`; + assert.false(battle.choose('p1', choiceString), `Choice '${choiceString}' should be rejected`); } }); @@ -232,18 +232,18 @@ describe('Choice parser', function () { {species: "Magnezone", ability: 'magnetpull', moves: ['discharge']}, ]); - const validDecisions = ['move 1', 'switch 4', 'shift']; + const validChoices = ['move 1', 'switch 4', 'shift']; - validDecisions.forEach(decision => { - const choiceString = `${decision}, move 1, move 1 1`; - assert(battle.choose('p1', choiceString), `Decision '${choiceString}' should be valid`); + validChoices.forEach(action => { + const choiceString = `${action}, move 1, move 1 1`; + assert(battle.choose('p1', choiceString), `Choice '${choiceString}' should be valid`); battle.p1.clearChoice(); }); - const badDecisions = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass']; - for (const badDecision of badDecisions) { - const choiceString = `${badDecision}, move 1, move 1 1`; - assert.false(battle.choose('p1', choiceString), `Decision '${choiceString}' should be rejected`); + const badChoices = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass']; + for (const badChoice of badChoices) { + const choiceString = `${badChoice}, move 1, move 1 1`; + assert.false(battle.choose('p1', choiceString), `Choice '${choiceString}' should be rejected`); } }); @@ -262,18 +262,18 @@ describe('Choice parser', function () { {species: "Magnezone", ability: 'magnetpull', moves: ['discharge']}, ]); - const validDecisions = ['move 1 1', 'switch 4', 'shift']; + const validChoices = ['move 1 1', 'switch 4', 'shift']; - validDecisions.forEach(decision => { - const choiceString = `move 1, move 1, ${decision}`; - assert(battle.choose('p1', choiceString), `Decision '${choiceString}' should be valid`); + validChoices.forEach(action => { + const choiceString = `move 1, move 1, ${action}`; + assert(battle.choose('p1', choiceString), `Choice '${choiceString}' should be valid`); battle.p1.clearChoice(); }); - const badDecisions = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass', 'shift blah']; - for (const badDecision of badDecisions) { - const choiceString = `move 1, move 1, ${badDecision}`; - assert.false(battle.choose('p1', choiceString), `Decision '${choiceString}' should be rejected`); + const badChoices = ['move 1 zmove', 'move 2 mega', 'team 1', 'pass', 'shift blah']; + for (const badChoice of badChoices) { + const choiceString = `move 1, move 1, ${badChoice}`; + assert.false(battle.choose('p1', choiceString), `Choice '${choiceString}' should be rejected`); } }); @@ -295,18 +295,18 @@ describe('Choice parser', function () { p1.choosePass().chooseSwitch(4).chooseDefault(); // Forretress switches in to slot #2 assert.species(p1.active[1], 'Forretress'); - const validDecisions = ['move spikes', 'move 1']; - validDecisions.forEach(decision => { - battle.choose('p1', decision); + const validChoices = ['move spikes', 'move 1']; + validChoices.forEach(action => { + battle.choose('p1', action); assert.strictEqual(battle.p1.getChoice(true), `pass, move spikes, pass`); battle.p1.clearChoice(); - battle.choose('p1', `pass, ${decision}, pass`); + battle.choose('p1', `pass, ${action}, pass`); assert.strictEqual(battle.p1.getChoice(true), `pass, move spikes, pass`); battle.p1.clearChoice(); - battle.choose('p1', `pass, ${decision}`); + battle.choose('p1', `pass, ${action}`); assert.strictEqual(battle.p1.getChoice(true), `pass, move spikes, pass`); battle.p1.clearChoice(); - battle.choose('p1', `${decision}, pass`); + battle.choose('p1', `${action}, pass`); assert.strictEqual(battle.p1.getChoice(true), `pass, move spikes, pass`); battle.p1.clearChoice(); }); diff --git a/test/simulator/misc/decisions.js b/test/simulator/misc/decisions.js index ab5008463955..db72cd4be834 100644 --- a/test/simulator/misc/decisions.js +++ b/test/simulator/misc/decisions.js @@ -98,13 +98,13 @@ const TRIPLES_TEAMS = { let battle; -describe('Decisions', function () { +describe('Choices', function () { afterEach(function () { battle.destroy(); }); describe('Generic', function () { - it('should wait for players to send their decisions and run them as soon as they are all received', function (done) { + it('should wait for players to send their choicess and run them as soon as they are all received', function (done) { battle = common.createBattle(); battle.join('p1', 'Guest 1', 1, [{species: "Mew", ability: 'synchronize', moves: ['recover']}]); battle.join('p2', 'Guest 2', 1, [{species: "Rhydon", ability: 'prankster', moves: ['sketch']}]); @@ -499,7 +499,7 @@ describe('Decisions', function () { } }); - it('should autocomplete a single-slot decision in Singles for no Illusion', function () { + it('should autocomplete a single-slot action in Singles for no Illusion', function () { // Backwards-compatibility with the client. It should be useful for 3rd party bots/clients (Android?) for (let i = 0; i < 5; i++) { battle = common.createBattle({preview: true}, SINGLES_TEAMS.full); @@ -736,11 +736,11 @@ describe('Decisions', function () { }); }); -describe('Decision extensions', function () { +describe('Choice extensions', function () { describe('Undo', function () { const MODES = ['revoke', 'override']; for (const mode of MODES) { - it(`should disallow to ${mode} decisions after every player has sent an unrevoked decision`, function () { + it(`should disallow to ${mode} decisions after every player has sent an unrevoked action`, function () { battle = common.createBattle({cancel: true}); battle.join('p1', 'Guest 1', 1, [{species: "Bulbasaur", ability: 'overgrow', moves: ['tackle', 'growl']}]); battle.join('p2', 'Guest 2', 1, [{species: "Charmander", ability: 'blaze', moves: ['tackle', 'growl']}]); @@ -1113,7 +1113,7 @@ describe('Decision extensions', function () { ['Deoxys-Attack', 'Chikorita'].forEach((species, index) => assert.species(battle.p1.active[index], species)); }); - it(`should support to ${mode} team order decision on team preview requests`, function () { + it(`should support to ${mode} team order action on team preview requests`, function () { battle = common.createBattle({preview: true, cancel: true}, [ [{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']}, {species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']}], [{species: 'Charmander', ability: 'blaze', moves: ['scratch']}, {species: 'Charmeleon', ability: 'blaze', moves: ['scratch']}], @@ -1140,7 +1140,7 @@ describe('Decision extensions', function () { ['Bulbasaur', 'Ivysaur'].forEach((species, index) => assert.species(battle.p1.pokemon[index], species)); }); - it(`should disallow to ${mode} team order decision on team preview requests by default`, function () { + it(`should disallow to ${mode} team order action on team preview requests by default`, function () { battle = common.createBattle({preview: true}, [ [{species: 'Bulbasaur', ability: 'overgrow', moves: ['tackle']}, {species: 'Ivysaur', ability: 'overgrow', moves: ['tackle']}], [{species: 'Charmander', ability: 'blaze', moves: ['scratch']}, {species: 'Charmeleon', ability: 'blaze', moves: ['scratch']}], @@ -1157,7 +1157,7 @@ describe('Decision extensions', function () { }); }); -describe('Decision internals', function () { +describe('Choice internals', function () { afterEach(function () { battle.destroy(); }); @@ -1248,7 +1248,7 @@ describe('Decision internals', function () { assert.strictEqual(p1.active[1].name, 'Ekans'); }); - it('should empty the decisions list when undoing a move', function () { + it('should empty the actions list when undoing a move', function () { battle = common.createBattle({gameType: 'doubles', cancel: true}); const p1 = battle.join('p1', 'Guest 1', 1, [ {species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']}, @@ -1271,7 +1271,7 @@ describe('Decision internals', function () { assert.fainted(p1.active[1]); }); - it('should empty the decisions list when undoing a switch', function () { + it('should empty the actions list when undoing a switch', function () { battle = common.createBattle({gameType: 'doubles', cancel: true}); const p1 = battle.join('p1', 'Guest 1', 1, [ {species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']}, @@ -1295,7 +1295,7 @@ describe('Decision internals', function () { assert.species(p1.active[1], 'Koffing'); }); - it('should empty the decisions list when undoing a pass', function () { + it('should empty the actions list when undoing a pass', function () { battle = common.createBattle({gameType: 'doubles', cancel: true}); const p1 = battle.join('p1', 'Guest 1', 1, [ {species: "Pineco", ability: 'sturdy', moves: ['selfdestruct']}, @@ -1319,7 +1319,7 @@ describe('Decision internals', function () { assert.species(p1.active[1], 'Koffing'); }); - it('should empty the decisions list when undoing a shift', function () { + it('should empty the actions list when undoing a shift', function () { battle = common.createBattle({gameType: 'triples', cancel: true}); battle.supportCancel = true; const p1 = battle.join('p1', 'Guest 1', 1, [