[Move] Implement (or re-implement?) Lucky Chant (#3352)
* Implement Lucky Chant * Add i18n keys for NoCritTag messages * Add Lucky Chant message translations (DE, FR, KO, PT-BR) Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr> Co-authored-by: Enoch <enoch.jwsong@gmail.com> Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br> * Add ZH translations Co-authored-by: Sonny Ding <93831983+sonnyding1@users.noreply.github.com> * Add keys for JA locale --------- Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr> Co-authored-by: Enoch <enoch.jwsong@gmail.com> Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br> Co-authored-by: Sonny Ding <93831983+sonnyding1@users.noreply.github.com>
This commit is contained in:
parent
f555dd6dc8
commit
a4c913d963
|
@ -303,6 +303,39 @@ class CraftyShieldTag extends ConditionalProtectTag {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Lucky_Chant_(move) Lucky Chant}.
|
||||
* Prevents critical hits against the tag's side.
|
||||
*/
|
||||
export class NoCritTag extends ArenaTag {
|
||||
/**
|
||||
* Constructor method for the NoCritTag class
|
||||
* @param turnCount `integer` the number of turns this effect lasts
|
||||
* @param sourceMove {@linkcode Moves} the move that created this effect
|
||||
* @param sourceId `integer` the ID of the {@linkcode Pokemon} that created this effect
|
||||
* @param side {@linkcode ArenaTagSide} the side to which this effect belongs
|
||||
*/
|
||||
constructor(turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide) {
|
||||
super(ArenaTagType.NO_CRIT, turnCount, sourceMove, sourceId, side);
|
||||
}
|
||||
|
||||
/** Queues a message upon adding this effect to the field */
|
||||
onAdd(arena: Arena): void {
|
||||
arena.scene.queueMessage(i18next.t(`arenaTag:noCritOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : "Enemy"}`, {
|
||||
moveName: this.getMoveName()
|
||||
}));
|
||||
}
|
||||
|
||||
/** Queues a message upon removing this effect from the field */
|
||||
onRemove(arena: Arena): void {
|
||||
const source = arena.scene.getPokemonById(this.sourceId);
|
||||
arena.scene.queueMessage(i18next.t("arenaTag:noCritOnRemove", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(source),
|
||||
moveName: this.getMoveName()
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Wish_(move) Wish}.
|
||||
* Heals the Pokémon in the user's position the turn after Wish is used.
|
||||
|
@ -803,6 +836,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
|
|||
return new MatBlockTag(sourceId, side);
|
||||
case ArenaTagType.CRAFTY_SHIELD:
|
||||
return new CraftyShieldTag(sourceId, side);
|
||||
case ArenaTagType.NO_CRIT:
|
||||
return new NoCritTag(turnCount, sourceMove, sourceId, side);
|
||||
case ArenaTagType.MUD_SPORT:
|
||||
return new MudSportTag(turnCount, sourceId);
|
||||
case ArenaTagType.WATER_SPORT:
|
||||
|
|
|
@ -1774,8 +1774,6 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
|
|||
case BattlerTagType.ALWAYS_CRIT:
|
||||
case BattlerTagType.IGNORE_ACCURACY:
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, 2, sourceMove);
|
||||
case BattlerTagType.NO_CRIT:
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.AFTER_MOVE, turnCount, sourceMove);
|
||||
case BattlerTagType.ALWAYS_GET_HIT:
|
||||
case BattlerTagType.RECEIVE_DOUBLE_DAMAGE:
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.PRE_MOVE, 1, sourceMove);
|
||||
|
|
|
@ -4258,7 +4258,6 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
|
|||
case BattlerTagType.INFATUATED:
|
||||
case BattlerTagType.NIGHTMARE:
|
||||
case BattlerTagType.DROWSY:
|
||||
case BattlerTagType.NO_CRIT:
|
||||
return -5;
|
||||
case BattlerTagType.SEEDED:
|
||||
case BattlerTagType.SALT_CURED:
|
||||
|
@ -7120,9 +7119,8 @@ export function initMoves() {
|
|||
new StatusMove(Moves.GASTRO_ACID, Type.POISON, 100, 10, -1, 0, 4)
|
||||
.attr(SuppressAbilitiesAttr),
|
||||
new StatusMove(Moves.LUCKY_CHANT, Type.NORMAL, -1, 30, -1, 0, 4)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.NO_CRIT, false, false, 5)
|
||||
.target(MoveTarget.USER_SIDE)
|
||||
.unimplemented(),
|
||||
.attr(AddArenaTagAttr, ArenaTagType.NO_CRIT, 5, true, true)
|
||||
.target(MoveTarget.USER_SIDE),
|
||||
new StatusMove(Moves.ME_FIRST, Type.NORMAL, -1, 20, -1, 0, 4)
|
||||
.ignoresVirtual()
|
||||
.target(MoveTarget.NEAR_ENEMY)
|
||||
|
|
|
@ -21,5 +21,6 @@ export enum ArenaTagType {
|
|||
MAT_BLOCK = "MAT_BLOCK",
|
||||
CRAFTY_SHIELD = "CRAFTY_SHIELD",
|
||||
TAILWIND = "TAILWIND",
|
||||
HAPPY_HOUR = "HAPPY_HOUR"
|
||||
HAPPY_HOUR = "HAPPY_HOUR",
|
||||
NO_CRIT = "NO_CRIT"
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ export enum BattlerTagType {
|
|||
FIRE_BOOST = "FIRE_BOOST",
|
||||
CRIT_BOOST = "CRIT_BOOST",
|
||||
ALWAYS_CRIT = "ALWAYS_CRIT",
|
||||
NO_CRIT = "NO_CRIT",
|
||||
IGNORE_ACCURACY = "IGNORE_ACCURACY",
|
||||
BYPASS_SLEEP = "BYPASS_SLEEP",
|
||||
IGNORE_FLYING = "IGNORE_FLYING",
|
||||
|
|
|
@ -22,7 +22,7 @@ import { BattleStat } from "../data/battle-stat";
|
|||
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag } from "../data/battler-tags";
|
||||
import { WeatherType } from "../data/weather";
|
||||
import { TempBattleStat } from "../data/temp-battle-stat";
|
||||
import { ArenaTagSide, WeakenMoveScreenTag } from "../data/arena-tag";
|
||||
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "../data/arena-tag";
|
||||
import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldBattleStatMultiplierAbAttrs, FieldMultiplyBattleStatAbAttr, AddSecondStrikeAbAttr, IgnoreOpponentEvasionAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr } from "../data/ability";
|
||||
import PokemonData from "../system/pokemon-data";
|
||||
import { BattlerIndex } from "../battle";
|
||||
|
@ -1885,6 +1885,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
apply(source: Pokemon, move: Move): HitResult {
|
||||
let result: HitResult;
|
||||
const damage = new Utils.NumberHolder(0);
|
||||
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||
const defendingSidePlayField = this.isPlayer() ? this.scene.getPlayerField() : this.scene.getEnemyField();
|
||||
|
||||
const variableCategory = new Utils.IntegerHolder(move.category);
|
||||
|
@ -1911,7 +1912,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
|
||||
// Apply arena tags for conditional protection
|
||||
if (!move.checkFlag(MoveFlags.IGNORE_PROTECT, source, this) && !move.isAllyTarget()) {
|
||||
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||
this.scene.arena.applyTagsForSide(ArenaTagType.QUICK_GUARD, defendingSide, cancelled, this, move.priority);
|
||||
this.scene.arena.applyTagsForSide(ArenaTagType.WIDE_GUARD, defendingSide, cancelled, this, move.moveTarget);
|
||||
this.scene.arena.applyTagsForSide(ArenaTagType.MAT_BLOCK, defendingSide, cancelled, this, move.category);
|
||||
|
@ -1978,15 +1978,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
console.log(`crit stage: +${critLevel.value}`);
|
||||
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(critLevel.value, 3))];
|
||||
isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !this.scene.randBattleSeedInt(critChance));
|
||||
isCritical = critChance === 1 || !this.scene.randBattleSeedInt(critChance);
|
||||
if (Overrides.NEVER_CRIT_OVERRIDE) {
|
||||
isCritical = false;
|
||||
}
|
||||
}
|
||||
if (isCritical) {
|
||||
const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide);
|
||||
const blockCrit = new Utils.BooleanHolder(false);
|
||||
applyAbAttrs(BlockCritAbAttr, this, null, blockCrit);
|
||||
if (blockCrit.value) {
|
||||
if (noCritTag || blockCrit.value) {
|
||||
isCritical = false;
|
||||
}
|
||||
}
|
||||
|
@ -1996,7 +1997,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
applyAbAttrs(MultCritAbAttr, source, null, criticalMultiplier);
|
||||
const screenMultiplier = new Utils.NumberHolder(1);
|
||||
if (!isCritical) {
|
||||
this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, move.category, this.scene.currentBattle.double, screenMultiplier);
|
||||
this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier);
|
||||
}
|
||||
const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier.value) === 0;
|
||||
const sourceTypes = source.getTypes();
|
||||
|
|
|
@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = {
|
|||
"conditionalProtectOnAddEnemy": "Die Pokémon auf der gegnerischen Seite werden von {{moveName}} behütet!",
|
||||
"conditionalProtectApply": "{{pokemonNameWithAffix}} wird durch {{moveName}} geschützt!",
|
||||
"matBlockOnAdd": "{{pokemonNameWithAffix}} bringt seinen Tatami-Schild in Position!",
|
||||
"noCritOnAddPlayer": "{{moveName}} schützt dein Team vor Volltreffern!",
|
||||
"noCritOnAddEnemy": "{{moveName}} schützt das gegnerische Team vor Volltreffern!",
|
||||
"noCritOnRemove": "{{moveName}} von {{pokemonNameWithAffix}} hört auf zu wirken!",
|
||||
"wishTagOnAdd": "Der Wunschtraum von {{pokemonNameWithAffix}} erfüllt sich!",
|
||||
"mudSportOnAdd": "Die Stärke aller Elektro-Attacken wurde reduziert!",
|
||||
"mudSportOnRemove": "Lehmsuhler hört auf zu wirken!",
|
||||
|
|
|
@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = {
|
|||
"conditionalProtectOnAddEnemy": "{{moveName}} protected the\nopposing team!",
|
||||
"conditionalProtectApply": "{{moveName}} protected {{pokemonNameWithAffix}}!",
|
||||
"matBlockOnAdd": "{{pokemonNameWithAffix}} intends to flip up a mat\nand block incoming attacks!",
|
||||
"noCritOnAddPlayer": "The {{moveName}} shielded your\nteam from critical hits!",
|
||||
"noCritOnAddEnemy": "The {{moveName}} shielded the opposing\nteam from critical hits!",
|
||||
"noCritOnRemove": "{{pokemonNameWithAffix}}'s {{moveName}}\nwore off!",
|
||||
"wishTagOnAdd": "{{pokemonNameWithAffix}}'s wish\ncame true!",
|
||||
"mudSportOnAdd": "Electricity's power was weakened!",
|
||||
"mudSportOnRemove": "The effects of Mud Sport\nhave faded.",
|
||||
|
|
|
@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = {
|
|||
"conditionalProtectOnAddEnemy": "{{moveName}} protected the\nopposing team!",
|
||||
"conditionalProtectApply": "{{moveName}} protected {{pokemonNameWithAffix}}!",
|
||||
"matBlockOnAdd": "{{pokemonNameWithAffix}} intends to flip up a mat\nand block incoming attacks!",
|
||||
"noCritOnAddPlayer": "The {{moveName}} shielded your\nteam from critical hits!",
|
||||
"noCritOnAddEnemy": "The {{moveName}} shielded the opposing\nteam from critical hits!",
|
||||
"noCritOnRemove": "{{pokemonNameWithAffix}}'s {{moveName}}\nwore off!",
|
||||
"wishTagOnAdd": "{{pokemonNameWithAffix}}'s wish\ncame true!",
|
||||
"mudSportOnAdd": "Electricity's power was weakened!",
|
||||
"mudSportOnRemove": "The effects of Mud Sport\nhave faded.",
|
||||
|
|
|
@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = {
|
|||
"conditionalProtectOnAddEnemy": "La capacité {{moveName}}\nprotège l’équipe ennemie !",
|
||||
"conditionalProtectApply": "{{pokemonNameWithAffix}} est protégé\npar {{moveName}} !",
|
||||
"matBlockOnAdd": "{{pokemonNameWithAffix}} se prépare\nà utiliser un tatami pour bloquer les attaques !",
|
||||
"noCritOnAddPlayer": "{{moveName}} immunise votre équipe\ncontre les coups critiques !",
|
||||
"noCritOnAddEnemy": "{{moveName}} immunise l’équipe ennemie\ncontre les coups critiques !",
|
||||
"noCritOnRemove": "Les effets d’{{moveName}}\nsur {{pokemonNameWithAffix}} prennent fin !",
|
||||
"wishTagOnAdd": "Le vœu de{{pokemonNameWithAffix}}\nse réalise !",
|
||||
"mudSportOnAdd": "La puissance des capacités\nde type Électrik diminue !",
|
||||
"mudSportOnRemove": "L’effet de Lance-Boue se dissipe !",
|
||||
|
|
|
@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = {
|
|||
"conditionalProtectOnAddEnemy": "{{moveName}} protected the\nopposing team!",
|
||||
"conditionalProtectApply": "{{moveName}} protected {{pokemonNameWithAffix}}!",
|
||||
"matBlockOnAdd": "{{pokemonNameWithAffix}} intends to flip up a mat\nand block incoming attacks!",
|
||||
"noCritOnAddPlayer": "The {{moveName}} shielded your\nteam from critical hits!",
|
||||
"noCritOnAddEnemy": "The {{moveName}} shielded the opposing\nteam from critical hits!",
|
||||
"noCritOnRemove": "{{pokemonNameWithAffix}}'s {{moveName}}\nwore off!",
|
||||
"wishTagOnAdd": "{{pokemonNameWithAffix}}'s wish\ncame true!",
|
||||
"mudSportOnAdd": "Electricity's power was weakened!",
|
||||
"mudSportOnRemove": "The effects of Mud Sport\nhave faded.",
|
||||
|
|
|
@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = {
|
|||
"conditionalProtectOnAddEnemy": "{{moveName}} protected the\nopposing team!",
|
||||
"conditionalProtectApply": "{{moveName}} protected {{pokemonNameWithAffix}}!",
|
||||
"matBlockOnAdd": "{{pokemonNameWithAffix}} intends to flip up a mat\nand block incoming attacks!",
|
||||
"noCritOnAddPlayer": "The {{moveName}} shielded your\nteam from critical hits!",
|
||||
"noCritOnAddEnemy": "The {{moveName}} shielded the opposing\nteam from critical hits!",
|
||||
"noCritOnRemove": "{{pokemonNameWithAffix}}'s {{moveName}}\nwore off!",
|
||||
"wishTagOnAdd": "{{pokemonNameWithAffix}}'s wish\ncame true!",
|
||||
"mudSportOnAdd": "Electricity's power was weakened!",
|
||||
"mudSportOnRemove": "The effects of Mud Sport\nhave faded.",
|
||||
|
|
|
@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = {
|
|||
"conditionalProtectOnAddEnemy": "상대 주변을\n{{moveName}}[[가]] 보호하고 있다!",
|
||||
"conditionalProtectApply": "{{pokemonNameWithAffix}}[[를]]\n{{moveName}}[[가]] 지켜주고 있다!",
|
||||
"matBlockOnAdd": "{{pokemonNameWithAffix}}[[는]]\n마룻바닥세워막기를 노리고 있다!",
|
||||
"noCritOnAddPlayer": "{{moveName}}의 힘으로\n우리 편의 급소가 숨겨졌다!",
|
||||
"noCritOnAddEnemy": "{{moveName}}의 힘으로\n상대의 급소가 숨겨졌다!",
|
||||
"noCritOnRemove": "{{pokemonNameWithAffix}}의 {{moveName}}[[가]] 풀렸다!",
|
||||
"wishTagOnAdd": "{{pokemonNameWithAffix}}의\n희망사항이 이루어졌다!",
|
||||
"mudSportOnAdd": "전기의 위력이 약해졌다!",
|
||||
"mudSportOnRemove": "흙놀이의 효과가\n없어졌다!",
|
||||
|
|
|
@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = {
|
|||
"conditionalProtectOnAddEnemy": "{{moveName}} protegeu a\nequipe adversária!",
|
||||
"conditionalProtectApply": "{{moveName}} protegeu {{pokemonNameWithAffix}}!",
|
||||
"matBlockOnAdd": "{{pokemonNameWithAffix}} pretende levantar um tapete\npara bloquear ataques!",
|
||||
"noCritOnAddPlayer": "{{moveName}} protegeu sua\equipe de acertos críticos!",
|
||||
"noCritOnAddEnemy": "{{moveName}} protegeu a\equipe adversária de acertos críticos",
|
||||
"noCritOnRemove": "{{moveName}} de {{pokemonNameWithAffix}}\nacabou!",
|
||||
"wishTagOnAdd": "O desejo de {{pokemonNameWithAffix}}\nfoi concedido!",
|
||||
"mudSportOnAdd": "O poder de movimentos elétricos foi enfraquecido!",
|
||||
"mudSportOnRemove": "Os efeitos de Mud Sport\nsumiram.",
|
||||
|
|
|
@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = {
|
|||
"conditionalProtectOnAddEnemy": "{{moveName}}\n保护了敌方!",
|
||||
"conditionalProtectApply": "{{moveName}}\n保护了{{pokemonNameWithAffix}}!",
|
||||
"matBlockOnAdd": "{{pokemonNameWithAffix}}正在\n伺机使出掀榻榻米!",
|
||||
"noCritOnAddPlayer": "{{moveName}}保护了你的\n队伍不被击中要害!",
|
||||
"noCritOnAddEnemy": "{{moveName}}保护了对方的\n队伍不被击中要害!",
|
||||
"noCritOnRemove": "{{pokemonNameWithAffix}}的{{moveName}}\n效果消失了!",
|
||||
"wishTagOnAdd": "{{pokemonNameWithAffix}}的\n祈愿实现了!",
|
||||
"mudSportOnAdd": "电气的威力减弱了!",
|
||||
"mudSportOnRemove": "玩泥巴的效果消失了!",
|
||||
|
|
|
@ -22,6 +22,9 @@ export const arenaTag: SimpleTranslationEntries = {
|
|||
"conditionalProtectOnAddEnemy": "{{moveName}} protected the\nopposing team!",
|
||||
"conditionalProtectApply": "{{moveName}} protected {{pokemonNameWithAffix}}!",
|
||||
"matBlockOnAdd": "{{pokemonNameWithAffix}} intends to flip up a mat\nand block incoming attacks!",
|
||||
"noCritOnAddPlayer": "{{moveName}}保護了你的\n隊伍不被擊中要害!",
|
||||
"noCritOnAddEnemy": "{{moveName}}保護了對方的\n隊伍不被擊中要害!",
|
||||
"noCritOnRemove": "{{pokemonNameWithAffix}}的{{moveName}}\n效果消失了!",
|
||||
"wishTagOnAdd": "{{pokemonNameWithAffix}}'s wish\ncame true!",
|
||||
"mudSportOnAdd": "Electricity's power was weakened!",
|
||||
"mudSportOnRemove": "The effects of Mud Sport\nhave faded.",
|
||||
|
|
|
@ -35,7 +35,6 @@ describe("Moves - Dragon Rage", () => {
|
|||
game = new GameManager(phaserGame);
|
||||
|
||||
game.override.battleType("single");
|
||||
game.override.disableCrits();
|
||||
|
||||
game.override.starterSpecies(Species.SNORLAX);
|
||||
game.override.moveset([Moves.DRAGON_RAGE]);
|
||||
|
@ -60,6 +59,7 @@ describe("Moves - Dragon Rage", () => {
|
|||
});
|
||||
|
||||
it("ignores weaknesses", async () => {
|
||||
game.override.disableCrits();
|
||||
vi.spyOn(enemyPokemon, "getTypes").mockReturnValue([Type.DRAGON]);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
|
@ -70,6 +70,7 @@ describe("Moves - Dragon Rage", () => {
|
|||
});
|
||||
|
||||
it("ignores resistances", async () => {
|
||||
game.override.disableCrits();
|
||||
vi.spyOn(enemyPokemon, "getTypes").mockReturnValue([Type.STEEL]);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
|
@ -80,6 +81,7 @@ describe("Moves - Dragon Rage", () => {
|
|||
});
|
||||
|
||||
it("ignores stat changes", async () => {
|
||||
game.override.disableCrits();
|
||||
partyPokemon.summonData.battleStats[BattleStat.SPATK] = 2;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
|
@ -90,6 +92,7 @@ describe("Moves - Dragon Rage", () => {
|
|||
});
|
||||
|
||||
it("ignores stab", async () => {
|
||||
game.override.disableCrits();
|
||||
vi.spyOn(partyPokemon, "getTypes").mockReturnValue([Type.DRAGON]);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
|
@ -100,7 +103,6 @@ describe("Moves - Dragon Rage", () => {
|
|||
});
|
||||
|
||||
it("ignores criticals", async () => {
|
||||
partyPokemon.removeTag(BattlerTagType.NO_CRIT);
|
||||
partyPokemon.addTag(BattlerTagType.ALWAYS_CRIT, 99);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
|
@ -111,6 +113,7 @@ describe("Moves - Dragon Rage", () => {
|
|||
});
|
||||
|
||||
it("ignores damage modification from abilities such as ice scales", async () => {
|
||||
game.override.disableCrits();
|
||||
game.override.enemyAbility(Abilities.ICE_SCALES);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
|
@ -121,6 +124,7 @@ describe("Moves - Dragon Rage", () => {
|
|||
});
|
||||
|
||||
it("ignores multi hit", async () => {
|
||||
game.override.disableCrits();
|
||||
game.scene.addModifier(modifierTypes.MULTI_LENS().newModifier(partyPokemon), false);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE));
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import GameManager from "../utils/gameManager";
|
||||
import { getMovePosition } from "../utils/gameManagerUtils";
|
||||
import { Moves } from "#app/enums/moves.js";
|
||||
import { Species } from "#app/enums/species.js";
|
||||
import { Abilities } from "#app/enums/abilities.js";
|
||||
import { BerryPhase, TurnEndPhase } from "#app/phases.js";
|
||||
import { BattlerTagType } from "#app/enums/battler-tag-type.js";
|
||||
|
||||
const TIMEOUT = 20 * 1000;
|
||||
|
||||
describe("Moves - Lucky Chant", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
|
||||
game.override
|
||||
.battleType("single")
|
||||
.moveset([Moves.LUCKY_CHANT, Moves.SPLASH, Moves.FOLLOW_ME])
|
||||
.enemySpecies(Species.SNORLAX)
|
||||
.enemyAbility(Abilities.INSOMNIA)
|
||||
.enemyMoveset(Array(4).fill(Moves.FLOWER_TRICK))
|
||||
.startingLevel(100)
|
||||
.enemyLevel(100);
|
||||
});
|
||||
|
||||
it(
|
||||
"should prevent critical hits from moves",
|
||||
async () => {
|
||||
await game.startBattle([Species.CHARIZARD]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon();
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
const firstTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.LUCKY_CHANT));
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
const secondTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp - firstTurnDamage;
|
||||
expect(secondTurnDamage).toBeLessThan(firstTurnDamage);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
it(
|
||||
"should prevent critical hits against the user's ally",
|
||||
async () => {
|
||||
game.override.battleType("double");
|
||||
|
||||
await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerField();
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FOLLOW_ME));
|
||||
game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
const firstTurnDamage = playerPokemon[0].getMaxHp() - playerPokemon[0].hp;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.FOLLOW_ME));
|
||||
game.doAttack(getMovePosition(game.scene, 1, Moves.LUCKY_CHANT));
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
const secondTurnDamage = playerPokemon[0].getMaxHp() - playerPokemon[0].hp - firstTurnDamage;
|
||||
expect(secondTurnDamage).toBeLessThan(firstTurnDamage);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
it(
|
||||
"should prevent critical hits from field effects",
|
||||
async () => {
|
||||
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
|
||||
|
||||
await game.startBattle([Species.CHARIZARD]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon();
|
||||
const enemyPokemon = game.scene.getEnemyPokemon();
|
||||
|
||||
enemyPokemon.addTag(BattlerTagType.ALWAYS_CRIT, 2, Moves.NONE, 0);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
const firstTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.LUCKY_CHANT));
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
const secondTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp - firstTurnDamage;
|
||||
expect(secondTurnDamage).toBeLessThan(firstTurnDamage);
|
||||
}, TIMEOUT
|
||||
);
|
||||
});
|
Loading…
Reference in New Issue