From cd489c6a608ef3cea48f25e716b5cc1ea47bf484 Mon Sep 17 00:00:00 2001 From: EnochG1 Date: Mon, 19 Aug 2024 08:46:49 +0900 Subject: [PATCH 01/30] fix wrong line break character --- src/locales/ko/move-trigger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/ko/move-trigger.ts b/src/locales/ko/move-trigger.ts index cea60e25333..e93639689d8 100644 --- a/src/locales/ko/move-trigger.ts +++ b/src/locales/ko/move-trigger.ts @@ -8,7 +8,7 @@ export const moveTriggers: SimpleTranslationEntries = { "goingAllOutForAttack": "{{pokemonName}}[[는]]\n전력을 다하기 시작했다!", "regainedHealth": "{{pokemonName}}[[는]]\n기력을 회복했다!", "keptGoingAndCrashed": "{{pokemonName}}[[는]]\n의욕이 넘쳐서 땅에 부딪쳤다!", - "fled": "{{pokemonName}}[[는]]\N도망쳤다!", + "fled": "{{pokemonName}}[[는]]\n도망쳤다!", "cannotBeSwitchedOut": "{{pokemonName}}[[를]]\n돌아오게 할 수 없습니다!", "swappedAbilitiesWithTarget": "{{pokemonName}}[[는]]\n서로의 특성을 교체했다!", "coinsScatteredEverywhere": "돈이 주위에 흩어졌다!", From e39ebb68f2d79c99d4b73e73f7bb2863c09c1702 Mon Sep 17 00:00:00 2001 From: damocleas Date: Tue, 20 Aug 2024 00:59:23 -0400 Subject: [PATCH 02/30] [Balance] Dark Void 80% Accurate (#1836) * Dark Void 80% Accurate * updated comment --- src/data/move.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/move.ts b/src/data/move.ts index af3f49bea0d..acb61042e70 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -7474,7 +7474,7 @@ export function initMoves() { .attr(OpponentHighHpPowerAttr, 120), new AttackMove(Moves.MAGMA_STORM, Type.FIRE, MoveCategory.SPECIAL, 100, 75, 5, -1, 0, 4) .attr(TrapAttr, BattlerTagType.MAGMA_STORM), - new StatusMove(Moves.DARK_VOID, Type.DARK, 50, 10, -1, 0, 4) + new StatusMove(Moves.DARK_VOID, Type.DARK, 80, 10, -1, 0, 4) //Accuracy from Generations 4-6 .attr(StatusEffectAttr, StatusEffect.SLEEP) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.SEED_FLARE, Type.GRASS, MoveCategory.SPECIAL, 120, 85, 5, 40, 0, 4) From 7946382817a2c2582cd96960d96d5cbf4f887734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ricardo=20Fleury=20Oliveira?= Date: Tue, 20 Aug 2024 02:06:49 -0300 Subject: [PATCH 03/30] [Localization] Portuguese hotfix of dialogue.ts and more (#3658) * [Localization] Portuguese hotfix of dialogue.ts and more * menu fixes * fix wrong message key of curse --- src/locales/pt_BR/dialogue.ts | 1406 +++++++++++++++++++++++++ src/locales/pt_BR/menu.ts | 4 +- src/locales/pt_BR/modifier.ts | 2 +- src/locales/pt_BR/move-trigger.ts | 2 +- src/locales/pt_BR/party-ui-handler.ts | 2 +- src/locales/pt_BR/pokemon-form.ts | 24 +- 6 files changed, 1423 insertions(+), 17 deletions(-) diff --git a/src/locales/pt_BR/dialogue.ts b/src/locales/pt_BR/dialogue.ts index cb0c05fab45..969c6fc36a9 100644 --- a/src/locales/pt_BR/dialogue.ts +++ b/src/locales/pt_BR/dialogue.ts @@ -2779,6 +2779,90 @@ export const PGFdialogue: DialogueTranslationEntries = { 9: "Estou realmente cansando de batalhar… Deve haver algo novo para fazer…" } }, + "breeder": { + "encounter": { + 1: "Pokémon obedientes, Pokémon egoístas… Pokémon têm características únicas.", + 2: "Embora minha criação e comportamento sejam ruins, criei meus Pokémon bem.", + 3: "Hmm, você disciplina seus Pokémon? Mimar demais não é bom." + }, + "victory": { + 1: "É importante nutrir e treinar as características de cada Pokémon.", + 2: "Ao contrário do meu lado diabólico, esses são bons Pokémon.", + 3: "Muito elogio pode estragar tanto Pokémon quanto pessoas." + }, + "defeat": { + 1: "Você não deve ficar com raiva dos seus Pokémon, mesmo se perder uma batalha.", + 2: "Certo? Pokémon bons, né? Eu sou adequado para criar coisas.", + 3: "Não importa o quanto você ame seus Pokémon, ainda precisa discipliná-los quando se comportam mal." + } + }, + "breeder_female": { + "encounter": { + 1: "Pokémon nunca te traem. Eles retribuem todo o amor que você dá a eles.", + 2: "Quer uma dica para treinar bons Pokémon?", + 3: "Eu criei esses Pokémon muito especiais usando um método especial." + }, + "victory": { + 1: "Ugh… Não era para ser assim. Será que administrei a mistura errada?", + 2: "Como isso aconteceu com meus Pokémon… O que você está dando de comer aos seus Pokémon?", + 3: "Se eu perder, isso significa que eu estava só matando o tempo. Não machuca meu ego nem um pouco." + }, + "defeat": { + 1: "Isso prova que meus Pokémon aceitaram meu amor.", + 2: "O verdadeiro truque para treinar bons Pokémon é capturar bons Pokémon.", + 3: "Pokémon serão fortes ou fracos dependendo de como você os cria." + } + }, + "fisherman": { + "encounter": { + 1: "Anem! Você me fez perder uma fisgada!\nO que vai fazer sobre isso?", + 2: "Sai daqui! Você está assustando os Pokémon!", + 3: "Vamos ver se você consegue fisgar uma vitória!", + }, + "victory": { + 1: "Esqueça isso.", + 2: "Da próxima vez, eu vou pescar a vitória!", + 3: "Acho que subestimei a força das correntes dessa vez.", + }, + }, + "fisherman_female": { + "encounter": { + 1: "Uau! Peguei um grande!", + 2: "Linha lançada, pronta para pescar o sucesso!", + 3: "Pronta para fazer ondas!" + }, + "victory": { + 1: "Vou voltar com um anzol mais forte.", + 2: "Vou pescar a vitória na próxima vez.", + 3: "Estou só afiando meus anzóis para a revanche!" + }, + }, + "swimmer": { + "encounter": { + 1: "Hora de mergulhar!", + 2: "Vamos surfar nas ondas da vitória!", + 3: "Pronto para fazer um splash!", + }, + "victory": { + 1: "Molhado na derrota!", + 2: "Uma onda de derrota!", + 3: "De volta à praia, eu acho.", + }, + }, + "backpacker": { + "encounter": { + 1: "Prepare-se, vamos começar!", + 2: "Vamos ver se você consegue acompanhar!", + 3: "Prepare-se, desafiante!", + 4: "Passei 20 anos tentando me encontrar… Mas onde estou?" + }, + "victory": { + 1: "Dessa vez tropecei!", + 2: "Ah, acho que estou perdido.", + 3: "Caminho sem saída!", + 4: "Espere um segundo! Ei! Você não sabe quem eu sou?" + }, + }, "ace_trainer": { "encounter": { 1: "Você parece bastante confiante.", @@ -2799,6 +2883,14 @@ export const PGFdialogue: DialogueTranslationEntries = { 4: "Claro que sou forte e não perco. É importante ganhar com graça." } }, + "parasol_lady": { + "encounter": { + 1: "Hora de embelezar o campo de batalha com elegância e postura!", + }, + "victory": { + 1: "Minha elegância permanece inabalável!", + } + }, "twins": { "encounter": { 1: "Prepare-se, porque quando nos unimos, é o dobro do problema!", @@ -2816,6 +2908,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Dobro de sorrisos, dobro da dança da vitória!" } }, + "cyclist": { + "encounter": { + 1: "Prepare-se para comer poeira!", + 2: "Prepare-se, desafiante! Estou prestes a te deixar para trás!", + 3: "Pé no pedal, vamos ver se você consegue acompanhar!" + }, + "victory": { + 1: "As rodas podem estar paradas, mas a determinação continua a pedalar.", + 2: "Fui mais rápido!", + 3: "O caminho para a vitória tem muitas curvas e voltas para explorar." + }, + }, "black_belt": { "encounter": { 1: "Elogio sua coragem ao me desafiar! Pois eu sou o que tem o chute mais forte!", @@ -2826,6 +2930,100 @@ export const PGFdialogue: DialogueTranslationEntries = { 2: "Hmmm… Se eu ia perder de qualquer maneira, esperava ficar totalmente destruído no processo." }, }, + "battle_girl": { + "encounter": { + 1: "Você não precisa tentar me impressionar. Você pode perder contra mim.", + }, + "victory": { + 1: "É difícil dizer adeus, mas estamos ficando sem tempo…", + }, + }, + "hiker": { + "encounter": { + 1: "Minha barriga de meia-idade me deu tanta gravidade quanto as montanhas que eu escalo!", + 2: "Herdei esse corpo ossudo dos meus pais… Sou como uma cadeia de montanhas viva…", + }, + "victory": { + 1: "Pelo menos não posso perder quando se trata de IMC!", + 2: "Não é suficiente… Nunca é suficiente. Meu colesterol ruim não está alto o suficiente…" + }, + }, + "ranger": { + "encounter": { + 1: "Quando estou cercado pela natureza, a maioria das outras coisas deixa de importar.", + 2: "Quando estou vivendo sem natureza na minha vida, às vezes sinto uma crise de ansiedade se aproximando." + }, + "victory": { + 1: "Não importa para a vastidão da natureza se eu ganhar ou perder…", + 2: "Algo assim é bastante trivial comparado aos sentimentos sufocantes da vida na cidade." + }, + "defeat": { + 1: "Ganhei a batalha. Mas a vitória não é nada comparada à vastidão da natureza…", + 2: "Tenho certeza de que como você se sente não é tão ruim se comparar aos meus ataques de ansiedade…" + } + }, + "scientist": { + "encounter": { + 1: "Minha pesquisa levará este mundo à paz e alegria.", + }, + "victory": { + 1: "Sou um gênio… Não devo perder para alguém como você…", + }, + }, + "school_kid": { + "encounter": { + 1: "Heehee. Estou confiante nos meus cálculos e análises.", + 2: "Estou ganhando o máximo de experiência que posso porque quero ser um Líder de Ginásio um dia." + }, + "victory": { + 1: "Aff… Cálculo e análise talvez não sejam páreo para o acaso…", + 2: "Até experiências difíceis e desafiadoras têm seu propósito, eu acho." + } + }, + "artist": { + "encounter": { + 1: "Eu costumava ser popular, mas agora estou acabado.", + }, + "victory": { + 1: "À medida que os tempos mudam, os valores também mudam. Percebi isso tarde demais.", + }, + }, + "guitarist": { + "encounter": { + 1: "Prepare-se para sentir o ritmo da derrota enquanto eu toco minha vitória!", + }, + "victory": { + 1: "Silenciado por agora, mas minha melodia de resiliência continuará a tocar.", + }, + }, + "worker": { + "encounter": { + 1: "Me incomoda que as pessoas sempre me entendam mal. Sou muito mais puro do que todos pensam.", + }, + "victory": { + 1: "Eu realmente não quero que minha pele queime, então quero ficar na sombra enquanto trabalho.", + }, + }, + "worker_female": { + "encounter": { + 1: `Me incomoda que as pessoas sempre me entendam mal. + $Sou muito mais pura do que todos pensam.` + }, + "victory": { + 1: "Eu realmente não quero que minha pele queime, então quero ficar na sombra enquanto trabalho." + }, + "defeat": { + 1: "Meu corpo e mente nem sempre estão necessariamente em sincronia." + } + }, + "worker_double": { + "encounter": { + 1: "Vou te mostrar que podemos te quebrar. Estamos treinando no campo!", + }, + "victory": { + 1: "Que estranho… Como isso pode ser… Não deveria ter sido superado.", + }, + }, "hex_maniac": { "encounter": { 1: "Normalmente, só escuto música clássica, mas se eu perder, acho que vou tentar um pouco de new age!", @@ -2840,6 +3038,32 @@ export const PGFdialogue: DialogueTranslationEntries = { 2: "Não fique presa na tristeza ou frustração. Você pode usar seus rancores para se motivar." } }, + "psychic": { + "encounter": { + 1: "Oi! Concentre-se!", + }, + "victory": { + 1: "Perdi minha concentração!", + }, + }, + "officer": { + "encounter": { + 1: "Prepare-se, porque a justiça está prestes a ser servida!", + 2: "Pronto para defender a lei e servir a justiça no campo de batalha!" + }, + "victory": { + 1: "O peso da justiça parece mais pesado do que nunca…", + 2: "As sombras da derrota pairam no distrito." + } + }, + "beauty": { + "encounter": { + 1: "Minha última batalha… É assim que eu gostaria que víssemos esta partida…", + }, + "victory": { + 1: "Foi divertido… Vamos ter outra última batalha algum dia…", + }, + }, "baker": { "encounter": { 1: "Espero que esteja pronta para saborear a derrota!" @@ -2848,6 +3072,26 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Vou assar uma revanche." }, }, + "biker": { + "encounter": { + 1: "Hora de acelerar e te deixar na poeira!" + }, + "victory": { + 1: "Vou me ajustar para a próxima corrida." + }, + }, + "firebreather": { + "encounter": { + 1: "Minhas chamas irão te consumir!", + 2: "Minha alma está pegando fogo. Irei te mostrar como queima!", + 3: "Cola aqui e dá uma olhada!" + }, + "victory": { + 1: "Fui reduzido a cinzas…", + 2: "Uau! Isso foi quente!", + 3: "Ai! Queimei minha língua!" + }, + }, "sailor": { "encounter": { 1: "Mano, você vai andar na prancha se perder!", @@ -2860,6 +3104,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Estou achando que quem tá enjoado sou eu..." }, }, + "archer": { + "encounter": { + 1: "Antes de você ir mais longe, vamos ver como você se sai contra nós, Equipe Rocket!", + 2: "Eu tenho recebido relatórios de que suas habilidades não são insignificantes. Vamos ver se são verdadeiros.", + 3: "Eu sou Archer, um Admin da Equipe Rocket. E não tenho piedade dos inimigos da nossa organização." + }, + "victory": { + 1: "Que vexame!", + 2: "Com minhas habilidades atuais, eu não estava à altura da tarefa, afinal.", + 3: "M-me perdoe, Giovanni... Por ser derrotado por um mero treinador..." + }, + }, "ariana": { "encounter": { 1: "Pera aí! Não podemos deixar alguém solto por aí. Isso é prejudicial para o orgulho da Equipe Rocket, entende?", @@ -2872,6 +3128,30 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Aaaieeeee! Isso não pode estar acontecendo! Eu lutei muito, mas ainda perdi…" }, }, + "proton": { + "encounter": { + 1: "O que você quer? Se você interromper nosso trabalho, não espere misericórdia!", + 2: "O que temos aqui? Costumam me chamar de o cara mais assustador e cruel da Equipe Rocket… Eu recomendo fortemente que você não interfira nos nossos negócios!", + 3: "Eu sou Proton, um Admin da Equipe Rocket. Estou aqui para acabar com a sua intromissão!" + }, + "victory": { + 1: "A fortaleza caiu!", + 2: "Você pode ter vencido desta vez… Mas tudo o que fez foi aumentar a ira da Equipe Rocket…", + 3: "Fui derrotado… Mas não esquecerei disso!" + }, + }, + "petrel": { + "encounter": { + 1: "Muhahaha, estávamos esperando por você. Eu? Você não sabe quem eu sou? Sou eu, Giovanni. O majestoso Giovanni em pessoa! Wahahaha! ...Huh? Eu não pareço nada com Giovanni? Eu nem mesmo pareço com Giovanni? Como assim? Trabalhei tanto para imitá-lo!", + 2: "Eu sou Petrel, um Admin da Equipe Rocket. Não permitirei que você interfira em nossos planos!", + 3: "O Executivo da Rocket, Petrel, vai lidar com este intruso!" + }, + "victory": { + 1: "OK, OK. Vou te contar onde ele está.", + 2: "Eu... Eu não consegui fazer nada... Giovanni, por favor, me perdoe...", + 3: "Não, eu não posso deixar isso me afetar. Tenho que informar os outros…" + }, + }, "tabitha": { "encounter": { 1: "Hehehe! Então você veio até aqui! Mas você chegou tarde demais!", @@ -2884,6 +3164,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Ahya! Como isso pode ser? Para um Admin como eu perder para uma treinadora qualquer..." }, }, + "courtney": { + "encounter": { + 1: "A coisa... A coisa que você segura... É o que... É o que nós da Equipe Magma procuramos...", + 2: "... Bem então... Deletando...", + 3: "...Ha. ...Analisando... ...Hah♪" + }, + "victory": { + 1: "... ...Mudar...o mundo.", + 2: "Como antecipado. Não antecipado. Você. Bloqueio de alvo... concluído. Iniciando... experimento. Você. Para sempre. Aha... ♪", + 3: "... De novo? Isso não foi antecipado. ...Eu sabia. Você... é interessante! ...Haha. ♪" + }, + }, "shelly": { "encounter": { 1: "Ahahahaha! Você vai se meter nos assuntos da Equipe Aqua? Você é absolutamente destemida, simplesmente ignorante ou ambos! Você é tão fofa que chega a ser nojenta! Vou te derrubar", @@ -2920,6 +3212,30 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Derrotada... Este foi um erro caro." } }, + "jupiter": { + "encounter": { + 1: "Júpiter, Comandante da Equipe Galáctica, ao seu serviço.", + 2: "A resistência é inútil. A Equipe Galáctica prevalecerá!", + 3: "Você está tremendo... já está com medo?" + }, + "victory": { + 1: "De jeito nenhum... Eu perdi?!", + 2: "Impressionante, você tem coragem!", + 3: "Perder assim... Que embaraço." + } + }, + "saturn": { + "encounter": { + 1: "Eu sou Saturno, Comandante da Equipe Galáctica.", + 2: "Nossa missão é absoluta. Qualquer obstáculo será obliterado!", + 3: "É medo o que vejo em seus olhos?" + }, + "victory": { + 1: "Impossível... Derrotado por você?!", + 2: "Você provou ser um adversário digno.", + 3: "Derrotado... Isso é inaceitável." + } + }, "zinzolin": { "encounter": { 1: "Você poderia se tornar uma ameaça para a Equipe Plasma, então vamos eliminá-la aqui e agora!", @@ -2984,6 +3300,38 @@ export const PGFdialogue: DialogueTranslationEntries = { 5: "Você diz o que? Equipe Rocket tchau-tchau a vai-vai? Quebrado é diz você?" // Uso de gramática incorreta é proposital }, }, + "magma_grunt": { + "encounter": { + 1: "Se você se meter com a Equipe Magma, não teremos piedade!", + 2: "É melhor você não interferir em nossos planos! Estamos tornando o mundo um lugar melhor!", + 3: "Você está no caminho! A Equipe Magma não tem tempo para crianças como você!", + 4: "Espero que você tenha trazido marshmallows porque as coisas estão prestes a esquentar!", + 5: "Vamos usar o poder de um vulcão! Vai ser... explosivo! Entendeu? Heh heh!" + }, + "victory": { + 1: "Ahn? Eu perdi?!", + 2: "Não posso acreditar que perdi! Até pulei o almoço por isso.", + 3: "De jeito nenhum! Você é apenas uma criança!", + 4: "Urrrgh... Eu deveria ter me escondido em nosso esconderijo imediatamente...", + 5: "Você me venceu... Você acha que o chefe vai cortar meu salário por isso?" + }, + }, + "aqua_grunt": { + "encounter": { + 1: "Não pegamos leve com quem se mete com a Equipe Aqua, nem mesmo crianças!", + 2: "Grrr... Você tem coragem de se intrometer com a Equipe Aqua!", + 3: "Você está prestes a se molhar! E não apenas por causa dos meus Pokémon aquáticos!", + 4: "Nós, da Equipe Aqua, existimos para o bem de todos!", + 5: "Prepare-se para ser levado pelas ondas do meu... uh, Pokémon! Sim, meu Pokémon!" + }, + "victory": { + 1: "Tá de brincadeira!", + 2: "Arrgh, eu não contei que seria atrapalhado por uma criança intrometida!", + 3: "Eu perdi?! Acho que vou ter que nadar de volta para o esconderijo agora...", + 4: "Oh, cara, que desastre... O chefe vai ficar furioso...", + 5: "Você me venceu... Você acha que o chefe vai me fazer andar na prancha por isso?" + }, + }, "galactic_grunt": { "encounter": { 1: "Não mexa com a Equipe Galáctica!", @@ -3000,6 +3348,104 @@ export const PGFdialogue: DialogueTranslationEntries = { 5: "Nota para mim mesmo: praticar batalhas Pokémon, o mais rápido possível." }, }, + "plasma_grunt": { + "encounter": { + 1: "Não toleramos pessoas que pensam diferente de nós!", + 2: "Se eu ganhar de você, liberte seus Pokémon!", + 3: "Se você atrapalhar a Equipe Plasma, eu cuidarei de você!", + 4: "A Equipe Plasma vai libertar os Pokémon de humanos egoístas como você!", + 5: "Nossos penteados são de outro mundo... mas nossas habilidades de batalha? Você descobrirá em breve." + }, + "victory": { + 1: "Plasmaaaaaaaaa!", + 2: "Como eu pude perder...", + 3: "...Que Pokémon fraco, vou ter que roubar alguns melhores!", + 4: "Grandes planos são sempre interrompidos.", + 5: "Isso é ruim... Ruim ruim ruim ruim ruim ruim ruim! Ruim para a Equipe Plasma! Ou Plasruim, para abreviar!" + }, + }, + "flare_grunt": { + "encounter": { + 1: "Seus Pokémon não são páreo para a elegância da Equipe Flare.", + 2: "Espero que você tenha trazido seus óculos de sol, porque as coisas vão ficar brilhantes!", + 3: "A Equipe Flare vai purificar o mundo da imperfeição!", + 4: "Prepare-se para enfrentar o brilho da Equipe Flare!", + 5: "A moda é o mais importante para nós!" + }, + "victory": { + 1: "O futuro não parece brilhante para mim.", + 2: "Talvez haja mais na batalha do que eu pensei. De volta à prancheta.", + 3: "Gahh?! Eu perdi?!", + 4: "Mesmo na derrota, a elegância da Equipe Flare brilha.", + 5: "Você pode ter me vencido, mas quando eu perco, eu saio com estilo!" + }, + }, + "rocket_boss_giovanni_1": { + "encounter": { + 1: "Tenho que admitir, estou impressionado que tenha chegado até aqui!" + }, + "victory": { + 1: "QUÊ! Isso não é possível!" + }, + "defeat": { + 1: "Guarde minhas palavras.\nNão ser capaz de medir sua própria força mostra que você ainda é uma criança." + } + }, + "rocket_boss_giovanni_2": { + "encounter": { + 1: "Meus antigos associados precisam de mim... Você vai ficar no meu caminho?" + }, + "victory": { + 1: "Como isso é possível...?\nO precioso sonho da Equipe Rocket se tornou pouco mais que uma ilusão..." + }, + "defeat": { + 1: "A Equipe Rocket renascerá, e eu dominarei o mundo!" + } + }, + "magma_boss_maxie_1": { + "encounter": { + 1: "Eu vou te enterrar com minhas próprias mãos.\nEspero que você aprecie essa honra!" + }, + "victory": { + 1: "Ugh! Você é... bastante capaz...\nEu fiquei para trás, mas apenas por um triz..." + }, + "defeat": { + 1: "A Equipe Magma vai prevalecer!" + } + }, + "magma_boss_maxie_2": { + "encounter": { + 1: "Você é o último obstáculo entre mim e meus objetivos.\nPrepare-se para meu ataque final! Fuhahaha!" + }, + "victory": { + 1: "Isso... Isso não é... Ngh..." + }, + "defeat": { + 1: "E agora... Eu transformarei este planeta em uma terra ideal para a humanidade." + } + }, + "aqua_boss_archie_1": { + "encounter": { + 1: "Eu sou o líder da Equipe Aqua, então temo que esse seja o fim da linha para você." + }, + "victory": { + 1: "Vamos nos encontrar de novo em algum lugar. Eu vou ter certeza de lembrar desse rosto." + }, + "defeat": { + 1: "Brilhante! Nada vai parar minha equipe agora!" + } + }, + "aqua_boss_archie_2": { + "encounter": { + 1: "Estive esperando tanto tempo por este dia.\nEste é o verdadeiro poder da minha equipe!" + }, + "victory": { + 1: "Como eu suspeitava..." + }, + "defeat": { + 1: "Eu vou voltar tudo neste mundo ao seu estado puro e original!!" + } + }, "galactic_boss_cyrus_1": { "encounter": { 1: "Você foi compelida a vir aqui por tal sentimentalismo vazio\nEu farei você se arrepender de ter ouvido seu coração!" @@ -3011,6 +3457,78 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Eu criarei meu novo mundo..." } }, + "galactic_boss_cyrus_2": { + "encounter": { + 1: "Nos encontramos novamente. Parece que nossos destinos estão entrelaçados.\nMas aqui e agora, eu finalmente quebrarei esse vínculo!" + }, + "victory": { + 1: "Como? Como? COMO?!" + }, + "defeat": { + 1: "Até logo." + } + }, + "plasma_boss_ghetsis_1": { + "encounter": { + 1: "Ninguém pode me deter! Não importa quem seja ou o que faça!" + }, + "victory": { + 1: "Como isso é possível? Eu sou o criador da Equipe Plasma! Eu sou perfeito!" + }, + "defeat": { + 1: "Eu sou o governante perfeito de um novo mundo perfeito! Mwa ha ha!" + } + }, + "plasma_boss_ghetsis_2": { + "encounter": { + 1: "Vamos! Eu quero ver sua cara depois que você perder toda a esperança!" + }, + "victory": { + 1: "Meus cálculos... Não! Meus planos cuidadosos! O mundo deveria ser meu!" + }, + "defeat": { + 1: "Kyurem! Use Absofusion!" + } + }, + "flare_boss_lysandre_1": { + "encounter": { + 1: "Você está aqui para me deter? Mostre-me em batalha." + }, + "victory": { + 1: "Você está aqui para me deter. Mas eu peço que você espere." + }, + "defeat": { + 1: "Pokémon... não devem mais existir." + } + }, + "flare_boss_lysandre_2": { + "encounter": { + 1: "O futuro que você quer, ou o futuro que eu quero... Vamos ver qual é o mais merecedor, não é mesmo?" + }, + "victory": { + 1: "Uau!" + }, + "defeat": { + 1: "Tolos sem visão continuarão a poluir este belo mundo." + } + }, + "brock": { + "encounter": { + 1: "Minha especialidade em Pokémon do tipo Pedra vai te derrubar! Vamos lá!", + 2: "Minha vontade firme como pedra vai te sobrecarregar!", + 3: "Permita-me mostrar a verdadeira força dos meus Pokémon!" + }, + "victory": { + 1: "A força dos seus Pokémon superou minhas defesas de pedra!", + 2: "O mundo é enorme! Estou feliz por ter tido a chance de batalhar com você.", + 3: "Talvez eu deva voltar a perseguir meu sonho de ser Criador de Pokémon…" + }, + "defeat": { + 1: "A melhor defesa é um bom ataque!\nEssa é a minha maneira de fazer as coisas!", + 2: "Venha estudar rochas comigo da próxima vez para aprender melhor a combatê-las!", + 3: "Hah, todas as minhas viagens pelas regiões estão valendo a pena!" + } + }, "misty": { "encounter": { 1: "Minha política é um ataque total com Pokémon do tipo Água!", @@ -3045,6 +3563,77 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Uma batalha de Pokémon é guerra, e eu te mostrei combate em primeira mão!" } }, + "erika": { + "encounter": { + 1: "Ah, o tempo está adorável aqui…\nOh, uma batalha? Muito bem então.", + 2: "Minhas habilidades de batalha Pokémon rivalizam com minhas habilidades de arranjo de flores.", + 3: "Oh, espero que o aroma agradável dos meus Pokémon não me faça dormir de novo…", + 4: "Ver flores em um jardim é tão calmante." + }, + "victory": { + 1: "Oh! Eu concedo a derrota.", + 2: "Aquela partida foi muito agradável.", + 3: "Ah, parece que perdi…", + 4: "Oh, meu Deus." + }, + "defeat": { + 1: "Tinha medo de adormecer…", + 2: "Oh, meu Deus, parece que meus Pokémon de Grama te dominaram.", + 3: "Essa batalha foi uma experiência tão calmante.", + 4: "Oh… É só isso?" + } + }, + "janine": { + "encounter": { + 1: "Estou dominando a arte dos ataques venenosos.\nVou lutar com você hoje!", + 2: "Meu pai confia que posso me defender.\nVou provar que ele está certo!", + 3: "Minhas técnicas de ninja só perdem para as do meu pai!\nVocê consegue acompanhar?" + }, + "victory": { + 1: "Ainda preciso de treinamento… Entendi.", + 2: "Sua técnica de batalha superou a minha.", + 3: "Vou me aplicar de verdade e melhorar minhas habilidades." + }, + "defeat": { + 1: "Hehe… o veneno drenou todas as suas forças para lutar.", + 2: "Ha! Você não teve chance contra minhas habilidades superiores de ninja!", + 3: "A fé do meu pai em mim não foi mal colocada." + } + }, + "sabrina": { + "encounter": { + 1: "Através da minha habilidade psíquica, tive uma visão da sua chegada!", + 2: "Não gosto de lutar, mas se você quiser, vou mostrar meus poderes!", + 3: "Posso sentir grande ambição em você. Vou ver se não é infundada." + }, + "victory": { + 1: "Seu poder… Ele supera o que eu previa…", + 2: "Não consegui prever seu poder com precisão.", + 3: "Mesmo com meus imensos poderes psíquicos, não consigo sentir outro tão forte quanto você." + }, + "defeat": { + 1: "Essa vitória… É exatamente como previ nas minhas visões!", + 2: "Talvez fosse outra pessoa que eu sentisse um grande desejo…", + 3: "Aprimore suas habilidades antes de entrar em batalha precipitadamente.\nVocê nunca sabe o que o futuro pode reservar se fizer isso…" + } + }, + "blaine": { + "encounter": { + 1: "Hah! Espero que tenha trazido uma Cura de Queimadura!", + 2: "Meus Pokémon de Fogo vão incinerar todos os desafiantes!", + 3: "Prepare-se para brincar com fogo!" + }, + "victory": { + 1: "Queimei até não restar nada! Nem cinzas sobraram!", + 2: "Não acendi as chamas alto o suficiente?", + 3: "Estou completamente exausto… Mas isso faz minha motivação para melhorar queimar ainda mais!" + }, + "defeat": { + 1: "Meu inferno ardente não pode ser apagado!", + 2: "Meus Pokémon foram fortalecidos com o calor desta vitória!", + 3: "Hah! Minha paixão queima mais do que a sua!" + } + }, "giovanni": { "encounter": { 1: "Eu, o líder da Equipe Rocket, vou te fazer sentir um mundo de dor!", @@ -3062,6 +3651,23 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Espero que entenda o quão tolo foi me desafiar." } }, + "roxanne": { + "encounter": { + 1: "Você poderia gentilmente demonstrar como batalha?", + 2: "Você pode aprender muitas coisas batalhando com muitos treinadores.", + 3: "Oh, você me pegou estrategizando.\nGostaria de batalhar?" + }, + "victory": { + 1: "Oh, parece que perdi.\nEu entendo.", + 2: "Parece que ainda tenho muito mais a aprender quando se trata de batalhas.", + 3: "Vou levar o que aprendi aqui hoje a sério." + }, + "defeat": { + 1: "Aprendi muitas coisas com nossa batalha.\nEspero que você também tenha aprendido.", + 2: "Espero batalhar com você novamente.\nEspero que use o que aprendeu aqui.", + 3: "Venci devido a tudo o que aprendi." + } + }, "brawly": { "encounter": { 1: "Oh cara, uma desafiante!\nVamos ver o que você pode fazer!", @@ -3096,6 +3702,40 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Wahahahaha! Que batalha eletrizante!" } }, + "flannery": { + "encounter": { + 1: "Meus Pokémon de fogo estão prontos para queimar a concorrência!\nVamos nessa!", + 2: "Prepare-se para sentir o calor da minha determinação!\nNão vou segurar nada!", + 3: "Minhas habilidades vão incinerar você!\nPrepare-se para a batalha mais quente da sua vida!" + }, + "victory": { + 1: "Essa derrota só faz minha determinação queimar mais!", + 2: "Essa perda não apagará minhas chamas!\nEstarei de volta mais forte!", + 3: "Vou usar essa experiência para reacender meu espírito competitivo!" + }, + "defeat": { + 1: "Minhas chamas nunca se apagarão!\nSou muito apaixonada por isso!", + 2: "Você foi incrível!\nVamos fazer isso de novo algum dia!", + 3: "Que batalha ardente!\nMal posso esperar pela próxima!" + } + }, + "norman": { + "encounter": { + 1: "Você está pronto para enfrentar a força pura do meu time?\nVou te mostrar o poder do equilíbrio!", + 2: "Minha experiência em batalha vai fazer você suar!\nPrepare-se!", + 3: "Treinei meu time rigorosamente.\nVamos ver se você consegue igualar!" + }, + "victory": { + 1: "Parece que subestimei você.\nFoi uma batalha dura.", + 2: "Você é forte, mas ainda há muito para aprender.", + 3: "Essa derrota não abalará minha determinação.\nEstarei de volta mais forte!" + }, + "defeat": { + 1: "Você lutou bravamente!\nEspero batalhar com você novamente.", + 2: "Sua força é incrível!\nNão posso esperar pela nossa próxima batalha.", + 3: "Foi uma honra batalhar com você!\nAté a próxima!" + } + }, "winona": { "encounter": { 1: "Tenho sobrevoado os céus em busca de presas...\nE você é meu alvo!", @@ -3147,6 +3787,60 @@ export const PGFdialogue: DialogueTranslationEntries = { 3: "Tudo graças ao meu treinamento rigoroso com Tate.\nPosso me sincronizar com meus Pokémon." } }, + "juan": { + "encounter": { + 1: "Agora não é hora de agir timidamente.\nVamos batalhar!", + 2: "Ahahaha, você será testemunha da minha arte com Pokémon de Água!", + 3: "Um tufão se aproxima!\nVocê será capaz de me testar?", + 4: "Por favor, você será testemunha da nossa arte.\nUma grande ilusão de água esculpida por meus Pokémon e por mim!" + }, + "victory": { + 1: "Você pode ser um gênio que pode enfrentar Wallace!", + 2: "Eu me concentrei na elegância enquanto você treinava.\nÉ natural que você me derrotasse.", + 3: "Ahahaha!\nMuito bem, você venceu desta vez.", + 4: "De você, sinto o brilho brilhante da habilidade que superará tudo." + }, + "defeat": { + 1: "Meus Pokémon e eu esculpimos uma ilusão de Água e saímos vitoriosos.", + 2: "Ahahaha, eu venci, e você perdeu.", + 3: "Posso emprestar meu traje? Pode te ajudar a batalhar!\nAhahaha, estou brincando!", + 4: "Eu sou o vencedor! O que quer dizer, você perdeu." + } + }, + "crasher_wake": { + "encounter": { + 1: "Crash! Crash! Cuidado!\nDemolidor Wake… está… aqui!", + 2: "Crash! Crash! Demolidor Wake!", + 3: "Sou a onda de poder que vai te lavar!" + }, + "victory": { + 1: "Isso coloca um sorriso no meu rosto!\nGuhahaha! Foi uma explosão!", + 2: "Hunwah! Acabou e terminou!\nComo vou dizer isso...\nQuero mais! Queria batalhar muito mais!", + 3: "O QUÊ?!" + }, + "defeat": { + 1: "Siiiiim! Isso mesmo!", + 2: "Eu venci, mas quero mais! Queria batalhar muito mais!", + 3: "Até logo!" + } + }, + "falkner": { + "encounter": { + 1: "Vou mostrar o verdadeiro poder dos magníficos Pokémon pássaros!", + 2: "Ventos, fiquem comigo!", + 3: "Pai! Espero que esteja vendo minha batalha de cima!" + }, + "victory": { + 1: "Eu entendo... Vou sair graciosamente.", + 2: "Uma derrota é uma derrota. Você é realmente forte.", + 3: "...Droga! Sim, eu perdi." + }, + "defeat": { + 1: "Pai! Venci com seus amados Pokémon pássaros...", + 2: "Pokémon pássaros são os melhores afinal!", + 3: "Sinto que estou alcançando meu pai!" + } + }, "nessa": { "encounter": { 1: "Não importa que tipo de plano sua mente refinada possa estar tramando, meu parceiro e eu vamos afundá-la.", @@ -3295,6 +3989,42 @@ export const PGFdialogue: DialogueTranslationEntries = { 6: "Eu sabia que venceria!" } }, + "crispin": { + "encounter": { + 1: "Quero vencer, então é exatamente isso que vou fazer!", + 2: "Eu batalho porque quero batalhar! E sabe de uma coisa? É assim que deve ser!" + }, + "victory": { + 1: "Queria vencer... mas perdi!", + 2: "Eu perdi... porque não consegui vencer!" + }, + "defeat": { + 1: "Ei, espere um segundo. Eu acabei de vencer? Acho que acabei de vencer! Que satisfação!", + 2: "Uou! Isso foi incrível!" + } + }, + "amarys": { + "encounter": { + 1: "Quero ser a pessoa a ajudar alguém em particular. Sendo assim, não posso me dar ao luxo de perder.\n... Nossa batalha começa agora." + }, + "victory": { + 1: "Eu sou... não o suficiente, eu vejo." + }, + "defeat": { + 1: "A vitória pertence a mim. Bem lutado." + } + }, + "lacey": { + "encounter": { + 1: "Vou enfrentar você com meu time usual como membro da Elite dos Quatro." + }, + "victory": { + 1: "Foi uma excelente batalha. Estou ansiosa para o próximo desafio." + }, + "defeat": { + 1: "Fufufu... Nada mal.\nDesafiantes que derrotam a Elite dos Quatro são dignos de notar." + } + }, "drayton": { "encounter": { 1: `Cara, eu amo cadeiras. Você não ama cadeiras? Que salva-vidas. @@ -3319,6 +4049,23 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Hohoho... De fato. Pequenas lâminas frágeis de grama conseguem quebrar até mesmo concreto." } }, + "viola": { + "encounter": { + 1: `Seja as lágrimas de frustração que seguem uma derrota ou o florescer da alegria que vem com a vitória… + $Ambos são ótimos temas para minha câmera! Fantástico! Isso vai ser simplesmente fantástico! + $Agora venha para cima de mim!`, + 2: "Minha lente está sempre focada na vitória – não vou deixar nada estragar esta foto!" + }, + "victory": { + 1: "Você e seus Pokémon me mostraram uma nova profundidade de campo! Fantástico! Simplesmente fantástico!", + 2: `O mundo que você vê através de uma lente, e o mundo que você vê com um Pokémon ao seu lado… + $O mesmo mundo pode parecer completamente diferente dependendo do seu ponto de vista.` + }, + "defeat": { + 1: "A foto do momento da minha vitória vai ser um verdadeiro sucesso!", + 2: "Sim! Tirei ótimas fotos!" + } + }, "candice": { "encounter": { 1: `Você quer desafiar a Candice? Com certeza! Eu estava esperando por alguém forte! @@ -3347,6 +4094,40 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Sim! Meus Pokémon e eu somos perfeitamente bons!" } }, + "aaron": { + "encounter": { + 1: "Ok! Deixe-me enfrentar você!" + }, + "victory": { + 1: "Batalhar é um assunto profundo e complexo..." + }, + "defeat": { + 1: "Vencer um membro da Elite dos Quatro não é fácil." + } + }, + "cress": { + "encounter": { + 1: "Isso mesmo! Serei eu e meus estimados tipos Água que você deve enfrentar na batalha!" + }, + "victory": { + 1: "Perder? Eu? Não acredito nisso." + }, + "defeat": { + 1: "Este é o resultado apropriado quando eu sou seu oponente." + } + }, + "allister": { + "encounter": { + 1: "Sou Allister.\nA-aqui... vou eu..." + }, + "victory": { + 1: `Quase perdi minha máscara de tanto choque... Isso foi… + $Uau. Posso ver sua habilidade pelo que ela é.`, + }, + "defeat": { + 1: "I-isso foi incrível!" + } + }, "clay": { "encounter": { 1: "Harrumph! Me deixou esperando, não foi, garota? Tudo bem, hora de ver o que você pode fazer!" @@ -3369,6 +4150,68 @@ export const PGFdialogue: DialogueTranslationEntries = { "defeat": { 1: "Volte para me ver novamente, ouviu?" } + }, "tulip": { + "encounter": { + 1: "Permita-me usar minhas habilidades para deixar seus lindos Pokémon ainda mais bonitos!" + }, + "victory": { + 1: "Sua força tem uma magia que não pode ser apagada." + }, + "defeat": { + 1: "Você sabe, na minha linha de trabalho, pessoas que carecem de talento em uma área ou outra frequentemente desaparecem rapidamente - nunca mais se ouve falar delas." + } + }, + "sidney": { + "encounter": { + 1: `Gostei desse olhar que você me deu. Acho que você vai ser um bom desafio. + $Isso é ótimo! Parece muito bom! Vamos nessa! + $Você e eu, vamos curtir uma batalha que só pode acontecer aqui!`, + }, + "victory": { + 1: "E aí, gostou? Eu perdi! Mas foi divertido, então não importa." + }, + "defeat": { + 1: "Sem ressentimentos, beleza?" + } + }, + "phoebe": { + "encounter": { + 1: `Enquanto treinava, adquiri a habilidade de me comunicar com Pokémon do tipo Fantasma. + $Sim, o vínculo que desenvolvi com os Pokémon é extremamente forte. + $Então, vamos lá, tente ver se você consegue até mesmo causar dano aos meus Pokémon!`, + }, + "victory": { + 1: "Ah, droga. Eu perdi." + }, + "defeat": { + 1: "Estou ansiosa para batalhar com você de novo algum dia!" + } + }, + "glacia": { + "encounter": { + 1: `Tudo o que vi foram desafios de Treinadores fracos e seus Pokémon. + $E você? Ficaria extremamente satisfeita se pudesse dar tudo de mim contra você!`, + }, + "victory": { + 1: `Você e seus Pokémon… Como seus espíritos queimam! + $O calor consumido é esmagador. + $Não é surpresa que minhas habilidades geladas falharam em te machucar.`, + }, + "defeat": { + 1: "Uma batalha intensamente apaixonada, sem dúvida." + } + }, + "drake": { + "encounter": { + 1: `Para nós, batalhar com Pokémon como parceiros, você sabe o que é necessário? Você sabe o que precisa? + $Se não souber, nunca prevalecerá contra mim!`, + }, + "victory": { + 1: "Excelente, deve-se dizer." + }, + "defeat": { + 1: "Dei meu máximo nessa batalha!" + } }, "wallace": { "encounter": { @@ -3420,6 +4263,17 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Estou encantada! Sim, encantada por poder esmagar você sob meu calcanhar." } }, + "hala": { + "encounter": { + 1: "O velho Hala está aqui para fazer você gritar!" + }, + "victory": { + 1: "Pude sentir o poder que você ganhou na sua jornada." + }, + "defeat": { + 1: "Haha! Que batalha deliciosa!" + } + }, "molayne": { "encounter": { 1: `Dei a posição de capitão ao meu primo Sophocles, mas estou confiante na minha habilidade. @@ -3443,6 +4297,17 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Nahahaha! Você realmente é algo mais, garota!" } }, + "bruno": { + "encounter": { + 1: "Nós vamos te triturar com nosso poder superior! Hoo hah!" + }, + "victory": { + 1: "Por quê? Como eu poderia perder?" + }, + "defeat": { + 1: "Você pode me desafiar o quanto quiser, mas os resultados nunca vão mudar!" + } + }, "bugsy": { "encounter": { 1: "Sou Bugsy! Eu nunca perco quando se trata de Pokémon do tipo Inseto!" @@ -3454,6 +4319,30 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Obrigado! Graças à nossa batalha, eu também pude fazer progressos na minha pesquisa!" } }, + "koga": { + "encounter": { + 1: "Fwahahahaha! Pokémon não são apenas sobre força bruta--você verá em breve!" + }, + "victory": { + 1: "Ah! Você provou seu valor!" + }, + "defeat": { + 1: "Você aprendeu a temer as técnicas do ninja?" + } + }, + "bertha": { + "encounter": { + 1: "Bem, você mostraria a esta velha senhora o quanto aprendeu?" + }, + "victory": { + 1: `Bem! Querida criança, devo dizer, isso foi muito impressionante. + $Seus Pokémon acreditaram em você e fizeram o melhor para te dar a vitória. + $Mesmo tendo perdido, me encontro com esse sorriso bobo!`, + }, + "defeat": { + 1: "Hahahahah! Parece que esta velha senhora ganhou!" + } + }, "lenora": { "encounter": { 1: "Bem, desafiadora, vou pesquisar como você batalha com os Pokémon que criou com tanto carinho!" @@ -3465,6 +4354,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Ah ha ha! Se você perder, certifique-se de analisar o porquê e use esse conhecimento na próxima batalha!" } }, + "siebold": { + "encounter": { + 1: "Enquanto eu estiver vivo, continuarei em busca da culinária suprema... e dos oponentes mais fortes em batalha!" + }, + "victory": { + 1: "Guardarei minha memória de você e seus Pokémon para sempre em meu coração." + }, + "defeat": { + 1: `Nossa batalha Pokémon foi como alimento para minha alma. Isso vai me manter em frente. + $É assim que vou prestar meus respeitos a você por dar tudo de si na batalha!`, + } + }, "roxie": { "encounter": { 1: "Prepare-se! Vou arrancar algum senso de você!" @@ -3476,6 +4377,40 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Ei, vamos lá! Seja séria! Você tem que dar mais de si!" } }, + "olivia": { + "encounter": { + 1: "Não precisa de introdução aqui. Hora de batalhar comigo, Olivia!" + }, + "victory": { + 1: "Realmente encantador… Tanto você quanto seus Pokémon…" + }, + "defeat": { + 1: "Mmm-hmm." + } + }, + "poppy": { + "encounter": { + 1: "Oooh! Você quer ter uma batalha Pokémon comigo?" + }, + "victory": { + 1: "Uagh?! Mmmuuuggghhh…" + }, + "defeat": { + 1: `Yaaay! Eu consegui! Eu der-ro-tei você! Você pode vir para… Para… Uma revanche? + $Venha para uma revanche quando quiser!`, + } + }, + "agatha": { + "encounter": { + 1: "Pokémon são para batalhas! Vou te mostrar como um verdadeiro Treinador batalha!" + }, + "victory": { + 1: "Oh meu! Você é algo especial, criança!" + }, + "defeat": { + 1: "Bahaha. É assim que uma batalha adequada é feita!" + } + }, "flint": { "encounter": { 1: "Espero que você esteja aquecida, porque aqui vem o Big Bang!" @@ -3487,6 +4422,17 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Huh? Isso é tudo? Acho que você precisa de um pouco mais de paixão." } }, + "grimsley": { + "encounter": { + 1: "O vencedor leva tudo, e não sobra nada para o perdedor." + }, + "victory": { + 1: "Quando se perde, perde-se tudo… A próxima coisa que vou procurar será a vitória, também!" + }, + "defeat": { + 1: "Se alguém vence, a pessoa que lutou contra essa pessoa perde." + } + }, "caitlin": { "encounter": { 1: `Sou eu que apareci quando a flor se abriu. Você que estava esperando… @@ -3501,6 +4447,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Aspiro a reivindicar a vitória com elegância e graça." } }, + "diantha": { + "encounter": { + 1: `Batalhar contra você e seus Pokémon, todos vocês cheios de esperança para o futuro… + $Honestamente, isso apenas me enche da energia que preciso para continuar enfrentando cada novo dia! Sim!`, + }, + "victory": { + 1: "Testemunhar os espíritos nobres de você e seus Pokémon em batalha realmente tocou meu coração…" + }, + "defeat": { + 1: "Oh, fantástico! O que achou? Minha equipe foi bem legal, né?" + } + }, "wikstrom": { "encounter": { 1: `Bem encontrado, jovem desafiadora! Verdadeiramente sou a lâmina famosa de aço endurecido, Duque Wikstrom! @@ -3525,6 +4483,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Ehaha! Que vitória incrível!" } }, + "larry_elite": { + "encounter": { + 1: `Olá… Sou eu, Larry. + $Eu também sou membro da Elite dos Quatro, sim… Infelizmente para mim.`, + }, + "victory": { + 1: "Bem, isso tirou o vento debaixo das nossas asas…" + }, + "defeat": { + 1: "É hora de uma reunião com o chefe." + } + }, "lance": { "encounter": { 1: "Estive esperando por você. Permita-me testar suas habilidades.", @@ -3539,6 +4509,23 @@ export const PGFdialogue: DialogueTranslationEntries = { 2: "Não é que você seja fraca. Não se incomode com isso." } }, + "karen": { + "encounter": { + 1: "Eu sou Karen. Você gostaria de um duelo com meus Pokémon do tipo Sombrio?", + 2: "Sou diferente daqueles que você já conheceu.", + 3: "Você montou uma equipe charmosa. Nossa batalha deve ser boa." + }, + "victory": { + 1: "Não! Eu não posso vencer. Como você ficou tão forte?", + 2: "Não me desviarei do meu caminho escolhido.", + 3: "O Campeão está ansioso para te conhecer." + }, + "defeat": { + 1: "Isso era o que eu esperava.", + 2: "Bem, isso foi relativamente divertido.", + 3: "Venha me visitar a qualquer momento." + } + }, "milo": { "encounter": { 1: `Parece que você entende bem os Pokémon. @@ -3552,6 +4539,20 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Isso realmente vai te deixar em choque e admiração." } }, + "lucian": { + "encounter": { + 1: `Só um momento, por favor. O livro que estou lendo está quase no clímax emocionante… + $O herói obteve uma espada mística e está prestes a enfrentar sua prova final… Ah, tanto faz. + $Já que você chegou tão longe, vou deixar isso de lado e batalhar com você. + $Deixe-me ver se você alcançará tanta glória quanto o herói do meu livro!`, + }, + "victory": { + 1: "Eu vejo… Parece que você me colocou em xeque-mate." + }, + "defeat": { + 1: "Tenho uma reputação a manter." + } + }, "drasna": { "encounter": { 1: `Você deve ser uma Treinadora forte. Sim, bastante forte… @@ -3564,6 +4565,29 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Como isso é possível?" } }, + "kahili": { + "encounter": { + 1: "Então, aqui está você… Por que não vemos para quem os ventos favorecem hoje, você… ou eu?" + }, + "victory": { + 1: "É frustrante para mim como membro da Elite dos Quatro, mas parece que sua força é real." + }, + "defeat": { + 1: "Essa foi uma jogada de mestre!" + } + }, + "hassel": { + "encounter": { + 1: "Prepare-se para aprender em primeira mão como é a respiração ardente de uma batalha feroz!" + }, + "victory": { + 1: `A sorte sorriu para mim desta vez, mas… + $Julgando pelo andamento da luta, quem sabe se serei tão sortudo na próxima vez.`, + }, + "defeat": { + 1: "Essa foi uma jogada de mestre!" + } + }, "blue": { "encounter": { 1: "Você deve ser muito boa para chegar tão longe." @@ -3575,6 +4599,39 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Viu? Meu poder é o que me trouxe até aqui." } }, + "piers": { + "encounter": { + 1: "Prepare-se para uma mosh pit comigo e minha galera! Spikemuth, é hora de roquear!" + }, + "victory": { + 1: "Eu e minha equipe demos o nosso melhor. Vamos nos encontrar novamente para uma batalha algum dia…" + }, + "defeat": { + 1: "Minha garganta está desgastada de tanto gritar… Mas essa foi uma batalha empolgante!" + } + }, + "red": { + "encounter": { + 1: "…!" + }, + "victory": { + 1: "…?" + }, + "defeat": { + 1: "…!" + } + }, + "jasmine": { + "encounter": { + 1: "Oh… Seus Pokémon são impressionantes. Acho que vou gostar disso." + }, + "victory": { + 1: "Você é realmente forte. Vou ter que me esforçar muito mais também." + }, + "defeat": { + 1: "Eu nunca esperei ganhar." + } + }, "lance_champion": { "encounter": { 1: "Ainda sou o Campeão. Não vou segurar nada." @@ -3586,6 +4643,96 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Defendi com sucesso meu Campeonato." } }, + "steven": { + "encounter": { + 1: `Diga-me… O que você viu na sua jornada com seus Pokémon? + $O que você sentiu, encontrando tantos outros Treinadores por aí? + $Viajar por esta terra rica… Isso despertou algo dentro de você? + $Quero que você venha até mim com tudo o que aprendeu. + $Meus Pokémon e eu responderemos com tudo o que sabemos!`, + }, + "victory": { + 1: "Então eu, o Campeão, caio em derrota…" + }, + "defeat": { + 1: "Esse tempo foi bem gasto! Obrigado!" + } + }, + "cynthia": { + "encounter": { + 1: "Eu, Cynthia, aceito seu desafio! Não haverá nenhuma trégua da minha parte!" + }, + "victory": { + 1: "Não importa o quão divertida a batalha seja, ela sempre terminará algum dia…" + }, + "defeat": { + 1: "Mesmo que você perca, nunca perca o amor pelos Pokémon." + } + }, + "iris": { + "encounter": { + 1: `Sabe de uma coisa? Estou realmente ansiosa para ter batalhas sérias com Treinadores fortes! + $Quero dizer, vamos lá! Os Treinadores que chegam aqui são Treinadores que desejam a vitória com todas as fibras do seu ser! + $E eles estão batalhando ao lado de Pokémon que passaram por inúmeras batalhas difíceis! + $Se eu batalhar com pessoas assim, não só eu ficarei mais forte, meus Pokémon também! + $E nós vamos nos conhecer ainda melhor! OK! Prepare-se! + $Sou Iris, a Campeã da Liga Pokémon, e vou te derrotar!`, + }, + "victory": { + 1: "Aghhhh… Eu dei o meu melhor, mas nós perdemos…" + }, + "defeat": { + 1: "Yay! Nós vencemos!" + } + }, + "hau": { + "encounter": { + 1: `Eu me pergunto se um Treinador batalha de maneira diferente dependendo se ele é de uma região quente ou fria. + $Vamos testar isso!`, + }, + "victory": { + 1: "Isso foi incrível! Acho que entendi um pouco melhor seu estilo agora!" + }, + "defeat": { + 1: "Cara, essa foi uma batalha e tanto!" + } + }, + "geeta": { + "encounter": { + 1: `Decidi entrar na batalha mais uma vez. + $Venha agora… Mostre-me os frutos do seu treinamento.`, + }, + "victory": { + 1: "Estou ansiosa para notícias de todas as suas conquistas!" + }, + "defeat": { + 1: "Qual o problema? Isso é tudo?" + } + }, + "nemona": { + "encounter": { + 1: "Yesss! Estou tão empolgada! Hora de soltar tudo!" + }, + "victory": { + 1: "Bem, isso foi ruim, mas ainda me diverti! Eu te pego na próxima!" + }, + "defeat": { + 1: "Bem, essa foi uma ótima batalha! Frutífera, com certeza." + } + }, + "leon": { + "encounter": { + 1: "Vamos ter um tempo absolutamente campeão!" + }, + "victory": { + 1: `Meu tempo como Campeão acabou… + $Mas que tempo campeão foi! + $Obrigado pela melhor batalha que já tive!`, + }, + "defeat": { + 1: "Um tempo absolutamente campeão, foi!" + } + }, "whitney": { "encounter": { 1: "Eai! Você não acha que os Pokémon são, tipo, super fofos?" @@ -3619,6 +4766,28 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Coma, meu adorável Vivillon!" } }, + "pryce": { + "encounter": { + 1: "A juventude sozinha não garante a vitória! Experiência é o que conta." + }, + "victory": { + 1: "Excelente! Isso foi perfeito. Tente não esquecer o que sente agora." + }, + "defeat": { + 1: "Exatamente como eu imaginei." + } + }, + "clair": { + "encounter": { + 1: "Você sabe quem eu sou? E ainda se atreve a me desafiar?" + }, + "victory": { + 1: "Eu me pergunto até onde você pode ir com seu nível de habilidade. Isso deve ser fascinante." + }, + "defeat": { + 1: "E é isso." + } + }, "maylene": { "encounter": { 1: `Vim desafiá-la agora e não vou segurar nada. @@ -3631,6 +4800,18 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Isso foi incrível." } }, + "fantina": { + "encounter": { + 1: `Você vai me desafiar, não é? Mas eu vou ganhar. + $É o que a Líder do Ginásio de Hearthome faz, não?`, + }, + "victory": { + 1: "Você é tão incrivelmente forte. Sei porque perdi." + }, + "defeat": { + 1: "Estou tão, tão, muito feliz!" + } + }, "byron": { "encounter": { 1: `Treinadora! Você é jovem, assim como meu filho, Roark. @@ -3644,6 +4825,17 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Gwahahaha! Como foram meus Pokémon robustos?!" } }, + "olympia": { + "encounter": { + 1: "Um costume antigo decidindo o destino de alguém. A batalha começa!" + }, + "victory": { + 1: "Crie seu próprio caminho. Não deixe nada te atrapalhar. Seu destino, seu futuro." + }, + "defeat": { + 1: "Nosso caminho está claro agora." + } + }, "volkner": { "encounter": { 1: `Já que você chegou tão longe, deve ser bastante forte… @@ -3659,6 +4851,108 @@ export const PGFdialogue: DialogueTranslationEntries = { $Isso não é o que eu queria!`, } }, + "burgh": { + "encounter": { + 1: `M'hm… Se eu ganhar esta batalha, sinto que posso desenhar um quadro diferente de qualquer outro. + $OK! Posso ouvir minha musa da batalha claramente. Vamos direto ao ponto!`, + 2: `Claro, estou realmente orgulhoso de todos os meus Pokémon! + $Bem agora… Vamos direto ao ponto!` + }, + "victory": { + 1: "Acabou? Minha musa me abandonou?", + 2: "Hmm… Acabou! Você é incrível!" + }, + "defeat": { + 1: "Uau… É bonito de alguma forma, não é…", + 2: `Às vezes ouço as pessoas dizerem que foi uma vitória feia. + $Acho que se você está dando o seu melhor, qualquer vitória é bonita.` + } + }, + "elesa": { + "encounter": { + 1: `C'est fini! Quando tenho certeza disso, sinto um choque elétrico percorrer meu corpo! + $Quero sentir essa sensação, então agora meus amados Pokémon vão fazer sua cabeça girar!`, + }, + "victory": { + 1: "Eu queria fazer sua cabeça girar, mas você me surpreendeu." + }, + "defeat": { + 1: "Isso foi insatisfatório de alguma forma… Você dará tudo de si na próxima vez?" + } + }, + "skyla": { + "encounter": { + 1: `Finalmente é hora do confronto! Isso significa a batalha Pokémon que decide quem está no topo, certo? + $Eu amo estar no topo! Porque você pode ver para sempre e sempre de lugares altos! + $Então, que tal nós nos divertirmos?`, + }, + "victory": { + 1: "Ser seu oponente na batalha é uma nova fonte de força para mim. Obrigada!" + }, + "defeat": { + 1: "Ganhar ou perder, você sempre ganha algo com uma batalha, certo?" + } + }, + "brycen": { + "encounter": { + 1: `Há também força em estar com outras pessoas e Pokémon. + $Receber o apoio deles te fortalece. Vou te mostrar esse poder!`, + }, + "victory": { + 1: "A maravilhosa combinação de você e seus Pokémon! Que amizade linda!" + }, + "defeat": { + 1: "Condições extremas realmente testam e treinam você!" + } + }, + "drayden": { + "encounter": { + 1: `O que eu quero encontrar é um jovem Treinador que possa me mostrar um futuro brilhante. + $Vamos batalhar com tudo o que temos: sua habilidade, minha experiência e o amor com que criamos nossos Pokémon!`, + }, + "victory": { + 1: "Esse sentimento intenso que me invade após uma derrota… Não sei como descrevê-lo." + }, + "defeat": { + 1: "Harrumph! Sei que sua habilidade é maior que isso!" + } + }, + "grant": { + "encounter": { + 1: `Só há uma coisa que desejo. + $Que, superando um ao outro, encontremos um caminho para alturas ainda maiores.`, + }, + "victory": { + 1: "Você é uma parede que não consigo superar!" + }, + "defeat": { + 1: `Não desista. + $Isso é tudo o que realmente importa. + $As lições mais importantes da vida são simples.`, + } + }, + "korrina": { + "encounter": { + 1: "Hora da grande aparição de Lady Korrina!" + }, + "victory": { + 1: "É o seu próprio ser que permite que seus Pokémon evoluam!" + }, + "defeat": { + 1: "Que batalha explosiva!" + } + }, + "clemont": { + "encounter": { + 1: "Oh! Estou feliz por termos nos encontrado!" + }, + "victory": { + 1: "Sua paixão pela batalha me inspira!" + }, + "defeat": { + 1: "Parece que minha Máquina Treinadora-Crescer-Forte, Mach 2 está realmente funcionando!" + } + }, "valerie": { "encounter": { 1: `Oh, se não é uma jovem Treinadora… É adorável conhecê-la assim. @@ -3732,6 +5026,42 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Bom trabalho, eu suponho." } }, + "gordie": { + "encounter": { + 1: "Então, vamos acabar com isso." + }, + "victory": { + 1: "Eu só quero me enterrar em um buraco… Bem, acho que seria mais como cair daqui." + }, + "defeat": { + 1: "Batalhe como sempre faz, a vitória seguirá!" + } + }, + "marnie": { + "encounter": { + 1: `A verdade é que, quando tudo está dito e feito… Eu realmente só quero me tornar Campeã por mim mesma! + $Então, não leve para o pessoal quando eu chutar seu traseiro!`, + }, + "victory": { + 1: "OK, então eu perdi… Mas consegui ver muitos dos pontos bons de você e seus Pokémon!" + }, + "defeat": { + 1: "Espero que você tenha gostado das nossas táticas de batalha." + } + }, + "raihan": { + "encounter": { + 1: "Vou derrotar o Campeão, vencer todo o torneio e provar ao mundo o quão forte o grande Raihan realmente é!" + }, + "victory": { + 1: `Eu pareço bem mesmo quando perco. + $É uma verdadeira maldição. + $Acho que é hora de mais uma selfie!`, + }, + "defeat": { + 1: "Vamos tirar uma selfie para lembrar disso." + } + }, "brassius": { "encounter": { 1: "Pressuponho que você está pronta? Que nossa obra de arte colaborativa comece!" @@ -3757,6 +5087,17 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Seus olhos são MEUS!" } }, + "larry": { + "encounter": { + 1: "Quando tudo está dito e feito, a simplicidade é mais forte." + }, + "victory": { + 1: "Uma porção de derrota, hein?" + }, + "defeat": { + 1: "Vou encerrar o dia." + } + }, "ryme": { "encounter": { 1: "Vamos lá, baby! Me agite até os ossos!" @@ -3768,6 +5109,31 @@ export const PGFdialogue: DialogueTranslationEntries = { 1: "Até mais, baby!" } }, + "grusha": { + "encounter": { + 1: "Tudo o que preciso fazer é garantir que o poder do meu Pokémon te arrependa até os ossos!" + }, + "victory": { + 1: "Sua paixão ardente... Eu meio que gosto, para ser honesto." + }, + "defeat": { + 1: "As coisas não esquentaram para você." + } + }, + "marnie_elite": { + "encounter": { + 1: "Você chegou até aqui, hein? Vamos ver se você pode lidar com meus Pokémon!", + 2: "Vou dar o meu melhor, mas não pense que vou pegar leve com você!" + }, + "victory": { + 1: "Não acredito que perdi... Mas você mereceu essa vitória. Bem feito!", + 2: "Parece que ainda tenho muito a aprender. Porém, grande batalha!" + }, + "defeat": { + 1: "Você lutou bem, mas eu tenho a vantagem! Melhor sorte na próxima vez!", + 2: "Parece que meu treinamento valeu a pena. Obrigado pela batalha!" + } + }, "nessa_elite": { "encounter": { 1: "As marés estão mudando a meu favor. Pronta para ser levada pela corrente?", @@ -3782,6 +5148,20 @@ export const PGFdialogue: DialogueTranslationEntries = { 2: "Você lutou bem, mas o poder do oceano é imparável!" } }, + "bea_elite": { + "encounter": { + 1: "Prepare-se! Meu espírito de luta brilha intensamente!", + 2: "Vamos ver se você consegue acompanhar meu ritmo implacável!" + }, + "victory": { + 1: "Sua força... É impressionante. Você realmente merece essa vitória.", + 2: "Nunca senti essa intensidade antes. Trabalho incrível!" + }, + "defeat": { + 1: "Outra vitória para meu rigoroso regime de treinamento! Bem feito!", + 2: "Você tem força, mas eu treinei mais. Grande batalha!" + } + }, "allister_elite": { "encounter": { 1: "As sombras caem... Você está pronta para enfrentar seus medos?", @@ -3810,6 +5190,32 @@ export const PGFdialogue: DialogueTranslationEntries = { 2: "Você foi pega na minha tempestade! Melhor sorte na próxima vez!" } }, + "alder": { + "encounter": { + 1: "Se prepare para uma batalha contra o Treinador mais forte de Unova!" + }, + "victory": { + 1: "Muito bem! Você certamente é um talento incomparável." + }, + "defeat": { + 1: `Um vento fresco sopra em meu coração... + $Que esforço extraordinário!` + } + }, + "kieran": { + "encounter": { + 1: `Através do trabalho duro, eu me torno cada vez mais forte! + $Eu não perco.` + }, + "victory": { + 1: `Eu não acredito... + $Que batalha divertida e emocionante!` + }, + "defeat": { + 1: `Uau, que batalha! + $Hora de você treinar ainda mais.` + } + }, "rival": { "encounter": { 1: `@c{smile}Eai, estava procurando você! Sabia que você estava ansiosa para começar, mas esperava pelo menos um tchau… diff --git a/src/locales/pt_BR/menu.ts b/src/locales/pt_BR/menu.ts index 927ccce518b..87be5d8bed0 100644 --- a/src/locales/pt_BR/menu.ts +++ b/src/locales/pt_BR/menu.ts @@ -35,11 +35,11 @@ export const menu: SimpleTranslationEntries = { "sessionSuccess": "Sessão carregada com sucesso.", "failedToLoadSession": "Não foi possível carregar os dados da sua sessão.\nEles podem estar corrompidos.", "boyOrGirl": "Você é um menino ou uma menina?", - "evolving": "Que?\n{{pokemonName}} tá evoluindo!", + "evolving": "Quê?\n{{pokemonName}} tá evoluindo!", "stoppedEvolving": "{{pokemonName}} parou de evoluir.", "pauseEvolutionsQuestion": "Gostaria de pausar evoluções para {{pokemonName}}?\nEvoluções podem ser religadas na tela de equipe.", "evolutionsPaused": "Evoluções foram paradas para {{pokemonName}}.", - "evolutionDone": "Parabéns!\nSeu {{pokemonName}} evolui para {{evolvedPokemonName}}!", + "evolutionDone": "Parabéns!\nSeu {{pokemonName}} evoluiu para {{evolvedPokemonName}}!", "dailyRankings": "Classificação Diária", "weeklyRankings": "Classificação Semanal", "noRankings": "Sem Classificação", diff --git a/src/locales/pt_BR/modifier.ts b/src/locales/pt_BR/modifier.ts index 168665205c3..eadd5c5667a 100644 --- a/src/locales/pt_BR/modifier.ts +++ b/src/locales/pt_BR/modifier.ts @@ -4,7 +4,7 @@ export const modifier: SimpleTranslationEntries = { "surviveDamageApply": "{{pokemonNameWithAffix}} aguentou o tranco\nusando sua {{typeName}}!", "turnHealApply": "{{pokemonNameWithAffix}} restaurou um pouco de PS usando\nsuas {{typeName}}!", "hitHealApply": "{{pokemonNameWithAffix}} restaurou um pouco de PS usando\nsua {{typeName}}!", - "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} foi revivido\npor sua {{typeName}}!", + "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} foi reanimado\npor sua {{typeName}}!", "pokemonResetNegativeStatStageApply": "Os atributos diminuídos de {{pokemonNameWithAffix}} foram\nrestaurados por seu(sua) {{typeName}}!", "moneyInterestApply": "Você recebeu um juros de ₽{{moneyAmount}}\nde sua {{typeName}}!", "turnHeldItemTransferApply": "{{itemName}} de {{pokemonNameWithAffix}} foi absorvido(a)\npelo {{typeName}} de {{pokemonName}}!", diff --git a/src/locales/pt_BR/move-trigger.ts b/src/locales/pt_BR/move-trigger.ts index 042d539338e..620f867ae9a 100644 --- a/src/locales/pt_BR/move-trigger.ts +++ b/src/locales/pt_BR/move-trigger.ts @@ -26,7 +26,7 @@ export const moveTriggers: SimpleTranslationEntries = { "soothingAromaWaftedThroughArea": "Um aroma suave se espalhou pelo ambiente!", "sprangUp": "{{pokemonName}} se levantou!", "choseDoomDesireAsDestiny": "{{pokemonName}} escolheu\no Desejo da Perdição como seu destino!", - "vanishedInstantly": "{{pokemonName}} desapareceu/nde repente!", + "vanishedInstantly": "{{pokemonName}} desapareceu\nde repente!", "tookTargetIntoSky": "{{pokemonName}} levou {{targetName}}\npara o céu!", "becameCloakedInFreezingLight": "{{pokemonName}} ficou envolto/nem uma luz congelante!", "becameCloakedInFreezingAir": "{{pokemonName}} ficou envolto/nem ar congelante!", diff --git a/src/locales/pt_BR/party-ui-handler.ts b/src/locales/pt_BR/party-ui-handler.ts index 08132b4bfc0..1f3e0fbe242 100644 --- a/src/locales/pt_BR/party-ui-handler.ts +++ b/src/locales/pt_BR/party-ui-handler.ts @@ -15,7 +15,7 @@ export const partyUiHandler: SimpleTranslationEntries = { "ALL": "Tudo", "PASS_BATON": "Passar Bastão", "UNPAUSE_EVOLUTION": "Ativar Evolução", - "REVIVE": "Reviver", + "REVIVE": "Reanimar", "RENAME": "Renomear", "choosePokemon": "Escolha um Pokémon.", diff --git a/src/locales/pt_BR/pokemon-form.ts b/src/locales/pt_BR/pokemon-form.ts index 062fc165ae0..dbe63fb7864 100644 --- a/src/locales/pt_BR/pokemon-form.ts +++ b/src/locales/pt_BR/pokemon-form.ts @@ -29,7 +29,7 @@ export const pokemonForm: SimpleTranslationEntries = { "pikachuPartner": "Parceiro", "eeveePartner": "Parceiro", // 2G - "pichuSpiky": "Spiky", + "pichuSpiky": "Orelha Espetada", "unownA": "A", "unownB": "B", "unownC": "C", @@ -74,8 +74,8 @@ export const pokemonForm: SimpleTranslationEntries = { "rotomFrost": "Congelante", "rotomFan": "Ventilador", "rotomMow": "Corte", - "giratinaAltered": "Altered", - "shayminLand": "Land", + "giratinaAltered": "Alterado", + "shayminLand": "Terrestre", // 5G "basculinRedStriped": "Listras Vermelhas", "basculinBlueStriped": "Listras Azuis", @@ -84,11 +84,11 @@ export const pokemonForm: SimpleTranslationEntries = { "deerlingSummer": "Verão", "deerlingAutumn": "Outono", "deerlingWinter": "Inverno", - "tornadusIncarnate": "Incarnate", - "thundurusIncarnate": "Incarnate", - "landorusIncarnate": "Incarnate", - "keldeoOrdinary": "Ordinary", - "meloettaAria": "Aria", + "tornadusIncarnate": "Materializado", + "thundurusIncarnate": "Materializado", + "landorusIncarnate": "Materializado", + "keldeoOrdinary": "Comum", + "meloettaAria": "Ária", // 6G "froakieBattleBond": "Vínculo de Batalha", "scatterbugMeadow": "Prado", @@ -165,11 +165,11 @@ export const pokemonForm: SimpleTranslationEntries = { "eiscueNoIce": "Descongelado", "indeedeeMale": "Macho", "indeedeeFemale": "Fêmea", - "morpekoFullBelly": "Full Belly", - "zacianHeroOfManyBattles": "Hero Of Many Battles", - "zamazentaHeroOfManyBattles": "Hero Of Many Battles", + "morpekoFullBelly": "Saciado", + "zacianHeroOfManyBattles": "Herói Veterano", + "zamazentaHeroOfManyBattles": "Herói Veterano", "zarudeDada": "Papa", - "enamorusIncarnate": "Incarnate", + "enamorusIncarnate": "Materializado", // 9G "squawkabillyGreenPlumage": "Plumas Verdes", "squawkabillyBluePlumage": "Plumas Azuis", From 3a9d24c49a54e18732a42a68f2760fc03816624c Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Tue, 20 Aug 2024 10:44:37 -0700 Subject: [PATCH 04/30] [Bug] Fix Jaw Lock leaving the user trapped after the target faints (#3450) * Fix Jaw Lock not removing TRAPPED tag after enemy faints * Create tests for Jaw Lock * Fix overrides import * Clean up implementation + tests * minor style change to phases * Update src/data/move.ts Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> * Jaw Lock no longer overlaps its trapping effect * Friendship ended with JAW_LOCK tag type Now TRAPPED is my new best friend --------- Co-authored-by: EmberCM Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> --- src/data/move.ts | 36 ++++++- src/test/moves/jaw_lock.test.ts | 172 ++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 src/test/moves/jaw_lock.test.ts diff --git a/src/data/move.ts b/src/data/move.ts index acb61042e70..51ba4058140 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -4438,6 +4438,39 @@ export class GulpMissileTagAttr extends MoveEffectAttr { } } +/** + * Attribute to implement Jaw Lock's linked trapping effect between the user and target + * @extends AddBattlerTagAttr + */ +export class JawLockAttr extends AddBattlerTagAttr { + constructor() { + super(BattlerTagType.TRAPPED); + } + + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!super.canApply(user, target, move, args)) { + return false; + } + + // If either the user or the target already has the tag, do not apply + if (user.getTag(TrappedTag) || target.getTag(TrappedTag)) { + return false; + } + + const moveChance = this.getMoveChance(user, target, move, this.selfTarget); + if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) { + /** + * Add the tag to both the user and the target. + * The target's tag source is considered to be the user and vice versa + */ + return target.addTag(BattlerTagType.TRAPPED, 1, move.id, user.id) + && user.addTag(BattlerTagType.TRAPPED, 1, move.id, target.id); + } + + return false; + } +} + export class CurseAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move:Move, args: any[]): boolean { @@ -8313,8 +8346,7 @@ export function initMoves() { .attr(HighCritAttr) .attr(BypassRedirectAttr), new AttackMove(Moves.JAW_LOCK, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8) - .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1, 1, false, true) - .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, true, false, 1, 1, false, true) + .attr(JawLockAttr) .bitingMove(), new SelfStatusMove(Moves.STUFF_CHEEKS, Type.NORMAL, -1, 10, -1, 0, 8) // TODO: Stuff Cheeks should not be selectable when the user does not have a berry, see wiki .attr(EatBerryAttr) diff --git a/src/test/moves/jaw_lock.test.ts b/src/test/moves/jaw_lock.test.ts new file mode 100644 index 00000000000..4fe996588e4 --- /dev/null +++ b/src/test/moves/jaw_lock.test.ts @@ -0,0 +1,172 @@ +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import GameManager from "#app/test/utils/gameManager"; +import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { SPLASH_ONLY } from "#app/test/utils/testUtils"; +import { BattlerIndex } from "#app/battle"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { BerryPhase } from "#app/phases/berry-phase"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Jaw Lock", () => { + 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") + .enemySpecies(Species.SNORLAX) + .enemyAbility(Abilities.INSOMNIA) + .enemyMoveset(SPLASH_ONLY) + .moveset([Moves.JAW_LOCK, Moves.SPLASH]) + .startingLevel(100) + .enemyLevel(100) + .disableCrits(); + }); + + it( + "should trap the move's user and target", + async () => { + await game.startBattle([ Species.BULBASAUR ]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); + + await game.phaseInterceptor.to(MoveEffectPhase, false); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); + }, TIMEOUT + ); + + it( + "should not trap either pokemon if the target faints", + async () => { + game.override.enemyLevel(1); + await game.startBattle([ Species.BULBASAUR ]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); + + await game.phaseInterceptor.to(MoveEffectPhase, false); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + + await game.phaseInterceptor.to(FaintPhase); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + }, TIMEOUT + ); + + it( + "should only trap the user until the target faints", + async () => { + await game.startBattle([ Species.BULBASAUR ]); + + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); + + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); + + await game.phaseInterceptor.to(TurnEndPhase); + + await game.doKillOpponents(); + + expect(leadPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + }, TIMEOUT + ); + + it( + "should not trap other targets after the first target is trapped", + async () => { + game.override.battleType("double"); + + await game.startBattle([ Species.CHARMANDER, Species.BULBASAUR ]); + + const playerPokemon = game.scene.getPlayerField(); + const enemyPokemon = game.scene.getEnemyField(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); + game.doSelectTarget(BattlerIndex.ENEMY); + + game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(playerPokemon[0].getTag(BattlerTagType.TRAPPED)).toBeDefined(); + expect(enemyPokemon[0].getTag(BattlerTagType.TRAPPED)).toBeDefined(); + + await game.toNextTurn(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); + game.doSelectTarget(BattlerIndex.ENEMY_2); + + game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(enemyPokemon[1].getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(playerPokemon[0].getTag(BattlerTagType.TRAPPED)).toBeDefined(); + expect(playerPokemon[0].getTag(BattlerTagType.TRAPPED)?.sourceId).toBe(enemyPokemon[0].id); + }, TIMEOUT + ); + + it( + "should not trap either pokemon if the target is protected", + async () => { + game.override.enemyMoveset(Array(4).fill(Moves.PROTECT)); + + await game.startBattle([ Species.BULBASAUR ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); + + await game.phaseInterceptor.to(BerryPhase, false); + + expect(playerPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + }, TIMEOUT + ); +}); From 6defc8c8f93664e0822793f4d1882bb8e307c34a Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:28:10 -0700 Subject: [PATCH 05/30] Fix Rollout test (#3660) --- src/test/moves/rollout.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/moves/rollout.test.ts b/src/test/moves/rollout.test.ts index 1fc208c6724..cad65768a1c 100644 --- a/src/test/moves/rollout.test.ts +++ b/src/test/moves/rollout.test.ts @@ -1,13 +1,13 @@ import { allMoves } from "#app/data/move.js"; import { CommandPhase } from "#app/phases/command-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Rollout", () => { let phaserGame: Phaser.Game; @@ -29,9 +29,9 @@ describe("Moves - Rollout", () => { game.override.disableCrits(); game.override.battleType("single"); game.override.starterSpecies(Species.RATTATA); - game.override.ability(Abilities.NONE); + game.override.ability(Abilities.BALL_FETCH); game.override.enemySpecies(Species.BIDOOF); - game.override.enemyAbility(Abilities.NONE); + game.override.enemyAbility(Abilities.BALL_FETCH); game.override.startingLevel(100); game.override.enemyLevel(100); game.override.enemyMoveset(SPLASH_ONLY); From f162c6af89cb8069c23abca70b7623e969e4cf75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2?= <123510358+NicusPulcis@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:06:01 +0200 Subject: [PATCH 06/30] Update ability-trigger.ts (IT) (#3546) * Update ability-trigger.ts (IT) * Update ability-trigger.ts --- src/locales/it/ability-trigger.ts | 68 +++++++++++++++---------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/locales/it/ability-trigger.ts b/src/locales/it/ability-trigger.ts index 95db5cbf26c..c834fa28fbe 100644 --- a/src/locales/it/ability-trigger.ts +++ b/src/locales/it/ability-trigger.ts @@ -25,39 +25,39 @@ export const abilityTriggers: SimpleTranslationEntries = { "postAttackStealHeldItem": "{{pokemonNameWithAffix}} ruba\n{{stolenItemType}} di {{defenderName}}!", "postDefendStealHeldItem": "{{pokemonNameWithAffix}} ruba\n{{stolenItemType}} di {{attackerName}}!", "copyFaintedAllyAbility": "L'abilità {{abilityName}} di {{pokemonNameWithAffix}} è passata all'alleato!", - "intimidateImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}} prevented it from being Intimidated!", - "postSummonAllyHeal": "{{pokemonNameWithAffix}} drank down all the\nmatcha that {{pokemonName}} made!", - "postSummonClearAllyStats": "{{pokemonNameWithAffix}}'s stat changes\nwere removed!", - "postSummonTransform": "{{pokemonNameWithAffix}} transformed\ninto {{targetName}}!", - "protectStat": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents lowering its {{statName}}!", - "statusEffectImmunityWithName": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents {{statusEffectName}}!", - "statusEffectImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents status problems!", - "battlerTagImmunity": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents {{battlerTagName}}!", - "forewarn": "{{pokemonNameWithAffix}} was forewarned about {{moveName}}!", - "frisk": "{{pokemonNameWithAffix}} frisked {{opponentName}}'s {{opponentAbilityName}}!", - "postWeatherLapseHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", - "postWeatherLapseDamage": "{{pokemonNameWithAffix}} is hurt\nby its {{abilityName}}!", - "postTurnLootCreateEatenBerry": "{{pokemonNameWithAffix}} harvested one {{berryName}}!", - "postTurnHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", - "fetchBall": "{{pokemonNameWithAffix}} found a\n{{pokeballName}}!", - "healFromBerryUse": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP!", - "arenaTrap": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents switching!", - "postBattleLoot": "{{pokemonNameWithAffix}} picked up\n{{itemName}}!", - "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", - "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", - "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", - "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", - "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", - "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", - "postSummonTeravolt": "{{pokemonNameWithAffix}} is radiating a bursting aura!", - "postSummonDarkAura": "{{pokemonNameWithAffix}} is radiating a Dark Aura!", - "postSummonFairyAura": "{{pokemonNameWithAffix}} is radiating a Fairy Aura!", - "postSummonNeutralizingGas": "{{pokemonNameWithAffix}}'s Neutralizing Gas filled the area!", - "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}} has two Abilities!", - "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}} has two Abilities!", - "postSummonVesselOfRuin": "{{pokemonNameWithAffix}}'s Vessel of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", - "postSummonSwordOfRuin": "{{pokemonNameWithAffix}}'s Sword of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", - "postSummonTabletsOfRuin": "{{pokemonNameWithAffix}}'s Tablets of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", - "postSummonBeadsOfRuin": "{{pokemonNameWithAffix}}'s Beads of Ruin lowered the {{statName}}\nof all surrounding Pokémon!", + "intimidateImmunity": "{{abilityName}} impedisce a {{pokemonNameWithAffix}} di\nessere intimidito!", + "postSummonAllyHeal": "{{pokemonNameWithAffix}} beve il\ntè che {{pokemonName}} gli ha preparato!", + "postSummonClearAllyStats": "Le statistiche di {{pokemonNameWithAffix}}\ntornano alla normalità!", + "postSummonTransform": "{{pokemonNameWithAffix}} assume le sembianze\ndi {{targetName}}!", + "protectStat": "{{abilityName}} di {{pokemonNameWithAffix}}\npreviene la riduzione del/della suo/a {{statName}}!", + "statusEffectImmunityWithName": "{{abilityName}} di {{pokemonNameWithAffix}}\nnon gli fa subire il/lo/la {{statusEffectName}}!", + "statusEffectImmunity": "{{abilityName}} di {{pokemonNameWithAffix}}\npreviene i problemi di stato!", + "battlerTagImmunity": "{{abilityName}} di {{pokemonNameWithAffix}}\npreviene {{battlerTagName}}!", + "forewarn": "{{pokemonNameWithAffix}} è stato messo in guardia da {{moveName}}!", + "frisk": "{{pokemonNameWithAffix}} perquisice {{opponentName}}\ne trova la sua abilità, {{opponentAbilityName}}!", + "postWeatherLapseHeal": "{{pokemonNameWithAffix}} recupera alcuni PS\ncon {{abilityName}}!", + "postWeatherLapseDamage": "{{pokemonNameWithAffix}} subisce danni\na causa della sua abilità, {{abilityName}}!", + "postTurnLootCreateEatenBerry": "{{pokemonNameWithAffix}} raccoglie una {{berryName}}!", + "postTurnHeal": "{{pokemonNameWithAffix}} recupera alcuni PS\ncon {{abilityName}}!", + "fetchBall": "{{pokemonNameWithAffix}} ha trovato una\n{{pokeballName}}!", + "healFromBerryUse": "{{abilityName}} di {{pokemonNameWithAffix}}\nristabilisce parte dei PS!", + "arenaTrap": "L’abilità {{abilityName}} di {{pokemonNameWithAffix}}\nimpedisce la sostituzione!", + "postBattleLoot": "{{pokemonNameWithAffix}} ha raccolto\nil/l'/lo/la {{itemName}}!", + "postFaintContactDamage": "{{abilityName}} di {{pokemonNameWithAffix}}\nferisce il Pokémon che lo ha attaccato!", + "postFaintHpDamage": "{{abilityName}} di {{pokemonNameWithAffix}}\nferisce il Pokémon che lo ha attaccato!", + "postSummonPressure": "{{pokemonNameWithAffix}} fa pressione!", + "postSummonMoldBreaker": "{{pokemonNameWithAffix}} ha l’abilità Rompiforma!", + "postSummonAnticipation": "{{pokemonNameWithAffix}} rabbrividisce!", + "postSummonTurboblaze": "{{pokemonNameWithAffix}} emana un’aura infuocata!", + "postSummonTeravolt": "{{pokemonNameWithAffix}} emana un’aura repulsiva!", + "postSummonDarkAura": "L’abilità Auratetra di {{pokemonNameWithAffix}} è attiva.", + "postSummonFairyAura": "L’abilità Aurafolletto di {{pokemonNameWithAffix}} è attiva.", + "postSummonNeutralizingGas": "Il Gas Reagente di {{pokemonNameWithAffix}}\nsi diffonde tutt’intorno!", + "postSummonAsOneGlastrier": "{{pokemonNameWithAffix}} ha due abilità!", + "postSummonAsOneSpectrier": "{{pokemonNameWithAffix}} ha due abilità!", + "postSummonVesselOfRuin": "La/l'{{statName}} dei Pokémon intorno si indebolisce a causa\ndell'abilità Vaso Nefasto di {{pokemonNameWithAffix}}!", + "postSummonSwordOfRuin": "La/l'{{statName}} dei Pokémon intorno si indebolisce a causa\ndell'abilità Spada Nefasta di {{pokemonNameWithAffix}}!", + "postSummonTabletsOfRuin": "La/l'{{statName}} dei Pokémon intorno si indebolisce a causa\ndell'abilità Amuleto Nefasto di {{pokemonNameWithAffix}}!", + "postSummonBeadsOfRuin": "La/l'{{statName}} dei Pokémon intorno si indebolisce a causa\ndell'abilità Monile Nefasto di {{pokemonNameWithAffix}}!", "preventBerryUse": "{{pokemonNameWithAffix}} non riesce a\nmangiare le bacche per l'agitazione!", } as const; From 677331221883a5d2bb8370a988fd4be8abc36ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2?= <123510358+NicusPulcis@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:06:18 +0200 Subject: [PATCH 07/30] Update achv.ts (IT) (#3558) --- src/locales/it/achv.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/locales/it/achv.ts b/src/locales/it/achv.ts index 91222b81579..756d95e6431 100644 --- a/src/locales/it/achv.ts +++ b/src/locales/it/achv.ts @@ -170,8 +170,8 @@ export const PGMachv: AchievementTranslationEntries = { description: "Vinci in modalità classica", }, "UNEVOLVED_CLASSIC_VICTORY": { - name: "Bring Your Child To Work Day", - description: "Beat the game in Classic Mode with at least one unevolved party member." + name: "Alternanza scuola-lavoro", + description: "Completa la modalità classica con almeno un membro della squadra non evoluto completamente." }, "MONO_GEN_ONE": { @@ -269,8 +269,8 @@ export const PGMachv: AchievementTranslationEntries = { name: "Follettini e follettine", }, "FRESH_START": { - name: "First Try!", - description: "Complete the Fresh Start challenge." + name: "Buona la prima!", + description: "Completa la modalità sfida 'Un nuovo inizio'." } } as const; From 767d802b942595008b6683efc7b1c3c8410bc662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2?= <123510358+NicusPulcis@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:06:34 +0200 Subject: [PATCH 08/30] Update challenges.ts (IT) (#3559) --- src/locales/it/challenges.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/locales/it/challenges.ts b/src/locales/it/challenges.ts index 784791f5425..dde5bd0d4e7 100644 --- a/src/locales/it/challenges.ts +++ b/src/locales/it/challenges.ts @@ -2,7 +2,7 @@ import { TranslationEntries } from "#app/interfaces/locales"; export const challenges: TranslationEntries = { "title": "Modificatori delle sfide", - "illegalEvolution": "{{pokemon}} changed into an ineligble pokémon\nfor this challenge!", + "illegalEvolution": "{{pokemon}} non è più utilizzabile\nsecondo le regole della sfida!", "singleGeneration": { "name": "Mono gen", "desc": "Puoi usare solo Pokémon di {{gen}} generazione.", @@ -23,8 +23,8 @@ export const challenges: TranslationEntries = { "desc_default": "Puoi usare solo Pokémon del tipo selezionato." }, "freshStart": { - "name": "Fresh Start", - "desc": "You can only use the original starters, and only as if you had just started PokéRogue.", + "name": "Un nuovo inizio", + "desc": "Puoi usare solo gli starter originali, e come se avessi appena cominciato Pokérogue.", "value.0": "Off", "value.1": "On", } From 2dc9900522dc8d9015dfc580d47a892e1543700a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2?= <123510358+NicusPulcis@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:06:44 +0200 Subject: [PATCH 09/30] Update battle.ts (IT) (#3560) --- src/locales/it/battle.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/locales/it/battle.ts b/src/locales/it/battle.ts index bd7227eacb6..347a9968e96 100644 --- a/src/locales/it/battle.ts +++ b/src/locales/it/battle.ts @@ -74,22 +74,22 @@ export const battle: SimpleTranslationEntries = { "fainted": "{{pokemonNameWithAffix}} non è più in\ngrado di combattere!", "statsAnd": "e", "stats": "statistiche", - "statRose_one": "{{pokemonNameWithAffix}}'s {{stats}} è aumentato/a!", - "statRose_other": "{{pokemonNameWithAffix}}'s {{stats}} rose!", - "statSharplyRose_one": "{{pokemonNameWithAffix}}'s {{stats}} è aumentato/a molto!", - "statSharplyRose_other": "{{pokemonNameWithAffix}}'s {{stats}} sharply rose!", - "statRoseDrastically_one": "{{pokemonNameWithAffix}}'s {{stats}} è aumentato/a drasticamente!", - "statRoseDrastically_other": "{{pokemonNameWithAffix}}'s {{stats}} rose drastically!", - "statWontGoAnyHigher_one": "{{pokemonNameWithAffix}}'s {{stats}} non può aumentare più di così!", - "statWontGoAnyHigher_other": "{{pokemonNameWithAffix}}'s {{stats}} won't go any higher!", - "statFell_one": "{{pokemonNameWithAffix}}'s {{stats}} è diminuito/a!", - "statFell_other": "{{pokemonNameWithAffix}}'s {{stats}} fell!", - "statHarshlyFell_one": "{{pokemonNameWithAffix}}'s {{stats}} è diminuito/a molto!", - "statHarshlyFell_other": "{{pokemonNameWithAffix}}'s {{stats}} harshly fell!", - "statSeverelyFell_one": "{{pokemonNameWithAffix}}'s {{stats}} è diminuito/a drasticamente!", - "statSeverelyFell_other": "{{pokemonNameWithAffix}}'s {{stats}} severely fell!", - "statWontGoAnyLower_one": "{{pokemonNameWithAffix}}'s {{stats}} non può diminuire più di così!", - "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}'s {{stats}} won't go any lower!", + "statRose_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è aumentata!", + "statRose_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono aumentate!", + "statSharplyRose_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è aumentata molto!", + "statSharplyRose_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono aumentate molto!", + "statRoseDrastically_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è aumentata drasticamente!", + "statRoseDrastically_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono aumentate drasticamente!", + "statWontGoAnyHigher_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} non può aumentare di più!", + "statWontGoAnyHigher_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} non possono aumentare di più!", + "statFell_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è diminuita!", + "statFell_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono diminuite!", + "statHarshlyFell_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è diminuita molto!", + "statHarshlyFell_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono diminuite molto!", + "statSeverelyFell_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} è diminuita drasticamente!", + "statSeverelyFell_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} sono diminuite drasticamente!", + "statWontGoAnyLower_one": "La statistica {{stats}} di {{pokemonNameWithAffix}} non può diminuire di più!", + "statWontGoAnyLower_other": "Le statistiche {{stats}} di {{pokemonNameWithAffix}} non possono diminuire di più!", "transformedIntoType": "{{pokemonName}} diventa\ndi tipo {{type}} type!", "retryBattle": "Vuoi riprovare dall'inizio della lotta?", "unlockedSomething": "{{unlockedThing}}\nè stato/a sbloccato/a.", From 548f7fce6f16c4d19fea6ca0afbf8bbede176786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2?= <123510358+NicusPulcis@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:07:00 +0200 Subject: [PATCH 10/30] Update battler-tags.ts (IT) (#3561) --- src/locales/it/battler-tags.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/locales/it/battler-tags.ts b/src/locales/it/battler-tags.ts index 7dd3ebc6fb1..518e9194521 100644 --- a/src/locales/it/battler-tags.ts +++ b/src/locales/it/battler-tags.ts @@ -1,14 +1,14 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const battlerTags: SimpleTranslationEntries = { - "trappedDesc": "trapping", - "flinchedDesc": "flinching", - "confusedDesc": "confusion", - "infatuatedDesc": "infatuation", - "seedDesc": "seeding", - "nightmareDesc": "nightmares", - "ingrainDesc": "roots", - "drowsyDesc": "drowsiness", + "trappedDesc": "intrappolando", + "flinchedDesc": "tentennando", + "confusedDesc": "confuso", + "infatuatedDesc": "infatuato", + "seedDesc": "pieno di semi", + "nightmareDesc": "incubi", + "ingrainDesc": "radici", + "drowsyDesc": "assonnato", "rechargingLapse": "{{pokemonNameWithAffix}} deve\nricaricarsi!", "trappedOnAdd": "{{pokemonNameWithAffix}} non può\npiù fuggire!", "trappedOnRemove": "{{pokemonNameWithAffix}} è stato liberato\nda {{moveName}}", From 742b5432aaded32c5383c37bd740fa24fdf09d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2?= <123510358+NicusPulcis@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:07:15 +0200 Subject: [PATCH 11/30] Update menu-ui-handler.ts (IT) (#3562) --- src/locales/it/menu-ui-handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/it/menu-ui-handler.ts b/src/locales/it/menu-ui-handler.ts index 0cfea8d67b7..3454de24f87 100644 --- a/src/locales/it/menu-ui-handler.ts +++ b/src/locales/it/menu-ui-handler.ts @@ -25,5 +25,5 @@ export const menuUiHandler: SimpleTranslationEntries = { "unlinkGoogle": "Scollega Google", "cancel": "Annulla", "losingProgressionWarning": "Perderai tutti i progressi dall'inizio della battaglia. Confermi?", - "noEggs": "You are not hatching\nany eggs at the moment!" + "noEggs": "Non stai schiudendo\nuova al momento!" } as const; From 3e44c4552c570a09ffa9f2e9b166b8d4ed2a3359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2?= <123510358+NicusPulcis@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:07:25 +0200 Subject: [PATCH 12/30] Update menu.ts (IT) (#3563) --- src/locales/it/menu.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/locales/it/menu.ts b/src/locales/it/menu.ts index 9766708f7ae..3787ceb0e70 100644 --- a/src/locales/it/menu.ts +++ b/src/locales/it/menu.ts @@ -17,7 +17,7 @@ export const menu: SimpleTranslationEntries = { "username": "Nome utente", "password": "Password", "login": "Accedi", - "orUse": "Or use", + "orUse": "O usa", "register": "Registrati", "emptyUsername": "Nome utente mancante!", "invalidLoginUsername": "Nome utente non valido!", @@ -39,9 +39,9 @@ export const menu: SimpleTranslationEntries = { "weeklyRankings": "Classifica settimanale", "noRankings": "Nessuna classifica", "positionIcon": "#", - "usernameScoreboard": "Username", - "score": "Score", - "wave": "Wave", + "usernameScoreboard": "Nome utente", + "score": "Punteggio", + "wave": "Onda", "loading": "Caricamento…", "loadingAsset": "Caricamento asset: {{assetName}}", "playersOnline": "Giocatori online", From 9ecebb942c649346ed5d53a9e8d9bffbcddc075f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2?= <123510358+NicusPulcis@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:07:39 +0200 Subject: [PATCH 13/30] Update modifier-type.ts (IT) (#3564) --- src/locales/it/modifier-type.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/it/modifier-type.ts b/src/locales/it/modifier-type.ts index 6f054e4e566..0b166d268a5 100644 --- a/src/locales/it/modifier-type.ts +++ b/src/locales/it/modifier-type.ts @@ -101,7 +101,7 @@ export const modifierType: ModifierTypeTranslationEntries = { }, "TmModifierTypeWithInfo": { name: "MT{{moveId}} - {{moveName}}", - description: "Insegna {{moveName}} a un Pokémon\n(Hold C or Shift for more info).", + description: "Insegna {{moveName}} a un Pokémon\n(Tieni premuto C o Shift per maggiori informazioni).", }, "EvolutionItemModifierType": { description: "Fa evolvere determinate specie di Pokémon.", @@ -153,7 +153,7 @@ export const modifierType: ModifierTypeTranslationEntries = { "REVIVER_SEED": { name: "Revitalseme", description: "Il possessore recupera 1/2 di PS in caso di KO causato da un colpo diretto." }, - "WHITE_HERB": { name: "Erbachiara", description: "An item to be held by a Pokémon. It will restore any lowered stat in battle." }, + "WHITE_HERB": { name: "Erbachiara", description: "Strumento da dare a un Pokémon. Ripristina le statistiche ridotte in lotta." }, "ETHER": { name: "Etere" }, "MAX_ETHER": { name: "Etere max" }, From 0ff94d832e2049fc18831ce41182a05c03994a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2?= <123510358+NicusPulcis@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:07:55 +0200 Subject: [PATCH 14/30] Update modifier.ts (IT) (#3565) * Update modifier.ts (IT) * Update src/locales/it/modifier.ts Co-authored-by: Enoch --------- Co-authored-by: Enoch --- src/locales/it/modifier.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/locales/it/modifier.ts b/src/locales/it/modifier.ts index 810524a9e5e..94512efef0d 100644 --- a/src/locales/it/modifier.ts +++ b/src/locales/it/modifier.ts @@ -1,14 +1,14 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; export const modifier: SimpleTranslationEntries = { - "surviveDamageApply": "{{pokemonNameWithAffix}} hung on\nusing its {{typeName}}!", - "turnHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!", - "hitHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!", - "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} was revived\nby its {{typeName}}!", - "pokemonResetNegativeStatStageApply": "{{pokemonNameWithAffix}}'s lowered stats were restored\nby its {{typeName}}!", - "moneyInterestApply": "You received interest of ₽{{moneyAmount}}\nfrom the {{typeName}}!", - "turnHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was absorbed\nby {{pokemonName}}'s {{typeName}}!", - "contactHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was snatched\nby {{pokemonName}}'s {{typeName}}!", - "enemyTurnHealApply": "{{pokemonNameWithAffix}}\nrestored some HP!", + "surviveDamageApply": "{{pokemonNameWithAffix}} resiste\ngrazie al/alla suo/a {{typeName}}!", + "turnHealApply": "{{pokemonNameWithAffix}} recupera alcuni PS con\nil/la suo/a {{typeName}}!", + "hitHealApply": "{{pokemonNameWithAffix}} recupera alcuni PS con\nil/la suo/a {{typeName}}!", + "pokemonInstantReviveApply": "{{pokemonNameWithAffix}} torna in forze\ngrazie al/alla suo/a {{typeName}}!", + "pokemonResetNegativeStatStageApply": "La riduzione alle statistiche di {{pokemonNameWithAffix}}\nviene annullata grazie al/alla suo/a {{typeName}}!", + "moneyInterestApply": "Ricevi un interesse pari a {{moneyAmount}}₽\ngrazie al/allo/a {{typeName}}!", + "turnHeldItemTransferApply": "Il/l'/lo/la {{itemName}} di {{pokemonNameWithAffix}} è stato assorbito\ndal {{typeName}} di {{pokemonName}}!", + "contactHeldItemTransferApply": "Il/l'/lo/la {{itemName}} di {{pokemonNameWithAffix}} è stato rubato\nda {{pokemonName}} con {{typeName}}!", + "enemyTurnHealApply": "{{pokemonNameWithAffix}}\nristabilisce parte dei PS!", "bypassSpeedChanceApply": "{{pokemonName}} agisce più rapidamente del normale grazie al suo {{itemName}}!", } as const; From 98df8a39bf94f9a44d94b933681c3b2fe11c86d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2?= <123510358+NicusPulcis@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:08:14 +0200 Subject: [PATCH 15/30] Update move-trigger.ts (IT) (#3572) --- src/locales/it/move-trigger.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/it/move-trigger.ts b/src/locales/it/move-trigger.ts index 198fc269785..92ce6a76a74 100644 --- a/src/locales/it/move-trigger.ts +++ b/src/locales/it/move-trigger.ts @@ -57,10 +57,10 @@ export const moveTriggers: SimpleTranslationEntries = { "sacrificialFullRestore": "{{pokemonName}} riceve i benefici\neffetti di Curardore!", "invertStats": "Le modifiche alle statistiche di {{pokemonName}}\nvengono invertite!", "resetStats": "Tutte le modifiche alle statistiche sono state annullate!", - "statEliminated": "All stat changes were eliminated!", + "statEliminated": "Tutte le modifiche alle statistiche sono state annullate!", "faintCountdown": "{{pokemonName}}\nandrà KO dopo {{turnCount}} turni.", "copyType": "{{pokemonName}} assume il tipo\ndi {{targetPokemonName}}!", "suppressAbilities": "L’abilità di {{pokemonName}}\nperde ogni efficacia!", "swapArenaTags": "{{pokemonName}} ha invertito gli effetti attivi\nnelle due metà del campo!", - "exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!", + "exposedMove": "{{pokemonName}} ha identificato\n{{targetPokemonName}}!", } as const; From e9d17f0605a3498f91e672e1283c80b6d1200ae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2?= <123510358+NicusPulcis@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:08:34 +0200 Subject: [PATCH 16/30] Update move.ts (IT) (#3575) --- src/locales/it/move.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/it/move.ts b/src/locales/it/move.ts index a2c99218cec..1c510d4df6d 100644 --- a/src/locales/it/move.ts +++ b/src/locales/it/move.ts @@ -2915,7 +2915,7 @@ export const move: MoveTranslationEntries = { }, zippyZap: { name: "Sprintaboom", - effect: "The user attacks the target with bursts of electricity at high speed. This move always goes first and raises the user's evasiveness.", + effect: "Un attacco elettrico ad altissima velocità. Questa mossa ha priorità alta e aumenta l'elusione dell'utilizzatore.", }, splishySplash: { name: "Surfasplash", From 6b21a777a1edc2bf37a7fcac50e49e0f450d9857 Mon Sep 17 00:00:00 2001 From: chaosgrimmon <31082757+chaosgrimmon@users.noreply.github.com> Date: Wed, 21 Aug 2024 18:27:25 -0400 Subject: [PATCH 17/30] [Sprite] Index Rookidee, remove stray pixel (#3653) * [Sprite] Index front static Rookidee * [Sprite] Index front exp Rookidee * [Sprite] Index back static Rookidee * [Sprite] Index shiny front static Rookidee * [Sprite] Index back exp Rookidee * [Sprite] Index shiny back static Rookidee * [Sprite] Index shiny back static Rookidee * [Sprite] Index shiny back exp Rookidee --- public/images/pokemon/back/821.png | Bin 1108 -> 474 bytes public/images/pokemon/back/shiny/821.png | Bin 1083 -> 474 bytes public/images/pokemon/exp/821.png | Bin 23268 -> 8520 bytes public/images/pokemon/exp/back/821.png | Bin 3863 -> 1624 bytes public/images/pokemon/exp/back/shiny/821.png | Bin 3822 -> 1624 bytes public/images/pokemon/exp/shiny/821.png | Bin 22683 -> 8516 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/public/images/pokemon/back/821.png b/public/images/pokemon/back/821.png index a102a0491de588b7e17475116581c10076cbe5f0..24e0edd705dd1f82587ff097beeb1bd3d82253cb 100644 GIT binary patch delta 459 zcmV;+0W|*92-*XX8Gi!+003ZT+XVms00DDSM?wIu&K&6g001OVOjJbx000OG2p}2~ zF)1BDH6&0_I43JdG(mD~U1Y44G)ZTxmz$o!Z6-_r000bhQchC<|NsC0|NsC0|Ns99 z#84&x00B%%L_t(YiS5(dvV$ND1yHwQBK80O_vC^gBG9Mv)PKx$+Tz+60heW7Sf`@; zv(LOuNLF9*orphHOxS0h=u>LOD{Oq^uQsmY;q>*?q8Xv@>518VhvTVjHACY;tyb)d zN#VwIH^AZv++q)`#mcBaxeD!6*N(Jl$~pZL5*Di;dnWdhTV!Q#l~AuM+B8#Z zd1j;@Xxlclvp4WOgN$Lu|z z)x+*JkV7MDTnKVg6TRe{Yhc)it0RQqKjzrD9BfJ8kVNyHqLbNAeHGMea>A{|A$W2W z5N+!cj+C=xUtiCs8Y$W5$hopGU#!r0>wopX^#_JX7Knh30Q~>}002ovPDHLkV1md_ B*vS9@ delta 1098 zcmV-Q1hxCx1JnqR8Gi-<0039HD>ncD00DDSM?wIu&K&6g00a(6L_t(&f$f;jYZE~f z$G?$+Lm?=|M%oIktt84u=%uBRV&kcQf%MX%hu|T25WMNx+{A;DL&-rgm(qWrJ();x zi@gXYwgxU#h+7W20x*T!(-%BS7J~|%9>FqNd0HQGs zz44TzT{NcggL)NvMp^X3+nU8`yG{&zaAXP_+raQ-#@Dtto0bgf`8GCo`_=*iVg`F0Oxl`Ub$e!h`}&&_UFw z=@#f@DvQ8+;y3)hR7~_K@ zQ%&_YD6}2eo~=er$Cvt1)H>PMCI|= zfgkSR_FYXHlv8~rcmUWl%1EZN79iPyS1;x;I5LId$&6(UAJnU!RY$g^IRPL&VDQq~ zj>veI3`_&=t`xAlQo!(JrfCuy2o9Tt)1VMspg1BGN^CH@As_1zPj!YpM)0)PXGvfy=v)sEkgN6AVN^3ijF4Ht46+O=h zM4p5uYSi>52UJARa>UyeMP95KI)?g1QU;K-W0#y26?>IA`??LRuJ6M%&4}25Ow)`U zMIZEY6J^6Mg7)kk)T@D`zGsw?yqp$o&kcyOK!0o14es~PHJnS_Zn`J0^L9mnG)*%C z_-sIAv{hXOGs=!m(Dk>i=K@}is?)<6RKx?cu;o+rA2p#jFA~Ma zNzC4__`(Cu4%p=dOllh(u)+h@=&Lw1d%vOtD=SoR8N@C(_#n_gh~RSD350CO0dg(~ zbZBCX@w;;wo*ug-er`65F&^}TsI1VxWPcr=mjNIr;N(}XCr|VAG62~@*+xa7gD=eI z0LTsi^vYFOEbbWtE4$)#LwQ(1_-H+dr2;R|FmXz)KNzrb@X59FEd?4I5+Q8 Q(f|Me07*qoM6N<$f{SnuLOD{Oq^uQsmY;q>*?q8Xv@>518VhvTVjHACY;tyb)d zN#VwIH^AZv++q)`#mcBaxeD!6*N(Jl$~pZL5*Di;dnWdhTV!Q#l~AuM+B8#Z zd1j;@Xxlclvp4WOgN$Lu|z z)x+*JkV7MDTnKVg6TRe{Yhc)it0RQqKjzrD9BfJ8kVNyHqLbNAeHGMea>A{|A$W2W z5N+!cj+C=xUtiCs8Y$W5$hopGU#!r0>wopX^#@)u7qIlxg_8gP002ovPDHLkV1f`% B-1z_i delta 1073 zcmV-11kU@~1G@;28Gi-<0039HD>ncD00DDSM?wIu&K&6g00Z?&L_t(&f$f-2XcIvc z#=qDe=3FT1LDXCWKbAb z{^|qn-qoZ|Ic*FC4*;dLeXLGfO+d1NzJX`R4#hB29BW#~cBSrHb=+(IcuZ{8&+*;8 zZ877$GB9;Gl`mr|U&c&v?D$-{r6V#EH0V^mEZX=%yW6@oG5Hwtvk>qR#u#6nw#4+& zAT$td)qhj@vbdfp*dp6r$`ov5hhnf&(U!jDnF#`xS+3m4VSaWFyL;P_UdM4FnS#v; zL>`0&T3^{}F`y%YmNVX|DDz^?&|_#&CS?G5cHEL@MJHZo%*}ca4vuPY94FFJ$K~hf zAQN&nRp7K#EG??RWswOtfPWi)x$+h7o~E?m!-{rO$8jP+zy?G? zuio#Vw6?D_LH{4Ts0es@Evs(Mup%CyjV+(54>|W*1^{sQyHEW8RNc_JZ-SI5*x|r| zzwp!%&@eERc_Lzp@7^}GF#+y|20D1GB^VhR0P+tWHXYwc-V`QHsx9#2njct?O~R|P z4S$4utd(nireI_FP02G>UOJzu8=!VN@`46Bd>)r;z8^f$o&k61Q_0AS#PXXG?mo%} z!UJjpZutR|jC`Ju;eqDt>lnKGD60i4%P6=E;+7YD7-+yCxO}Vvp%^ki=E6W56Jv}| z-XG`Ju3i^EuNcM{5Bot>mhmrH6O)et0Dtqd5ODG#`&v86ITdJolF zO`IbG0K{7h#l_M|%+JnACg<9QHwvy&ES&_l1&D|ceKU{nt)XmVs~#F2S>H08BrMofXlTSlJU6K*haQ|zmle!kt(40 rpcvo;KkU0fbYQybs;jQL`hWBXW4T3_A_@eU00000NkvXXu0mjfTde!l diff --git a/public/images/pokemon/exp/821.png b/public/images/pokemon/exp/821.png index e4a73a66140a2698346f09bd0a87a4c360176ec2..8bb00063bcb6029da354c9826f76c309e610e6c4 100644 GIT binary patch literal 8520 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 ci}syVySzomPEyIYM|Ws%Al-Ej-`~CK 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 diff --git a/public/images/pokemon/exp/back/821.png b/public/images/pokemon/exp/back/821.png index 80d88dce2f943ebb39e261fdc69a561a7dd6aea7..13aa5c24e83d65a509243a9e0b685f5379f75fa7 100644 GIT binary patch delta 1618 zcmV-Y2Cey*9@q?!8Gi!+000i>FSq~z00DDSM?wIu&K&6g001OVOjJbx000OG2p}2~ zF)1BDH6&0_I43JdG(mD~U1Y44G)ZTxmz$o!Z6-_r000bhQchC<|NsC0|NsC0|Ns99 z#84&x00qiPL_t(&fz_Igma8fZh0UoGT7CcbJthGOpb)_6y?=)_KTap?odCA-Etci~ z+u+Y{hNpd#KYm(Do#7QB>WbVl`{>rR%K_=B|#7dwc=u2Tu`-DQ+@%?$#J` zTS?PoOm`i9gMUM>b6g#1t5Ql|PBOFQOgT+lo4)9)LZEKoq+V@HLzf7JBo`l^x{7nt z7s7BNbtDh2moDRsk@0?-sw_F2=0zpys zHog?1pm3#lXxaL~mhc;6c)#C}Wf`KKtbfcpn0(Jlk_Dq=j#g%Q+%M5M?_jm{%lOZE zha@sL0~ErGakkpBv5XhKUaz*V0~LNTKpnm$_4#~WrO<^+ExgB8ls=!J1Z`g(KH+@iQ=lg}J!m|PDvt=o5{7UbhR(NnW-$GjHb1Jd(tFDoS z1GF-&DUsAE*HGyUuL&KU;T*u0PG@*UC=3)ywa2V8ydv~o*HtboGM(X7q1|5?KDjx3 zCuO=wMzb& zz$%&S*Kkf}1&`sJ&I%sFIZIy!PvM+GtATS;(lCs3a@R%$=xGQ!diwKQ{E8=i5yU~3 zK!00q-j^}`8N*0TASFB`AdU@~hHSFkNBJ?$uTE|daZbb`wCQDAu2lEH0bjVX)gvvN zMvq}&6E+bb48?CF^_Cn=NSIP$93p^2_R2D^&bg4~VQl~KXAl;E6oH{Y?AU+R{K{`e z0)_$1=>V?w4}WG>9!R+o*rBT6q#?3@Du1C0?z_ntgQem*sDT{;I4z_(*9DX?84G}; zdqV*!hv!;}QrbA8lnYyeKMkDY0UX_e36OF*I>WpI@C&(QA;(LR{@k>Lh(sWTt{_P% zkO~y$_%2zB4|o|Ji2fE1<`2!$b>Q!)nsM%pk=x-pQt;zGxZdA>Z*gmVgCgMaNJ z537I*NSU}08DohUu0reX8c11q6rMPz$$lEfeLyP04do02OvUvADNTQX({AyQYhw+h z+Q4p+O}>IEO$>8qtbmkht#s0#n)Ah+RhG(wI7d|wfZe^?V>lkvi}4L45TD zk($4cr~3c1tqsD`=gco2gVF)tJO)Md=RY_Gr8vwNk3p66=PSpcavt)fV^E4;S;Fo4 z=jWR6hF?u`GaE*_z*lqJ%z%-u@zoqQ%WeG`A3bus`Mkwf+YmNO&NYcG4S%UybqUw_ z>g>>o%>WffQm%YKV;4VF{T`ygS7X>rHj!R3aphxNKk+f5jjxOkcv5P_NE_3->=>7p zYTzp)HuK9cVkDt;$%@V}QVU<1u$jkKE)JKUvgqo{b4?X|Wx!@U#zjAncYstY!ALcH zWx-|(q+l*!r1Icbfsrcs%71d00?tEqGH0yBOEj_0b*lKv#IGRmD+fX8;uzPZ@eRJR z`Rd9~CBr$cJA7rvb-IEZnseoQgF*)QmBL7-b6grF_ z^me(2BRz<(dO6Zl_^OX1J&Lca9O+qnW#>o_;;S)^G>@+?w7UDv`TlPE3j?x(_wIT$ QDgXcg07*qoM6N<$f}HLILjV8( literal 3863 zcmV+y59siTP)Px@(Md!>RCt{2olk5O$rZ*wZ;)tNvce*4mIcWRCN|a>i36-KLf9N4HuoT3j1ol- zQ7j);%Ox^r`?ktTVFw8;6ck@9?`co#6MtmwSjz>;GqCoq4M=uaY#>CABImGl4#R7| zuBqy-?yl*s9{ZClX2$L5uivXzRsE-a06W@I8gd-R{piEJZq`2H$VTt))$5BL$BA{I zCqC{()Pq`D^e#K)c3{lhO^ z0BC6|ptZACx?M|K!QEJ^;MvkW@9*&4qA$2siBzIkZUlD6{fT_t15 z?J8VwkDMJ+j!iPHFP5aQ->kj_klySwj(4ssy$8uiw@ks&6$N8st>XI^sE&8f{|jJs z340FiGhr`KQN45Vqyp9Iohub_8c4-^m?Dh{j^nuP9euv(zvDRW`QQD+x80rFm(bqP z=j-CcgdRCNgyq=@?={!@VhI~-6&z@;CivlZi>Sx=CG)N;@p!lnG%o=_UA^nbSGoLL zZlN;MB)$i#rv+VryHJ^F3JOk3TLH_n6KH8Gc%N;oReXt5RZMGVuQX_JBx{iLd7Q+` z8majE=WFlYtS-fHY)-oXe^RNUcz3ltI|0XW+@9WH?+?2Q7u;t{_prWLl8#jyYn8yn z%EY_5mbOAQND%`R)*yAf2YFK^l|-jTTSI`M>+% z$JD##T>!ATx!F`M6}`$_Uo3gaprrDIx3m>dE)~%uBv(8*v2x>T;PNN{p#RiSmj_Ag zamerT>;#US9jZx$=SOnELFQd-(5l1+HO?*n<8Imst{I1b>WmertxN!3ms2hkYg#|4 zTq>fyqYo`@1!a{-(Z*T@pME+30GPTl?p1-<T!FyFLK@pzLYnT{dIdp#8SA_h?&Vrm0*iqFgH0jGHZO1?+zRRE>9DrTl%;V@UiZ5f!;|y~&cI+X8^*w~xHnzxd!{ zB7@~pF>zqHZ=klCRo)*zSvS`Eq z?@m1cY>m4-H~?^UWWVoy_u*Dhw`hx^de5sO-_PCL+-wRPgRYM3_XVo^a4Ue?$&pIb z7)-W!YM46SxsoI}%c=z;cFQC_Yc7|HnEbpaaC_d}J$|wd`dhBFW|UF9>-bhF>%;;v zlS|^~l~EE5Yz2#yb|B(<4@+`Zr#&(yHsCmpJNbDJ2CtMug9Owj8K?#)_T2AJsOFD?O9 zz~xa4Tpoog(cF7bW3WZWmy=B07{~RSx7?{4G(>7B##%mt`u|-Cc+jFcRK=pwA0Rn(=cW!EnkmH@{NzGzEZ~fTbBAm3P&4 zBv~T>wi-hD`@4;kTp;+AKvo^HMgXK6prqd8erKAhy9u5^orAV1KvBK#r~zdo$p)w+ z%_gsE6p{^4XW&UxIgaCgu(vt0C6A;=AlU$QOs?ZNZk%z-2A)JCkZgb*4pogBg=7Ql zaHu>r8p#IO;ZOlc#>D`LWK+Z^pPVr0D!Gi$mb(fUTnbi_fTc~c0ji6xgH{64l1&wb zhh)pl*=Xew%PB7MCus%9kO^EOA468LwCI{Xmw`=W6ds_8DYAiLLo$}!q+OfHa@uon zpZ55rVtVjxWPFSxXNPd-_9bxe318I46U&c{)(?rt%MnX9jJ=s~s6_F^_VfejZlGE-2&9=mSv!>FqV^u`w@d8Vv%I9t~aY+YafV= z=ON=_tS^?Ts3IY`BF96u@;FR6msl>1Y zq80V3m3b=fRJr{3LB|@TmbL=E_+rZUnXZ46QeOAvN#G~;QCLpA5_m<#p~%TNRAEwa zF=Q2Kis8Zc!6#Q~lWYKBR}h5_fJcv`aWe|biT*xl(iBxuSWXnog~|+^rF&j?&AVNC zO$-kc38t?9*X-G=xq11sSM^j=5QR-8U6vECn54*}xGX2m!w94B{QT3j5rzlN*!i{c zEFRN&Pdrc5p||jDb!CDK!=EkI6&)WXSlmA+*#P>h|8@5rmBM3EKCmaCzj7SdN|?xU z5>Y?&Iw{jBDWuD*;CDX6S#dMA5D!UZ!#!%{iZ-tHjy|u8k%70w1poZgGyuC$KO)U0 zh-4ELkzm@ZDb<_`@vek}x`67zK3lq1)4dU_%M_5S9uh$V zhS@-o;}TcPt0WtX`J~I%!~!XBsK&0nuyTTuX9|z5QsGcV8N*0vjA4yTvH>#UP=!(C zfOM|tG&0Es$e2SV1~W^FoNE7hCfRIj4i&AxU0e1cFu^t-RVJn4S* z-c5I24QU5Uqpc_$DwVSG9wsBnr2xg{Pl=c!AlmGcdJNfuaW2Pke7_6g zP|3V)JIGkpm?Bz!NymD@1F9xWX_+K@%$(he{Mj6cMaT=RWDl^Pedj0Wqjl+zT0GO!gi87)03w z7y~rB<(dPPoV4p;^nl=`^8IbBRq%B6HBKDdrwuuw6$`TD(KxF5JCO?_<9_4`Fa}7v zWfg;p4mOPV!ihIxw}yf7D@>FqI?a%E z$y_ROoK9CQlSpIJeNHKB29*s2IS(ogY$cGyd5K(MsG^|piziukLKYWOM3Nx`MGRnA zqm3dP0Aqjv8C#y6s7aVC0Xp54X}DO=vHr-Qt{?^#S4&#~-ESWywFtl-z!)Gv##9pt zl~kA_UIj_vV*ESfR;@>56`*G#fyV^ifs=noSv@!AgN$+5n5c58sHE6%P*?N23J_DS6 z;<6#JKtc{x7^cWJf*idz&F?Vf(X=98TMny2(^X(9Xw(b!^bSi`{*$hb?8nuS{pdd2 z8af8k5M^UxftVaBARSB*P@lj>VNm52Fa{)8P7<}7i^DOtfk7p9(|G}m0R}i!x+P>kF3-|-23ewoM7v5xx zk7c6L3Yac~N|xX}Bo;83T>f4>a9NPD4v;Q`3dkp5%$PXOiGEbu*ubCSesmaAws?-J zZHp=rD|g`XC}a#O(Q_7FFSq~z00DDSM?wIu&K&6g001OVOjJbx000OG2p}2~ zGA}l3Q8PnAMM_UqY+Yoyja8SMp0uH*_TY@R-zC)m000bhQchC<|NsC0|NsC0|Ns99 z#84&x00qiPL_t(&fz_Igma8fZh0QUYVtxPjJthGOpb)_6y?=)_KTap?odCA-Etci~ z+u+Y{hNpd#KYm(Do#7QB>WbVl`{>rR%K_=B|#7dwc=u2Tu`-Def|N?$#J` zTS?PoOm`i9gMUM>b6g#1t5Ql|PBOFQOgT+lo4)9)LZEKoq+V@HLzf7JBo`l^x{7nt z7s7BNbtDh2moDRsk@0q#sw_F2=0zpys zHog?1pm3#lXxaL~mhc;6c)#C}Wf`KKtbfcpn0(Jlk_Dq=j#g%Q+%M5M?_jm{%lOZE zha@sL0~ErGakkpBv5XhKUaz*V0~LNTKpnm$_4#~WrO<^+ExgB8ls=!J1Z`g(KH+@iQ=lg}J!m|PDvt=o5{7UbhR(NnW-$GjHb1Jd(tFDoS z1GF-&DUsAE*HGyUuL&KU;T*u0PG@*UC=3)ywa2V8ydv~o*HtboGM(X7q0L_yKDjx3 zBWn)#ISl%)BqT~rA+DDn2`HRW81{Wr35No#?|-%sg&)m_ufxuV4v^ONX2q5ehaYWy z`vf?J5nEavB*YIG&=87?Nx~(C5#O*Txe&QcDnfa9`r=5_Z+8Zv(Rat^+6>O=wMzb& zz$%&S*Kkf}1&`sJ&I%sFIZIy!PvM+GtATS;(lCs3a@R%$=xGQ!diwKQ{E8=i5yU~3 zK!00q-j^}`8N*0TASFB`AdVfFhU~K4NBJ?$uTE|daZbb`wCQDAu2lEH0bjVX)gvvN zMvq}&7d8V?w4}WG>9!R+o*rBT6q#?3@Du1C0Zkx#%gQem*sDT{;I4z_(*9DX?84G}; zdqV*!hv!;}QrbA8lnYyeKMkDY0UX_e36OF*I>WpI@C&(QA;(LR{@k>Lh(sWTt{_P% zkO~y$_%2zB4|o|Ji2fE1<`2!$b>Q!)nsM%pk=x-pQt;zGxZdA>Z*gmVgCgMaNJ z537I*NSU}08DohUu0reX8c11q6rMPz$$lEfeLyP04do02OvUvADNTQX({AyQYhw+h z+Q4p+O}>IEO$>8qtbmkht#s0#n)Ah+RhG(wI7d|wfZe^?V>lkvi}4L45TD zk($4cr~3c1tqsD`=gco2gVF)tJO)Md=RY_Gr8vwNk3p66=PSpcavt)fV^E4;S;Fo4 z=jWR6hF?u`GaE*_z*lqJ%z%-u@zoqQ%WeG`A3bus`Mkwf`w%uu&NYcG4S%UybqUw_ z>g>>o%>WffQm%YKV;4VF{T`ygS7X>rHj!R3aphxNKk+f5jjxOkcv5P_NITQI>=>7p zYTzp)HuK9cVkDt;$%@V}QVU<1u$jkKE)JKUvgqo{b4?X|Wx!@U#zjAncYstY!ALcH zWx-|(q+l*!r1Icbfsrcs%71d00?tEqGH0yBOEj_0b*lKv#IGRmD+fX8;uzPZ@eRJR z`Rd9~CBr$cJA7rvb-IEZnseoQgF*)QmBL7-b6grF_ z^me(2BRz<(dO6Zl_^OX1J&Lca9O+qnW#>o_;;S)^G>@+?w7UDv`TlPE3mWx^|GMEL QuK)l507*qoM6N<$f}PL{Hvj+t literal 3822 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 diff --git a/public/images/pokemon/exp/shiny/821.png b/public/images/pokemon/exp/shiny/821.png index 5c03c17c7931d3b8a25b058ee21823d8d89bcb6f..6f6cf7f2991c80006cdef1790edb8af343aad46e 100644 GIT binary patch literal 8516 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
(Db}CwiNaC+#Wt-uM~4X;hg|muRVs`8 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 A;s5{u From 3a167610cf04b389e3a0326133e11c721a440789 Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:40:55 -0700 Subject: [PATCH 18/30] [Bug] Fix: Gravity tag removes flying type during damage calculation (#3670) * add `hasTag` to arena * fix flying type damage calculation for grounded states Before the grounded state would make e.g. electric moves no more very-effective. This is invalid Co-authored-by: Tristan D Gant * add tests for gravity v. ground v. electric * Update src/test/arena/arena_gravity.test.ts fix typo Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> * use `arena` instead of this.scene.arena * use `const arena = this.scene.arean` instead of destructoring * Apply suggestions from code review Co-authored-by: Mumble <171087428+frutescens@users.noreply.github.com> * fix es-lint --------- Co-authored-by: Tristan D Gant Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> Co-authored-by: Mumble <171087428+frutescens@users.noreply.github.com> --- src/field/arena.ts | 4 ++ src/field/pokemon.ts | 18 +++--- src/test/arena/arena_gravity.test.ts | 91 ++++++++++++++++++++++++---- 3 files changed, 93 insertions(+), 20 deletions(-) diff --git a/src/field/arena.ts b/src/field/arena.ts index eb3770d61d5..2ef6ce7dab3 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -584,6 +584,10 @@ export class Arena { return this.getTagOnSide(tagType, ArenaTagSide.BOTH); } + hasTag(tagType: ArenaTagType) : boolean { + return !!this.getTag(tagType); + } + getTagOnSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide): ArenaTag | undefined { return typeof(tagType) === "string" ? this.tags.find(t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 6a445a83b4e..b3a96a5f1fc 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -978,12 +978,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // this.scene potentially can be undefined for a fainted pokemon in doubles // use optional chaining to avoid runtime errors - if (forDefend && (this.getTag(GroundedTag) || this.scene?.arena.getTag(ArenaTagType.GRAVITY))) { - const flyingIndex = types.indexOf(Type.FLYING); - if (flyingIndex > -1) { - types.splice(flyingIndex, 1); - } - } if (!types.length) { // become UNKNOWN if no types are present types.push(Type.UNKNOWN); @@ -1272,6 +1266,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.isTerastallized() ? 2 : 1; } const types = this.getTypes(true, true); + const arena = this.scene.arena; + + // Handle flying v ground type immunity without removing flying type so effective types are still effective + // Related to https://github.com/pagefaultgames/pokerogue/issues/524 + if (moveType === Type.GROUND && (this.isGrounded() || arena.hasTag(ArenaTagType.GRAVITY))) { + const flyingIndex = types.indexOf(Type.FLYING); + if (flyingIndex > -1) { + types.splice(flyingIndex, 1); + } + } let multiplier = types.map(defType => { if (source) { @@ -1293,7 +1297,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier; // Handle strong winds lowering effectiveness of types super effective against pure flying - if (!ignoreStrongWinds && this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) { + if (!ignoreStrongWinds && arena.weather?.weatherType === WeatherType.STRONG_WINDS && !arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) { multiplier /= 2; if (!simulated) { this.scene.queueMessage(i18next.t("weather:strongWindsEffectMessage")); diff --git a/src/test/arena/arena_gravity.test.ts b/src/test/arena/arena_gravity.test.ts index 68c31258454..8fad4dde83d 100644 --- a/src/test/arena/arena_gravity.test.ts +++ b/src/test/arena/arena_gravity.test.ts @@ -1,14 +1,15 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import { SPLASH_ONLY } from "../utils/testUtils"; describe("Arena - Gravity", () => { let phaserGame: Phaser.Game; @@ -26,14 +27,17 @@ describe("Arena - Gravity", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); - game.override.moveset([Moves.TACKLE, Moves.GRAVITY, Moves.FISSURE]); - game.override.ability(Abilities.UNNERVE); - game.override.enemyAbility(Abilities.BALL_FETCH); - game.override.enemySpecies(Species.SHUCKLE); - game.override.enemyMoveset(new Array(4).fill(Moves.SPLASH)); + game.override + .battleType("single") + .moveset([Moves.TACKLE, Moves.GRAVITY, Moves.FISSURE]) + .ability(Abilities.UNNERVE) + .enemyAbility(Abilities.BALL_FETCH) + .enemySpecies(Species.SHUCKLE) + .enemyMoveset(SPLASH_ONLY); }); + // Reference: https://bulbapedia.bulbagarden.net/wiki/Gravity_(move) + it("non-OHKO move accuracy is multiplied by 1.67", async () => { const moveToCheck = allMoves[Moves.TACKLE]; @@ -77,4 +81,65 @@ describe("Arena - Gravity", () => { expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(30); }); + + describe("Against flying types", () => { + it("can be hit by ground-type moves now", async () => { + game.override + .startingLevel(5) + .enemyLevel(5) + .enemySpecies(Species.PIDGEOT) + .moveset([Moves.GRAVITY, Moves.EARTHQUAKE]); + + await game.startBattle([Species.PIKACHU]); + + const pidgeot = game.scene.getEnemyPokemon()!; + vi.spyOn(pidgeot, "getAttackTypeEffectiveness"); + + // Try earthquake on 1st turn (fails!); + game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(0); + + // Setup Gravity on 2nd turn + await game.toNextTurn(); + game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY)); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined(); + + // Use ground move on 3rd turn + await game.toNextTurn(); + game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(1); + }); + + it("keeps super-effective moves super-effective after using gravity", async () => { + game.override + .startingLevel(5) + .enemyLevel(5) + .enemySpecies(Species.PIDGEOT) + .moveset([Moves.GRAVITY, Moves.THUNDERBOLT]); + + await game.startBattle([Species.PIKACHU]); + + const pidgeot = game.scene.getEnemyPokemon()!; + vi.spyOn(pidgeot, "getAttackTypeEffectiveness"); + + // Setup Gravity on 1st turn + game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY)); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined(); + + // Use electric move on 2nd turn + await game.toNextTurn(); + game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); + await game.phaseInterceptor.to(TurnEndPhase); + + expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(2); + }); + }); }); From 0241a0a086f07f5bed54c4e3cf986d5ea43fd83c Mon Sep 17 00:00:00 2001 From: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Date: Thu, 22 Aug 2024 00:49:22 +0200 Subject: [PATCH 19/30] Translated missing DE files (#3650) --- src/locales/de/battler-tags.ts | 2 +- src/locales/de/starter-select-ui-handler.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/locales/de/battler-tags.ts b/src/locales/de/battler-tags.ts index 27d5f14c597..da0150836b0 100644 --- a/src/locales/de/battler-tags.ts +++ b/src/locales/de/battler-tags.ts @@ -69,5 +69,5 @@ export const battlerTags: SimpleTranslationEntries = { "saltCuredLapse": "{{pokemonNameWithAffix}} wurde durch {{moveName}} verletzt!", "cursedOnAdd": "{{pokemonNameWithAffix}} nimmt einen Teil seiner KP und legt einen Fluch auf {{pokemonName}}!", "cursedLapse": "{{pokemonNameWithAffix}} wurde durch den Fluch verletzt!", - "stockpilingOnAdd": "{{pokemonNameWithAffix}} stockpiled {{stockpiledCount}}!", + "stockpilingOnAdd": "{{pokemonNameWithAffix}} hortet {{stockpiledCount}}!", } as const; diff --git a/src/locales/de/starter-select-ui-handler.ts b/src/locales/de/starter-select-ui-handler.ts index 284152bbd33..c96af29a3c0 100644 --- a/src/locales/de/starter-select-ui-handler.ts +++ b/src/locales/de/starter-select-ui-handler.ts @@ -7,7 +7,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales"; */ export const starterSelectUiHandler: SimpleTranslationEntries = { "confirmStartTeam": "Mit diesen Pokémon losziehen?", - "confirmExit": "Do you want to exit?", + "confirmExit": "Willst du zurück?", "invalidParty": "Das ist kein gültiges Team!", "gen1": "I", "gen2": "II", @@ -28,8 +28,8 @@ export const starterSelectUiHandler: SimpleTranslationEntries = { "toggleIVs": "DVs anzeigen/verbergen", "manageMoves": "Attacken ändern", "manageNature": "Wesen ändern", - "addToFavorites": "Add to Favorites", - "removeFromFavorites": "Remove from Favorites", + "addToFavorites": "Zu Favoriten hinzufügen", + "removeFromFavorites": "Von Favoriten entfernen", "useCandies": "Bonbons verwenden", "selectNature": "Wähle das neue Wesen.", "selectMoveSwapOut": "Wähle die zu ersetzende Attacke.", From 1487d7f51cb359d741a5ad3b3d5184ef0506a8f3 Mon Sep 17 00:00:00 2001 From: cgyu7 <149552611+cgyu7@users.noreply.github.com> Date: Wed, 21 Aug 2024 18:33:33 -0500 Subject: [PATCH 20/30] [Move] Some more implementation for Tera Blast (#3469) * terablast updated * terablast update * terablast * fix trailing spaces * fixed misspelling in a comment * split tera blast dmg calc and type calc into different classes * terablastpowerattr update removed dupe code and added user terastallized check to conditional * Update src/data/move.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/data/move.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * removed spaces and added missing semicolon * added tsdocs for tera blast * deleted extra spaces * tsdoc update Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * tsdoc update Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * tsdoc update Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Add files via upload * Update src/test/moves/tera_blast.test.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/test/moves/tera_blast.test.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/test/moves/tera_blast.test.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/test/moves/tera_blast.test.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/test/moves/tera_blast.test.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * remove trailing spaces * The style police are here * Fixed conflict resolution issues --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> Co-authored-by: innerthunder --- src/data/move.ts | 51 +++++++++++++- src/test/moves/tera_blast.test.ts | 106 ++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 src/test/moves/tera_blast.test.ts diff --git a/src/data/move.ts b/src/data/move.ts index 51ba4058140..3a2903cb7c6 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -3784,6 +3784,30 @@ export class TeraBlastCategoryAttr extends VariableMoveCategoryAttr { } } +/** + * Increases the power of Tera Blast if the user is Terastallized into Stellar type + * @extends VariablePowerAttr + */ +export class TeraBlastPowerAttr extends VariablePowerAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + /** + * @param user {@linkcode Pokemon} Pokemon using the move + * @param target {@linkcode Pokemon} N/A + * @param move {@linkcode Move} {@linkcode Move.TERA_BLAST} + * @param {any[]} args N/A + * @returns true or false + */ + const power = args[0] as Utils.NumberHolder; + if (user.isTerastallized() && move.type === Type.STELLAR) { + //200 instead of 100 to reflect lack of stellar being 2x dmg on any type + power.value = 200; + return true; + } + + return false; + } +} + /** * Change the move category to status when used on the ally * @extends VariableMoveCategoryAttr @@ -4037,6 +4061,28 @@ export class HiddenPowerTypeAttr extends VariableMoveTypeAttr { } } +/** + * Changes the type of Tera Blast to match the user's tera type + * @extends VariableMoveTypeAttr + */ +export class TeraBlastTypeAttr extends VariableMoveTypeAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + /** + * @param user {@linkcode Pokemon} the user's type is checked + * @param target {@linkcode Pokemon} N/A + * @param move {@linkcode Move} {@linkcode Move.TeraBlastTypeAttr} + * @param {any[]} args N/A + * @returns true or false + */ + if (user.isTerastallized()) { + move.type = user.getTeraType(); //changes move type to tera type + return true; + } + + return false; + } +} + export class MatchUserTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const userTypes = user.getTypes(true); @@ -8791,7 +8837,10 @@ export function initMoves() { End Unused */ new AttackMove(Moves.TERA_BLAST, Type.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9) .attr(TeraBlastCategoryAttr) - .unimplemented(), + .attr(TeraBlastTypeAttr) + .attr(TeraBlastPowerAttr) + .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1, true, (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR)) + .partial(), new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9) .attr(ProtectAttr, BattlerTagType.SILK_TRAP), new AttackMove(Moves.AXE_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 90, 10, 30, 0, 9) diff --git a/src/test/moves/tera_blast.test.ts b/src/test/moves/tera_blast.test.ts new file mode 100644 index 00000000000..0bd2ad24e23 --- /dev/null +++ b/src/test/moves/tera_blast.test.ts @@ -0,0 +1,106 @@ +import { allMoves } from "#app/data/move"; +import GameManager from "#test/utils/gameManager"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { Abilities } from "#app/enums/abilities"; +import { SPLASH_ONLY } from "../utils/testUtils"; +import { Type } from "#app/data/type"; +import { getMovePosition } from "../utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { Stat } from "#app/enums/stat"; +import { BattlerIndex } from "#app/battle"; +import { HitResult } from "#app/field/pokemon"; + +describe("Moves - Tera Blast", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const moveToCheck = allMoves[Moves.TERA_BLAST]; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override + .battleType("single") + .disableCrits() + .starterSpecies(Species.FEEBAS) + .moveset([Moves.TERA_BLAST]) + .ability(Abilities.BALL_FETCH) + .startingHeldItems([{name: "TERA_SHARD", type: Type.FIRE}]) + .enemySpecies(Species.MAGIKARP) + .enemyMoveset(SPLASH_ONLY) + .enemyAbility(Abilities.BALL_FETCH) + .enemyLevel(20); + + vi.spyOn(moveToCheck, "calculateBattlePower"); + }); + + it("changes type to match user's tera type", async() => { + game.override + .enemySpecies(Species.FURRET) + .startingHeldItems([{name: "TERA_SHARD", type: Type.FIGHTING}]); + await game.startBattle(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + vi.spyOn(enemyPokemon, "apply"); + + game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST)); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.SUPER_EFFECTIVE); + }, 20000); + + it("increases power if user is Stellar tera type", async() => { + game.override.startingHeldItems([{name: "TERA_SHARD", type: Type.STELLAR}]); + const stellarTypeMultiplier = 2; + const stellarTypeDmgBonus = 20; + const basePower = moveToCheck.power; + + await game.startBattle(); + + game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST)); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(moveToCheck.calculateBattlePower).toHaveReturnedWith((basePower + stellarTypeDmgBonus) * stellarTypeMultiplier); + }, 20000); + + // Currently abilities are bugged and can't see when a move's category is changed + it.skip("uses the higher stat of the user's Atk and SpAtk for damage calculation", async() => { + game.override.enemyAbility(Abilities.TOXIC_DEBRIS); + await game.startBattle(); + + const playerPokemon = game.scene.getPlayerPokemon()!; + playerPokemon.stats[Stat.ATK] = 100; + playerPokemon.stats[Stat.SPATK] = 1; + + game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST)); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(game.scene.getEnemyPokemon()!.battleData.abilityRevealed).toBe(true); + }, 20000); + + it("causes stat drops if user is Stellar tera type", async() => { + game.override.startingHeldItems([{name: "TERA_SHARD", type: Type.STELLAR}]); + await game.startBattle(); + + const playerPokemon = game.scene.getPlayerPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST)); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(playerPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(-1); + expect(playerPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-1); + }, 20000); +}); From 051d38e0a2f6b61340a25e2f68b1333b5b412b11 Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Wed, 21 Aug 2024 18:29:21 -0700 Subject: [PATCH 21/30] [Enhancement] Add support for simulated calls to Abilities (#3529) * Adds simulated tag support to all abilities * Fix compiler errors in ability.ts Most things are still on fire :cry: * Fix errors left over after merge * Another pass through simulated ability call logic * Fix leftover errors from merge resolution * Another gh pages issue :pikamad: * Simulated call fixes based on Kev's feedback * RIP phases.ts --------- Co-authored-by: Xavion3 --- src/battle-scene.ts | 6 +- src/data/ability.ts | 979 ++++++++++++-------- src/data/arena-tag.ts | 2 +- src/data/berry.ts | 14 +- src/data/move.ts | 26 +- src/data/terrain.ts | 2 +- src/field/pokemon.ts | 80 +- src/phases/attempt-run-phase.ts | 2 +- src/phases/command-phase.ts | 2 +- src/phases/encounter-phase.ts | 2 +- src/phases/enemy-command-phase.ts | 2 +- src/phases/move-effect-phase.ts | 2 +- src/phases/move-phase.ts | 2 +- src/phases/post-turn-status-effect-phase.ts | 2 +- src/phases/stat-change-phase.ts | 4 +- src/phases/turn-start-phase.ts | 8 +- src/test/abilities/sand_veil.test.ts | 2 +- src/test/abilities/serene_grace.test.ts | 4 +- src/test/abilities/sheer_force.test.ts | 16 +- src/test/abilities/shield_dust.test.ts | 4 +- 20 files changed, 677 insertions(+), 484 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index b72e79c866d..aad0d355e38 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1100,7 +1100,7 @@ export default class BattleScene extends SceneBase { } else if (trainerConfigs[trainerType].hasDouble) { const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8); this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); - playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance)); + playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance)); doubleTrainer = !Utils.randSeedInt(doubleChance.value); // Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance if (trainerConfigs[trainerType].trainerTypeDouble && ![ TrainerType.TATE, TrainerType.LIZA ].includes(trainerType)) { @@ -1116,7 +1116,7 @@ export default class BattleScene extends SceneBase { if (newBattleType === BattleType.WILD && !this.gameMode.isWaveFinal(newWaveIndex)) { const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8); this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); - playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance)); + playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance)); newDouble = !Utils.randSeedInt(doubleChance.value); } else if (newBattleType === BattleType.TRAINER) { newDouble = newTrainer?.variant === TrainerVariant.DOUBLE; @@ -2136,7 +2136,7 @@ export default class BattleScene extends SceneBase { pushMovePhase(movePhase: MovePhase, priorityOverride?: integer): void { const movePriority = new Utils.IntegerHolder(priorityOverride !== undefined ? priorityOverride : movePhase.move.getMove().priority); - applyAbAttrs(ChangeMovePriorityAbAttr, movePhase.pokemon, null, movePhase.move.getMove(), movePriority); + applyAbAttrs(ChangeMovePriorityAbAttr, movePhase.pokemon, null, false, movePhase.move.getMove(), movePriority); const lowerPriorityPhase = this.phaseQueue.find(p => p instanceof MovePhase && p.move.getMove().priority < movePriority.value); if (lowerPriorityPhase) { this.phaseQueue.splice(this.phaseQueue.indexOf(lowerPriorityPhase), 0, movePhase); diff --git a/src/data/ability.ts b/src/data/ability.ts index 8e020849a17..27a11eb0b9a 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -135,7 +135,7 @@ export abstract class AbAttr { this.showAbility = showAbility; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { return false; } @@ -154,7 +154,7 @@ export abstract class AbAttr { } export class BlockRecoilDamageAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; @@ -170,7 +170,7 @@ export class DoubleBattleChanceAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const doubleChance = (args[0] as Utils.IntegerHolder); doubleChance.value = Math.max(doubleChance.value / 2, 1); return true; @@ -178,7 +178,7 @@ export class DoubleBattleChanceAbAttr extends AbAttr { } export class PostBattleInitAbAttr extends AbAttr { - applyPostBattleInit(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { return false; } } @@ -192,9 +192,9 @@ export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr { this.formFunc = formFunc; } - applyPostBattleInit(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const formIndex = this.formFunc(pokemon); - if (formIndex !== pokemon.formIndex) { + if (formIndex !== pokemon.formIndex && !simulated) { return pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); } @@ -217,22 +217,24 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr { this.selfTarget = !!selfTarget; } - applyPostBattleInit(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const statChangePhases: StatChangePhase[] = []; - if (this.selfTarget) { - statChangePhases.push(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); - } else { - for (const opponent of pokemon.getOpponents()) { - statChangePhases.push(new StatChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.levels)); + if (!simulated) { + if (this.selfTarget) { + statChangePhases.push(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + } else { + for (const opponent of pokemon.getOpponents()) { + statChangePhases.push(new StatChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.levels)); + } } - } - for (const statChangePhase of statChangePhases) { - if (!this.selfTarget && !statChangePhase.getPokemon()?.summonData) { - pokemon.scene.pushPhase(statChangePhase); - } else { // TODO: This causes the ability bar to be shown at the wrong time - pokemon.scene.unshiftPhase(statChangePhase); + for (const statChangePhase of statChangePhases) { + if (!this.selfTarget && !statChangePhase.getPokemon()?.summonData) { + pokemon.scene.pushPhase(statChangePhase); + } else { // TODO: This causes the ability bar to be shown at the wrong time + pokemon.scene.unshiftPhase(statChangePhase); + } } } @@ -243,17 +245,17 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr { type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move) => boolean; export class PreDefendAbAttr extends AbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { return false; } } export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (pokemon.isFullHp() && - pokemon.getMaxHp() > 1 && //Checks if pokemon has wonder_guard (which forces 1hp) - (args[0] as Utils.NumberHolder).value >= pokemon.hp) { //Damage >= hp - return pokemon.addTag(BattlerTagType.STURDY, 1); + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (pokemon.isFullHp() + && pokemon.getMaxHp() > 1 //Checks if pokemon has wonder_guard (which forces 1hp) + && (args[0] as Utils.NumberHolder).value >= pokemon.hp) { //Damage >= hp + return simulated || pokemon.addTag(BattlerTagType.STURDY, 1); } return false; @@ -261,7 +263,7 @@ export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { } export class BlockItemTheftAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; @@ -276,7 +278,7 @@ export class BlockItemTheftAbAttr extends AbAttr { } export class StabBoostAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if ((args[0] as Utils.NumberHolder).value > 1) { (args[0] as Utils.NumberHolder).value += 0.5; return true; @@ -297,7 +299,7 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr { this.damageMultiplier = damageMultiplier; } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { (args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * this.damageMultiplier); @@ -341,7 +343,7 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { * @param args [0] {@linkcode Utils.NumberHolder} gets set to 0 if move is immuned by an ability. * @param args [1] - Whether the move is simulated. */ - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { // Field moves should ignore immunity if ([ MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE ].includes(move.moveTarget)) { return false; @@ -368,9 +370,9 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr { * Type immunity abilities that do not give additional benefits (HP recovery, stat boosts, etc) are not immune to status moves of the type * Example: Levitate */ - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (move.category !== MoveCategory.STATUS) { - return super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args); + return super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); } return false; } @@ -381,17 +383,14 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { super(immuneType); } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args); + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); if (ret) { - if (!pokemon.isFullHp()) { - const simulated = args.length > 1 && args[1]; - if (!simulated) { - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 4), 1), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); - } + if (!pokemon.isFullHp() && !simulated) { + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), + Math.max(Math.floor(pokemon.getMaxHp() / 4), 1), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); } return true; } @@ -411,12 +410,11 @@ class TypeImmunityStatChangeAbAttr extends TypeImmunityAbAttr { this.levels = levels; } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args); + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); if (ret) { cancelled.value = true; - const simulated = args.length > 1 && args[1]; if (!simulated) { pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); } @@ -437,12 +435,11 @@ class TypeImmunityAddBattlerTagAbAttr extends TypeImmunityAbAttr { this.turnCount = turnCount; } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args); + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); if (ret) { cancelled.value = true; - const simulated = args.length > 1 && args[1]; if (!simulated) { pokemon.addTag(this.tagType, this.turnCount, undefined, pokemon.id); } @@ -457,7 +454,7 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { super(null, condition); } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (move instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.type, attacker) < 2) { cancelled.value = true; (args[0] as Utils.NumberHolder).value = 0; @@ -476,7 +473,7 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { } export class PostDefendAbAttr extends AbAttr { - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { return false; } } @@ -500,12 +497,16 @@ export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr { * @param {any[]} args - n/a * @returns Whether the effects of the ability are applied. */ - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { const battlerTag = pokemon.getTag(GulpMissileTag); if (!battlerTag || move.category === MoveCategory.STATUS || pokemon.getTag(SemiInvulnerableTag)) { return false; } + if (simulated) { + return true; + } + const cancelled = new Utils.BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled); @@ -525,12 +526,12 @@ export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr { } export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { const attackPriority = new Utils.IntegerHolder(move.priority); - applyMoveAttrs(IncrementMovePriorityAttr,attacker,null,move,attackPriority); - applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, move, attackPriority); + applyMoveAttrs(IncrementMovePriorityAttr, attacker, null, move, attackPriority); + applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, simulated, move, attackPriority); - if (move.moveTarget===MoveTarget.USER || move.moveTarget===MoveTarget.NEAR_ALLY) { + if (move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) { return false; } @@ -544,7 +545,7 @@ export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { } export class PostStatChangeAbAttr extends AbAttr { - applyPostStatChange(pokemon: Pokemon, statsChanged: BattleStat[], levelChanged: integer, selfTarget: boolean, args: any[]): boolean | Promise { + applyPostStatChange(pokemon: Pokemon, simulated: boolean, statsChanged: BattleStat[], levelChanged: integer, selfTarget: boolean, args: any[]): boolean | Promise { return false; } } @@ -558,7 +559,7 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr { this.immuneCondition = immuneCondition; } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.immuneCondition(pokemon, attacker, move)) { cancelled.value = true; return true; @@ -579,7 +580,7 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr { * @extends PreDefendAbAttr */ export class WonderSkinAbAttr extends PreDefendAbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { const moveAccuracy = args[0] as Utils.NumberHolder; if (move.category === MoveCategory.STATUS && moveAccuracy.value >= 50) { moveAccuracy.value = 50; @@ -600,13 +601,10 @@ export class MoveImmunityStatChangeAbAttr extends MoveImmunityAbAttr { this.levels = levels; } - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args); - if (ret) { - const simulated = args.length > 1 && args[1]; - if (!simulated) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); - } + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const ret = super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + if (ret && !simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); } return ret; @@ -630,9 +628,11 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr { * @args N/A * @returns true if healing should be reversed on a healing move, false otherwise. */ - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (move.hasAttr(HitHealAttr)) { - pokemon.scene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); + if (!simulated) { + pokemon.scene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); + } return true; } return false; @@ -656,8 +656,12 @@ export class PostDefendStatChangeAbAttr extends PostDefendAbAttr { this.allOthers = allOthers; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { + if (simulated) { + return true; + } + if (this.allOthers) { const otherPokemon = pokemon.getAlly() ? pokemon.getOpponents().concat([ pokemon.getAlly() ]) : pokemon.getOpponents(); for (const other of otherPokemon) { @@ -690,13 +694,15 @@ export class PostDefendHpGatedStatChangeAbAttr extends PostDefendAbAttr { this.selfTarget = selfTarget; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { const hpGateFlat: integer = Math.ceil(pokemon.getMaxHp() * this.hpGate); const lastAttackReceived = pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1]; const damageReceived = lastAttackReceived?.damage || 0; if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat)) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.levels)); + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.levels)); + } return true; } @@ -715,11 +721,13 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { const tag = pokemon.scene.arena.getTag(this.tagType) as ArenaTrapTag; if (!pokemon.scene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) { - pokemon.scene.arena.addTag(this.tagType, 0, undefined, pokemon.id, pokemon.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER); + if (!simulated) { + pokemon.scene.arena.addTag(this.tagType, 0, undefined, pokemon.id, pokemon.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER); + } return true; } } @@ -737,9 +745,9 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { - if (!pokemon.getTag(this.tagType)) { + if (!pokemon.getTag(this.tagType) && !simulated) { pokemon.addTag(this.tagType, undefined, undefined, pokemon.id); pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name })); } @@ -750,8 +758,11 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { } export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (hitResult < HitResult.NO_EFFECT) { + if (simulated) { + return true; + } const type = move.type; const pokemonTypes = pokemon.getTypes(true); if (pokemonTypes.length !== 1 || pokemonTypes[0] !== type) { @@ -781,9 +792,13 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { this.terrainType = terrainType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (hitResult < HitResult.NO_EFFECT) { - return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + if (simulated) { + return pokemon.scene.arena.terrain?.terrainType !== (this.terrainType || undefined); + } else { + return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + } } return false; @@ -801,10 +816,14 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { this.effects = effects; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; - return attacker.trySetStatus(effect, true, pokemon); + if (simulated) { + return attacker.canSetStatus(effect, true, false, pokemon); + } else { + return attacker.trySetStatus(effect, true, pokemon); + } } return false; @@ -816,11 +835,11 @@ export class EffectSporeAbAttr extends PostDefendContactApplyStatusEffectAbAttr super(10, StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP); } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (attacker.hasAbility(Abilities.OVERCOAT) || attacker.isOfType(Type.GRASS)) { return false; } - return super.applyPostDefend(pokemon, passive, attacker, move, hitResult, args); + return super.applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args); } } @@ -837,9 +856,13 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { this.turnCount = turnCount; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance) { - return attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id); + if (simulated) { + return attacker.canAddTag(this.tagType); + } else { + return attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id); + } } return false; @@ -857,8 +880,10 @@ export class PostDefendCritStatChangeAbAttr extends PostDefendAbAttr { this.levels = levels; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels)); + } return true; } @@ -877,8 +902,8 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { this.damageRatio = damageRatio; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + if (!simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); attacker.turnData.damageTaken += Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)); return true; @@ -910,13 +935,15 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { this.turns = turns; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { if (pokemon.getTag(BattlerTagType.PERISH_SONG) || attacker.getTag(BattlerTagType.PERISH_SONG)) { return false; } else { - attacker.addTag(BattlerTagType.PERISH_SONG, this.turns); - pokemon.addTag(BattlerTagType.PERISH_SONG, this.turns); + if (!simulated) { + attacker.addTag(BattlerTagType.PERISH_SONG, this.turns); + pokemon.addTag(BattlerTagType.PERISH_SONG, this.turns); + } return true; } } @@ -939,11 +966,14 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { this.condition = condition ?? null; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (this.condition !== null && !this.condition(pokemon, attacker, move)) { return false; } if (!pokemon.scene.arena.weather?.isImmutable()) { + if (simulated) { + return pokemon.scene.arena.weather?.weatherType !== this.weatherType; + } return pokemon.scene.arena.trySetWeather(this.weatherType, true); } @@ -956,11 +986,13 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { super(); } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr)) { - const tempAbilityId = attacker.getAbility().id; - attacker.summonData.ability = pokemon.getAbility().id; - pokemon.summonData.ability = tempAbilityId; + if (!simulated) { + const tempAbilityId = attacker.getAbility().id; + attacker.summonData.ability = pokemon.getAbility().id; + pokemon.summonData.ability = tempAbilityId; + } return true; } @@ -980,9 +1012,11 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { this.ability = ability; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr)) { - attacker.summonData.ability = this.ability; + if (!simulated) { + attacker.summonData.ability = this.ability; + } return true; } @@ -1009,9 +1043,13 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { this.chance = chance; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (!attacker.summonData.disabledMove) { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !attacker.isMax()) { + if (simulated) { + return true; + } + this.attacker = attacker; this.move = move; @@ -1044,9 +1082,11 @@ export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr { this.levels = levels; } - applyPostStatChange(pokemon: Pokemon, statsChanged: BattleStat[], levelsChanged: integer, selfTarget: boolean, args: any[]): boolean { + applyPostStatChange(pokemon: Pokemon, simulated: boolean, statsChanged: BattleStat[], levelsChanged: integer, selfTarget: boolean, args: any[]): boolean { if (this.condition(pokemon, statsChanged, levelsChanged) && !selfTarget) { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (pokemon).getBattlerIndex(), true, this.statsToChange, this.levels)); + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (pokemon).getBattlerIndex(), true, this.statsToChange, this.levels)); + } return true; } @@ -1055,7 +1095,7 @@ export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr { } export class PreAttackAbAttr extends AbAttr { - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean | Promise { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean | Promise { return false; } } @@ -1076,7 +1116,7 @@ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { * @param args [0]: {@linkcode Utils.NumberHolder} Move additional effect chance. Has to be higher than or equal to 0. * [1]: {@linkcode Moves } Move used by the ability user. */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { // Disable showAbility during getTargetBenefitScore this.showAbility = args[4]; if ((args[0] as Utils.NumberHolder).value <= 0 || (args[1] as Move).id === Moves.ORDER_UP) { @@ -1099,7 +1139,7 @@ export class IgnoreMoveEffectsAbAttr extends PreDefendAbAttr { /** * @param args [0]: {@linkcode Utils.NumberHolder} Move additional effect chance. */ - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if ((args[0] as Utils.NumberHolder).value <= 0) { return false; @@ -1112,14 +1152,14 @@ export class IgnoreMoveEffectsAbAttr extends PreDefendAbAttr { } export class VariableMovePowerAbAttr extends PreAttackAbAttr { - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { //const power = args[0] as Utils.NumberHolder; return false; } } export class FieldPreventExplosiveMovesAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { cancelled.value = true; return true; } @@ -1156,7 +1196,7 @@ export class FieldMultiplyBattleStatAbAttr extends AbAttr { * @param args {any[]} unused * @returns true if this changed the checked stat, false otherwise. */ - applyFieldBattleStat(pokemon: Pokemon, passive: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, args: any[]): boolean { + applyFieldBattleStat(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, args: any[]): boolean { if (!this.canStack && hasApplied.value) { return false; } @@ -1180,7 +1220,7 @@ export class MoveTypeChangeAttr extends PreAttackAbAttr { super(true); } - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { if (this.condition && this.condition(pokemon, defender, move)) { move.type = this.newType; if (args[0] && args[0] instanceof Utils.NumberHolder) { @@ -1201,7 +1241,7 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { super(true); } - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { if ( !pokemon.isTerastallized() && move.id !== Moves.STRUGGLE && @@ -1229,9 +1269,11 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { } if (pokemon.getTypes().some((t) => t !== moveCopy.type)) { - this.moveType = moveCopy.type; - pokemon.summonData.types = [moveCopy.type]; - pokemon.updateInfo(); + if (!simulated) { + this.moveType = moveCopy.type; + pokemon.summonData.types = [moveCopy.type]; + pokemon.updateInfo(); + } return true; } @@ -1307,7 +1349,7 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr { * @param {Utils.NumberHolder} args\[2\] the damage multiplier for the current strike * @returns */ - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { const numTargets = args[0] as integer; const hitCount = args[1] as Utils.IntegerHolder; const multiplier = args[2] as Utils.NumberHolder; @@ -1352,7 +1394,7 @@ export class DamageBoostAbAttr extends PreAttackAbAttr { * @param args Utils.NumberHolder as damage * @returns true if the function succeeds */ - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { if (this.condition(pokemon, defender, move)) { const power = args[0] as Utils.NumberHolder; power.value = Math.floor(power.value * this.damageMultiplier); @@ -1373,7 +1415,7 @@ export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { this.powerMultiplier = powerMultiplier; } - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { if (this.condition(pokemon, defender, move)) { (args[0] as Utils.NumberHolder).value *= this.powerMultiplier; @@ -1420,7 +1462,7 @@ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr { /** * @override */ - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move, args: any[]): boolean { const multiplier = this.mult(pokemon, defender, move); if (multiplier !== 1) { (args[0] as Utils.NumberHolder).value *= multiplier; @@ -1449,7 +1491,7 @@ export class FieldMovePowerBoostAbAttr extends AbAttr { this.powerMultiplier = powerMultiplier; } - applyPreAttack(pokemon: Pokemon | null, passive: boolean | null, defender: Pokemon | null, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon | null, passive: boolean | null, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { if (this.condition(pokemon, defender, move)) { (args[0] as Utils.NumberHolder).value *= this.powerMultiplier; @@ -1513,7 +1555,7 @@ export class BattleStatMultiplierAbAttr extends AbAttr { this.condition = condition ?? null; } - applyBattleStat(pokemon: Pokemon, passive: boolean, battleStat: BattleStat, statValue: Utils.NumberHolder, args: any[]): boolean | Promise { + applyBattleStat(pokemon: Pokemon, passive: boolean, simulated: boolean, battleStat: BattleStat, statValue: Utils.NumberHolder, args: any[]): boolean | Promise { const move = (args[0] as Move); if (battleStat === this.battleStat && (!this.condition || this.condition(pokemon, null, move))) { statValue.value *= this.multiplier; @@ -1539,11 +1581,11 @@ export class PostAttackAbAttr extends AbAttr { * applying the effect of any inherited class. This can be changed by providing a different {@link attackCondition} to the constructor. See {@link ConfusionOnStatusEffectAbAttr} * for an example of an effect that does not require a damaging move. */ - applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { + applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { // When attackRequired is true, we require the move to be an attack move and to deal damage before checking secondary requirements. // If attackRequired is false, we always defer to the secondary requirements. if (this.attackCondition(pokemon, defender, move)) { - return this.applyPostAttackAfterMoveTypeCheck(pokemon, passive, defender, move, hitResult, args); + return this.applyPostAttackAfterMoveTypeCheck(pokemon, passive, simulated, defender, move, hitResult, args); } else { return false; } @@ -1552,7 +1594,7 @@ export class PostAttackAbAttr extends AbAttr { /** * This method is only called after {@link applyPostAttack} has already been applied. Use this for handling checks specific to the ability in question. */ - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { return false; } } @@ -1566,9 +1608,9 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { this.stealCondition = stealCondition ?? null; } - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { return new Promise(resolve => { - if (hitResult < HitResult.NO_EFFECT && (!this.stealCondition || this.stealCondition(pokemon, defender, move))) { + if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.stealCondition || this.stealCondition(pokemon, defender, move))) { const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferrable); if (heldItems.length) { const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; @@ -1581,7 +1623,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { return; } } - resolve(false); + resolve(simulated); }); } @@ -1604,14 +1646,14 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { this.effects = effects; } - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { /**Status inflicted by abilities post attacking are also considered additional effects.*/ - if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) { + if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !simulated && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; return attacker.trySetStatus(effect, true, pokemon); } - return false; + return simulated; } } @@ -1635,11 +1677,11 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { this.effects = effects; } - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { /**Battler tags inflicted by abilities post attacking are also considered additional effects.*/ if (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; - return attacker.addTag(effect); + return simulated || attacker.addTag(effect); } return false; @@ -1655,9 +1697,9 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { this.condition = condition ?? null; } - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { + applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { return new Promise(resolve => { - if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { + if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferrable); if (heldItems.length) { const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; @@ -1670,7 +1712,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { return; } } - resolve(false); + resolve(simulated); }); } @@ -1681,7 +1723,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { } export class PostVictoryAbAttr extends AbAttr { - applyPostVictory(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { return false; } } @@ -1697,12 +1739,13 @@ class PostVictoryStatChangeAbAttr extends PostVictoryAbAttr { this.levels = levels; } - applyPostVictory(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.levels)); - + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.levels)); + } return true; } } @@ -1716,10 +1759,12 @@ export class PostVictoryFormChangeAbAttr extends PostVictoryAbAttr { this.formFunc = formFunc; } - applyPostVictory(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { const formIndex = this.formFunc(pokemon); if (formIndex !== pokemon.formIndex) { - pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + if (!simulated) { + pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + } return true; } @@ -1728,7 +1773,7 @@ export class PostVictoryFormChangeAbAttr extends PostVictoryAbAttr { } export class PostKnockOutAbAttr extends AbAttr { - applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { + applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { return false; } } @@ -1744,12 +1789,13 @@ export class PostKnockOutStatChangeAbAttr extends PostKnockOutAbAttr { this.levels = levels; } - applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { + applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.levels)); - + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], this.levels)); + } return true; } } @@ -1759,10 +1805,12 @@ export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr { super(); } - applyPostKnockOut(pokemon: Pokemon, passive: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { + applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise { if (pokemon.isPlayer() === knockedOut.isPlayer() && !knockedOut.getAbility().hasAttr(UncopiableAbilityAbAttr)) { - pokemon.summonData.ability = knockedOut.getAbility().id; - pokemon.scene.queueMessage(i18next.t("abilityTriggers:copyFaintedAllyAbility", { pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), abilityName: allAbilities[knockedOut.getAbility().id].name })); + if (!simulated) { + pokemon.summonData.ability = knockedOut.getAbility().id; + pokemon.scene.queueMessage(i18next.t("abilityTriggers:copyFaintedAllyAbility", { pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), abilityName: allAbilities[knockedOut.getAbility().id].name })); + } return true; } @@ -1775,7 +1823,7 @@ export class IgnoreOpponentStatChangesAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]) { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]) { (args[0] as Utils.IntegerHolder).value = 0; return true; @@ -1798,7 +1846,7 @@ export class IgnoreOpponentEvasionAbAttr extends AbAttr { * @param args [0] {@linkcode Utils.IntegerHolder} of BattleStat.EVA * @returns if evasion level was successfully considered as 0 */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]) { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]) { (args[0] as Utils.IntegerHolder).value = 0; return true; } @@ -1809,7 +1857,7 @@ export class IntimidateImmunityAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; } @@ -1834,8 +1882,10 @@ export class PostIntimidateStatChangeAbAttr extends AbAttr { this.overwrites = !!overwrites; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - pokemon.scene.pushPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, this.levels)); + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (!simulated) { + pokemon.scene.pushPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, this.levels)); + } cancelled.value = this.overwrites; return true; } @@ -1853,7 +1903,7 @@ export class PostSummonAbAttr extends AbAttr { * @param args Set of unique arguments needed by this attribute * @returns true if application of the ability succeeds */ - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { return false; } } @@ -1872,9 +1922,11 @@ export class PostSummonRemoveArenaTagAbAttr extends PostSummonAbAttr { this.arenaTags = arenaTags; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { - for (const arenaTag of this.arenaTags) { - pokemon.scene.arena.removeTag(arenaTag); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { + if (!simulated) { + for (const arenaTag of this.arenaTags) { + pokemon.scene.arena.removeTag(arenaTag); + } } return true; } @@ -1889,8 +1941,10 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { this.messageFunc = messageFunc; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - pokemon.scene.queueMessage(this.messageFunc(pokemon)); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (!simulated) { + pokemon.scene.queueMessage(this.messageFunc(pokemon)); + } return true; } @@ -1906,8 +1960,10 @@ export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr { this.message = message; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - pokemon.scene.queueMessage(this.message); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (!simulated) { + pokemon.scene.queueMessage(this.message); + } return true; } @@ -1924,8 +1980,12 @@ export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr { this.turnCount = turnCount; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - return pokemon.addTag(this.tagType, this.turnCount); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (simulated) { + return pokemon.canAddTag(this.tagType); + } else { + return pokemon.addTag(this.tagType, this.turnCount); + } } } @@ -1946,7 +2006,11 @@ export class PostSummonStatChangeAbAttr extends PostSummonAbAttr { this.intimidate = !!intimidate; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (simulated) { + return true; + } + queueShowAbility(pokemon, passive); // TODO: Better solution than manually showing the ability here if (this.selfTarget) { // we unshift the StatChangePhase to put it right after the showAbility and not at the end of the @@ -1957,8 +2021,8 @@ export class PostSummonStatChangeAbAttr extends PostSummonAbAttr { for (const opponent of pokemon.getOpponents()) { const cancelled = new Utils.BooleanHolder(false); if (this.intimidate) { - applyAbAttrs(IntimidateImmunityAbAttr, opponent, cancelled); - applyAbAttrs(PostIntimidateStatChangeAbAttr, opponent, cancelled); + applyAbAttrs(IntimidateImmunityAbAttr, opponent, cancelled, simulated); + applyAbAttrs(PostIntimidateStatChangeAbAttr, opponent, cancelled, simulated); } if (!cancelled.value) { const statChangePhase = new StatChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.levels); @@ -1980,11 +2044,14 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { this.showAnim = showAnim; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const target = pokemon.getAlly(); if (target?.isActive(true)) { - target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / this.healRatio), 1), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); + if (!simulated) { + target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), + Math.max(Math.floor(pokemon.getMaxHp() / this.healRatio), 1), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); + } + return true; } @@ -2005,14 +2072,16 @@ export class PostSummonClearAllyStatsAbAttr extends PostSummonAbAttr { super(); } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const target = pokemon.getAlly(); if (target?.isActive(true)) { - for (let s = 0; s < target.summonData.battleStats.length; s++) { - target.summonData.battleStats[s] = 0; - } + if (!simulated) { + for (let s = 0; s < target.summonData.battleStats.length; s++) { + target.summonData.battleStats[s] = 0; + } - target.scene.queueMessage(i18next.t("abilityTriggers:postSummonClearAllyStats", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); + target.scene.queueMessage(i18next.t("abilityTriggers:postSummonClearAllyStats", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); + } return true; } @@ -2043,7 +2112,7 @@ export class DownloadAbAttr extends PostSummonAbAttr { * @param {any[]} args N/A * @returns Returns true if ability is used successful, false if not. */ - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { this.enemyDef = 0; this.enemySpDef = 0; this.enemyCountTally = 0; @@ -2063,7 +2132,9 @@ export class DownloadAbAttr extends PostSummonAbAttr { } if (this.enemyDef > 0 && this.enemySpDef > 0) { // only activate if there's actually an enemy to download from - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, 1)); + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, this.stats, 1)); + } return true; } @@ -2080,11 +2151,15 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr { this.weatherType = weatherType; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { if ((this.weatherType === WeatherType.HEAVY_RAIN || this.weatherType === WeatherType.HARSH_SUN || this.weatherType === WeatherType.STRONG_WINDS) || !pokemon.scene.arena.weather?.isImmutable()) { - return pokemon.scene.arena.trySetWeather(this.weatherType, true); + if (simulated) { + return pokemon.scene.arena.weather?.weatherType !== this.weatherType; + } else { + return pokemon.scene.arena.trySetWeather(this.weatherType, true); + } } return false; @@ -2100,8 +2175,12 @@ export class PostSummonTerrainChangeAbAttr extends PostSummonAbAttr { this.terrainType = terrainType; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (simulated) { + return pokemon.scene.arena.terrain?.terrainType !== this.terrainType; + } else { + return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + } } } @@ -2114,10 +2193,10 @@ export class PostSummonFormChangeAbAttr extends PostSummonAbAttr { this.formFunc = formFunc; } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const formIndex = this.formFunc(pokemon); if (formIndex !== pokemon.formIndex) { - return pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + return simulated || pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); } return false; @@ -2129,7 +2208,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { private target: Pokemon; private targetAbilityName: string; - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const targets = pokemon.getOpponents(); if (!targets.length) { return false; @@ -2150,11 +2229,13 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { return false; } - this.target = target!; - this.targetAbilityName = allAbilities[target!.getAbility().id].name; - pokemon.summonData.ability = target!.getAbility().id; - setAbilityRevealed(target!); - pokemon.updateInfo(); + if (!simulated) { + this.target = target!; + this.targetAbilityName = allAbilities[target!.getAbility().id].name; + pokemon.summonData.ability = target!.getAbility().id; + setAbilityRevealed(target!); + pokemon.updateInfo(); + } return true; } @@ -2191,7 +2272,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt * @param args - n/a * @returns A boolean or a promise that resolves to a boolean indicating the result of the ability application. */ - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { const party = pokemon instanceof PlayerPokemon ? pokemon.scene.getPlayerField() : pokemon.scene.getEnemyField(); const allowedParty = party.filter(p => p.isAllowedInBattle()); @@ -2199,14 +2280,15 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt return false; } - for (const pokemon of allowedParty) { - if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { - pokemon.scene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); - pokemon.resetStatus(false); - pokemon.updateInfo(); + if (!simulated) { + for (const pokemon of allowedParty) { + if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { + pokemon.scene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); + pokemon.resetStatus(false); + pokemon.updateInfo(); + } } } - return true; } } @@ -2214,7 +2296,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt /** Attempt to copy the stat changes on an ally pokemon */ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { if (!pokemon.scene.currentBattle.double) { return false; } @@ -2224,8 +2306,10 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { return false; } - pokemon.summonData.battleStats = ally.summonData.battleStats; - pokemon.updateInfo(); + if (!simulated) { + pokemon.summonData.battleStats = ally.summonData.battleStats; + pokemon.updateInfo(); + } return true; } @@ -2243,10 +2327,10 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { super(true); } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const targets = pokemon.getOpponents(); - if (!targets.length) { - return false; + if (simulated || !targets.length) { + return simulated; } let target: Pokemon; @@ -2282,16 +2366,19 @@ export class PreSwitchOutAbAttr extends AbAttr { super(true); } - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { return false; } } export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr { - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { if (pokemon.status) { - pokemon.resetStatus(); - pokemon.updateInfo(); + if (!simulated) { + pokemon.resetStatus(); + pokemon.updateInfo(); + } + return true; } @@ -2310,7 +2397,7 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { * @param args N/A * @returns {boolean} Returns true if the weather clears, otherwise false. */ - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { const weatherType = pokemon.scene.arena.weather?.weatherType; let turnOffWeather = false; @@ -2336,6 +2423,10 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { break; } + if (simulated) { + return turnOffWeather; + } + if (turnOffWeather) { pokemon.scene.arena.trySetWeather(WeatherType.NONE, false); return true; @@ -2346,11 +2437,14 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { } export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { if (!pokemon.isFullHp()) { - const healAmount = Math.floor(pokemon.getMaxHp() * 0.33); - pokemon.heal(healAmount); - pokemon.updateInfo(); + if (!simulated) { + const healAmount = Math.floor(pokemon.getMaxHp() * 0.33); + pokemon.heal(healAmount); + pokemon.updateInfo(); + } + return true; } @@ -2379,10 +2473,12 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { * @param args N/A * @returns true if the form change was successful */ - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { const formIndex = this.formFunc(pokemon); if (formIndex !== pokemon.formIndex) { - pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + if (!simulated) { + pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + } return true; } @@ -2392,7 +2488,7 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { } export class PreStatChangeAbAttr extends AbAttr { - applyPreStatChange(pokemon: Pokemon | null, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreStatChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -2406,7 +2502,7 @@ export class ProtectStatAbAttr extends PreStatChangeAbAttr { this.protectedStat = protectedStat; } - applyPreStatChange(pokemon: Pokemon, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreStatChange(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (Utils.isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) { cancelled.value = true; return true; @@ -2450,16 +2546,20 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { * @param args [0] {@linkcode StatusEffect} applied by move * @returns true if defender is confused */ - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (this.effects.indexOf(args[0]) > -1 && !defender.isFainted()) { - return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3,2), move.id, defender.id); + if (simulated) { + return defender.canAddTag(BattlerTagType.CONFUSED); + } else { + return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3,2), move.id, defender.id); + } } return false; } } export class PreSetStatusAbAttr extends AbAttr { - applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -2489,7 +2589,7 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { * @param args - n/a * @returns A boolean indicating the result of the status application. */ - applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.immuneEffects.length < 1 || this.immuneEffects.includes(effect)) { cancelled.value = true; return true; @@ -2525,7 +2625,7 @@ export class StatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr export class UserFieldStatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr { } export class PreApplyBattlerTagAbAttr extends AbAttr { - applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -2543,10 +2643,12 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { this.immuneTagType = immuneTagType; } - applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (tag.tagType === this.immuneTagType) { cancelled.value = true; - this.battlerTag = tag; + if (!simulated) { + this.battlerTag = tag; + } return true; } @@ -2575,14 +2677,14 @@ export class BattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr { export class UserFieldBattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr { } export class BlockCritAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.BooleanHolder).value = true; return true; } } export class BonusCritAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.BooleanHolder).value = true; return true; } @@ -2597,7 +2699,7 @@ export class MultCritAbAttr extends AbAttr { this.multAmount = multAmount; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const critMult = args[0] as Utils.NumberHolder; if (critMult.value > 1) { critMult.value *= this.multAmount; @@ -2628,7 +2730,7 @@ export class ConditionalCritAbAttr extends AbAttr { * [1] {@linkcode Pokemon} Target. * [2] {@linkcode Move} used by ability user. */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const target = (args[1] as Pokemon); const move = (args[2] as Move); if (!this.condition(pokemon,target,move)) { @@ -2641,7 +2743,7 @@ export class ConditionalCritAbAttr extends AbAttr { } export class BlockNonDirectDamageAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; } @@ -2669,7 +2771,7 @@ export class BlockStatusDamageAbAttr extends AbAttr { * @param {any[]} args N/A * @returns Returns true if status damage is blocked */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (pokemon.status && this.effects.includes(pokemon.status.effect)) { cancelled.value = true; return true; @@ -2679,7 +2781,7 @@ export class BlockStatusDamageAbAttr extends AbAttr { } export class BlockOneHitKOAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; } @@ -2705,7 +2807,7 @@ export class ChangeMovePriorityAbAttr extends AbAttr { this.changeAmount = changeAmount; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (!this.moveFunc(pokemon, args[0] as Move)) { return false; } @@ -2718,7 +2820,7 @@ export class ChangeMovePriorityAbAttr extends AbAttr { export class IgnoreContactAbAttr extends AbAttr { } export class PreWeatherEffectAbAttr extends AbAttr { - applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather | null, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreWeatherEffect(pokemon: Pokemon, passive: Boolean, simulated: boolean, weather: Weather | null, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -2734,7 +2836,7 @@ export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr { this.weatherTypes = weatherTypes; } - applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (!this.weatherTypes.length || this.weatherTypes.indexOf(weather?.weatherType) > -1) { cancelled.value = true; } @@ -2752,7 +2854,7 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr { this.affectsImmutable = !!affectsImmutable; } - applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.affectsImmutable || weather.isImmutable()) { cancelled.value = true; return true; @@ -2859,7 +2961,7 @@ export class ForewarnAbAttr extends PostSummonAbAttr { super(true); } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { let maxPowerSeen = 0; let maxMove = ""; let movePower = 0; @@ -2883,7 +2985,9 @@ export class ForewarnAbAttr extends PostSummonAbAttr { } } } - pokemon.scene.queueMessage(i18next.t("abilityTriggers:forewarn", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: maxMove })); + if (!simulated) { + pokemon.scene.queueMessage(i18next.t("abilityTriggers:forewarn", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: maxMove })); + } return true; } } @@ -2893,17 +2997,19 @@ export class FriskAbAttr extends PostSummonAbAttr { super(true); } - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - for (const opponent of pokemon.getOpponents()) { - pokemon.scene.queueMessage(i18next.t("abilityTriggers:frisk", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), opponentName: opponent.name, opponentAbilityName: opponent.getAbility().name })); - setAbilityRevealed(opponent); + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (!simulated) { + for (const opponent of pokemon.getOpponents()) { + pokemon.scene.queueMessage(i18next.t("abilityTriggers:frisk", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), opponentName: opponent.name, opponentAbilityName: opponent.getAbility().name })); + setAbilityRevealed(opponent); + } } return true; } } export class PostWeatherChangeAbAttr extends AbAttr { - applyPostWeatherChange(pokemon: Pokemon, passive: boolean, weather: WeatherType, args: any[]): boolean { + applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { return false; } } @@ -2921,13 +3027,17 @@ export class PostWeatherChangeAddBattlerTagAttr extends PostWeatherChangeAbAttr this.weatherTypes = weatherTypes; } - applyPostWeatherChange(pokemon: Pokemon, passive: boolean, weather: WeatherType, args: any[]): boolean { + applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { console.log(this.weatherTypes.find(w => weather === w), WeatherType[weather]); if (!this.weatherTypes.find(w => weather === w)) { return false; } - return pokemon.addTag(this.tagType, this.turnCount); + if (simulated) { + return pokemon.canAddTag(this.tagType); + } else { + return pokemon.addTag(this.tagType, this.turnCount); + } } } @@ -2940,7 +3050,7 @@ export class PostWeatherLapseAbAttr extends AbAttr { this.weatherTypes = weatherTypes; } - applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather | null, args: any[]): boolean | Promise { + applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather | null, args: any[]): boolean | Promise { return false; } @@ -2958,12 +3068,14 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr { this.healFactor = healFactor; } - applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather, args: any[]): boolean { + applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): boolean { if (!pokemon.isFullHp()) { const scene = pokemon.scene; const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / (16 / this.healFactor)), 1), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + if (!simulated) { + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), + Math.max(Math.floor(pokemon.getMaxHp() / (16 / this.healFactor)), 1), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + } return true; } @@ -2980,20 +3092,24 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { this.damageFactor = damageFactor; } - applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather, args: any[]): boolean { + applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): boolean { const scene = pokemon.scene; if (pokemon.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { return false; } - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - scene.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName })); - pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / (16 / this.damageFactor)), HitResult.OTHER); + + if (!simulated) { + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + scene.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName })); + pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / (16 / this.damageFactor)), HitResult.OTHER); + } + return true; } } export class PostTerrainChangeAbAttr extends AbAttr { - applyPostTerrainChange(pokemon: Pokemon, passive: boolean, terrain: TerrainType, args: any[]): boolean { + applyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): boolean { return false; } } @@ -3011,12 +3127,16 @@ export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr this.terrainTypes = terrainTypes; } - applyPostTerrainChange(pokemon: Pokemon, passive: boolean, terrain: TerrainType, args: any[]): boolean { + applyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): boolean { if (!this.terrainTypes.find(t => t === terrain)) { return false; } - return pokemon.addTag(this.tagType, this.turnCount); + if (simulated) { + return pokemon.canAddTag(this.tagType); + } else { + return pokemon.addTag(this.tagType, this.turnCount); + } } } @@ -3028,7 +3148,7 @@ function getTerrainCondition(...terrainTypes: TerrainType[]): AbAttrCondition { } export class PostTurnAbAttr extends AbAttr { - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { return false; } } @@ -3054,13 +3174,15 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { * @param {any[]} args N/A * @returns Returns true if healed from status, false if not */ - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { if (pokemon.status && this.effects.includes(pokemon.status.effect)) { if (!pokemon.isFullHp()) { - const scene = pokemon.scene; - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 8), 1), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); + if (!simulated) { + const scene = pokemon.scene; + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), + Math.max(Math.floor(pokemon.getMaxHp() / 8), 1), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); + } return true; } } @@ -3081,17 +3203,19 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { this.allyTarget = allyTarget; } - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { if (this.allyTarget) { this.target = pokemon.getAlly(); } else { this.target = pokemon; } if (this.target?.status) { + if (!simulated) { + this.target.scene.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target))); + this.target.resetStatus(false); + this.target.updateInfo(); + } - this.target.scene.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target))); - this.target.resetStatus(false); - this.target.updateInfo(); return true; } @@ -3116,7 +3240,7 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr { super(); } - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const pass = Phaser.Math.RND.realInRange(0, 1); // Clamp procChance to [0, 1]. Skip if didn't proc (less than pass) if (Math.max(Math.min(this.procChance(pokemon), 1), 0) < pass) { @@ -3124,7 +3248,7 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr { } if (this.itemType === "EATEN_BERRIES") { - return this.createEatenBerry(pokemon); + return this.createEatenBerry(pokemon, simulated); } else { return false; } @@ -3133,15 +3257,20 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr { /** * Create a new berry chosen randomly from the berries the pokemon ate this battle * @param pokemon The pokemon with this ability + * @param simulated whether the associated ability call is simulated * @returns whether a new berry was created */ - createEatenBerry(pokemon: Pokemon): boolean { + createEatenBerry(pokemon: Pokemon, simulated: boolean): boolean { const berriesEaten = pokemon.battleData.berriesEaten; if (!berriesEaten.length) { return false; } + if (simulated) { + return true; + } + const randomIdx = Utils.randSeedInt(berriesEaten.length); const chosenBerryType = berriesEaten[randomIdx]; const chosenBerry = new BerryModifierType(chosenBerryType); @@ -3175,17 +3304,17 @@ export class MoodyAbAttr extends PostTurnAbAttr { super(true); } - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const selectableStats = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD]; const increaseStatArray = selectableStats.filter(s => pokemon.summonData.battleStats[s] < 6); let decreaseStatArray = selectableStats.filter(s => pokemon.summonData.battleStats[s] > -6); - if (increaseStatArray.length > 0) { + if (!simulated && increaseStatArray.length > 0) { const increaseStat = increaseStatArray[Utils.randInt(increaseStatArray.length)]; decreaseStatArray = decreaseStatArray.filter(s => s !== increaseStat); pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [increaseStat], 2)); } - if (decreaseStatArray.length > 0) { + if (!simulated && decreaseStatArray.length > 0) { const decreaseStat = selectableStats[Utils.randInt(selectableStats.length)]; pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [decreaseStat], -1)); } @@ -3206,19 +3335,24 @@ export class PostTurnStatChangeAbAttr extends PostTurnAbAttr { this.levels = levels; } - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + } return true; } } export class PostTurnHealAbAttr extends PostTurnAbAttr { - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { if (!pokemon.isFullHp()) { - const scene = pokemon.scene; - const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 16), 1), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + if (!simulated) { + const scene = pokemon.scene; + const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), + Math.max(Math.floor(pokemon.getMaxHp() / 16), 1), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + } + return true; } @@ -3235,10 +3369,13 @@ export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { this.formFunc = formFunc; } - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const formIndex = this.formFunc(pokemon); if (formIndex !== pokemon.formIndex) { - pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + if (!simulated) { + pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); + } + return true; } @@ -3256,15 +3393,18 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { * Deals damage to all sleeping opponents equal to 1/8 of their max hp (min 1) * @param {Pokemon} pokemon Pokemon that has this ability * @param {boolean} passive N/A + * @param {boolean} simulated true if applying in a simulated call. * @param {any[]} args N/A * @returns {boolean} true if any opponents are sleeping */ - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { let hadEffect: boolean = false; for (const opp of pokemon.getOpponents()) { if ((opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(Abilities.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { - opp.damageAndUpdate(Math.floor(Math.max(1, opp.getMaxHp() / 8)), HitResult.OTHER); - pokemon.scene.queueMessage(i18next.t("abilityTriggers:badDreams", {pokemonName: getPokemonNameWithAffix(opp)})); + if (!simulated) { + opp.damageAndUpdate(Math.floor(Math.max(1, opp.getMaxHp() / 8)), HitResult.OTHER); + pokemon.scene.queueMessage(i18next.t("abilityTriggers:badDreams", {pokemonName: getPokemonNameWithAffix(opp)})); + } hadEffect = true; } @@ -3289,7 +3429,10 @@ export class FetchBallAbAttr extends PostTurnAbAttr { * @param args N/A * @returns true if player has used a pokeball and this pokemon is owned by the player */ - applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (simulated) { + return false; + } const lastUsed = pokemon.scene.currentBattle.lastUsedPokeball; if (lastUsed !== null && !!pokemon.isPlayer) { pokemon.scene.pokeballCounts[lastUsed]++; @@ -3312,9 +3455,13 @@ export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { this.weatherType = weatherType; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (!pokemon.scene.arena.weather?.isImmutable()) { - return pokemon.scene.arena.trySetWeather(this.weatherType, true); + if (simulated) { + return pokemon.scene.arena.weather?.weatherType !== this.weatherType; + } else { + return pokemon.scene.arena.trySetWeather(this.weatherType, true); + } } return false; @@ -3330,8 +3477,12 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { this.terrainType = terrainType; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (simulated) { + return pokemon.scene.arena.terrain?.terrainType !== this.terrainType; + } else { + return pokemon.scene.arena.trySetTerrain(this.terrainType, true); + } } } @@ -3340,7 +3491,7 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { * @extends AbAttr */ export class PostMoveUsedAbAttr extends AbAttr { - applyPostMoveUsed(pokemon: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], args: any[]): boolean | Promise { + applyPostMoveUsed(pokemon: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], simulated: boolean, args: any[]): boolean | Promise { return false; } } @@ -3361,20 +3512,22 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { * * @return true if the Dancer ability was resolved */ - applyPostMoveUsed(dancer: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], args: any[]): boolean | Promise { + applyPostMoveUsed(dancer: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], simulated: boolean, args: any[]): boolean | Promise { // List of tags that prevent the Dancer from replicating the move const forbiddenTags = [BattlerTagType.FLYING, BattlerTagType.UNDERWATER, BattlerTagType.UNDERGROUND, BattlerTagType.HIDDEN]; // The move to replicate cannot come from the Dancer if (source.getBattlerIndex() !== dancer.getBattlerIndex() && !dancer.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType))) { - // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance - if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { - const target = this.getTarget(dancer, source, targets); - dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, target, move, true)); - } else if (move.getMove() instanceof SelfStatusMove) { - // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself - dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [dancer.getBattlerIndex()], move, true)); + if (!simulated) { + // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance + if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { + const target = this.getTarget(dancer, source, targets); + dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, target, move, true)); + } else if (move.getMove() instanceof SelfStatusMove) { + // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself + dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [dancer.getBattlerIndex()], move, true)); + } } return true; } @@ -3405,7 +3558,7 @@ export class StatChangeMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.IntegerHolder).value *= this.multiplier; return true; @@ -3413,8 +3566,10 @@ export class StatChangeMultiplierAbAttr extends AbAttr { } export class StatChangeCopyAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, (args[0] as BattleStat[]), (args[1] as integer), true, false, false)); + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, (args[0] as BattleStat[]), (args[1] as integer), true, false, false)); + } return true; } } @@ -3424,7 +3579,7 @@ export class BypassBurnDamageReductionAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; @@ -3448,7 +3603,7 @@ export class ReduceBurnDamageAbAttr extends AbAttr { * @param args `[0]` {@linkcode Utils.NumberHolder} The damage value being modified * @returns `true` */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.NumberHolder).value = Math.max(Math.floor((args[0] as Utils.NumberHolder).value * this.multiplier), 1); return true; @@ -3456,7 +3611,7 @@ export class ReduceBurnDamageAbAttr extends AbAttr { } export class DoubleBerryEffectAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.NumberHolder).value *= 2; return true; @@ -3464,7 +3619,7 @@ export class DoubleBerryEffectAbAttr extends AbAttr { } export class PreventBerryUseAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; @@ -3487,24 +3642,25 @@ export class HealFromBerryUseAbAttr extends AbAttr { this.healPercent = Math.max(Math.min(healPercent, 1), 0); } - apply(pokemon: Pokemon, passive: boolean, ...args: [Utils.BooleanHolder, any[]]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, ...args: [Utils.BooleanHolder, any[]]): boolean { const { name: abilityName } = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); - pokemon.scene.unshiftPhase( - new PokemonHealPhase( - pokemon.scene, - pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() * this.healPercent), 1), - i18next.t("abilityTriggers:healFromBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), - true - ) - ); - + if (!simulated) { + pokemon.scene.unshiftPhase( + new PokemonHealPhase( + pokemon.scene, + pokemon.getBattlerIndex(), + Math.max(Math.floor(pokemon.getMaxHp() * this.healPercent), 1), + i18next.t("abilityTriggers:healFromBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), + true + ) + ); + } return true; } } export class RunSuccessAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.IntegerHolder).value = 256; return true; @@ -3527,7 +3683,7 @@ export class CheckTrappedAbAttr extends AbAttr { this.arenaTrapCondition = condition; } - applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean | Promise { + applyCheckTrapped(pokemon: Pokemon, passive: boolean, simulated: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean | Promise { return false; } } @@ -3552,7 +3708,7 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { * @param args N/A * @returns if enemy Pokemon is trapped or not */ - applyCheckTrapped(pokemon: Pokemon, passive: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean { + applyCheckTrapped(pokemon: Pokemon, passive: boolean, simulated: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean { if (this.arenaTrapCondition(pokemon, otherPokemon)) { if (otherPokemon.getTypes(true).includes(Type.GHOST) || (otherPokemon.getTypes(true).includes(Type.STELLAR) && otherPokemon.getTypes().includes(Type.GHOST))) { trapped.value = false; @@ -3574,7 +3730,7 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { } export class MaxMultiHitAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.IntegerHolder).value = 0; return true; @@ -3586,15 +3742,15 @@ export class PostBattleAbAttr extends AbAttr { super(true); } - applyPostBattle(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { return false; } } export class PostBattleLootAbAttr extends PostBattleAbAttr { - applyPostBattle(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const postBattleLoot = pokemon.scene.currentBattle.postBattleLoot; - if (postBattleLoot.length) { + if (!simulated && postBattleLoot.length) { const randItem = Utils.randSeedItem(postBattleLoot); //@ts-ignore - TODO see below if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, true, 1, true)) { // TODO: fix. This is a promise!? @@ -3609,7 +3765,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { } export class PostFaintAbAttr extends AbAttr { - applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { return false; } } @@ -3628,7 +3784,7 @@ export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr { * @param args N/A * @returns {boolean} Returns true if the weather clears, otherwise false. */ - applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { const weatherType = pokemon.scene.arena.weather?.weatherType; let turnOffWeather = false; @@ -3654,6 +3810,10 @@ export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr { break; } + if (simulated) { + return turnOffWeather; + } + if (turnOffWeather) { pokemon.scene.arena.trySetWeather(WeatherType.NONE, false); return true; @@ -3672,15 +3832,17 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { this.damageRatio = damageRatio; } - applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { const cancelled = new Utils.BooleanHolder(false); - pokemon.scene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled)); + pokemon.scene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated)); if (cancelled.value || attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { return false; } - attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); - attacker.turnData.damageTaken += Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)); + if (!simulated) { + attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); + attacker.turnData.damageTaken += Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)); + } return true; } @@ -3700,10 +3862,12 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { super (); } - applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - const damage = pokemon.turnData.attacksReceived[0].damage; - attacker.damageAndUpdate((damage), HitResult.OTHER); - attacker.turnData.damageTaken += damage; + applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + if (!simulated) { + const damage = pokemon.turnData.attacksReceived[0].damage; + attacker.damageAndUpdate((damage), HitResult.OTHER); + attacker.turnData.damageTaken += damage; + } return true; } @@ -3713,7 +3877,7 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { } export class RedirectMoveAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.canRedirect(args[0] as Moves)) { const target = args[1] as Utils.IntegerHolder; const newTarget = pokemon.getBattlerIndex(); @@ -3756,7 +3920,7 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr { this.statusEffect = statusEffect; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (args[0] === this.statusEffect) { (args[1] as Utils.IntegerHolder).value = Math.floor((args[1] as Utils.IntegerHolder).value / 2); return true; @@ -3785,8 +3949,10 @@ export class FlinchStatChangeAbAttr extends FlinchEffectAbAttr { this.levels = levels; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (!simulated) { + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + } return true; } } @@ -3794,7 +3960,7 @@ export class FlinchStatChangeAbAttr extends FlinchEffectAbAttr { export class IncreasePpAbAttr extends AbAttr { } export class ForceSwitchOutImmunityAbAttr extends AbAttr { - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { cancelled.value = true; return true; } @@ -3805,7 +3971,7 @@ export class ReduceBerryUseThresholdAbAttr extends AbAttr { super(); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const hpRatio = pokemon.getHpRatio(); if (args[0].value < hpRatio) { @@ -3826,7 +3992,7 @@ export class WeightMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Utils.NumberHolder).value *= this.multiplier; return true; @@ -3838,7 +4004,7 @@ export class SyncEncounterNatureAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { (args[0] as Pokemon).setNature(pokemon.getNature()); return true; @@ -3854,7 +4020,7 @@ export class MoveAbilityBypassAbAttr extends AbAttr { this.moveIgnoreFunc = moveIgnoreFunc || ((pokemon, move) => true); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.moveIgnoreFunc(pokemon, (args[0] as Move))) { cancelled.value = true; return true; @@ -3868,7 +4034,7 @@ export class SuppressFieldAbilitiesAbAttr extends AbAttr { super(false); } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const ability = (args[0] as Ability); if (!ability.hasAttr(UnsuppressableAbilityAbAttr) && !ability.hasAttr(SuppressFieldAbilitiesAbAttr)) { cancelled.value = true; @@ -3923,7 +4089,7 @@ export class IgnoreTypeImmunityAbAttr extends AbAttr { this.allowedMoveTypes = allowedMoveTypes; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.defenderType === (args[1] as Type) && this.allowedMoveTypes.includes(args[0] as Type)) { cancelled.value = true; return true; @@ -3946,7 +4112,7 @@ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { this.defenderType = defenderType; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.statusEffect.includes(args[0] as StatusEffect) && this.defenderType.includes(args[1] as Type)) { cancelled.value = true; return true; @@ -3973,8 +4139,10 @@ export class MoneyAbAttr extends PostBattleAbAttr { * @param args N/A * @returns true */ - applyPostBattle(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - pokemon.scene.currentBattle.moneyScattered += pokemon.scene.getWaveMoneyAmount(0.2); + applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + if (!simulated) { + pokemon.scene.currentBattle.moneyScattered += pokemon.scene.getWaveMoneyAmount(0.2); + } return true; } } @@ -4013,11 +4181,11 @@ export class PostSummonStatChangeOnArenaAbAttr extends PostSummonStatChangeAbAtt * @param {any[]} args - Additional arguments. * @returns {boolean} - Returns true if the stat change was applied, otherwise false. */ - applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const side = pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; if (pokemon.scene.arena.getTagOnSide(this.tagType, side)) { - return super.applyPostSummon(pokemon, passive, args); + return super.applyPostSummon(pokemon, passive, simulated, args); } return false; } @@ -4055,12 +4223,14 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { * @param {any[]} args Additional arguments. * @returns {boolean} Whether the immunity was applied. */ - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { + applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { - (args[0] as Utils.NumberHolder).value = this.multiplier; - pokemon.removeTag(this.tagType); - if (this.recoilDamageFunc) { - pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), HitResult.OTHER); + if (!simulated) { + (args[0] as Utils.NumberHolder).value = this.multiplier; + pokemon.removeTag(this.tagType); + if (this.recoilDamageFunc) { + pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), HitResult.OTHER); + } } return true; } @@ -4104,7 +4274,10 @@ export class BypassSpeedChanceAbAttr extends AbAttr { * @param {any[]} args [0] {@linkcode Utils.BooleanHolder} set to true when the ability activated * @returns {boolean} - whether the ability was activated. */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (simulated) { + return false; + } const bypassSpeed = args[0] as Utils.BooleanHolder; if (!bypassSpeed.value && pokemon.randSeedInt(100) < this.chance) { @@ -4147,7 +4320,7 @@ export class PreventBypassSpeedChanceAbAttr extends AbAttr { * @argument {boolean} bypassSpeed - determines if a Pokemon is able to bypass speed at the moment * @argument {boolean} canCheckHeldItems - determines if a Pokemon has access to Quick Claw's effects or not */ - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { const bypassSpeed = args[0] as Utils.BooleanHolder; const canCheckHeldItems = args[1] as Utils.BooleanHolder; @@ -4169,7 +4342,7 @@ async function applyAbAttrsInternal( applyFunc: AbAttrApplyFunc, args: any[], showAbilityInstant: boolean = false, - isQuiet: boolean = false, + quiet: boolean = false, messages: string[] = [], ) { for (const passive of [false, true]) { @@ -4196,11 +4369,11 @@ async function applyAbAttrsInternal( if (pokemon.summonData && !pokemon.summonData.abilitiesApplied.includes(ability.id)) { pokemon.summonData.abilitiesApplied.push(ability.id); } - if (pokemon.battleData && !pokemon.battleData.abilitiesApplied.includes(ability.id)) { + if (pokemon.battleData && !quiet && !pokemon.battleData.abilitiesApplied.includes(ability.id)) { pokemon.battleData.abilitiesApplied.push(ability.id); } - if (attr.showAbility && !isQuiet) { + if (attr.showAbility && !quiet) { if (showAbilityInstant) { pokemon.scene.abilityBar.showAbility(pokemon, passive); } else { @@ -4208,12 +4381,12 @@ async function applyAbAttrsInternal( } } - const message = attr.getTriggerMessage(pokemon, ability.name, args); - if (message) { - if (!isQuiet) { + if (!quiet) { + const message = attr.getTriggerMessage(pokemon, ability.name, args); + if (message) { pokemon.scene.queueMessage(message); + messages.push(message); } - messages.push(message); } } } @@ -4222,34 +4395,33 @@ async function applyAbAttrsInternal( } } -export function applyAbAttrs(attrType: Constructor, pokemon: Pokemon, cancelled: Utils.BooleanHolder | null, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, cancelled, args), args); +export function applyAbAttrs(attrType: Constructor, pokemon: Pokemon, cancelled: Utils.BooleanHolder | null, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, simulated, cancelled, args), args, false, simulated); } export function applyPostBattleInitAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostBattleInit(pokemon, passive, args), args); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostBattleInit(pokemon, passive, simulated, args), args, false, simulated); } export function applyPreDefendAbAttrs(attrType: Constructor, - pokemon: Pokemon, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, ...args: any[]): Promise { - const simulated = args.length > 1 && args[1]; - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, attacker, move, cancelled, args), args, false, simulated); + pokemon: Pokemon, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args), args, false, simulated); } export function applyPostDefendAbAttrs(attrType: Constructor, - pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult | null, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, attacker, move, hitResult, args), args); + pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult | null, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args), args, false, simulated); } export function applyPostMoveUsedAbAttrs(attrType: Constructor, - pokemon: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostMoveUsed(pokemon, move, source, targets, args), args); + pokemon: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostMoveUsed(pokemon, move, source, targets, simulated, args), args, false, simulated); } export function applyBattleStatMultiplierAbAttrs(attrType: Constructor, - pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyBattleStat(pokemon, passive, battleStat, statValue, args), args); + pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyBattleStat(pokemon, passive, simulated, battleStat, statValue, args), args, false, simulated); } /** @@ -4263,99 +4435,98 @@ export function applyBattleStatMultiplierAbAttrs(attrType: Constructor, - pokemon: Pokemon, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyFieldBattleStat(pokemon, passive, stat, statValue, checkedPokemon, hasApplied, args), args); + pokemon: Pokemon, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyFieldBattleStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, hasApplied, args), args, false, simulated); } export function applyPreAttackAbAttrs(attrType: Constructor, - pokemon: Pokemon, defender: Pokemon | null, move: Move, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, defender, move, args), args); + pokemon: Pokemon, defender: Pokemon | null, move: Move, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, simulated, defender, move, args), args, false, simulated); } export function applyPostAttackAbAttrs(attrType: Constructor, - pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult | null, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, defender, move, hitResult, args), args); + pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult | null, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args), args, false, simulated); } export function applyPostKnockOutAbAttrs(attrType: Constructor, - pokemon: Pokemon, knockedOut: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostKnockOut(pokemon, passive, knockedOut, args), args); + pokemon: Pokemon, knockedOut: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostKnockOut(pokemon, passive, simulated, knockedOut, args), args, false, simulated); } export function applyPostVictoryAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostVictory(pokemon, passive, args), args); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostVictory(pokemon, passive, simulated, args), args, false, simulated); } export function applyPostSummonAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostSummon(pokemon, passive, args), args); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), args, false, simulated); } export function applyPreSwitchOutAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSwitchOut(pokemon, passive, args), args, true); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSwitchOut(pokemon, passive, simulated, args), args, true, simulated); } export function applyPreStatChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon | null, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, stat, cancelled, args), args); + pokemon: Pokemon | null, stat: BattleStat, cancelled: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, simulated, stat, cancelled, args), args, false, simulated); } export function applyPostStatChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon, stats: BattleStat[], levels: integer, selfTarget: boolean, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostStatChange(pokemon, stats, levels, selfTarget, args), args); + pokemon: Pokemon, stats: BattleStat[], levels: integer, selfTarget: boolean, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostStatChange(pokemon, simulated, stats, levels, selfTarget, args), args, false, simulated); } export function applyPreSetStatusAbAttrs(attrType: Constructor, - pokemon: Pokemon, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - const simulated = args.length > 1 && args[1]; - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSetStatus(pokemon, passive, effect, cancelled, args), args, false, !simulated); + pokemon: Pokemon, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args), args, false, simulated); } export function applyPreApplyBattlerTagAbAttrs(attrType: Constructor, - pokemon: Pokemon, tag: BattlerTag, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreApplyBattlerTag(pokemon, passive, tag, cancelled, args), args); + pokemon: Pokemon, tag: BattlerTag, cancelled: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args), args, false, simulated); } export function applyPreWeatherEffectAbAttrs(attrType: Constructor, - pokemon: Pokemon, weather: Weather | null, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreWeatherEffect(pokemon, passive, weather, cancelled, args), args, true); + pokemon: Pokemon, weather: Weather | null, cancelled: Utils.BooleanHolder, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args), args, true, simulated); } export function applyPostTurnAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostTurn(pokemon, passive, args), args); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostTurn(pokemon, passive, simulated, args), args, false, simulated); } export function applyPostWeatherChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon, weather: WeatherType, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostWeatherChange(pokemon, passive, weather, args), args); + pokemon: Pokemon, weather: WeatherType, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostWeatherChange(pokemon, passive, simulated, weather, args), args, false, simulated); } export function applyPostWeatherLapseAbAttrs(attrType: Constructor, - pokemon: Pokemon, weather: Weather | null, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostWeatherLapse(pokemon, passive, weather, args), args); + pokemon: Pokemon, weather: Weather | null, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostWeatherLapse(pokemon, passive, simulated, weather, args), args, false, simulated); } export function applyPostTerrainChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon, terrain: TerrainType, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostTerrainChange(pokemon, passive, terrain, args), args); + pokemon: Pokemon, terrain: TerrainType, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostTerrainChange(pokemon, passive, simulated, terrain, args), args, false, simulated); } export function applyCheckTrappedAbAttrs(attrType: Constructor, - pokemon: Pokemon, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, isQuiet: boolean, messages: string[], ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyCheckTrapped(pokemon, passive, trapped, otherPokemon, args), args, false, isQuiet, messages); + pokemon: Pokemon, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, messages: string[], simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args), args, false, simulated, messages); } export function applyPostBattleAbAttrs(attrType: Constructor, - pokemon: Pokemon, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostBattle(pokemon, passive, args), args); + pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostBattle(pokemon, passive, simulated, args), args, false, simulated); } export function applyPostFaintAbAttrs(attrType: Constructor, - pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise { - return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostFaint(pokemon, passive, attacker, move, hitResult, args), args); + pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, simulated: boolean = false, ...args: any[]): Promise { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), args, false, simulated); } function queueShowAbility(pokemon: Pokemon, passive: boolean): void { diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 3394df827fb..026003d0b61 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -269,7 +269,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => { if (effectPhase instanceof MoveEffectPhase) { const attacker = effectPhase.getUserPokemon()!; applyMoveAttrs(IncrementMovePriorityAttr, attacker, null, move, priority); - applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, move, priority); + applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, false, move, priority); } return priority.value > 0; }; diff --git a/src/data/berry.ts b/src/data/berry.ts index e962219ca46..ff82a683e5a 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -36,25 +36,25 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate { return (pokemon: Pokemon) => { const threshold = new Utils.NumberHolder(0.25); const battleStat = (berryType - BerryType.LIECHI) as BattleStat; - applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold); + applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); return pokemon.getHpRatio() < threshold.value && pokemon.summonData.battleStats[battleStat] < 6; }; case BerryType.LANSAT: return (pokemon: Pokemon) => { const threshold = new Utils.NumberHolder(0.25); - applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold); + applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST); }; case BerryType.STARF: return (pokemon: Pokemon) => { const threshold = new Utils.NumberHolder(0.25); - applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold); + applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); return pokemon.getHpRatio() < 0.25; }; case BerryType.LEPPA: return (pokemon: Pokemon) => { const threshold = new Utils.NumberHolder(0.25); - applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold); + applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); return !!pokemon.getMoveset().find(m => !m?.getPpRatio()); }; } @@ -71,7 +71,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { pokemon.battleData.berriesEaten.push(berryType); } const hpHealed = new Utils.NumberHolder(Math.floor(pokemon.getMaxHp() / 4)); - applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, hpHealed); + applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed); pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true)); }; @@ -97,7 +97,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { } const battleStat = (berryType - BerryType.LIECHI) as BattleStat; const statLevels = new Utils.NumberHolder(1); - applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels); + applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statLevels); pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ battleStat ], statLevels.value)); }; case BerryType.LANSAT: @@ -113,7 +113,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { pokemon.battleData.berriesEaten.push(berryType); } const statLevels = new Utils.NumberHolder(2); - applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, statLevels); + applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statLevels); pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ BattleStat.RAND ], statLevels.value)); }; case BerryType.LEPPA: diff --git a/src/data/move.ts b/src/data/move.ts index 3a2903cb7c6..682ab0739d5 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -590,7 +590,7 @@ export default class Move implements Localizable { case MoveFlags.IGNORE_ABILITIES: if (user.hasAbilityWithAttr(MoveAbilityBypassAbAttr)) { const abilityEffectsIgnored = new Utils.BooleanHolder(false); - applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, this); + applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, false, this); if (abilityEffectsIgnored.value) { return true; } @@ -686,11 +686,11 @@ export default class Move implements Localizable { * @param target {@linkcode Pokemon} The Pokémon being targeted by the move. * @returns The calculated accuracy of the move. */ - calculateBattleAccuracy(user: Pokemon, target: Pokemon) { + calculateBattleAccuracy(user: Pokemon, target: Pokemon, simulated: boolean = false) { const moveAccuracy = new Utils.NumberHolder(this.accuracy); applyMoveAttrs(VariableAccuracyAttr, user, target, this, moveAccuracy); - applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, moveAccuracy); + applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, simulated, moveAccuracy); if (moveAccuracy.value === -1) { return moveAccuracy.value; @@ -724,7 +724,7 @@ export default class Move implements Localizable { * @param target {@linkcode Pokemon} The Pokémon being targeted by the move. * @returns The calculated power of the move. */ - calculateBattlePower(source: Pokemon, target: Pokemon): number { + calculateBattlePower(source: Pokemon, target: Pokemon, simulated: boolean = false): number { if (this.category === MoveCategory.STATUS) { return -1; } @@ -732,17 +732,17 @@ export default class Move implements Localizable { const power = new Utils.NumberHolder(this.power); const typeChangeMovePowerMultiplier = new Utils.NumberHolder(1); - applyPreAttackAbAttrs(MoveTypeChangeAttr, source, target, this, typeChangeMovePowerMultiplier); + applyPreAttackAbAttrs(MoveTypeChangeAttr, source, target, this, simulated, typeChangeMovePowerMultiplier); const sourceTeraType = source.getTeraType(); if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !source.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) { power.value = 60; } - applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, power); + applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, simulated, power); if (source.getAlly()) { - applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, source.getAlly(), target, this, power); + applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, source.getAlly(), target, this, simulated, power); } const fieldAuras = new Set( @@ -752,11 +752,11 @@ export default class Move implements Localizable { ); for (const aura of fieldAuras) { // The only relevant values are `move` and the `power` holder - aura.applyPreAttack(null, null, null, this, [power]); + aura.applyPreAttack(null, null, simulated, null, this, [power]); } const alliedField: Pokemon[] = source instanceof PlayerPokemon ? source.scene.getPlayerField() : source.scene.getEnemyField(); - alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, target, this, power)); + alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, target, this, simulated, power)); power.value *= typeChangeMovePowerMultiplier.value; @@ -984,9 +984,9 @@ export class MoveEffectAttr extends MoveAttr { */ getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): integer { const moveChance = new Utils.NumberHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, moveChance, move, target, selfEffect, showAbility); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, moveChance, move, target, selfEffect, showAbility); if (!selfEffect) { - applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, moveChance); + applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, false, moveChance); } return moveChance.value; } @@ -1883,7 +1883,7 @@ export class MultiHitAttr extends MoveAttr { { const rand = user.randSeedInt(16); const hitValue = new Utils.IntegerHolder(rand); - applyAbAttrs(MaxMultiHitAbAttr, user, null, hitValue); + applyAbAttrs(MaxMultiHitAbAttr, user, null, false, hitValue); if (hitValue.value >= 10) { return 2; } else if (hitValue.value >= 4) { @@ -1954,7 +1954,7 @@ export class StatusEffectAttr extends MoveEffectAttr { } if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0)) && pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) { - applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, this.effect); + applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect); return true; } } diff --git a/src/data/terrain.ts b/src/data/terrain.ts index e29344ffea2..d4789078af7 100644 --- a/src/data/terrain.ts +++ b/src/data/terrain.ts @@ -59,7 +59,7 @@ export class Terrain { case TerrainType.PSYCHIC: if (!move.hasAttr(ProtectAttr)) { const priority = new Utils.IntegerHolder(move.priority); - applyAbAttrs(ChangeMovePriorityAbAttr, user, null, move, priority); + applyAbAttrs(ChangeMovePriorityAbAttr, user, null, false, move, priority); // Cancels move if the move has positive priority and targets a Pokemon grounded on the Psychic Terrain return priority.value > 0 && user.getOpponents().some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded()); } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index b3a96a5f1fc..999ebb3cb21 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -694,7 +694,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { break; } } - applyAbAttrs(IgnoreOpponentStatChangesAbAttr, opponent, null, statLevel); + applyAbAttrs(IgnoreOpponentStatChangesAbAttr, opponent, null, false, statLevel); if (move) { applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, opponent, move, statLevel); } @@ -1122,10 +1122,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const suppressed = new Utils.BooleanHolder(false); this.scene.getField(true).filter(p => p !== this).map(p => { if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) { - p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, suppressed, [ability])); + p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, false, suppressed, [ability])); } if (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true)) { - p.getPassiveAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, true, suppressed, [ability])); + p.getPassiveAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, true, false, suppressed, [ability])); } }); if (suppressed.value) { @@ -1177,7 +1177,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getWeight(): number { const weight = new Utils.NumberHolder(this.species.weight); // This will trigger the ability overlay so only call this function when necessary - applyAbAttrs(WeightMultiplierAbAttr, this, null, weight); + applyAbAttrs(WeightMultiplierAbAttr, this, null, false, weight); return weight.value; } @@ -1237,10 +1237,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const cancelled = new Utils.BooleanHolder(false); applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier); if (!typeless && !ignoreAbility) { - applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true); + applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, true, typeMultiplier); } if (!cancelled.value && !ignoreAbility) { - applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true); + applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, true, typeMultiplier); } return (!cancelled.value ? Number(typeMultiplier.value) : 0) as TypeDamageMultiplier; @@ -1281,7 +1281,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (source) { const ignoreImmunity = new Utils.BooleanHolder(false); if (source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr)) { - applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, moveType, defType); + applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, false, moveType, defType); } if (ignoreImmunity.value) { return 1; @@ -1922,9 +1922,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const userAccuracyLevel = new Utils.IntegerHolder(this.summonData.battleStats[BattleStat.ACC]); const targetEvasionLevel = new Utils.IntegerHolder(target.summonData.battleStats[BattleStat.EVA]); - applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, userAccuracyLevel); - applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this, null, targetEvasionLevel); - applyAbAttrs(IgnoreOpponentEvasionAbAttr, this, null, targetEvasionLevel); + applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, false, userAccuracyLevel); + applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this, null, false, targetEvasionLevel); + applyAbAttrs(IgnoreOpponentEvasionAbAttr, this, null, false, targetEvasionLevel); applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, target, sourceMove, targetEvasionLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, this.isPlayer(), TempBattleStat.ACC, userAccuracyLevel); @@ -1939,7 +1939,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { : 3 / (3 + Math.min(targetEvasionLevel.value - userAccuracyLevel.value, 6)); } - applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, BattleStat.ACC, accuracyMultiplier, sourceMove); + applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, this, BattleStat.ACC, accuracyMultiplier, false, sourceMove); const evasionMultiplier = new Utils.NumberHolder(1); applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, target, BattleStat.EVA, evasionMultiplier); @@ -1949,6 +1949,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return accuracyMultiplier.value; } + /** + * Apply the results of a move to this pokemon + * @param {Pokemon} source The pokemon using the move + * @param {PokemonMove} battlerMove The move being used + * @returns {HitResult} The result of the attack + */ apply(source: Pokemon, move: Move): HitResult { let result: HitResult; const damage = new Utils.NumberHolder(0); @@ -1984,12 +1990,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const sourceTeraType = source.getTeraType(); if (!typeless) { - applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier); + applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier); applyMoveAttrs(NeutralDamageAgainstFlyingTypeMultiplierAttr, source, this, move, typeMultiplier); } if (!cancelled.value) { - applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier); - defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, typeMultiplier)); + applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier); + defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, false, typeMultiplier)); } if (cancelled.value) { @@ -2012,7 +2018,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const critOnly = new Utils.BooleanHolder(false); const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT); applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly); - applyAbAttrs(ConditionalCritAbAttr, source, null, critOnly, this, move); + applyAbAttrs(ConditionalCritAbAttr, source, null, false, critOnly, this, move); if (critOnly.value || critAlways) { isCritical = true; } else { @@ -2022,7 +2028,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel); const bonusCrit = new Utils.BooleanHolder(false); //@ts-ignore - if (applyAbAttrs(BonusCritAbAttr, source, null, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus. + if (applyAbAttrs(BonusCritAbAttr, source, null, false, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus. if (bonusCrit.value) { critLevel.value += 1; } @@ -2040,7 +2046,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (isCritical) { const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide); const blockCrit = new Utils.BooleanHolder(false); - applyAbAttrs(BlockCritAbAttr, this, null, blockCrit); + applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit); if (noCritTag || blockCrit.value) { isCritical = false; } @@ -2048,7 +2054,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, isCritical)); const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical)); const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1); - applyAbAttrs(MultCritAbAttr, source, null, criticalMultiplier); + applyAbAttrs(MultCritAbAttr, source, null, false, criticalMultiplier); const screenMultiplier = new Utils.NumberHolder(1); if (!isCritical) { this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier); @@ -2063,7 +2069,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { stabMultiplier.value += 0.5; } - applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier); + applyAbAttrs(StabBoostAbAttr, source, null, false, stabMultiplier); if (sourceTeraType !== Type.UNKNOWN && matchesSourceType) { stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25); @@ -2081,7 +2087,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { numTargets = effectPhase.getTargets().length; } const twoStrikeMultiplier = new Utils.NumberHolder(1); - applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier); + applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, false, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier); if (!isTypeImmune) { const levelMultiplier = (2 * source.level / 5 + 2); @@ -2100,14 +2106,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) { if (!move.hasAttr(BypassBurnDamageReductionAttr)) { const burnDamageReductionCancelled = new Utils.BooleanHolder(false); - applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled); + applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, false); if (!burnDamageReductionCancelled.value) { damage.value = Math.floor(damage.value / 2); } } } - applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, damage); + applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, false, damage); /** * For each {@link HitsTagAttr} the move has, doubles the damage of the move if: @@ -2165,7 +2171,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage); } - applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, damage); + applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, false, damage); } // This attribute may modify damage arbitrarily, so be careful about changing its order of application. @@ -2178,7 +2184,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (damage.value) { if (this.isFullHp()) { - applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, damage); + applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage); } else if (!this.isPlayer() && damage.value >= this.hp) { this.scene.applyModifiers(EnemyEndureChanceModifier, false, this); } @@ -2245,11 +2251,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { break; case MoveCategory.STATUS: if (!typeless) { - applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier); + applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier); } if (!cancelled.value) { - applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier); - defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, typeMultiplier)); + applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, false, typeMultiplier); + defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, false, typeMultiplier)); } if (!typeMultiplier.value) { this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) })); @@ -2341,6 +2347,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return maxForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && maxForms.includes(this.getFusionFormKey()!)); } + canAddTag(tagType: BattlerTagType): boolean { + if (this.getTag(tagType)) { + return false; + } + + const stubTag = new BattlerTag(tagType, 0, 0); + + const cancelled = new Utils.BooleanHolder(false); + applyPreApplyBattlerTagAbAttrs(BattlerTagImmunityAbAttr, this, stubTag, cancelled, true); + + const userField = this.getAlliedField(); + userField.forEach(pokemon => applyPreApplyBattlerTagAbAttrs(UserFieldBattlerTagImmunityAbAttr, pokemon, stubTag, cancelled, true)); + + return !cancelled.value; + } + addTag(tagType: BattlerTagType, turnCount: integer = 0, sourceMove?: Moves, sourceId?: integer): boolean { const existingTag = this.getTag(tagType); if (existingTag) { @@ -2727,7 +2749,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity const cancelImmunity = new Utils.BooleanHolder(false); if (sourcePokemon) { - applyAbAttrs(IgnoreTypeStatusEffectImmunityAbAttr, sourcePokemon, cancelImmunity, effect, defType); + applyAbAttrs(IgnoreTypeStatusEffectImmunityAbAttr, sourcePokemon, cancelImmunity, false, effect, defType); if (cancelImmunity.value) { return false; } @@ -2799,7 +2821,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (effect === StatusEffect.SLEEP) { statusCureTurn = new Utils.IntegerHolder(this.randSeedIntRange(2, 4)); - applyAbAttrs(ReduceStatusEffectDurationAbAttr, this, null, effect, statusCureTurn); + applyAbAttrs(ReduceStatusEffectDurationAbAttr, this, null, false, effect, statusCureTurn); this.setFrameRate(4); diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts index 9781ca6d360..17625c57fc6 100644 --- a/src/phases/attempt-run-phase.ts +++ b/src/phases/attempt-run-phase.ts @@ -23,7 +23,7 @@ export class AttemptRunPhase extends PokemonPhase { const enemySpeed = enemyField.reduce((total: integer, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), 0) / enemyField.length; const escapeChance = new Utils.IntegerHolder((((playerPokemon.getStat(Stat.SPD) * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256); - applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, escapeChance); + applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance); if (playerPokemon.randSeedInt(256) < escapeChance.value) { this.scene.playSound("flee"); diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 5d466e5d3b6..68ede826d95 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -189,7 +189,7 @@ export class CommandPhase extends FieldPhase { const batonPass = isSwitch && args[0] as boolean; const trappedAbMessages: string[] = []; if (!batonPass) { - enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon, true, trappedAbMessages)); + enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped, playerPokemon, trappedAbMessages, true)); } if (batonPass || (!trapTag && !trapped.value)) { this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 739bb1d93f1..dfa198c8339 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -67,7 +67,7 @@ export class EncounterPhase extends BattlePhase { battle.enemyParty[e].ivs = new Array(6).fill(31); } this.scene.getParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => { - applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, battle.enemyParty[e]); + applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, false, battle.enemyParty[e]); }); } } diff --git a/src/phases/enemy-command-phase.ts b/src/phases/enemy-command-phase.ts index d7f553681c2..0b62fcbe813 100644 --- a/src/phases/enemy-command-phase.ts +++ b/src/phases/enemy-command-phase.ts @@ -47,7 +47,7 @@ export class EnemyCommandPhase extends FieldPhase { const trapTag = enemyPokemon.findTag(t => t instanceof TrappedTag) as TrappedTag; const trapped = new Utils.BooleanHolder(false); - opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped, enemyPokemon, true, [])); + opponents.forEach(playerPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, playerPokemon, trapped, enemyPokemon, [], true)); if (!trapTag && !trapped.value) { const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true); diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index a5ac913cc5d..12018656458 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -74,7 +74,7 @@ export class MoveEffectPhase extends PokemonPhase { // Assume single target for multi hit applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount); // If Parental Bond is applicable, double the hit count - applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, targets.length, hitCount, new Utils.IntegerHolder(0)); + applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, targets.length, hitCount, new Utils.IntegerHolder(0)); // If Multi-Lens is applicable, multiply the hit count by 1 + the number of Multi-Lenses held by the user if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) { this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0)); diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index b9656df856b..2aed0bb9495 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -90,7 +90,7 @@ export class MovePhase extends BattlePhase { : null; if (moveTarget) { const oldTarget = moveTarget.value; - this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, this.move.moveId, moveTarget)); + this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, moveTarget)); this.pokemon.getOpponents().forEach(p => { const redirectTag = p.getTag(CenterOfAttentionTag) as CenterOfAttentionTag; if (redirectTag && (!redirectTag.powder || (!this.pokemon.isOfType(Type.GRASS) && !this.pokemon.hasAbility(Abilities.OVERCOAT)))) { diff --git a/src/phases/post-turn-status-effect-phase.ts b/src/phases/post-turn-status-effect-phase.ts index 8b533f2e90a..47db32303a5 100644 --- a/src/phases/post-turn-status-effect-phase.ts +++ b/src/phases/post-turn-status-effect-phase.ts @@ -34,7 +34,7 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { break; case StatusEffect.BURN: damage.value = Math.max(pokemon.getMaxHp() >> 4, 1); - applyAbAttrs(ReduceBurnDamageAbAttr, pokemon, null, damage); + applyAbAttrs(ReduceBurnDamageAbAttr, pokemon, null, false, damage); break; } if (damage.value) { diff --git a/src/phases/stat-change-phase.ts b/src/phases/stat-change-phase.ts index 3469cc62942..fec3da9bc9a 100644 --- a/src/phases/stat-change-phase.ts +++ b/src/phases/stat-change-phase.ts @@ -68,7 +68,7 @@ export class StatChangePhase extends PokemonPhase { const levels = new Utils.IntegerHolder(this.levels); if (!this.ignoreAbilities) { - applyAbAttrs(StatChangeMultiplierAbAttr, pokemon, null, levels); + applyAbAttrs(StatChangeMultiplierAbAttr, pokemon, null, false, levels); } const battleStats = this.getPokemon().summonData.battleStats; @@ -90,7 +90,7 @@ export class StatChangePhase extends PokemonPhase { if (levels.value > 0 && this.canBeCopied) { for (const opponent of pokemon.getOpponents()) { - applyAbAttrs(StatChangeCopyAbAttr, opponent, null, this.stats, levels.value); + applyAbAttrs(StatChangeCopyAbAttr, opponent, null, false, this.stats, levels.value); } } diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 1320cb6235c..e4064fc784a 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -34,8 +34,8 @@ export class TurnStartPhase extends FieldPhase { this.scene.getField(true).filter(p => p.summonData).map(p => { const bypassSpeed = new Utils.BooleanHolder(false); const canCheckHeldItems = new Utils.BooleanHolder(true); - applyAbAttrs(BypassSpeedChanceAbAttr, p, null, bypassSpeed); - applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, bypassSpeed, canCheckHeldItems); + applyAbAttrs(BypassSpeedChanceAbAttr, p, null, false, bypassSpeed); + applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, false, bypassSpeed, canCheckHeldItems); if (canCheckHeldItems.value) { this.scene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed); } @@ -64,8 +64,8 @@ export class TurnStartPhase extends FieldPhase { applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? - applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? - applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? + applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, false, aMove, aPriority); //TODO: is the bang correct here? + applyAbAttrs(ChangeMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, false, bMove, bPriority); //TODO: is the bang correct here? if (aPriority.value !== bPriority.value) { const bracketDifference = Math.ceil(aPriority.value) - Math.ceil(bPriority.value); diff --git a/src/test/abilities/sand_veil.test.ts b/src/test/abilities/sand_veil.test.ts index 010878db68d..3c5f97bd653 100644 --- a/src/test/abilities/sand_veil.test.ts +++ b/src/test/abilities/sand_veil.test.ts @@ -52,7 +52,7 @@ describe("Abilities - Sand Veil", () => { const sandVeilAttr = allAbilities[Abilities.SAND_VEIL].getAttrs(BattleStatMultiplierAbAttr)[0]; vi.spyOn(sandVeilAttr, "applyBattleStat").mockImplementation( - (pokemon, passive, battleStat, statValue, args) => { + (pokemon, passive, simulated, battleStat, statValue, args) => { if (battleStat === BattleStat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) { statValue.value *= -1; // will make all attacks miss return true; diff --git a/src/test/abilities/serene_grace.test.ts b/src/test/abilities/serene_grace.test.ts index 5e4841f005a..b126bb5eb7a 100644 --- a/src/test/abilities/serene_grace.test.ts +++ b/src/test/abilities/serene_grace.test.ts @@ -67,7 +67,7 @@ describe("Abilities - Serene Grace", () => { const chance = new Utils.IntegerHolder(move.chance); console.log(move.chance + " Their ability is " + phase.getUserPokemon()!.getAbility().name); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); expect(chance.value).toBe(30); }, 20000); @@ -99,7 +99,7 @@ describe("Abilities - Serene Grace", () => { expect(move.id).toBe(Moves.AIR_SLASH); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); expect(chance.value).toBe(60); }, 20000); diff --git a/src/test/abilities/sheer_force.test.ts b/src/test/abilities/sheer_force.test.ts index 33b34124cc4..564e2040af4 100644 --- a/src/test/abilities/sheer_force.test.ts +++ b/src/test/abilities/sheer_force.test.ts @@ -69,8 +69,8 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power); expect(chance.value).toBe(0); expect(power.value).toBe(move.power * 5461/4096); @@ -108,8 +108,8 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power); expect(chance.value).toBe(-1); expect(power.value).toBe(move.power); @@ -147,8 +147,8 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power); expect(chance.value).toBe(-1); expect(power.value).toBe(move.power); @@ -191,8 +191,8 @@ describe("Abilities - Sheer Force", () => { const target = phase.getTarget()!; const opponentType = target.getTypes()[0]; - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, chance, move, target, false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, user, target, move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, chance, move, target, false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, user, target, move, false, power); applyPostDefendAbAttrs(PostDefendTypeChangeAbAttr, target, user, move, target.apply(user, move)); expect(chance.value).toBe(0); diff --git a/src/test/abilities/shield_dust.test.ts b/src/test/abilities/shield_dust.test.ts index b40689a180a..fe6c941752c 100644 --- a/src/test/abilities/shield_dust.test.ts +++ b/src/test/abilities/shield_dust.test.ts @@ -67,8 +67,8 @@ describe("Abilities - Shield Dust", () => { expect(move.id).toBe(Moves.AIR_SLASH); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); - applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget()!, phase.getUserPokemon()!, null!, null!, chance); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false); + applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget()!, phase.getUserPokemon()!, null, null, false, chance); expect(chance.value).toBe(0); }, 20000); From 34bf932722e4109502ae6cbabf4758984b11e148 Mon Sep 17 00:00:00 2001 From: chaosgrimmon <31082757+chaosgrimmon@users.noreply.github.com> Date: Wed, 21 Aug 2024 21:48:39 -0400 Subject: [PATCH 22/30] [Sprite] Trim exp Rellor palette (#3674) * [Sprite] Trim exp Rellor palette * [Sprite] Trim Rellor exp Palette * [Sprite] Trim Rellor exp palette --- public/images/pokemon/exp/953.png | Bin 2164 -> 1296 bytes public/images/pokemon/variant/exp/953.json | 66 ++++----------------- 2 files changed, 11 insertions(+), 55 deletions(-) diff --git a/public/images/pokemon/exp/953.png b/public/images/pokemon/exp/953.png index fb4255a112b02fc6462c0420bcc33a808bae7ac9..8a6ca2e7eff12603d14fac0859f03e4abe10f72f 100644 GIT binary patch delta 1276 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 dh0k}s^9z@YZ|Wo1v{?WE002ovPDHLkV1igW Date: Thu, 22 Aug 2024 12:32:03 +0900 Subject: [PATCH 23/30] =?UTF-8?q?[Enhancement]=20Support=20for=20special?= =?UTF-8?q?=20characters(=E2=99=AA.=E2=98=85,=E2=99=A5,=E2=99=A3)=20in=20g?= =?UTF-8?q?ame=20font=20(issue#3180)=20(#3535)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added specialCharacters: "♪.★,♥,♣" * did refactoring...maybe * added note --- public/fonts/PokePT_Wansung.woff2 | Bin 37032 -> 37456 bytes src/plugins/i18n.ts | 10 ++++++++++ 2 files changed, 10 insertions(+) diff --git a/public/fonts/PokePT_Wansung.woff2 b/public/fonts/PokePT_Wansung.woff2 index 02b299ef7a05091853c64ad10d0fa26587826163..15aab3eb4f4de725cb1692f3ac3f7cab7b61635a 100644 GIT binary patch literal 37456 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�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 JUI+g>=P&CqKM()_ literal 37032 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%0Agl%=;JD^L$>;y9DO2Y=Uw}0d}1z8N&=qb7) zQw2D#g{$5HR~2ET{{eIu-bT~^;5r+Y#aj0zBs$uuVWSj>*vo80@h(BG;VlFPRkYR3 zFz!1Eh(CZ#>_yG+Xon^k0`?>P3(~ubyGWmxX1x8Xr(U_}J`W1f6T`W#C!^2I$gzh4*J8%5oz8L`c zF$U;$wUe{cyD{M2Ur_x++=QmU0YZ4N1O06?14QaS82-Vbko?y!Ck9S0>}C4{pr(od zlaVLSC1gbdl!iIV-ya|czldiu=<}qcA2k~M~ z-m|y=6@q>I;yySm;Qw!;_nnO3-$i+_KlI$6fkQunH`4R~BqEX!&u-@?B`zZ-E!p4n z-J6zunL)Kwzt@+M|A>&s`e%<1J@=M?!`{zcKW5=85X_#3r$7v8{<#D5IN(M4;3_gz zQ18Ot2~_$sFo-PpJ8-=CKO@JxPVV0zOt6XLB)?lE-Nfjsh5&kbk>P>w<88w|(=9hh zst>TD7ohmMPpkU<(qxDR_??#^m3OnQCkIPYVLI+tkpEYJ>(2s8%5I#4_kRO!Qyry> z{`c^v^MA-BAYeX_0pQPvr|AWO z|2y5igC+l+j)RL3NPYH1BID%IR**o~1Y8OF3|X^pz8M5 zE=99-=R0fNRV0m97tp|D3DN6+pT|Oo>cV5gO5VUbKWcO-L&O+~*DPvgsBs;*@Z$Ob zN>nN|%5Otn<#2<3vGo7iyY^_P@;L5M6d|$R29b`rrXNE~~XpieyXW zF&lL{j!JDh)=Fh&v!M`2c1g?#<#FWLLQ{G5&`g`x(U6HIduQg({oT=Q?v&ZHchC3> z=lY)S{O<4f`+gqZ-%so+Zm%v=5v?1%=Q(#+zWBjw{TsJd`9LR<#pXPv%k5@6+jHB} zZ2cIPfn~P_iGvO6QY{?xq5ScyhaDPX7@L+^vRaZUv)m2Av>#1uf z3P#YK>6|!PFJ)w;#2ZbW1#(cI8bBk$MqpXk1{jcC%}!fv(aWw4Dw`Tcj8)C(B{0V(&W`ey&5Y+0MC#}Qu7dSi*pbjbQ< z4|BWpV3)_c=as{2I{bWv_h-h;_A%4u;FWwg%y&+b5Vkmo$j&}a>^{5bG#e!l{M)xV5O6Z?e z>9_hFNiY$}2UD};Rv4Hn;+^Gp)A zZDP_C-kkseK^p3PvWc;e?`A}9a@^emcl~}#h3*+>&y03Sj8l`LQeJ z2X){%WRKkZ!mkDW_tBbbj=}`5VOj`4iEY85MPV#{y&Gm1DetCl$J(^cvhZy?!njAz z;6vx(P%rt-%l1s`pyNCM>nG|{AQm#*BE)U{$YoElI2>Nr54_tRx;YhuO*?`X&4I^_ z@w3>|@k1qRpiZS_u*>@$kiTbY-w3uNvBOKvh8Y*Hq#OG*Bo#b=>%!L2{VTJ<1oprO zmLrzgxF51VP3pRijg}}NkZK?WJY@==vPDc5&O8A?x}K;cP!fV&kZy=#ioC#qquYS6 znIP;65OyU9s{q0(BVlVm*ghm|?6ek$i-&Yo@s%zCPw7r@N@VerZ|wA9e5JbpU+L=L zE8ThcN>>wK=`O-ox(4`4R}Nq4F2Ps2O881w4`1m@0WZ`kBr$w01-z0Q-3d;#48GQy z6!1!JHpEU#0k0%SR|+Cv zk{n&B!Yj$ql`6dN8{Owm`(%;xkaRU!ftNkt4&2!9fg8IJxUrhRjYaO%2zF5vH1igR zfUq_o>{bvq5`X=&u_+iRd3iegD=S{PxUI J(;VohzX8yaY3Kj| diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 33f00d22555..b786787f83e 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -26,6 +26,7 @@ const unicodeRanges = { kana: "U+3040-30FF", CJKCommon: "U+2E80-2EFF,U+3000-303F,U+31C0-31EF,U+3200-32FF,U+3400-4DBF,U+F900-FAFF,U+FE30-FE4F", CJKIdeograph: "U+4E00-9FFF", + specialCharacters: "U+266A,U+2605,U+2665,U+2663" //♪.★,♥,♣ }; const rangesByLanguage = { korean: [unicodeRanges.CJKCommon, unicodeRanges.hangul].join(","), @@ -34,6 +35,15 @@ const rangesByLanguage = { }; const fonts: Array = [ + // unicode (special character from PokePT) + { + face: new FontFace("emerald", "url(./fonts/PokePT_Wansung.woff2)", { unicodeRange: unicodeRanges.specialCharacters }), + }, + { + face: new FontFace("pkmnems", "url(./fonts/PokePT_Wansung.woff2)", { unicodeRange: unicodeRanges.specialCharacters }), + extraOptions: { sizeAdjust: "133%" }, + }, + // unicode (korean) { face: new FontFace("emerald", "url(./fonts/PokePT_Wansung.woff2)", { unicodeRange: rangesByLanguage.korean }), }, From 61d659d8bb26a204fafa71f85674335ef84167af Mon Sep 17 00:00:00 2001 From: Lugiad Date: Thu, 22 Aug 2024 05:57:46 +0200 Subject: [PATCH 24/30] [Localization] Localizable owned money symbol on battle UI (#3646) Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: Enoch Co-authored-by: frutescens --- src/battle-scene.ts | 2 +- src/locales/ca_ES/battle-scene.ts | 5 +++++ src/locales/ca_ES/config.ts | 2 ++ src/locales/de/battle-scene.ts | 6 ++++++ src/locales/de/config.ts | 2 ++ src/locales/en/battle-scene.ts | 5 +++++ src/locales/en/config.ts | 2 ++ src/locales/es/battle-scene.ts | 5 +++++ src/locales/es/config.ts | 2 ++ src/locales/fr/battle-scene.ts | 5 +++++ src/locales/fr/config.ts | 2 ++ src/locales/it/battle-scene.ts | 5 +++++ src/locales/it/config.ts | 2 ++ src/locales/ja/battle-scene.ts | 5 +++++ src/locales/ja/config.ts | 2 ++ src/locales/ko/battle-scene.ts | 5 +++++ src/locales/ko/config.ts | 2 ++ src/locales/pt_BR/battle-scene.ts | 5 +++++ src/locales/pt_BR/config.ts | 2 ++ src/locales/zh_CN/battle-scene.ts | 5 +++++ src/locales/zh_CN/config.ts | 2 ++ src/locales/zh_TW/battle-scene.ts | 5 +++++ src/locales/zh_TW/config.ts | 2 ++ 23 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 src/locales/ca_ES/battle-scene.ts create mode 100644 src/locales/de/battle-scene.ts create mode 100644 src/locales/en/battle-scene.ts create mode 100644 src/locales/es/battle-scene.ts create mode 100644 src/locales/fr/battle-scene.ts create mode 100644 src/locales/it/battle-scene.ts create mode 100644 src/locales/ja/battle-scene.ts create mode 100644 src/locales/ko/battle-scene.ts create mode 100644 src/locales/pt_BR/battle-scene.ts create mode 100644 src/locales/zh_CN/battle-scene.ts create mode 100644 src/locales/zh_TW/battle-scene.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index aad0d355e38..af63f0b0a39 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1518,7 +1518,7 @@ export default class BattleScene extends SceneBase { return; } const formattedMoney = Utils.formatMoney(this.moneyFormat, this.money); - this.moneyText.setText(`₽${formattedMoney}`); + this.moneyText.setText(i18next.t("battleScene:moneyOwned", { formattedMoney })); this.fieldUI.moveAbove(this.moneyText, this.luckText); if (forceVisible) { this.moneyText.setVisible(true); diff --git a/src/locales/ca_ES/battle-scene.ts b/src/locales/ca_ES/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/ca_ES/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/ca_ES/config.ts b/src/locales/ca_ES/config.ts index 831ab5d99f5..36aee87fc75 100644 --- a/src/locales/ca_ES/config.ts +++ b/src/locales/ca_ES/config.ts @@ -6,6 +6,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const caESConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/de/battle-scene.ts b/src/locales/de/battle-scene.ts new file mode 100644 index 00000000000..bfa96445f6c --- /dev/null +++ b/src/locales/de/battle-scene.ts @@ -0,0 +1,6 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "{{formattedMoney}} ₽" + +} as const; diff --git a/src/locales/de/config.ts b/src/locales/de/config.ts index d0779c9eec4..080c9ecc598 100644 --- a/src/locales/de/config.ts +++ b/src/locales/de/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const deConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/en/battle-scene.ts b/src/locales/en/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/en/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/en/config.ts b/src/locales/en/config.ts index a98dd750fbe..d456b0540cc 100644 --- a/src/locales/en/config.ts +++ b/src/locales/en/config.ts @@ -6,6 +6,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const enConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/es/battle-scene.ts b/src/locales/es/battle-scene.ts new file mode 100644 index 00000000000..995ca744302 --- /dev/null +++ b/src/locales/es/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "{{formattedMoney}} ₽" +} as const; diff --git a/src/locales/es/config.ts b/src/locales/es/config.ts index ce9ad19aac3..6c038188da2 100644 --- a/src/locales/es/config.ts +++ b/src/locales/es/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const esConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/fr/battle-scene.ts b/src/locales/fr/battle-scene.ts new file mode 100644 index 00000000000..995ca744302 --- /dev/null +++ b/src/locales/fr/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "{{formattedMoney}} ₽" +} as const; diff --git a/src/locales/fr/config.ts b/src/locales/fr/config.ts index 246ae9a073f..a2ab67eefe0 100644 --- a/src/locales/fr/config.ts +++ b/src/locales/fr/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const frConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/it/battle-scene.ts b/src/locales/it/battle-scene.ts new file mode 100644 index 00000000000..995ca744302 --- /dev/null +++ b/src/locales/it/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "{{formattedMoney}} ₽" +} as const; diff --git a/src/locales/it/config.ts b/src/locales/it/config.ts index ceb52665796..d6265061a9f 100644 --- a/src/locales/it/config.ts +++ b/src/locales/it/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const itConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/ja/battle-scene.ts b/src/locales/ja/battle-scene.ts new file mode 100644 index 00000000000..d2f074416e1 --- /dev/null +++ b/src/locales/ja/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "{{formattedMoney}}円" +} as const; diff --git a/src/locales/ja/config.ts b/src/locales/ja/config.ts index 6af79547a04..61be36c08df 100644 --- a/src/locales/ja/config.ts +++ b/src/locales/ja/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -61,6 +62,7 @@ export const jaConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/ko/battle-scene.ts b/src/locales/ko/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/ko/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/ko/config.ts b/src/locales/ko/config.ts index 114950a4d35..2bc60f04bef 100644 --- a/src/locales/ko/config.ts +++ b/src/locales/ko/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const koConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/pt_BR/battle-scene.ts b/src/locales/pt_BR/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/pt_BR/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/pt_BR/config.ts b/src/locales/pt_BR/config.ts index b48fcfdc8d8..9ebb4867ae4 100644 --- a/src/locales/pt_BR/config.ts +++ b/src/locales/pt_BR/config.ts @@ -4,6 +4,7 @@ import { PGFachv, PGMachv } from "./achv"; import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const ptBrConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/zh_CN/battle-scene.ts b/src/locales/zh_CN/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/zh_CN/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/zh_CN/config.ts b/src/locales/zh_CN/config.ts index 24c8b870ffa..99b4e56ffc2 100644 --- a/src/locales/zh_CN/config.ts +++ b/src/locales/zh_CN/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const zhCnConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, diff --git a/src/locales/zh_TW/battle-scene.ts b/src/locales/zh_TW/battle-scene.ts new file mode 100644 index 00000000000..573e1791724 --- /dev/null +++ b/src/locales/zh_TW/battle-scene.ts @@ -0,0 +1,5 @@ +import { SimpleTranslationEntries } from "#app/interfaces/locales"; + +export const battleScene: SimpleTranslationEntries = { + "moneyOwned": "₽{{formattedMoney}}" +} as const; diff --git a/src/locales/zh_TW/config.ts b/src/locales/zh_TW/config.ts index 004ed1da1ab..269ea3003b9 100644 --- a/src/locales/zh_TW/config.ts +++ b/src/locales/zh_TW/config.ts @@ -4,6 +4,7 @@ import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; import { PGFachv, PGMachv } from "./achv"; import { battle } from "./battle"; +import { battleScene } from "./battle-scene"; import { battleInfo } from "./battle-info"; import { battleMessageUiHandler } from "./battle-message-ui-handler"; import { battlerTags } from "./battler-tags"; @@ -60,6 +61,7 @@ export const zhTwConfig = { arenaFlyout: arenaFlyout, arenaTag: arenaTag, battle: battle, + battleScene: battleScene, battleInfo: battleInfo, battleMessageUiHandler: battleMessageUiHandler, battlePokemonForm: battlePokemonForm, From b1d4037a57c01d794a49c0f0a5d299918e153ac6 Mon Sep 17 00:00:00 2001 From: Leo Kim <47556641+KimJeongSun@users.noreply.github.com> Date: Thu, 22 Aug 2024 14:39:11 +0900 Subject: [PATCH 25/30] [Bug] Fix some damage formulas processed with ceil instead of floor (#3557) * fix damage calculations. add test code * define toIntValue function to replace every repeatitive min floor function. * revert unnecessary minimum boundary * update function name `toIntValue` -> `toDmgValue`. update comments. * add missing updates for changing function name * Update src/utils.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> * remove redundant comment * update import code for test with phase --------- Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> --- src/data/ability.ts | 34 +++++------ src/data/arena-tag.ts | 6 +- src/data/battler-tags.ts | 18 +++--- src/data/berry.ts | 2 +- src/data/move.ts | 31 +++++----- src/field/pokemon.ts | 10 +-- src/modifier/modifier.ts | 6 +- src/test/abilities/disguise.test.ts | 7 ++- src/test/abilities/heatproof.test.ts | 3 +- src/test/abilities/parental_bond.test.ts | 5 +- src/test/battle/damage_calculation.test.ts | 71 ++++++++++++++++++++++ src/test/moves/belly_drum.test.ts | 7 ++- src/test/moves/clangorous_soul.test.ts | 7 ++- src/test/moves/fillet_away.test.ts | 7 ++- src/test/moves/tackle.test.ts | 2 +- src/utils.ts | 14 +++++ 16 files changed, 161 insertions(+), 69 deletions(-) create mode 100644 src/test/battle/damage_calculation.test.ts diff --git a/src/data/ability.ts b/src/data/ability.ts index 27a11eb0b9a..284a9cb4e91 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -301,7 +301,7 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr { applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { - (args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * this.damageMultiplier); + (args[0] as Utils.NumberHolder).value = Utils.toDmgValue((args[0] as Utils.NumberHolder).value * this.damageMultiplier); return true; } @@ -390,7 +390,7 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { if (!pokemon.isFullHp() && !simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 4), 1), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + Utils.toDmgValue(pokemon.getMaxHp() / 4), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); } return true; } @@ -904,8 +904,8 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { if (!simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { - attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); - attacker.turnData.damageTaken += Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)); + attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); + attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); return true; } @@ -2049,7 +2049,7 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { if (target?.isActive(true)) { if (!simulated) { target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / this.healRatio), 1), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); + Utils.toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); } return true; @@ -2440,7 +2440,7 @@ export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise { if (!pokemon.isFullHp()) { if (!simulated) { - const healAmount = Math.floor(pokemon.getMaxHp() * 0.33); + const healAmount = Utils.toDmgValue(pokemon.getMaxHp() * 0.33); pokemon.heal(healAmount); pokemon.updateInfo(); } @@ -3074,7 +3074,7 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; if (!simulated) { scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / (16 / this.healFactor)), 1), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); } return true; } @@ -3101,7 +3101,7 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; scene.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName })); - pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / (16 / this.damageFactor)), HitResult.OTHER); + pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.damageFactor)), HitResult.OTHER); } return true; @@ -3181,7 +3181,7 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { const scene = pokemon.scene; const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 8), 1), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); + Utils.toDmgValue(pokemon.getMaxHp() / 8), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); } return true; } @@ -3350,7 +3350,7 @@ export class PostTurnHealAbAttr extends PostTurnAbAttr { const scene = pokemon.scene; const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 16), 1), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + Utils.toDmgValue(pokemon.getMaxHp() / 16), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); } return true; @@ -3402,7 +3402,7 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { for (const opp of pokemon.getOpponents()) { if ((opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(Abilities.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { if (!simulated) { - opp.damageAndUpdate(Math.floor(Math.max(1, opp.getMaxHp() / 8)), HitResult.OTHER); + opp.damageAndUpdate(Utils.toDmgValue(opp.getMaxHp() / 8), HitResult.OTHER); pokemon.scene.queueMessage(i18next.t("abilityTriggers:badDreams", {pokemonName: getPokemonNameWithAffix(opp)})); } hadEffect = true; @@ -3604,7 +3604,7 @@ export class ReduceBurnDamageAbAttr extends AbAttr { * @returns `true` */ apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = Math.max(Math.floor((args[0] as Utils.NumberHolder).value * this.multiplier), 1); + (args[0] as Utils.NumberHolder).value = Utils.toDmgValue((args[0] as Utils.NumberHolder).value * this.multiplier); return true; } @@ -3649,7 +3649,7 @@ export class HealFromBerryUseAbAttr extends AbAttr { new PokemonHealPhase( pokemon.scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() * this.healPercent), 1), + Utils.toDmgValue(pokemon.getMaxHp() * this.healPercent), i18next.t("abilityTriggers:healFromBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true ) @@ -3840,8 +3840,8 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { return false; } if (!simulated) { - attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); - attacker.turnData.damageTaken += Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)); + attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); + attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); } return true; } @@ -3922,7 +3922,7 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr { apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (args[0] === this.statusEffect) { - (args[1] as Utils.IntegerHolder).value = Math.floor((args[1] as Utils.IntegerHolder).value / 2); + (args[1] as Utils.IntegerHolder).value = Utils.toDmgValue((args[1] as Utils.IntegerHolder).value / 2); return true; } @@ -5211,7 +5211,7 @@ export function initAbilities() { .conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false) .attr(FormBlockDamageAbAttr, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getAttackTypeEffectiveness(move.type, user) > 0, 0, BattlerTagType.DISGUISE, (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }), - (pokemon) => Math.floor(pokemon.getMaxHp() / 8)) + (pokemon) => Utils.toDmgValue(pokemon.getMaxHp() / 8)) .attr(PostBattleInitFormChangeAbAttr, () => 0) .bypassFaint() .ignorable(), diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 026003d0b61..a60ea5c2981 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -427,7 +427,7 @@ class WishTag extends ArenaTag { if (user) { this.battlerIndex = user.getBattlerIndex(); this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) }); - this.healHp = Math.max(Math.floor(user.getMaxHp() / 2), 1); + this.healHp = Utils.toDmgValue(user.getMaxHp() / 2); } else { console.warn("Failed to get source for WishTag onAdd"); } @@ -585,7 +585,7 @@ class SpikesTag extends ArenaTrapTag { if (!cancelled.value) { const damageHpRatio = 1 / (10 - 2 * this.layers); - const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio); + const damage = Utils.toDmgValue(pokemon.getMaxHp() * damageHpRatio); pokemon.scene.queueMessage(i18next.t("arenaTag:spikesActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.damageAndUpdate(damage, HitResult.OTHER); @@ -745,7 +745,7 @@ class StealthRockTag extends ArenaTrapTag { const damageHpRatio = this.getDamageHpRatio(pokemon); if (damageHpRatio) { - const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio); + const damage = Utils.toDmgValue(pokemon.getMaxHp() * damageHpRatio); pokemon.scene.queueMessage(i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.damageAndUpdate(damage, HitResult.OTHER); if (pokemon.turnData) { diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index ede8d029327..8c05d296e76 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -347,7 +347,7 @@ export class ConfusedTag extends BattlerTag { if (pokemon.randSeedInt(3) === 0) { const atk = pokemon.getBattleStat(Stat.ATK); const def = pokemon.getBattleStat(Stat.DEF); - const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100)); + const damage = Utils.toDmgValue(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100)); pokemon.scene.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself")); pokemon.damageAndUpdate(damage); pokemon.battleData.hitCount++; @@ -524,7 +524,7 @@ export class SeedTag extends BattlerTag { if (!cancelled.value) { pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED)); - const damage = pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1)); + const damage = pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 8)); const reverseDrain = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr, false); pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), !reverseDrain ? damage : damage * -1, @@ -570,7 +570,7 @@ export class NightmareTag extends BattlerTag { applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (!cancelled.value) { - pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 4)); + pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 4)); } } @@ -714,7 +714,7 @@ export class IngrainTag extends TrappedTag { new PokemonHealPhase( pokemon.scene, pokemon.getBattlerIndex(), - Math.floor(pokemon.getMaxHp() / 16), + Utils.toDmgValue(pokemon.getMaxHp() / 16), i18next.t("battlerTags:ingrainLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), true ) @@ -777,7 +777,7 @@ export class AquaRingTag extends BattlerTag { new PokemonHealPhase( pokemon.scene, pokemon.getBattlerIndex(), - Math.floor(pokemon.getMaxHp() / 16), + Utils.toDmgValue(pokemon.getMaxHp() / 16), i18next.t("battlerTags:aquaRingLapse", { moveName: this.getMoveName(), pokemonName: getPokemonNameWithAffix(pokemon) @@ -883,7 +883,7 @@ export abstract class DamagingTrapTag extends TrappedTag { applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (!cancelled.value) { - pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 8)); + pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 8)); } } @@ -1067,7 +1067,7 @@ export class ContactDamageProtectedTag extends ProtectedTag { if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) { const attacker = effectPhase.getPokemon(); if (!attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { - attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); + attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); } } } @@ -1541,7 +1541,7 @@ export class SaltCuredTag extends BattlerTag { if (!cancelled.value) { const pokemonSteelOrWater = pokemon.isOfType(Type.STEEL) || pokemon.isOfType(Type.WATER); - pokemon.damageAndUpdate(Math.max(Math.floor(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8), 1)); + pokemon.damageAndUpdate(Utils.toDmgValue(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8)); pokemon.scene.queueMessage( i18next.t("battlerTags:saltCuredLapse", { @@ -1587,7 +1587,7 @@ export class CursedTag extends BattlerTag { applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (!cancelled.value) { - pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 4), 1)); + pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 4)); pokemon.scene.queueMessage(i18next.t("battlerTags:cursedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); } } diff --git a/src/data/berry.ts b/src/data/berry.ts index ff82a683e5a..d0c9c311e16 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -70,7 +70,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { if (pokemon.battleData) { pokemon.battleData.berriesEaten.push(berryType); } - const hpHealed = new Utils.NumberHolder(Math.floor(pokemon.getMaxHp() / 4)); + const hpHealed = new Utils.NumberHolder(Utils.toDmgValue(pokemon.getMaxHp() / 4)); applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed); pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true)); diff --git a/src/data/move.ts b/src/data/move.ts index 682ab0739d5..b1b82009f3e 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1162,7 +1162,7 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.IntegerHolder).value = Math.max(Math.floor(target.hp / 2), 1); + (args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(target.hp / 2); return true; } @@ -1208,7 +1208,7 @@ export class CounterDamageAttr extends FixedDamageAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const damage = user.turnData.attacksReceived.filter(ar => this.moveFilter(allMoves[ar.move])).reduce((total: integer, ar: AttackMoveResult) => total + ar.damage, 0); - (args[0] as Utils.IntegerHolder).value = Math.floor(Math.max(damage * this.multiplier, 1)); + (args[0] as Utils.IntegerHolder).value = Utils.toDmgValue(damage * this.multiplier); return true; } @@ -1234,7 +1234,7 @@ export class RandomLevelDamageAttr extends FixedDamageAttr { } getDamage(user: Pokemon, target: Pokemon, move: Move): number { - return Math.max(Math.floor(user.level * (user.randSeedIntRange(50, 150) * 0.01)), 1); + return Utils.toDmgValue(user.level * (user.randSeedIntRange(50, 150) * 0.01)); } } @@ -1293,8 +1293,9 @@ export class RecoilAttr extends MoveEffectAttr { return false; } - const recoilDamage = Math.max(Math.floor((!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) * this.damageRatio), - user.turnData.damageDealt ? 1 : 0); + const damageValue = (!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) * this.damageRatio; + const minValue = user.turnData.damageDealt ? 1 : 0; + const recoilDamage = Utils.toDmgValue(damageValue, minValue); if (!recoilDamage) { return false; } @@ -1415,7 +1416,7 @@ export class HalfSacrificialAttr extends MoveEffectAttr { // Check to see if the Pokemon has an ability that blocks non-direct damage applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled); if (!cancelled.value) { - user.damageAndUpdate(Math.ceil(user.getMaxHp()/2), HitResult.OTHER, false, true, true); + user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp()/2), HitResult.OTHER, false, true, true); user.scene.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", {pokemonName: getPokemonNameWithAffix(user)})); // Queue recoil message } return true; @@ -1466,7 +1467,7 @@ export class HealAttr extends MoveEffectAttr { */ addHealPhase(target: Pokemon, healRatio: number) { target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), - Math.max(Math.floor(target.getMaxHp() * healRatio), 1), i18next.t("moveTriggers:healHp", {pokemonName: getPokemonNameWithAffix(target)}), true, !this.showAnim)); + Utils.toDmgValue(target.getMaxHp() * healRatio), i18next.t("moveTriggers:healHp", {pokemonName: getPokemonNameWithAffix(target)}), true, !this.showAnim)); } getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { @@ -1750,7 +1751,7 @@ export class HitHealAttr extends MoveEffectAttr { message = i18next.t("battle:drainMessage", {pokemonName: getPokemonNameWithAffix(target)}); } else { // Default healing formula used by draining moves like Absorb, Draining Kiss, Bitter Blade, etc. - healAmount = Math.max(Math.floor(user.turnData.currDamageDealt * this.healRatio), 1); + healAmount = Utils.toDmgValue(user.turnData.currDamageDealt * this.healRatio); message = i18next.t("battle:regainHealth", {pokemonName: getPokemonNameWithAffix(user)}); } if (reverseDrain) { @@ -2710,7 +2711,7 @@ export class CutHpStatBoostAttr extends StatChangeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { return new Promise(resolve => { - user.damageAndUpdate(Math.floor(user.getMaxHp() / this.cutRatio), HitResult.OTHER, false, true); + user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / this.cutRatio), HitResult.OTHER, false, true); user.updateInfo().then(() => { const ret = super.apply(user, target, move, args); if (this.messageCallback) { @@ -3190,7 +3191,7 @@ export class CompareWeightPowerAttr extends VariablePowerAttr { export class HpPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = Math.max(Math.floor(150 * user.getHpRatio()), 1); + (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(150 * user.getHpRatio()); return true; } @@ -3218,7 +3219,7 @@ export class OpponentHighHpPowerAttr extends VariablePowerAttr { * @returns true */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = Math.max(Math.floor(this.maxBasePower * target.getHpRatio()), 1); + (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(this.maxBasePower * target.getHpRatio()); return true; } @@ -3412,7 +3413,7 @@ export class PresentPowerAttr extends VariablePowerAttr { // If this move is multi-hit, disable all other hits user.stopMultiHit(); target.scene.unshiftPhase(new PokemonHealPhase(target.scene, target.getBattlerIndex(), - Math.max(Math.floor(target.getMaxHp() / 4), 1), i18next.t("moveTriggers:regainedHealth", {pokemonName: getPokemonNameWithAffix(target)}), true)); + Utils.toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", {pokemonName: getPokemonNameWithAffix(target)}), true)); } return true; @@ -4232,9 +4233,9 @@ const crashDamageFunc = (user: Pokemon, move: Move) => { return false; } - user.damageAndUpdate(Math.floor(user.getMaxHp() / 2), HitResult.OTHER, false, true); + user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / 2), HitResult.OTHER, false, true); user.scene.queueMessage(i18next.t("moveTriggers:keptGoingAndCrashed", {pokemonName: getPokemonNameWithAffix(user)})); - user.turnData.damageTaken += Math.floor(user.getMaxHp() / 2); + user.turnData.damageTaken += Utils.toDmgValue(user.getMaxHp() / 2); return true; }; @@ -4944,7 +4945,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr { const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length)]; const slotIndex = user.scene.getEnemyParty().findIndex(p => pokemon.id === p.id); pokemon.resetStatus(); - pokemon.heal(Math.min(Math.max(Math.ceil(Math.floor(0.5 * pokemon.getMaxHp())), 1), pokemon.getMaxHp())); + pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); user.scene.queueMessage(`${getPokemonNameWithAffix(pokemon)} was revived!`,0,true); if (user.scene.currentBattle.double && user.scene.getEnemyParty().length > 1) { diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 999ebb3cb21..030297a2126 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2092,7 +2092,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!isTypeImmune) { const levelMultiplier = (2 * source.level / 5 + 2); const randomMultiplier = ((this.scene.randBattleSeedInt(16) + 85) / 100); - damage.value = Math.ceil((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2) + damage.value = Utils.toDmgValue((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value @@ -2108,7 +2108,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const burnDamageReductionCancelled = new Utils.BooleanHolder(false); applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, false); if (!burnDamageReductionCancelled.value) { - damage.value = Math.floor(damage.value / 2); + damage.value = Utils.toDmgValue(damage.value / 2); } } } @@ -2129,7 +2129,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && move.type === Type.DRAGON) { - damage.value = Math.floor(damage.value / 2); + damage.value = Utils.toDmgValue(damage.value / 2); } const fixedDamage = new Utils.IntegerHolder(0); @@ -3455,7 +3455,7 @@ export class PlayerPokemon extends Pokemon { pokemon.resetTurnData(); pokemon.resetStatus(); - pokemon.heal(Math.min(Math.max(Math.ceil(Math.floor(0.5 * pokemon.getMaxHp())), 1), pokemon.getMaxHp())); + pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); this.scene.queueMessage(`${pokemon.name} was revived!`,0,true); if (this.scene.currentBattle.double && this.scene.getParty().length > 1) { @@ -4382,7 +4382,7 @@ export class PokemonMove { } getMovePp(): integer { - return this.getMove().pp + this.ppUp * Math.max(Math.floor(this.getMove().pp / 5), 1); + return this.getMove().pp + this.ppUp * Utils.toDmgValue(this.getMove().pp / 5); } getPpRatio(): number { diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 8a6598f5849..99f4540f493 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1160,7 +1160,7 @@ export class TurnHealModifier extends PokemonHeldItemModifier { if (!pokemon.isFullHp()) { const scene = pokemon.scene; scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 16) * this.stackCount, 1), i18next.t("modifier:turnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true)); + Utils.toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount, i18next.t("modifier:turnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true)); return true; } @@ -1251,7 +1251,7 @@ export class HitHealModifier extends PokemonHeldItemModifier { if (pokemon.turnData.damageDealt && !pokemon.isFullHp()) { const scene = pokemon.scene; scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.turnData.damageDealt / 8) * this.stackCount, 1), i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true)); + Utils.toDmgValue(pokemon.turnData.damageDealt / 8) * this.stackCount, i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true)); } return true; @@ -1386,7 +1386,7 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier { const pokemon = args[0] as Pokemon; pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), - Math.max(Math.floor(pokemon.getMaxHp() / 2), 1), i18next.t("modifier:pokemonInstantReviveApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), false, false, true)); + Utils.toDmgValue(pokemon.getMaxHp() / 2), i18next.t("modifier:pokemonInstantReviveApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), false, false, true)); pokemon.resetStatus(true, false, true); return true; diff --git a/src/test/abilities/disguise.test.ts b/src/test/abilities/disguise.test.ts index 969375c397e..a22c4cb55d5 100644 --- a/src/test/abilities/disguise.test.ts +++ b/src/test/abilities/disguise.test.ts @@ -6,6 +6,7 @@ import { Species } from "#enums/species"; import { StatusEffect } from "#app/data/status-effect.js"; import { BattleStat } from "#app/data/battle-stat.js"; import { SPLASH_ONLY } from "../utils/testUtils"; +import { toDmgValue } from "#app/utils"; import { Mode } from "#app/ui/ui.js"; import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; import { MoveEndPhase } from "#app/phases/move-end-phase.js"; @@ -47,7 +48,7 @@ describe("Abilities - Disguise", () => { const mimikyu = game.scene.getEnemyPokemon()!; const maxHp = mimikyu.getMaxHp(); - const disguiseDamage = Math.floor(maxHp / 8); + const disguiseDamage = toDmgValue(maxHp / 8); expect(mimikyu.formIndex).toBe(disguisedForm); @@ -80,7 +81,7 @@ describe("Abilities - Disguise", () => { const mimikyu = game.scene.getEnemyPokemon()!; const maxHp = mimikyu.getMaxHp(); - const disguiseDamage = Math.floor(maxHp / 8); + const disguiseDamage = toDmgValue(maxHp / 8); expect(mimikyu.formIndex).toBe(disguisedForm); @@ -121,7 +122,7 @@ describe("Abilities - Disguise", () => { const mimikyu = game.scene.getPlayerPokemon()!; const maxHp = mimikyu.getMaxHp(); - const disguiseDamage = Math.floor(maxHp / 8); + const disguiseDamage = toDmgValue(maxHp / 8); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); diff --git a/src/test/abilities/heatproof.test.ts b/src/test/abilities/heatproof.test.ts index 64a45c5023f..ee6c0bb6ec9 100644 --- a/src/test/abilities/heatproof.test.ts +++ b/src/test/abilities/heatproof.test.ts @@ -8,6 +8,7 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { SPLASH_ONLY } from "#test/utils/testUtils"; import { StatusEffect } from "#app/enums/status-effect.js"; +import { toDmgValue } from "#app/utils"; describe("Abilities - Heatproof", () => { let phaserGame: Phaser.Game; @@ -72,6 +73,6 @@ describe("Abilities - Heatproof", () => { await game.toNextTurn(); // Normal burn damage is /16 - expect(enemy.hp).toBe(enemy.getMaxHp() - Math.floor(enemy.getMaxHp() / 32)); + expect(enemy.hp).toBe(enemy.getMaxHp() - toDmgValue(enemy.getMaxHp() / 32)); }); }); diff --git a/src/test/abilities/parental_bond.test.ts b/src/test/abilities/parental_bond.test.ts index ef0ad7785d2..d14d5871ef7 100644 --- a/src/test/abilities/parental_bond.test.ts +++ b/src/test/abilities/parental_bond.test.ts @@ -16,6 +16,7 @@ import { DamagePhase } from "#app/phases/damage-phase.js"; import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; import { MoveEndPhase } from "#app/phases/move-end-phase.js"; import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import { toDmgValue } from "#app/utils"; const TIMEOUT = 20 * 1000; @@ -73,7 +74,7 @@ describe("Abilities - Parental Bond", () => { const secondStrikeDamage = enemyStartingHp - enemyPokemon.hp; expect(leadPokemon.turnData.hitCount).toBe(2); - expect(secondStrikeDamage).toBe(Math.ceil(0.25 * firstStrikeDamage)); + expect(secondStrikeDamage).toBe(toDmgValue(0.25 * firstStrikeDamage)); }, TIMEOUT ); @@ -303,7 +304,7 @@ describe("Abilities - Parental Bond", () => { // This test will time out if the user faints await game.phaseInterceptor.to(BerryPhase, false); - expect(leadPokemon.hp).toBe(Math.floor(leadPokemon.getMaxHp()/2)); + expect(leadPokemon.hp).toBe(toDmgValue(leadPokemon.getMaxHp()/2)); }, TIMEOUT ); diff --git a/src/test/battle/damage_calculation.test.ts b/src/test/battle/damage_calculation.test.ts new file mode 100644 index 00000000000..9b13a266d33 --- /dev/null +++ b/src/test/battle/damage_calculation.test.ts @@ -0,0 +1,71 @@ +import { DamagePhase } from "#app/phases/damage-phase.js"; +import GameManager from "#test/utils/gameManager"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; +import { toDmgValue } from "#app/utils"; + +describe("Round Down and Minimun 1 test in Damage Calculation", () => { + 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"); + game.override.startingLevel(10); + }); + + it("When the user fails to use Jump Kick with Wonder Guard ability, the damage should be 1.", async () => { + game.override.enemySpecies(Species.GASTLY); + game.override.enemyMoveset(SPLASH_ONLY); + game.override.starterSpecies(Species.SHEDINJA); + game.override.moveset([Moves.JUMP_KICK]); + game.override.ability(Abilities.WONDER_GUARD); + + await game.startBattle(); + + const shedinja = game.scene.getPlayerPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.JUMP_KICK)); + + await game.phaseInterceptor.to(DamagePhase); + + expect(shedinja.hp).toBe(shedinja.getMaxHp() - 1); + }); + + + it("Charizard with odd HP survives Stealth Rock damage twice", async () => { + game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0); + game.override.seed("Charizard Stealth Rock test"); + game.override.enemySpecies(Species.CHARIZARD); + game.override.enemyAbility(Abilities.BLAZE); + game.override.starterSpecies(Species.PIKACHU); + game.override.enemyLevel(100); + + await game.startBattle(); + + const charizard = game.scene.getEnemyPokemon()!; + + const maxHp = charizard.getMaxHp(); + const damage_prediction = toDmgValue(charizard.getMaxHp() / 2); + const currentHp = charizard.hp; + const expectedHP = maxHp - damage_prediction; + + expect(currentHp).toBe(expectedHP); + }); +}); diff --git a/src/test/moves/belly_drum.test.ts b/src/test/moves/belly_drum.test.ts index e579a4587ad..229314c96e6 100644 --- a/src/test/moves/belly_drum.test.ts +++ b/src/test/moves/belly_drum.test.ts @@ -6,6 +6,7 @@ import { getMovePosition } from "#test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { BattleStat } from "#app/data/battle-stat"; +import { toDmgValue } from "#app/utils"; const TIMEOUT = 20 * 1000; // RATIO : HP Cost of Move @@ -44,7 +45,7 @@ describe("Moves - BELLY DRUM", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); await game.phaseInterceptor.to(TurnEndPhase); @@ -59,7 +60,7 @@ describe("Moves - BELLY DRUM", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); // Here - BattleStat.ATK -> -3 and BattleStat.SPATK -> 6 leadPokemon.summonData.battleStats[BattleStat.ATK] = -3; @@ -95,7 +96,7 @@ describe("Moves - BELLY DRUM", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); diff --git a/src/test/moves/clangorous_soul.test.ts b/src/test/moves/clangorous_soul.test.ts index 5b2e8b6e06d..afab4c2e9be 100644 --- a/src/test/moves/clangorous_soul.test.ts +++ b/src/test/moves/clangorous_soul.test.ts @@ -7,6 +7,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { BattleStat } from "#app/data/battle-stat"; import { SPLASH_ONLY } from "#test/utils/testUtils"; +import { toDmgValue } from "#app/utils"; const TIMEOUT = 20 * 1000; /** HP Cost of Move */ @@ -45,7 +46,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); await game.phaseInterceptor.to(TurnEndPhase); @@ -64,7 +65,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); //Here - BattleStat.SPD -> 0 and BattleStat.SPDEF -> 4 leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; @@ -113,7 +114,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); diff --git a/src/test/moves/fillet_away.test.ts b/src/test/moves/fillet_away.test.ts index fcad704ef29..fc87d600eb5 100644 --- a/src/test/moves/fillet_away.test.ts +++ b/src/test/moves/fillet_away.test.ts @@ -7,6 +7,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { BattleStat } from "#app/data/battle-stat"; import { SPLASH_ONLY } from "#test/utils/testUtils"; +import { toDmgValue } from "#app/utils"; const TIMEOUT = 20 * 1000; /** HP Cost of Move */ @@ -45,7 +46,7 @@ describe("Moves - FILLET AWAY", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); await game.phaseInterceptor.to(TurnEndPhase); @@ -62,7 +63,7 @@ describe("Moves - FILLET AWAY", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); //Here - BattleStat.SPD -> 0 and BattleStat.SPATK -> 3 leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; @@ -103,7 +104,7 @@ describe("Moves - FILLET AWAY", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); + const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); diff --git a/src/test/moves/tackle.test.ts b/src/test/moves/tackle.test.ts index f442645baa9..3da8bc6f978 100644 --- a/src/test/moves/tackle.test.ts +++ b/src/test/moves/tackle.test.ts @@ -78,6 +78,6 @@ describe("Moves - Tackle", () => { await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp; expect(hpLost).toBeGreaterThan(0); - expect(hpLost).toBe(4); + expect(hpLost).toBeLessThan(4); }, 20000); }); diff --git a/src/utils.ts b/src/utils.ts index c51ac2b5b0b..a9bbc93d684 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -560,3 +560,17 @@ export function capitalizeString(str: string, sep: string, lowerFirstChar: boole export function isNullOrUndefined(object: any): boolean { return null === object || undefined === object; } + +/** + * This function is used in the context of a Pokémon battle game to calculate the actual integer damage value from a float result. + * Many damage calculation formulas involve various parameters and result in float values. + * The actual damage applied to a Pokémon's HP must be an integer. + * This function helps in ensuring that by flooring the float value and enforcing a minimum damage value. + * + * @param value - The float value to convert. + * @param minValue - The minimum integer value to return. Defaults to 1. + * @returns The converted value as an integer. + */ +export function toDmgValue(value: number, minValue: number = 1) { + return Math.max(Math.floor(value), minValue); +} From 10f1a96ed6b3977e7f30ff90a7f9c770ff44fab5 Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Thu, 22 Aug 2024 05:04:26 -0700 Subject: [PATCH 26/30] Increase Target Select UI Opacitiy (#3683) Co-authored-by: frutescens --- src/ui/target-select-ui-handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/target-select-ui-handler.ts b/src/ui/target-select-ui-handler.ts index 42c7fef5660..6ca580dc2b2 100644 --- a/src/ui/target-select-ui-handler.ts +++ b/src/ui/target-select-ui-handler.ts @@ -118,7 +118,7 @@ export default class TargetSelectUiHandler extends UiHandler { this.targetFlashTween = this.scene.tweens.add({ targets: this.targetsHighlighted, - key: { start: 0.55, to: 1 }, + key: { start: 1, to: 0.25 }, loop: -1, loopDelay: 150, duration: Utils.fixedInt(450), From 828897316e089ba390bc5fd3503e9175c7f45e8e Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Thu, 22 Aug 2024 06:49:33 -0700 Subject: [PATCH 27/30] [Test] Replace `doAttack()` with `move.select()` in tests (#3567) * Consolidate `doSelectTarget()` into `doAttack()` * Fix ternary * Add error message to aid in debugging tests * Update docs * [Test] Change `doAttack()` to `selectMove()` * Add `select()` to `src/test/utils/helpers/moveHelper.ts` * Replace instances of `game.selectMove()` with `game.move.select()` * Fix imports * Replace `selectMove()` with `move.select()` helper Fix broken tests for Pastel Veil and Sweet Veil * Update tsdocs --- src/test/abilities/ability_timing.test.ts | 10 +- src/test/abilities/aura_break.test.ts | 15 ++- src/test/abilities/battery.test.ts | 25 ++-- src/test/abilities/battle_bond.test.ts | 9 +- src/test/abilities/costar.test.ts | 25 ++-- src/test/abilities/disguise.test.ts | 41 +++---- src/test/abilities/dry_skin.test.ts | 29 +++-- src/test/abilities/flash_fire.test.ts | 43 ++++--- src/test/abilities/gulp_missile.test.ts | 47 ++++--- src/test/abilities/heatproof.test.ts | 19 ++- src/test/abilities/hustle.test.ts | 23 ++-- src/test/abilities/hyper_cutter.test.ts | 11 +- src/test/abilities/ice_face.test.ts | 37 +++--- src/test/abilities/intimidate.test.ts | 71 +++-------- src/test/abilities/intrepid_sword.test.ts | 4 +- src/test/abilities/libero.test.ts | 49 ++++---- src/test/abilities/magic_guard.test.ts | 69 +++++------ src/test/abilities/moxie.test.ts | 22 +--- src/test/abilities/mycelium_might.test.ts | 27 ++-- src/test/abilities/parental_bond.test.ts | 95 +++++++------- src/test/abilities/pastel_veil.test.ts | 66 +++++----- src/test/abilities/power_construct.test.ts | 9 +- src/test/abilities/power_spot.test.ts | 25 ++-- src/test/abilities/protean.test.ts | 49 ++++---- src/test/abilities/quick_draw.test.ts | 11 +- src/test/abilities/sand_spit.test.ts | 13 +- src/test/abilities/sand_veil.test.ts | 19 ++- src/test/abilities/sap_sipper.test.ts | 35 +++--- src/test/abilities/schooling.test.ts | 9 +- src/test/abilities/screen_cleaner.test.ts | 15 ++- src/test/abilities/serene_grace.test.ts | 32 ++--- src/test/abilities/sheer_force.test.ts | 58 +++------ src/test/abilities/shield_dust.test.ts | 22 +--- src/test/abilities/shields_down.test.ts | 9 +- src/test/abilities/stall.test.ts | 23 ++-- src/test/abilities/steely_spirit.test.ts | 34 ++--- src/test/abilities/sturdy.test.ts | 17 ++- src/test/abilities/sweet_veil.test.ts | 53 ++++---- src/test/abilities/unseen_fist.test.ts | 7 +- src/test/abilities/volt_absorb.test.ts | 9 +- src/test/abilities/wind_power.test.ts | 17 ++- src/test/abilities/wind_rider.test.ts | 19 ++- src/test/abilities/wonder_skin.test.ts | 17 ++- src/test/abilities/zen_mode.test.ts | 55 +++------ src/test/abilities/zero_to_hero.test.ts | 13 +- src/test/account.spec.ts | 2 +- src/test/achievements/achievement.test.ts | 4 +- src/test/arena/arena_gravity.test.ts | 19 ++- src/test/arena/weather_fog.test.ts | 13 +- src/test/arena/weather_strong_winds.test.ts | 15 ++- src/test/battle-scene.test.ts | 2 +- src/test/battle-stat.spec.ts | 2 +- src/test/battle/battle-order.test.ts | 112 +++-------------- src/test/battle/battle.test.ts | 116 ++++++++---------- src/test/battle/damage_calculation.test.ts | 11 +- src/test/battle/double_battle.test.ts | 17 ++- src/test/battle/error-handling.test.ts | 10 +- src/test/battle/special_battle.test.ts | 4 +- src/test/battlerTags/octolock.test.ts | 12 +- src/test/battlerTags/stockpiling.test.ts | 12 +- src/test/eggs/egg.test.ts | 18 +-- src/test/evolution.test.ts | 6 +- src/test/evolutions/evolutions.test.ts | 8 +- src/test/field/pokemon.test.ts | 2 +- src/test/final_boss.test.ts | 6 +- src/test/game-mode.test.ts | 4 +- src/test/imports.test.ts | 2 +- src/test/inputs/inputs.test.ts | 8 +- src/test/internals.test.ts | 8 +- src/test/items/eviolite.test.ts | 2 +- src/test/items/exp_booster.test.ts | 6 +- src/test/items/grip_claw.test.ts | 33 ++--- src/test/items/leek.test.ts | 28 ++--- src/test/items/leftovers.test.ts | 13 +- src/test/items/light_ball.test.ts | 2 +- src/test/items/lock_capsule.test.ts | 15 ++- src/test/items/metal_powder.test.ts | 2 +- src/test/items/quick_powder.test.ts | 2 +- src/test/items/scope_lens.test.ts | 16 +-- src/test/items/thick_club.test.ts | 2 +- src/test/items/toxic_orb.test.ts | 24 +--- src/test/localization/battle-stat.test.ts | 41 +++---- src/test/localization/french.test.ts | 10 +- src/test/localization/status-effect.test.ts | 4 +- src/test/localization/terrain.test.ts | 4 +- src/test/moves/astonish.test.ts | 19 ++- src/test/moves/aurora_veil.test.ts | 41 +++---- src/test/moves/baton_pass.test.ts | 19 ++- src/test/moves/beak_blast.test.ts | 31 +++-- src/test/moves/beat_up.test.ts | 23 ++-- src/test/moves/belly_drum.test.ts | 29 +++-- src/test/moves/ceaseless_edge.test.ts | 29 +++-- src/test/moves/clangorous_soul.test.ts | 35 +++--- src/test/moves/crafty_shield.test.ts | 31 +++-- src/test/moves/double_team.test.ts | 11 +- src/test/moves/dragon_rage.test.ts | 23 ++-- src/test/moves/dragon_tail.test.ts | 61 +++------ src/test/moves/dynamax_cannon.test.ts | 49 ++++---- src/test/moves/fillet_away.test.ts | 29 +++-- src/test/moves/fissure.test.ts | 17 ++- src/test/moves/flame_burst.test.ts | 52 ++++---- src/test/moves/flower_shield.test.ts | 25 ++-- src/test/moves/focus_punch.test.ts | 27 ++-- src/test/moves/follow_me.test.ts | 90 ++++---------- src/test/moves/foresight.test.ts | 23 ++-- src/test/moves/freezy_frost.test.ts | 21 ++-- src/test/moves/fusion_bolt.test.ts | 19 ++- src/test/moves/fusion_flare.test.ts | 19 ++- src/test/moves/fusion_flare_bolt.test.ts | 108 +++++++--------- src/test/moves/gastro_acid.test.ts | 29 ++--- src/test/moves/glaive_rush.test.ts | 49 ++++---- src/test/moves/growth.test.ts | 22 +--- src/test/moves/hard_press.test.ts | 17 ++- src/test/moves/haze.test.ts | 19 ++- src/test/moves/hyper_beam.test.ts | 19 ++- src/test/moves/jaw_lock.test.ts | 47 ++++--- src/test/moves/light_screen.test.ts | 33 +++-- src/test/moves/lucky_chant.test.ts | 29 +++-- src/test/moves/magnet_rise.test.ts | 14 +-- src/test/moves/make_it_rain.test.ts | 25 ++-- src/test/moves/mat_block.test.ts | 31 +++-- src/test/moves/miracle_eye.test.ts | 19 ++- src/test/moves/multi_target.test.ts | 23 ++-- src/test/moves/octolock.test.ts | 19 ++- src/test/moves/parting_shot.test.ts | 37 +++--- src/test/moves/protect.test.ts | 23 ++-- src/test/moves/purify.test.ts | 15 ++- src/test/moves/quick_guard.test.ts | 25 ++-- src/test/moves/rage_powder.test.ts | 53 ++------ src/test/moves/reflect.test.ts | 33 +++-- src/test/moves/rollout.test.ts | 10 +- src/test/moves/roost.test.ts | 19 ++- src/test/moves/shell_trap.test.ts | 37 +++--- src/test/moves/spikes.test.ts | 38 +++--- src/test/moves/spit_up.test.ts | 28 ++--- src/test/moves/spotlight.test.ts | 51 ++------ src/test/moves/stockpile.test.ts | 19 ++- src/test/moves/swallow.test.ts | 26 ++-- src/test/moves/tackle.test.ts | 32 ++--- src/test/moves/tail_whip.test.ts | 22 +--- src/test/moves/tailwind.test.ts | 27 ++-- src/test/moves/tera_blast.test.ts | 39 +++--- src/test/moves/thousand_arrows.test.ts | 27 ++-- src/test/moves/tidy_up.test.ts | 47 ++++--- src/test/moves/u_turn.test.ts | 21 ++-- src/test/moves/wide_guard.test.ts | 29 +++-- src/test/phases/phases.test.ts | 14 +-- src/test/settingMenu/helpers/inGameManip.ts | 2 +- src/test/settingMenu/helpers/menuManip.ts | 4 +- .../settingMenu/rebinding_setting.test.ts | 14 +-- src/test/sprites/pokemonSprite.test.ts | 6 +- src/test/ui/starter-select.test.ts | 28 ++--- src/test/ui/transfer-item.test.ts | 10 +- src/test/ui/type-hints.test.ts | 8 +- src/test/utils/gameManager.ts | 107 +++++++--------- src/test/utils/gameManagerUtils.ts | 10 +- src/test/utils/gameWrapper.ts | 35 +++--- src/test/utils/helpers/classicModeHelper.ts | 12 +- src/test/utils/helpers/dailyModeHelper.ts | 12 +- src/test/utils/helpers/moveHelper.ts | 29 ++++- src/test/utils/inputsHandler.ts | 8 +- src/test/utils/misc.test.ts | 6 +- src/test/utils/mocks/mockTextureManager.ts | 10 +- .../utils/mocks/mocksContainer/mockSprite.ts | 2 +- .../utils/mocks/mocksContainer/mockTexture.ts | 2 +- src/test/utils/phaseInterceptor.ts | 74 +++++------ src/test/vitest.setup.ts | 4 +- 167 files changed, 1827 insertions(+), 2378 deletions(-) diff --git a/src/test/abilities/ability_timing.test.ts b/src/test/abilities/ability_timing.test.ts index c117c62d45b..3238f880992 100644 --- a/src/test/abilities/ability_timing.test.ts +++ b/src/test/abilities/ability_timing.test.ts @@ -1,15 +1,15 @@ +import { CommandPhase } from "#app/phases/command-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import i18next, { initI18n } from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { MessagePhase } from "#app/phases/message-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; describe("Ability Timing", () => { diff --git a/src/test/abilities/aura_break.test.ts b/src/test/abilities/aura_break.test.ts index bca400bc0e3..7de300c157a 100644 --- a/src/test/abilities/aura_break.test.ts +++ b/src/test/abilities/aura_break.test.ts @@ -1,19 +1,18 @@ -import { allMoves } from "#app/data/move.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; describe("Abilities - Aura Break", () => { let phaserGame: Phaser.Game; let game: GameManager; - const auraBreakMultiplier = 9/16 * 4/3; + const auraBreakMultiplier = 9 / 16 * 4 / 3; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -42,7 +41,7 @@ describe("Abilities - Aura Break", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.MOONBLAST)); + game.move.select(Moves.MOONBLAST); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(expect.closeTo(basePower * auraBreakMultiplier)); @@ -56,7 +55,7 @@ describe("Abilities - Aura Break", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DARK_PULSE)); + game.move.select(Moves.DARK_PULSE); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(expect.closeTo(basePower * auraBreakMultiplier)); diff --git a/src/test/abilities/battery.test.ts b/src/test/abilities/battery.test.ts index 766c1c30ecc..020866509d6 100644 --- a/src/test/abilities/battery.test.ts +++ b/src/test/abilities/battery.test.ts @@ -1,14 +1,13 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Abilities - Battery", () => { let phaserGame: Phaser.Game; @@ -43,8 +42,8 @@ describe("Abilities - Battery", () => { await game.startBattle([Species.PIKACHU, Species.CHARJABUG]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DAZZLING_GLEAM)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.DAZZLING_GLEAM); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower * batteryMultiplier); @@ -58,8 +57,8 @@ describe("Abilities - Battery", () => { await game.startBattle([Species.PIKACHU, Species.CHARJABUG]); - game.doAttack(getMovePosition(game.scene, 0, Moves.BREAKING_SWIPE)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.BREAKING_SWIPE); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower); @@ -73,8 +72,8 @@ describe("Abilities - Battery", () => { await game.startBattle([Species.CHARJABUG, Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DAZZLING_GLEAM)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.DAZZLING_GLEAM); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower); diff --git a/src/test/abilities/battle_bond.test.ts b/src/test/abilities/battle_bond.test.ts index c28a00e821d..71e9438db8f 100644 --- a/src/test/abilities/battle_bond.test.ts +++ b/src/test/abilities/battle_bond.test.ts @@ -1,11 +1,10 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import { Status, StatusEffect } from "#app/data/status-effect"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; @@ -53,7 +52,7 @@ describe("Abilities - BATTLE BOND", () => { greninja!.status = new Status(StatusEffect.FAINT); expect(greninja!.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); diff --git a/src/test/abilities/costar.test.ts b/src/test/abilities/costar.test.ts index 9410ee55069..9a4baeef1fb 100644 --- a/src/test/abilities/costar.test.ts +++ b/src/test/abilities/costar.test.ts @@ -1,14 +1,13 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; +import { BattleStat } from "#app/data/battle-stat"; +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { MessagePhase } from "#app/phases/message-phase.js"; const TIMEOUT = 20 * 1000; @@ -44,15 +43,15 @@ describe("Abilities - COSTAR", () => { let [leftPokemon, rightPokemon] = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.NASTY_PLOT)); + game.move.select(Moves.NASTY_PLOT); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.toNextTurn(); expect(leftPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(+2); expect(rightPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(CommandPhase); game.doSwitchPokemon(2); await game.phaseInterceptor.to(MessagePhase); @@ -76,7 +75,7 @@ describe("Abilities - COSTAR", () => { expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(CommandPhase); game.doSwitchPokemon(2); await game.phaseInterceptor.to(MessagePhase); diff --git a/src/test/abilities/disguise.test.ts b/src/test/abilities/disguise.test.ts index a22c4cb55d5..58087b408a5 100644 --- a/src/test/abilities/disguise.test.ts +++ b/src/test/abilities/disguise.test.ts @@ -1,18 +1,17 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { StatusEffect } from "#app/data/status-effect"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { Mode } from "#app/ui/ui"; +import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { StatusEffect } from "#app/data/status-effect.js"; -import { BattleStat } from "#app/data/battle-stat.js"; +import GameManager from "#test/utils/gameManager"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { toDmgValue } from "#app/utils"; -import { Mode } from "#app/ui/ui.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; const TIMEOUT = 20 * 1000; @@ -52,7 +51,7 @@ describe("Abilities - Disguise", () => { expect(mimikyu.formIndex).toBe(disguisedForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SHADOW_SNEAK)); + game.move.select(Moves.SHADOW_SNEAK); await game.phaseInterceptor.to(MoveEndPhase); @@ -67,7 +66,7 @@ describe("Abilities - Disguise", () => { expect(mimikyu.formIndex).toBe(disguisedForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.VACUUM_WAVE)); + game.move.select(Moves.VACUUM_WAVE); await game.phaseInterceptor.to(MoveEndPhase); @@ -85,7 +84,7 @@ describe("Abilities - Disguise", () => { expect(mimikyu.formIndex).toBe(disguisedForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURGING_STRIKES)); + game.move.select(Moves.SURGING_STRIKES); // First hit await game.phaseInterceptor.to(MoveEffectPhase); @@ -104,7 +103,7 @@ describe("Abilities - Disguise", () => { const mimikyu = game.scene.getEnemyPokemon()!; expect(mimikyu.hp).toBe(mimikyu.getMaxHp()); - game.doAttack(getMovePosition(game.scene, 0, Moves.TOXIC_THREAD)); + game.move.select(Moves.TOXIC_THREAD); await game.phaseInterceptor.to(TurnEndPhase); @@ -124,7 +123,7 @@ describe("Abilities - Disguise", () => { const maxHp = mimikyu.getMaxHp(); const disguiseDamage = toDmgValue(maxHp / 8); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -149,7 +148,7 @@ describe("Abilities - Disguise", () => { const mimikyu = game.scene.getParty()[1]!; expect(mimikyu.formIndex).toBe(bustedForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.toNextWave(); @@ -169,7 +168,7 @@ describe("Abilities - Disguise", () => { expect(mimikyu.formIndex).toBe(bustedForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.toNextWave(); @@ -189,11 +188,11 @@ describe("Abilities - Disguise", () => { expect(mimikyu1.formIndex).toBe(bustedForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.killPokemon(mimikyu1); game.doSelectPartyPokemon(1); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { // TODO: Make tests run in set mode instead of switch mode game.setMode(Mode.MESSAGE); diff --git a/src/test/abilities/dry_skin.test.ts b/src/test/abilities/dry_skin.test.ts index 1e3860da985..b337e4d96f7 100644 --- a/src/test/abilities/dry_skin.test.ts +++ b/src/test/abilities/dry_skin.test.ts @@ -1,12 +1,11 @@ -import { Species } from "#app/enums/species.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { Species } from "#app/enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Abilities - Dry Skin", () => { let phaserGame: Phaser.Game; @@ -43,13 +42,13 @@ describe("Abilities - Dry Skin", () => { // first turn let previousEnemyHp = enemy.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.SUNNY_DAY)); + game.move.select(Moves.SUNNY_DAY); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeLessThan(previousEnemyHp); // second turn previousEnemyHp = enemy.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeLessThan(previousEnemyHp); }); @@ -66,13 +65,13 @@ describe("Abilities - Dry Skin", () => { // first turn let previousEnemyHp = enemy.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.RAIN_DANCE)); + game.move.select(Moves.RAIN_DANCE); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeGreaterThan(previousEnemyHp); // second turn previousEnemyHp = enemy.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeGreaterThan(previousEnemyHp); }); @@ -87,7 +86,7 @@ describe("Abilities - Dry Skin", () => { enemy.hp = initialHP; // first turn - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER)); + game.move.select(Moves.FLAMETHROWER); await game.phaseInterceptor.to(TurnEndPhase); const fireDamageTakenWithDrySkin = initialHP - enemy.hp; @@ -96,7 +95,7 @@ describe("Abilities - Dry Skin", () => { game.override.enemyAbility(Abilities.NONE); // second turn - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER)); + game.move.select(Moves.FLAMETHROWER); await game.phaseInterceptor.to(TurnEndPhase); const fireDamageTakenWithoutDrySkin = initialHP - enemy.hp; @@ -113,7 +112,7 @@ describe("Abilities - Dry Skin", () => { enemy.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); + game.move.select(Moves.WATER_GUN); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeGreaterThan(1); }); @@ -129,7 +128,7 @@ describe("Abilities - Dry Skin", () => { enemy.hp = 1; game.override.enemyMoveset([Moves.PROTECT, Moves.PROTECT, Moves.PROTECT, Moves.PROTECT]); - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); + game.move.select(Moves.WATER_GUN); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBe(1); }); @@ -145,14 +144,14 @@ describe("Abilities - Dry Skin", () => { enemy.hp = 1; // first turn - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_SHURIKEN)); + game.move.select(Moves.WATER_SHURIKEN); await game.phaseInterceptor.to(TurnEndPhase); const healthGainedFromWaterShuriken = enemy.hp - 1; enemy.hp = 1; // second turn - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); + game.move.select(Moves.WATER_GUN); await game.phaseInterceptor.to(TurnEndPhase); const healthGainedFromWaterGun = enemy.hp - 1; diff --git a/src/test/abilities/flash_fire.test.ts b/src/test/abilities/flash_fire.test.ts index 28c59903b68..de40873998f 100644 --- a/src/test/abilities/flash_fire.test.ts +++ b/src/test/abilities/flash_fire.test.ts @@ -1,16 +1,15 @@ -import { Species } from "#app/enums/species.js"; -import GameManager from "#test/utils/gameManager"; +import { BattlerIndex } from "#app/battle"; +import { StatusEffect } from "#app/data/status-effect"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Species } from "#app/enums/species"; +import { MovePhase } from "#app/phases/move-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { StatusEffect } from "#app/data/status-effect.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { BattlerIndex } from "#app/battle.js"; -import { MovePhase } from "#app/phases/move-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Abilities - Flash Fire", () => { let phaserGame: Phaser.Game; @@ -38,35 +37,35 @@ describe("Abilities - Flash Fire", () => { }); - it("immune to Fire-type moves", async() => { + it("immune to Fire-type moves", async () => { game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset(SPLASH_ONLY); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(blissey.hp).toBe(blissey.getMaxHp()); }, 20000); - it("not activate if the Pokémon is protected from the Fire-type move", async() => { + it("not activate if the Pokémon is protected from the Fire-type move", async () => { game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset([Moves.PROTECT]); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(TurnEndPhase); expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined(); }, 20000); - it("activated by Will-O-Wisp", async() => { + it("activated by Will-O-Wisp", async () => { game.override.enemyMoveset(Array(4).fill(Moves.WILL_O_WISP)).moveset(SPLASH_ONLY); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.move.forceHit(); await game.phaseInterceptor.to(MovePhase, false); await game.move.forceHit(); @@ -75,25 +74,25 @@ describe("Abilities - Flash Fire", () => { expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeDefined(); }, 20000); - it("activated after being frozen", async() => { + it("activated after being frozen", async () => { game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset(SPLASH_ONLY); game.override.statusEffect(StatusEffect.FREEZE); await game.startBattle([Species.BLISSEY]); const blissey = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeDefined(); }, 20000); - it("not passing with baton pass", async() => { + it("not passing with baton pass", async () => { game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset([Moves.BATON_PASS]); await game.startBattle([Species.BLISSEY, Species.CHANSEY]); // ensure use baton pass after enemy moved - game.doAttack(getMovePosition(game.scene, 0, Moves.BATON_PASS)); + game.move.select(Moves.BATON_PASS); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); game.doSelectPartyPokemon(1); @@ -104,7 +103,7 @@ describe("Abilities - Flash Fire", () => { expect(chansey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined(); }, 20000); - it("boosts Fire-type move when the ability is activated", async() => { + it("boosts Fire-type move when the ability is activated", async () => { game.override.enemyMoveset(Array(4).fill(Moves.FIRE_PLEDGE)).moveset([Moves.EMBER, Moves.SPLASH]); game.override.enemyAbility(Abilities.FLASH_FIRE).ability(Abilities.NONE); await game.startBattle([Species.BLISSEY]); @@ -113,7 +112,7 @@ describe("Abilities - Flash Fire", () => { blissey.hp = initialHP; // first turn - game.doAttack(getMovePosition(game.scene, 0, Moves.EMBER)); + game.move.select(Moves.EMBER); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to(TurnEndPhase); const originalDmg = initialHP - blissey.hp; @@ -122,7 +121,7 @@ describe("Abilities - Flash Fire", () => { blissey.hp = initialHP; // second turn - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); const flashFireDmg = initialHP - blissey.hp; diff --git a/src/test/abilities/gulp_missile.test.ts b/src/test/abilities/gulp_missile.test.ts index d033604fe00..a451d290906 100644 --- a/src/test/abilities/gulp_missile.test.ts +++ b/src/test/abilities/gulp_missile.test.ts @@ -1,19 +1,18 @@ -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; +import { BattleStat } from "#app/data/battle-stat"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { StatusEffect } from "#app/enums/status-effect"; +import Pokemon from "#app/field/pokemon"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import Pokemon from "#app/field/pokemon.js"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import { TurnStartPhase } from "#app/phases/turn-start-phase.js"; describe("Abilities - Gulp Missile", () => { let phaserGame: Phaser.Game; @@ -30,7 +29,7 @@ describe("Abilities - Gulp Missile", () => { * @returns The effect damage of Gulp Missile */ const getEffectDamage = (pokemon: Pokemon): number => { - return Math.max(1, Math.floor(pokemon.getMaxHp() * 1/4)); + return Math.max(1, Math.floor(pokemon.getMaxHp() * 1 / 4)); }; beforeAll(() => { @@ -58,9 +57,9 @@ describe("Abilities - Gulp Missile", () => { await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + game.move.select(Moves.DIVE); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + game.move.select(Moves.DIVE); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getHpRatio()).toBeGreaterThanOrEqual(.5); @@ -75,7 +74,7 @@ describe("Abilities - Gulp Missile", () => { vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.49); expect(cramorant.getHpRatio()).toBe(.49); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined(); @@ -86,7 +85,7 @@ describe("Abilities - Gulp Missile", () => { await game.startBattle([Species.CRAMORANT, Species.MAGIKARP]); const cramorant = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.toNextTurn(); game.doSwitchPokemon(1); @@ -101,7 +100,7 @@ describe("Abilities - Gulp Missile", () => { await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + game.move.select(Moves.DIVE); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -115,7 +114,7 @@ describe("Abilities - Gulp Missile", () => { const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "damageAndUpdate"); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy)); @@ -128,7 +127,7 @@ describe("Abilities - Gulp Missile", () => { const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -150,7 +149,7 @@ describe("Abilities - Gulp Missile", () => { vi.spyOn(enemy, "damageAndUpdate"); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -174,7 +173,7 @@ describe("Abilities - Gulp Missile", () => { vi.spyOn(enemy, "damageAndUpdate"); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.45); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeDefined(); @@ -194,7 +193,7 @@ describe("Abilities - Gulp Missile", () => { const cramorant = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + game.move.select(Moves.DIVE); await game.phaseInterceptor.to(BerryPhase, false); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -210,7 +209,7 @@ describe("Abilities - Gulp Missile", () => { vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); const enemyHpPreEffect = enemy.hp; @@ -232,7 +231,7 @@ describe("Abilities - Gulp Missile", () => { const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -252,7 +251,7 @@ describe("Abilities - Gulp Missile", () => { const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(MoveEndPhase); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); @@ -269,7 +268,7 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyAbility(Abilities.TRACE); await game.startBattle([Species.CRAMORANT]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnStartPhase); expect(game.scene.getEnemyPokemon()?.hasAbility(Abilities.GULP_MISSILE)).toBe(false); diff --git a/src/test/abilities/heatproof.test.ts b/src/test/abilities/heatproof.test.ts index ee6c0bb6ec9..e2a558e6d99 100644 --- a/src/test/abilities/heatproof.test.ts +++ b/src/test/abilities/heatproof.test.ts @@ -1,14 +1,13 @@ -import { Species } from "#app/enums/species.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { Species } from "#app/enums/species"; +import { StatusEffect } from "#app/enums/status-effect"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import { toDmgValue } from "#app/utils"; describe("Abilities - Heatproof", () => { let phaserGame: Phaser.Game; @@ -46,14 +45,14 @@ describe("Abilities - Heatproof", () => { const initialHP = 1000; enemy.hp = initialHP; - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER)); + game.move.select(Moves.FLAMETHROWER); await game.phaseInterceptor.to(TurnEndPhase); const heatproofDamage = initialHP - enemy.hp; enemy.hp = initialHP; game.override.enemyAbility(Abilities.BALL_FETCH); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER)); + game.move.select(Moves.FLAMETHROWER); await game.phaseInterceptor.to(TurnEndPhase); const regularDamage = initialHP - enemy.hp; @@ -69,7 +68,7 @@ describe("Abilities - Heatproof", () => { const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); // Normal burn damage is /16 diff --git a/src/test/abilities/hustle.test.ts b/src/test/abilities/hustle.test.ts index 8f5547a5518..b7c3b723c4b 100644 --- a/src/test/abilities/hustle.test.ts +++ b/src/test/abilities/hustle.test.ts @@ -1,15 +1,14 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Stat } from "#app/enums/stat.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { Stat } from "#app/enums/stat"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; describe("Abilities - Hustle", () => { let phaserGame: Phaser.Game; @@ -44,7 +43,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(pikachu, "getBattleStat"); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -57,7 +56,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(pikachu, "getAccuracyMultiplier"); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEffectPhase); expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(0.8); @@ -71,7 +70,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(pikachu, "getBattleStat"); vi.spyOn(pikachu, "getAccuracyMultiplier"); - game.doAttack(getMovePosition(game.scene, 0, Moves.GIGA_DRAIN)); + game.move.select(Moves.GIGA_DRAIN); await game.phaseInterceptor.to(DamagePhase); expect(pikachu.getBattleStat).toHaveReturnedWith(spatk); @@ -89,7 +88,7 @@ describe("Abilities - Hustle", () => { vi.spyOn(pikachu, "getAccuracyMultiplier"); vi.spyOn(allMoves[Moves.FISSURE], "calculateBattleAccuracy"); - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); await game.phaseInterceptor.to(DamagePhase); expect(enemyPokemon.turnData.damageTaken).toBe(enemyPokemon.getMaxHp()); diff --git a/src/test/abilities/hyper_cutter.test.ts b/src/test/abilities/hyper_cutter.test.ts index 9637a80ddb4..28fcc2f6085 100644 --- a/src/test/abilities/hyper_cutter.test.ts +++ b/src/test/abilities/hyper_cutter.test.ts @@ -3,7 +3,6 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -40,16 +39,16 @@ describe("Abilities - Hyper Cutter", () => { const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.OCTOLOCK)); + game.move.select(Moves.OCTOLOCK); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.DEFOG)); + game.move.select(Moves.DEFOG); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.NOBLE_ROAR)); + game.move.select(Moves.NOBLE_ROAR); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SAND_ATTACK)); + game.move.select(Moves.SAND_ATTACK); await game.toNextTurn(); game.override.moveset([Moves.STRING_SHOT]); - game.doAttack(getMovePosition(game.scene, 0, Moves.STRING_SHOT)); + game.move.select(Moves.STRING_SHOT); await game.toNextTurn(); expect(enemy.summonData.battleStats[BattleStat.ATK]).toEqual(0); diff --git a/src/test/abilities/ice_face.test.ts b/src/test/abilities/ice_face.test.ts index 905e0dfdaf7..fbc660c27c2 100644 --- a/src/test/abilities/ice_face.test.ts +++ b/src/test/abilities/ice_face.test.ts @@ -1,16 +1,15 @@ -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; describe("Abilities - Ice Face", () => { let phaserGame: Phaser.Game; @@ -39,7 +38,7 @@ describe("Abilities - Ice Face", () => { it("takes no damage from physical move and transforms to Noice", async () => { await game.startBattle([Species.HITMONLEE]); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEndPhase); @@ -55,7 +54,7 @@ describe("Abilities - Ice Face", () => { game.override.enemyLevel(1); await game.startBattle([Species.HITMONLEE]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURGING_STRIKES)); + game.move.select(Moves.SURGING_STRIKES); const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined(); @@ -81,7 +80,7 @@ describe("Abilities - Ice Face", () => { it("takes damage from special moves", async () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); + game.move.select(Moves.ICE_BEAM); await game.phaseInterceptor.to(MoveEndPhase); @@ -95,7 +94,7 @@ describe("Abilities - Ice Face", () => { it("takes effects from status moves", async () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.TOXIC_THREAD)); + game.move.select(Moves.TOXIC_THREAD); await game.phaseInterceptor.to(MoveEndPhase); @@ -111,7 +110,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.phaseInterceptor.to(MoveEndPhase); @@ -133,7 +132,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.EISCUE, Species.NINJASK]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SNOWSCAPE)); + game.move.select(Moves.SNOWSCAPE); await game.phaseInterceptor.to(TurnEndPhase); let eiscue = game.scene.getPlayerPokemon()!; @@ -160,7 +159,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.EISCUE]); - game.doAttack(getMovePosition(game.scene, 0, Moves.HAIL)); + game.move.select(Moves.HAIL); const eiscue = game.scene.getPlayerPokemon()!; await game.phaseInterceptor.to(QuietFormChangePhase); @@ -179,7 +178,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.EISCUE, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); + game.move.select(Moves.ICE_BEAM); await game.phaseInterceptor.to(TurnEndPhase); let eiscue = game.scene.getPlayerPokemon()!; @@ -213,7 +212,7 @@ describe("Abilities - Ice Face", () => { expect(eiscue.formIndex).toBe(noiceForm); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); + game.move.select(Moves.ICE_BEAM); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); @@ -228,7 +227,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.GASTRO_ACID)); + game.move.select(Moves.GASTRO_ACID); await game.phaseInterceptor.to(TurnEndPhase); @@ -244,7 +243,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SKILL_SWAP)); + game.move.select(Moves.SKILL_SWAP); await game.phaseInterceptor.to(TurnEndPhase); @@ -260,7 +259,7 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SIMPLE_BEAM)); + game.move.select(Moves.SIMPLE_BEAM); await game.phaseInterceptor.to(TurnInitPhase); diff --git a/src/test/abilities/intimidate.test.ts b/src/test/abilities/intimidate.test.ts index 842b33108a3..93b663d06da 100644 --- a/src/test/abilities/intimidate.test.ts +++ b/src/test/abilities/intimidate.test.ts @@ -1,22 +1,21 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Mode } from "#app/ui/ui"; import { BattleStat } from "#app/data/battle-stat"; -import { generateStarter, getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; import { Status, StatusEffect } from "#app/data/status-effect"; import { GameModes, getGameMode } from "#app/game-mode"; +import { CommandPhase } from "#app/phases/command-phase"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { generateStarter } from "#test/utils/gameManagerUtils"; import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { EncounterPhase } from "#app/phases/encounter-phase.js"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Abilities - Intimidate", () => { let phaserGame: Phaser.Game; @@ -217,13 +216,7 @@ describe("Abilities - Intimidate", () => { let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(DamagePhase); await game.killPokemon(game.scene.currentBattle.enemyParty[0]); expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true); @@ -243,13 +236,7 @@ describe("Abilities - Intimidate", () => { let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); console.log("===to new turn==="); await game.toNextTurn(); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -268,13 +255,7 @@ describe("Abilities - Intimidate", () => { let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); console.log("===to new turn==="); await game.toNextTurn(); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -282,13 +263,7 @@ describe("Abilities - Intimidate", () => { battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); console.log("===to new turn==="); await game.toNextTurn(); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -307,13 +282,7 @@ describe("Abilities - Intimidate", () => { let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); console.log("===to new turn==="); await game.toNextTurn(); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; @@ -321,13 +290,7 @@ describe("Abilities - Intimidate", () => { battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.AERIAL_ACE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.AERIAL_ACE); console.log("===to new turn==="); await game.toNextTurn(); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; diff --git a/src/test/abilities/intrepid_sword.test.ts b/src/test/abilities/intrepid_sword.test.ts index c1c05b59997..18d6c04adbc 100644 --- a/src/test/abilities/intrepid_sword.test.ts +++ b/src/test/abilities/intrepid_sword.test.ts @@ -1,10 +1,10 @@ import { BattleStat } from "#app/data/battle-stat"; -import GameManager from "#test/utils/gameManager"; +import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; describe("Abilities - Intrepid Sword", () => { diff --git a/src/test/abilities/libero.test.ts b/src/test/abilities/libero.test.ts index d35cb8b6e2d..16597e90285 100644 --- a/src/test/abilities/libero.test.ts +++ b/src/test/abilities/libero.test.ts @@ -1,18 +1,17 @@ -import { allMoves } from "#app/data/move.js"; -import { Type } from "#app/data/type.js"; -import { Weather, WeatherType } from "#app/data/weather.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; +import { allMoves } from "#app/data/move"; +import { Type } from "#app/data/type"; +import { Weather, WeatherType } from "#app/data/weather"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -49,7 +48,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); @@ -67,12 +66,12 @@ describe("Abilities - Libero", () => { let leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); - game.doAttack(getMovePosition(game.scene, 0, Moves.AGILITY)); + game.move.select(Moves.AGILITY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied.filter((a) => a === Abilities.LIBERO)).toHaveLength(1); @@ -89,7 +88,7 @@ describe("Abilities - Libero", () => { leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); @@ -108,7 +107,7 @@ describe("Abilities - Libero", () => { expect(leadPokemon).not.toBe(undefined); game.scene.arena.weather = new Weather(WeatherType.SUNNY); - game.doAttack(getMovePosition(game.scene, 0, Moves.WEATHER_BALL)); + game.move.select(Moves.WEATHER_BALL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).toContain(Abilities.LIBERO); @@ -131,7 +130,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).toContain(Abilities.LIBERO); @@ -154,7 +153,7 @@ describe("Abilities - Libero", () => { expect(leadPokemon).not.toBe(undefined); game.scene.arena.biomeType = Biome.MOUNTAIN; - game.doAttack(getMovePosition(game.scene, 0, Moves.NATURE_POWER)); + game.move.select(Moves.NATURE_POWER); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.AIR_SLASH); @@ -172,7 +171,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.DIG)); + game.move.select(Moves.DIG); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.DIG); @@ -191,7 +190,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.move.forceMiss(); await game.phaseInterceptor.to(TurnEndPhase); @@ -213,7 +212,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); @@ -232,7 +231,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); @@ -251,7 +250,7 @@ describe("Abilities - Libero", () => { expect(leadPokemon).not.toBe(undefined); leadPokemon.summonData.types = [allMoves[Moves.SPLASH].defaultType]; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); @@ -271,7 +270,7 @@ describe("Abilities - Libero", () => { vi.spyOn(leadPokemon, "isTerastallized").mockReturnValue(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); @@ -289,7 +288,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); + game.move.select(Moves.STRUGGLE); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); @@ -307,7 +306,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); + game.move.select(Moves.BURN_UP); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); @@ -326,7 +325,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TRICK_OR_TREAT)); + game.move.select(Moves.TRICK_OR_TREAT); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TRICK_OR_TREAT); @@ -344,7 +343,7 @@ describe("Abilities - Libero", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); + game.move.select(Moves.CURSE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.CURSE); diff --git a/src/test/abilities/magic_guard.test.ts b/src/test/abilities/magic_guard.test.ts index c7404f83a54..64c1746c7d9 100644 --- a/src/test/abilities/magic_guard.test.ts +++ b/src/test/abilities/magic_guard.test.ts @@ -1,17 +1,16 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import { Moves } from "#enums/moves"; -import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagSide, getArenaTag } from "#app/data/arena-tag"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Abilities } from "#enums/abilities"; -import { WeatherType } from "#app/data/weather.js"; import { StatusEffect, getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; +import { WeatherType } from "#app/data/weather"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Abilities } from "#enums/abilities"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import { SPLASH_ONLY } from "#test/utils/testUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const TIMEOUT = 20 * 1000; // 20 sec timeout @@ -58,7 +57,7 @@ describe("Abilities - Magic Guard", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -82,7 +81,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -99,14 +98,14 @@ describe("Abilities - Magic Guard", () => { it( "ability effect should not persist when the ability is replaced", async () => { - game.override.enemyMoveset([Moves.WORRY_SEED,Moves.WORRY_SEED,Moves.WORRY_SEED,Moves.WORRY_SEED]); + game.override.enemyMoveset([Moves.WORRY_SEED, Moves.WORRY_SEED, Moves.WORRY_SEED, Moves.WORRY_SEED]); game.override.statusEffect(StatusEffect.POISON); await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -126,7 +125,7 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -150,7 +149,7 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -180,7 +179,7 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -206,7 +205,7 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -233,7 +232,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); + game.move.select(Moves.CURSE); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -257,7 +256,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.HIGH_JUMP_KICK)); + game.move.select(Moves.HIGH_JUMP_KICK); await game.move.forceMiss(); await game.phaseInterceptor.to(TurnEndPhase); @@ -276,7 +275,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.TAKE_DOWN)); + game.move.select(Moves.TAKE_DOWN); await game.phaseInterceptor.to(TurnEndPhase); @@ -294,7 +293,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); + game.move.select(Moves.STRUGGLE); await game.phaseInterceptor.to(TurnEndPhase); @@ -313,7 +312,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.STEEL_BEAM)); + game.move.select(Moves.STEEL_BEAM); await game.phaseInterceptor.to(TurnEndPhase); @@ -329,7 +328,7 @@ describe("Abilities - Magic Guard", () => { it("Magic Guard does not prevent self-damage from confusion", async () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM)); + game.move.select(Moves.CHARM); await game.phaseInterceptor.to(TurnEndPhase); }); @@ -341,7 +340,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); + game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); @@ -353,7 +352,7 @@ describe("Abilities - Magic Guard", () => { }, TIMEOUT ); - it("Magic Guard prevents damage from abilities with PostTurnHurtIfSleepingAbAttr", async() => { + it("Magic Guard prevents damage from abilities with PostTurnHurtIfSleepingAbAttr", async () => { //Tests the ability Bad Dreams game.override.statusEffect(StatusEffect.SLEEP); //enemy pokemon is given Spore just in case player pokemon somehow awakens during test @@ -364,7 +363,7 @@ describe("Abilities - Magic Guard", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -378,7 +377,7 @@ describe("Abilities - Magic Guard", () => { }, TIMEOUT ); - it("Magic Guard prevents damage from abilities with PostFaintContactDamageAbAttr", async() => { + it("Magic Guard prevents damage from abilities with PostFaintContactDamageAbAttr", async () => { //Tests the abilities Innards Out/Aftermath game.override.moveset([Moves.TACKLE]); game.override.enemyAbility(Abilities.AFTERMATH); @@ -390,7 +389,7 @@ describe("Abilities - Magic Guard", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; enemyPokemon.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); /** @@ -403,7 +402,7 @@ describe("Abilities - Magic Guard", () => { }, TIMEOUT ); - it("Magic Guard prevents damage from abilities with PostDefendContactDamageAbAttr", async() => { + it("Magic Guard prevents damage from abilities with PostDefendContactDamageAbAttr", async () => { //Tests the abilities Iron Barbs/Rough Skin game.override.moveset([Moves.TACKLE]); game.override.enemyAbility(Abilities.IRON_BARBS); @@ -414,7 +413,7 @@ describe("Abilities - Magic Guard", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); /** @@ -427,7 +426,7 @@ describe("Abilities - Magic Guard", () => { }, TIMEOUT ); - it("Magic Guard prevents damage from abilities with ReverseDrainAbAttr", async() => { + it("Magic Guard prevents damage from abilities with ReverseDrainAbAttr", async () => { //Tests the ability Liquid Ooze game.override.moveset([Moves.ABSORB]); game.override.enemyAbility(Abilities.LIQUID_OOZE); @@ -438,7 +437,7 @@ describe("Abilities - Magic Guard", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.ABSORB)); + game.move.select(Moves.ABSORB); await game.phaseInterceptor.to(TurnEndPhase); /** @@ -451,14 +450,14 @@ describe("Abilities - Magic Guard", () => { }, TIMEOUT ); - it("Magic Guard prevents HP loss from abilities with PostWeatherLapseDamageAbAttr", async() => { + it("Magic Guard prevents HP loss from abilities with PostWeatherLapseDamageAbAttr", async () => { //Tests the abilities Solar Power/Dry Skin game.override.passiveAbility(Abilities.SOLAR_POWER); game.override.weather(WeatherType.SUNNY); await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); /** diff --git a/src/test/abilities/moxie.test.ts b/src/test/abilities/moxie.test.ts index 6550dcab526..6a1838c9a98 100644 --- a/src/test/abilities/moxie.test.ts +++ b/src/test/abilities/moxie.test.ts @@ -1,17 +1,13 @@ import { BattleStat } from "#app/data/battle-stat"; import { Stat } from "#app/data/pokemon-stat"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { VictoryPhase } from "#app/phases/victory-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js"; -import { VictoryPhase } from "#app/phases/victory-phase.js"; describe("Abilities - Moxie", () => { @@ -37,10 +33,10 @@ describe("Abilities - Moxie", () => { game.override.ability(Abilities.MOXIE); game.override.startingLevel(2000); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("MOXIE", async() => { + it("MOXIE", async () => { const moveToUse = Moves.AERIAL_ACE; await game.startBattle([ Species.MIGHTYENA, @@ -50,13 +46,7 @@ describe("Abilities - Moxie", () => { let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[Stat.ATK]).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(VictoryPhase); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.ATK]).toBe(1); diff --git a/src/test/abilities/mycelium_might.test.ts b/src/test/abilities/mycelium_might.test.ts index 2fcdc28b279..83396f7950f 100644 --- a/src/test/abilities/mycelium_might.test.ts +++ b/src/test/abilities/mycelium_might.test.ts @@ -1,11 +1,10 @@ -import { MovePhase } from "#app/phases/move-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Abilities } from "#enums/abilities"; import { BattleStat } from "#app/data/battle-stat"; +import { MovePhase } from "#app/phases/move-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -42,14 +41,14 @@ describe("Abilities - Mycelium Might", () => { * https://www.smogon.com/forums/threads/scarlet-violet-battle-mechanics-research.3709545/page-24 **/ - it("If a Pokemon with Mycelium Might uses a status move, it will always move last but the status move will ignore protective abilities", async() => { - await game.startBattle([ Species.REGIELEKI ]); + it("will move last in its priority bracket and ignore protective abilities", async () => { + await game.startBattle([Species.REGIELEKI]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyPokemon = game.scene.getEnemyPokemon(); const enemyIndex = enemyPokemon?.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.BABY_DOLL_EYES)); + game.move.select(Moves.BABY_DOLL_EYES); await game.phaseInterceptor.to(MovePhase, false); // The opponent Pokemon (without Mycelium Might) goes first despite having lower speed than the player Pokemon. @@ -64,15 +63,15 @@ describe("Abilities - Mycelium Might", () => { expect(enemyPokemon?.summonData.battleStats[BattleStat.ATK]).toBe(-1); }, 20000); - it("Pokemon with Mycelium Might will go first if a status move that is in a higher priority bracket than the opponent's move is used", async() => { + it("will still go first if a status move that is in a higher priority bracket than the opponent's move is used", async () => { game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); - await game.startBattle([ Species.REGIELEKI ]); + await game.startBattle([Species.REGIELEKI]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyPokemon = game.scene.getEnemyPokemon(); const enemyIndex = enemyPokemon?.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.BABY_DOLL_EYES)); + game.move.select(Moves.BABY_DOLL_EYES); await game.phaseInterceptor.to(MovePhase, false); // The player Pokemon (with M.M.) goes first because its move is still within a higher priority bracket than its opponent. @@ -86,13 +85,13 @@ describe("Abilities - Mycelium Might", () => { expect(enemyPokemon?.summonData.battleStats[BattleStat.ATK]).toBe(-1); }, 20000); - it("Order is established normally if the Pokemon uses a non-status move", async() => { - await game.startBattle([ Species.REGIELEKI ]); + it("will not affect non-status moves", async () => { + await game.startBattle([Species.REGIELEKI]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.phaseInterceptor.to(MovePhase, false); // The player Pokemon (with M.M.) goes first because it has a higher speed and did not use a status move. diff --git a/src/test/abilities/parental_bond.test.ts b/src/test/abilities/parental_bond.test.ts index d14d5871ef7..1404f597ccf 100644 --- a/src/test/abilities/parental_bond.test.ts +++ b/src/test/abilities/parental_bond.test.ts @@ -1,22 +1,21 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { StatusEffect } from "#app/data/status-effect.js"; -import { Type } from "#app/data/type.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; +import { BattleStat } from "#app/data/battle-stat"; +import { StatusEffect } from "#app/data/status-effect"; +import { Type } from "#app/data/type"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import { toDmgValue } from "#app/utils"; const TIMEOUT = 20 * 1000; @@ -61,7 +60,7 @@ describe("Abilities - Parental Bond", () => { let enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -92,7 +91,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.POWER_UP_PUNCH)); + game.move.select(Moves.POWER_UP_PUNCH); await game.phaseInterceptor.to(BerryPhase, false); @@ -114,7 +113,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.BABY_DOLL_EYES)); + game.move.select(Moves.BABY_DOLL_EYES); await game.phaseInterceptor.to(BerryPhase, false); expect(enemyPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-1); @@ -134,7 +133,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_HIT)); + game.move.select(Moves.DOUBLE_HIT); await game.move.forceHit(); await game.phaseInterceptor.to(BerryPhase, false); @@ -156,7 +155,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SELF_DESTRUCT)); + game.move.select(Moves.SELF_DESTRUCT); await game.phaseInterceptor.to(DamagePhase, false); @@ -177,7 +176,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT)); + game.move.select(Moves.ROLLOUT); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase, false); @@ -201,7 +200,7 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(BerryPhase, false); expect(enemyPokemon.hp).toBe(enemyStartingHp - 80); @@ -212,7 +211,7 @@ describe("Abilities - Parental Bond", () => { "ability should not apply multiplier to counter moves", async () => { game.override.moveset([Moves.COUNTER]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle([Species.CHARIZARD]); @@ -225,14 +224,14 @@ describe("Abilities - Parental Bond", () => { const playerStartingHp = leadPokemon.hp; const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.COUNTER)); + game.move.select(Moves.COUNTER); await game.phaseInterceptor.to(DamagePhase); const playerDamage = playerStartingHp - leadPokemon.hp; await game.phaseInterceptor.to(BerryPhase, false); - expect(enemyPokemon.hp).toBe(enemyStartingHp - 4*playerDamage); + expect(enemyPokemon.hp).toBe(enemyStartingHp - 4 * playerDamage); }, TIMEOUT ); @@ -252,10 +251,10 @@ describe("Abilities - Parental Bond", () => { expect(enemyPokemon.length).toBe(2); enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); - game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); + game.move.select(Moves.EARTHQUAKE); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.EARTHQUAKE)); + game.move.select(Moves.EARTHQUAKE, 1); await game.phaseInterceptor.to(BerryPhase, false); playerPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1)); @@ -275,7 +274,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); + game.move.select(Moves.EARTHQUAKE); await game.phaseInterceptor.to(DamagePhase, false); expect(leadPokemon.turnData.hitCount).toBe(2); @@ -295,7 +294,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.MIND_BLOWN)); + game.move.select(Moves.MIND_BLOWN); await game.phaseInterceptor.to(DamagePhase, false); @@ -304,7 +303,7 @@ describe("Abilities - Parental Bond", () => { // This test will time out if the user faints await game.phaseInterceptor.to(BerryPhase, false); - expect(leadPokemon.hp).toBe(toDmgValue(leadPokemon.getMaxHp()/2)); + expect(leadPokemon.hp).toBe(toDmgValue(leadPokemon.getMaxHp() / 2)); }, TIMEOUT ); @@ -321,7 +320,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); + game.move.select(Moves.BURN_UP); await game.phaseInterceptor.to(DamagePhase); @@ -339,7 +338,7 @@ describe("Abilities - Parental Bond", () => { "Moves boosted by this ability and Multi-Lens should strike 4 times", async () => { game.override.moveset([Moves.TACKLE]); - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.CHARIZARD]); @@ -349,7 +348,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(DamagePhase); @@ -361,7 +360,7 @@ describe("Abilities - Parental Bond", () => { "Super Fang boosted by this ability and Multi-Lens should strike twice", async () => { game.override.moveset([Moves.SUPER_FANG]); - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.CHARIZARD]); @@ -373,7 +372,7 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.SUPER_FANG)); + game.move.select(Moves.SUPER_FANG); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -390,7 +389,7 @@ describe("Abilities - Parental Bond", () => { "Seismic Toss boosted by this ability and Multi-Lens should strike twice", async () => { game.override.moveset([Moves.SEISMIC_TOSS]); - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.CHARIZARD]); @@ -402,7 +401,7 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.SEISMIC_TOSS)); + game.move.select(Moves.SEISMIC_TOSS); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -428,7 +427,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); + game.move.select(Moves.HYPER_BEAM); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -456,7 +455,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.ANCHOR_SHOT)); + game.move.select(Moves.ANCHOR_SHOT); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -486,7 +485,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SMACK_DOWN)); + game.move.select(Moves.SMACK_DOWN); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -513,7 +512,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); + game.move.select(Moves.U_TURN); await game.move.forceHit(); await game.phaseInterceptor.to(MoveEffectPhase); @@ -537,7 +536,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.WAKE_UP_SLAP)); + game.move.select(Moves.WAKE_UP_SLAP); await game.move.forceHit(); await game.phaseInterceptor.to(DamagePhase); @@ -555,7 +554,7 @@ describe("Abilities - Parental Bond", () => { "ability should not cause user to hit into King's Shield more than once", async () => { game.override.moveset([Moves.TACKLE]); - game.override.enemyMoveset([Moves.KINGS_SHIELD,Moves.KINGS_SHIELD,Moves.KINGS_SHIELD,Moves.KINGS_SHIELD]); + game.override.enemyMoveset([Moves.KINGS_SHIELD, Moves.KINGS_SHIELD, Moves.KINGS_SHIELD, Moves.KINGS_SHIELD]); await game.startBattle([Species.CHARIZARD]); @@ -565,7 +564,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(BerryPhase, false); @@ -587,7 +586,7 @@ describe("Abilities - Parental Bond", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); + game.move.select(Moves.WATER_GUN); await game.phaseInterceptor.to(BerryPhase, false); @@ -600,7 +599,7 @@ describe("Abilities - Parental Bond", () => { async () => { game.override.battleType("double"); game.override.moveset([Moves.EARTHQUAKE, Moves.SPLASH]); - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.CHARIZARD, Species.PIDGEOT]); @@ -614,10 +613,10 @@ describe("Abilities - Parental Bond", () => { const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); + game.move.select(Moves.EARTHQUAKE); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -626,7 +625,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to(BerryPhase, false); - enemyPokemon.forEach((p, i) => expect(enemyStartingHp[i] - p.hp).toBe(2*enemyFirstHitDamage[i])); + enemyPokemon.forEach((p, i) => expect(enemyStartingHp[i] - p.hp).toBe(2 * enemyFirstHitDamage[i])); }, TIMEOUT ); diff --git a/src/test/abilities/pastel_veil.test.ts b/src/test/abilities/pastel_veil.test.ts index cb6be666d5f..ba90c7e3b3f 100644 --- a/src/test/abilities/pastel_veil.test.ts +++ b/src/test/abilities/pastel_veil.test.ts @@ -1,15 +1,14 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; +import { BattlerIndex } from "#app/battle"; +import { StatusEffect } from "#app/data/status-effect"; +import { Abilities } from "#app/enums/abilities"; +import { CommandPhase } from "#app/phases/command-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { StatusEffect } from "#app/data/status-effect.js"; -import { allAbilities } from "#app/data/ability.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerIndex } from "#app/battle.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { SPLASH_ONLY } from "../utils/testUtils"; describe("Abilities - Pastel Veil", () => { let phaserGame: Phaser.Game; @@ -27,50 +26,49 @@ describe("Abilities - Pastel Veil", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); - game.override.moveset([Moves.SPLASH]); - game.override.enemyAbility(Abilities.BALL_FETCH); - game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyMoveset([Moves.TOXIC_THREAD, Moves.TOXIC_THREAD, Moves.TOXIC_THREAD, Moves.TOXIC_THREAD]); + game.override + .battleType("double") + .moveset([Moves.TOXIC_THREAD, Moves.SPLASH]) + .enemyAbility(Abilities.BALL_FETCH) + .enemySpecies(Species.SUNKERN) + .enemyMoveset(SPLASH_ONLY); }); it("prevents the user and its allies from being afflicted by poison", async () => { - await game.startBattle([Species.GALAR_PONYTA, Species.MAGIKARP]); - const ponyta = game.scene.getPlayerField()[0]; - - vi.spyOn(ponyta, "getAbility").mockReturnValue(allAbilities[Abilities.PASTEL_VEIL]); + await game.startBattle([Species.MAGIKARP, Species.GALAR_PONYTA]); + const ponyta = game.scene.getPlayerField()[1]; + const magikarp = game.scene.getPlayerField()[0]; + ponyta.abilityIndex = 1; expect(ponyta.hasAbility(Abilities.PASTEL_VEIL)).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.TOXIC_THREAD, 1, BattlerIndex.PLAYER); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerField().every(p => p.status?.effect)).toBe(false); + expect(magikarp.status?.effect).toBeUndefined(); }); it("it heals the poisoned status condition of allies if user is sent out into battle", async () => { - await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.GALAR_PONYTA]); - const ponyta = game.scene.getParty().find(p => p.species.speciesId === Species.GALAR_PONYTA)!; - - vi.spyOn(ponyta, "getAbility").mockReturnValue(allAbilities[Abilities.PASTEL_VEIL]); + await game.startBattle([Species.MAGIKARP, Species.FEEBAS, Species.GALAR_PONYTA]); + const ponyta = game.scene.getParty()[2]; + const magikarp = game.scene.getPlayerField()[0]; + ponyta.abilityIndex = 1; expect(ponyta.hasAbility(Abilities.PASTEL_VEIL)).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.TOXIC_THREAD, 1, BattlerIndex.PLAYER); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerField().some(p => p.status?.effect === StatusEffect.POISON)).toBe(true); - - const poisonedMon = game.scene.getPlayerField().find(p => p.status?.effect === StatusEffect.POISON); + expect(magikarp.status?.effect).toBe(StatusEffect.POISON); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, (poisonedMon!.getBattlerIndex() as BattlerIndex.PLAYER | BattlerIndex.PLAYER_2), Moves.SPLASH)); + game.move.select(Moves.SPLASH); game.doSwitchPokemon(2); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerField().every(p => p.status?.effect)).toBe(false); + expect(magikarp.status?.effect).toBeUndefined(); }); }); diff --git a/src/test/abilities/power_construct.test.ts b/src/test/abilities/power_construct.test.ts index e6a319d229f..ec37bc96c2f 100644 --- a/src/test/abilities/power_construct.test.ts +++ b/src/test/abilities/power_construct.test.ts @@ -1,11 +1,10 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import { Status, StatusEffect } from "#app/data/status-effect"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; @@ -53,7 +52,7 @@ describe("Abilities - POWER CONSTRUCT", () => { zygarde!.status = new Status(StatusEffect.FAINT); expect(zygarde!.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); diff --git a/src/test/abilities/power_spot.test.ts b/src/test/abilities/power_spot.test.ts index 467fc677ac0..b83284c0bac 100644 --- a/src/test/abilities/power_spot.test.ts +++ b/src/test/abilities/power_spot.test.ts @@ -1,14 +1,13 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Abilities - Power Spot", () => { let phaserGame: Phaser.Game; @@ -42,8 +41,8 @@ describe("Abilities - Power Spot", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); await game.startBattle([Species.REGIELEKI, Species.STONJOURNER]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DAZZLING_GLEAM)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.DAZZLING_GLEAM); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower * powerSpotMultiplier); @@ -56,8 +55,8 @@ describe("Abilities - Power Spot", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); await game.startBattle([Species.REGIELEKI, Species.STONJOURNER]); - game.doAttack(getMovePosition(game.scene, 0, Moves.BREAKING_SWIPE)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.BREAKING_SWIPE); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower * powerSpotMultiplier); @@ -70,8 +69,8 @@ describe("Abilities - Power Spot", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); await game.startBattle([Species.STONJOURNER, Species.REGIELEKI]); - game.doAttack(getMovePosition(game.scene, 0, Moves.BREAKING_SWIPE)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.BREAKING_SWIPE); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower); diff --git a/src/test/abilities/protean.test.ts b/src/test/abilities/protean.test.ts index ed63613945a..a7c6799132f 100644 --- a/src/test/abilities/protean.test.ts +++ b/src/test/abilities/protean.test.ts @@ -1,18 +1,17 @@ -import { allMoves } from "#app/data/move.js"; -import { Type } from "#app/data/type.js"; -import { Weather, WeatherType } from "#app/data/weather.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; +import { allMoves } from "#app/data/move"; +import { Type } from "#app/data/type"; +import { Weather, WeatherType } from "#app/data/weather"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -49,7 +48,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); @@ -67,12 +66,12 @@ describe("Abilities - Protean", () => { let leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); - game.doAttack(getMovePosition(game.scene, 0, Moves.AGILITY)); + game.move.select(Moves.AGILITY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied.filter((a) => a === Abilities.PROTEAN)).toHaveLength(1); @@ -89,7 +88,7 @@ describe("Abilities - Protean", () => { leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); @@ -108,7 +107,7 @@ describe("Abilities - Protean", () => { expect(leadPokemon).not.toBe(undefined); game.scene.arena.weather = new Weather(WeatherType.SUNNY); - game.doAttack(getMovePosition(game.scene, 0, Moves.WEATHER_BALL)); + game.move.select(Moves.WEATHER_BALL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).toContain(Abilities.PROTEAN); @@ -131,7 +130,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).toContain(Abilities.PROTEAN); @@ -154,7 +153,7 @@ describe("Abilities - Protean", () => { expect(leadPokemon).not.toBe(undefined); game.scene.arena.biomeType = Biome.MOUNTAIN; - game.doAttack(getMovePosition(game.scene, 0, Moves.NATURE_POWER)); + game.move.select(Moves.NATURE_POWER); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.AIR_SLASH); @@ -172,7 +171,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.DIG)); + game.move.select(Moves.DIG); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.DIG); @@ -191,7 +190,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.move.forceMiss(); await game.phaseInterceptor.to(TurnEndPhase); @@ -213,7 +212,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); @@ -232,7 +231,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); @@ -251,7 +250,7 @@ describe("Abilities - Protean", () => { expect(leadPokemon).not.toBe(undefined); leadPokemon.summonData.types = [allMoves[Moves.SPLASH].defaultType]; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); @@ -271,7 +270,7 @@ describe("Abilities - Protean", () => { vi.spyOn(leadPokemon, "isTerastallized").mockReturnValue(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); @@ -289,7 +288,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); + game.move.select(Moves.STRUGGLE); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); @@ -307,7 +306,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); + game.move.select(Moves.BURN_UP); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.PROTEAN); @@ -326,7 +325,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.TRICK_OR_TREAT)); + game.move.select(Moves.TRICK_OR_TREAT); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TRICK_OR_TREAT); @@ -344,7 +343,7 @@ describe("Abilities - Protean", () => { const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); + game.move.select(Moves.CURSE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.CURSE); diff --git a/src/test/abilities/quick_draw.test.ts b/src/test/abilities/quick_draw.test.ts index 6e3416b0724..00d344ed333 100644 --- a/src/test/abilities/quick_draw.test.ts +++ b/src/test/abilities/quick_draw.test.ts @@ -1,12 +1,11 @@ import { allAbilities, BypassSpeedChanceAbAttr } from "#app/data/ability"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { FaintPhase } from "#app/phases/faint-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import { FaintPhase } from "#app/phases/faint-phase.js"; describe("Abilities - Quick Draw", () => { let phaserGame: Phaser.Game; @@ -47,7 +46,7 @@ describe("Abilities - Quick Draw", () => { pokemon.hp = 1; enemy.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(FaintPhase, false); expect(pokemon.isFainted()).toBe(false); @@ -67,7 +66,7 @@ describe("Abilities - Quick Draw", () => { pokemon.hp = 1; enemy.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.TAIL_WHIP)); + game.move.select(Moves.TAIL_WHIP); await game.phaseInterceptor.to(FaintPhase, false); expect(pokemon.isFainted()).toBe(true); @@ -87,7 +86,7 @@ describe("Abilities - Quick Draw", () => { pokemon.hp = 1; enemy.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(FaintPhase, false); expect(pokemon.isFainted()).toBe(true); diff --git a/src/test/abilities/sand_spit.test.ts b/src/test/abilities/sand_spit.test.ts index 59d311adb80..041e20faf7f 100644 --- a/src/test/abilities/sand_spit.test.ts +++ b/src/test/abilities/sand_spit.test.ts @@ -1,11 +1,10 @@ -import GameManager from "#test/utils/gameManager"; +import { WeatherType } from "#app/enums/weather-type"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { WeatherType } from "#app/enums/weather-type.js"; describe("Abilities - Sand Spit", () => { @@ -35,21 +34,21 @@ describe("Abilities - Sand Spit", () => { game.override.moveset([Moves.SPLASH, Moves.COIL]); }); - it("should trigger when hit with damaging move", async() => { + it("should trigger when hit with damaging move", async () => { game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SANDSTORM); }, 20000); - it("should not trigger when targetted with status moves", async() => { + it("should not trigger when targetted with status moves", async () => { game.override.enemyMoveset(Array(4).fill(Moves.GROWL)); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.COIL)); + game.move.select(Moves.COIL); await game.toNextTurn(); expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.SANDSTORM); diff --git a/src/test/abilities/sand_veil.test.ts b/src/test/abilities/sand_veil.test.ts index 3c5f97bd653..2336e2b50de 100644 --- a/src/test/abilities/sand_veil.test.ts +++ b/src/test/abilities/sand_veil.test.ts @@ -1,16 +1,15 @@ -import { BattleStatMultiplierAbAttr, allAbilities } from "#app/data/ability.js"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { WeatherType } from "#app/data/weather.js"; +import { BattleStatMultiplierAbAttr, allAbilities } from "#app/data/ability"; +import { BattleStat } from "#app/data/battle-stat"; +import { WeatherType } from "#app/data/weather"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -64,11 +63,11 @@ describe("Abilities - Sand Veil", () => { expect(leadPokemon[0].hasAbility(Abilities.SAND_VEIL)).toBe(true); expect(leadPokemon[1].hasAbility(Abilities.SAND_VEIL)).toBe(false); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase, false); diff --git a/src/test/abilities/sap_sipper.test.ts b/src/test/abilities/sap_sipper.test.ts index dfb4ab7e976..f9c20e85eab 100644 --- a/src/test/abilities/sap_sipper.test.ts +++ b/src/test/abilities/sap_sipper.test.ts @@ -1,15 +1,14 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { TerrainType } from "#app/data/terrain.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { TerrainType } from "#app/data/terrain"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; // See also: TypeImmunityAbAttr describe("Abilities - Sap Sipper", () => { @@ -32,7 +31,7 @@ describe("Abilities - Sap Sipper", () => { game.override.disableCrits(); }); - it("raise attack 1 level and block effects when activated against a grass attack", async() => { + it("raise attack 1 level and block effects when activated against a grass attack", async () => { const moveToUse = Moves.LEAFAGE; const enemyAbility = Abilities.SAP_SIPPER; @@ -45,7 +44,7 @@ describe("Abilities - Sap Sipper", () => { const startingOppHp = game.scene.currentBattle.enemyParty[0].hp; - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -53,7 +52,7 @@ describe("Abilities - Sap Sipper", () => { expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1); }); - it("raise attack 1 level and block effects when activated against a grass status move", async() => { + it("raise attack 1 level and block effects when activated against a grass status move", async () => { const moveToUse = Moves.SPORE; const enemyAbility = Abilities.SAP_SIPPER; @@ -64,7 +63,7 @@ describe("Abilities - Sap Sipper", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -72,7 +71,7 @@ describe("Abilities - Sap Sipper", () => { expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1); }); - it("do not activate against status moves that target the field", async() => { + it("do not activate against status moves that target the field", async () => { const moveToUse = Moves.GRASSY_TERRAIN; const enemyAbility = Abilities.SAP_SIPPER; @@ -83,7 +82,7 @@ describe("Abilities - Sap Sipper", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -92,7 +91,7 @@ describe("Abilities - Sap Sipper", () => { expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(0); }); - it("activate once against multi-hit grass attacks", async() => { + it("activate once against multi-hit grass attacks", async () => { const moveToUse = Moves.BULLET_SEED; const enemyAbility = Abilities.SAP_SIPPER; @@ -105,7 +104,7 @@ describe("Abilities - Sap Sipper", () => { const startingOppHp = game.scene.currentBattle.enemyParty[0].hp; - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -113,7 +112,7 @@ describe("Abilities - Sap Sipper", () => { expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(1); }); - it("do not activate against status moves that target the user", async() => { + it("do not activate against status moves that target the user", async () => { const moveToUse = Moves.SPIKY_SHIELD; const ability = Abilities.SAP_SIPPER; @@ -125,7 +124,7 @@ describe("Abilities - Sap Sipper", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(MoveEndPhase); @@ -139,7 +138,7 @@ describe("Abilities - Sap Sipper", () => { // TODO Add METRONOME outcome override // To run this testcase, manually modify the METRONOME move to always give SAP_SIPPER, then uncomment - it.todo("activate once against multi-hit grass attacks (metronome)", async() => { + it.todo("activate once against multi-hit grass attacks (metronome)", async () => { const moveToUse = Moves.METRONOME; const enemyAbility = Abilities.SAP_SIPPER; @@ -152,7 +151,7 @@ describe("Abilities - Sap Sipper", () => { const startingOppHp = game.scene.currentBattle.enemyParty[0].hp; - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/schooling.test.ts b/src/test/abilities/schooling.test.ts index 62a7e98bc76..ad9663bf8e5 100644 --- a/src/test/abilities/schooling.test.ts +++ b/src/test/abilities/schooling.test.ts @@ -1,11 +1,10 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import { Status, StatusEffect } from "#app/data/status-effect"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; @@ -53,7 +52,7 @@ describe("Abilities - SCHOOLING", () => { wishiwashi.status = new Status(StatusEffect.FAINT); expect(wishiwashi.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); diff --git a/src/test/abilities/screen_cleaner.test.ts b/src/test/abilities/screen_cleaner.test.ts index 403efcce1c0..3c0d12a06ea 100644 --- a/src/test/abilities/screen_cleaner.test.ts +++ b/src/test/abilities/screen_cleaner.test.ts @@ -1,13 +1,12 @@ -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import { PostSummonPhase } from "#app/phases/post-summon-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { PostSummonPhase } from "#app/phases/post-summon-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Abilities - Screen Cleaner", () => { let phaserGame: Phaser.Game; @@ -36,7 +35,7 @@ describe("Abilities - Screen Cleaner", () => { await game.startBattle([Species.MAGIKARP, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.HAIL)); + game.move.select(Moves.HAIL); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.AURORA_VEIL)).toBeDefined(); @@ -53,7 +52,7 @@ describe("Abilities - Screen Cleaner", () => { await game.startBattle([Species.MAGIKARP, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.LIGHT_SCREEN)).toBeDefined(); @@ -70,7 +69,7 @@ describe("Abilities - Screen Cleaner", () => { await game.startBattle([Species.MAGIKARP, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.REFLECT)).toBeDefined(); diff --git a/src/test/abilities/serene_grace.test.ts b/src/test/abilities/serene_grace.test.ts index b126bb5eb7a..7316b2ea920 100644 --- a/src/test/abilities/serene_grace.test.ts +++ b/src/test/abilities/serene_grace.test.ts @@ -1,18 +1,14 @@ +import { BattlerIndex } from "#app/battle"; import { applyAbAttrs, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability"; import { Stat } from "#app/data/pokemon-stat"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { BattlerIndex } from "#app/battle.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; describe("Abilities - Serene Grace", () => { @@ -36,10 +32,10 @@ describe("Abilities - Serene Grace", () => { game.override.enemySpecies(Species.ONIX); game.override.startingLevel(100); game.override.moveset(movesToUse); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("Move chance without Serene Grace", async() => { + it("Move chance without Serene Grace", async () => { const moveToUse = Moves.AIR_SLASH; await game.startBattle([ Species.PIDGEOT @@ -49,13 +45,7 @@ describe("Abilities - Serene Grace", () => { game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -72,7 +62,7 @@ describe("Abilities - Serene Grace", () => { }, 20000); - it("Move chance with Serene Grace", async() => { + it("Move chance with Serene Grace", async () => { const moveToUse = Moves.AIR_SLASH; game.override.ability(Abilities.SERENE_GRACE); await game.startBattle([ @@ -82,13 +72,7 @@ describe("Abilities - Serene Grace", () => { game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); diff --git a/src/test/abilities/sheer_force.test.ts b/src/test/abilities/sheer_force.test.ts index 564e2040af4..f73b749dac2 100644 --- a/src/test/abilities/sheer_force.test.ts +++ b/src/test/abilities/sheer_force.test.ts @@ -1,18 +1,14 @@ +import { BattlerIndex } from "#app/battle"; import { applyAbAttrs, applyPostDefendAbAttrs, applyPreAttackAbAttrs, MoveEffectChanceMultiplierAbAttr, MovePowerBoostAbAttr, PostDefendTypeChangeAbAttr } from "#app/data/ability"; import { Stat } from "#app/data/pokemon-stat"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { BattlerIndex } from "#app/battle.js"; describe("Abilities - Sheer Force", () => { @@ -36,10 +32,10 @@ describe("Abilities - Sheer Force", () => { game.override.enemySpecies(Species.ONIX); game.override.startingLevel(100); game.override.moveset(movesToUse); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("Sheer Force", async() => { + it("Sheer Force", async () => { const moveToUse = Moves.AIR_SLASH; game.override.ability(Abilities.SHEER_FORCE); await game.startBattle([ @@ -50,13 +46,7 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -73,12 +63,12 @@ describe("Abilities - Sheer Force", () => { applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power); expect(chance.value).toBe(0); - expect(power.value).toBe(move.power * 5461/4096); + expect(power.value).toBe(move.power * 5461 / 4096); }, 20000); - it("Sheer Force with exceptions including binding moves", async() => { + it("Sheer Force with exceptions including binding moves", async () => { const moveToUse = Moves.BIND; game.override.ability(Abilities.SHEER_FORCE); await game.startBattle([ @@ -89,13 +79,7 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -117,7 +101,7 @@ describe("Abilities - Sheer Force", () => { }, 20000); - it("Sheer Force with moves with no secondary effect", async() => { + it("Sheer Force with moves with no secondary effect", async () => { const moveToUse = Moves.TACKLE; game.override.ability(Abilities.SHEER_FORCE); await game.startBattle([ @@ -128,13 +112,7 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -156,10 +134,10 @@ describe("Abilities - Sheer Force", () => { }, 20000); - it("Sheer Force Disabling Specific Abilities", async() => { + it("Sheer Force Disabling Specific Abilities", async () => { const moveToUse = Moves.CRUSH_CLAW; game.override.enemyAbility(Abilities.COLOR_CHANGE); - game.override.startingHeldItems([{name: "KINGS_ROCK", count: 1}]); + game.override.startingHeldItems([{ name: "KINGS_ROCK", count: 1 }]); game.override.ability(Abilities.SHEER_FORCE); await game.startBattle([ Species.PIDGEOT @@ -169,13 +147,7 @@ describe("Abilities - Sheer Force", () => { game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -196,7 +168,7 @@ describe("Abilities - Sheer Force", () => { applyPostDefendAbAttrs(PostDefendTypeChangeAbAttr, target, user, move, target.apply(user, move)); expect(chance.value).toBe(0); - expect(power.value).toBe(move.power * 5461/4096); + expect(power.value).toBe(move.power * 5461 / 4096); expect(target.getTypes().length).toBe(2); expect(target.getTypes()[0]).toBe(opponentType); diff --git a/src/test/abilities/shield_dust.test.ts b/src/test/abilities/shield_dust.test.ts index fe6c941752c..14770c49427 100644 --- a/src/test/abilities/shield_dust.test.ts +++ b/src/test/abilities/shield_dust.test.ts @@ -1,18 +1,14 @@ +import { BattlerIndex } from "#app/battle"; import { applyAbAttrs, applyPreDefendAbAttrs, IgnoreMoveEffectsAbAttr, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability"; import { Stat } from "#app/data/pokemon-stat"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { BattlerIndex } from "#app/battle.js"; describe("Abilities - Shield Dust", () => { @@ -37,10 +33,10 @@ describe("Abilities - Shield Dust", () => { game.override.enemyAbility(Abilities.SHIELD_DUST); game.override.startingLevel(100); game.override.moveset(movesToUse); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("Shield Dust", async() => { + it("Shield Dust", async () => { const moveToUse = Moves.AIR_SLASH; await game.startBattle([ Species.PIDGEOT @@ -50,13 +46,7 @@ describe("Abilities - Shield Dust", () => { game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); diff --git a/src/test/abilities/shields_down.test.ts b/src/test/abilities/shields_down.test.ts index e07c12ebb63..9bfec23ddf1 100644 --- a/src/test/abilities/shields_down.test.ts +++ b/src/test/abilities/shields_down.test.ts @@ -1,11 +1,10 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import { Status, StatusEffect } from "#app/data/status-effect"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; @@ -53,7 +52,7 @@ describe("Abilities - SHIELDS DOWN", () => { minior.status = new Status(StatusEffect.FAINT); expect(minior.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); diff --git a/src/test/abilities/stall.test.ts b/src/test/abilities/stall.test.ts index 5410d2e953e..d8dbe9d0e06 100644 --- a/src/test/abilities/stall.test.ts +++ b/src/test/abilities/stall.test.ts @@ -1,11 +1,10 @@ -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { MovePhase } from "#app/phases/move-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { MovePhase } from "#app/phases/move-phase.js"; describe("Abilities - Stall", () => { @@ -38,13 +37,13 @@ describe("Abilities - Stall", () => { * https://bulbapedia.bulbagarden.net/wiki/Priority **/ - it("Pokemon with Stall should move last in its priority bracket regardless of speed", async() => { - await game.startBattle([ Species.SHUCKLE ]); + it("Pokemon with Stall should move last in its priority bracket regardless of speed", async () => { + await game.startBattle([Species.SHUCKLE]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.phaseInterceptor.to(MovePhase, false); // The player Pokemon (without Stall) goes first despite having lower speed than the opponent. @@ -56,13 +55,13 @@ describe("Abilities - Stall", () => { expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(enemyIndex); }, 20000); - it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async() => { - await game.startBattle([ Species.SHUCKLE ]); + it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async () => { + await game.startBattle([Species.SHUCKLE]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MovePhase, false); // The opponent Pokemon (with Stall) goes first because its move is still within a higher priority bracket than its opponent. @@ -74,14 +73,14 @@ describe("Abilities - Stall", () => { expect((game.scene.getCurrentPhase() as MovePhase).pokemon.getBattlerIndex()).toBe(leadIndex); }, 20000); - it("If both Pokemon have stall and use the same move, speed is used to determine who goes first.", async() => { + it("If both Pokemon have stall and use the same move, speed is used to determine who goes first.", async () => { game.override.ability(Abilities.STALL); - await game.startBattle([ Species.SHUCKLE ]); + await game.startBattle([Species.SHUCKLE]); const leadIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MovePhase, false); // The opponent Pokemon (with Stall) goes first because it has a higher speed. diff --git a/src/test/abilities/steely_spirit.test.ts b/src/test/abilities/steely_spirit.test.ts index 3ca1a55ebee..c632d0be777 100644 --- a/src/test/abilities/steely_spirit.test.ts +++ b/src/test/abilities/steely_spirit.test.ts @@ -1,15 +1,13 @@ -import { allAbilities } from "#app/data/ability.js"; -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allAbilities } from "#app/data/ability"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { SelectTargetPhase } from "#app/phases/select-target-phase.js"; describe("Abilities - Steely Spirit", () => { let phaserGame: Phaser.Game; @@ -47,10 +45,8 @@ describe("Abilities - Steely Spirit", () => { expect(boostSource.hasAbility(Abilities.STEELY_SPIRIT)).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, moveToCheck)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(enemyToCheck.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower * steelySpiritMultiplier); @@ -66,12 +62,8 @@ describe("Abilities - Steely Spirit", () => { expect(game.scene.getPlayerField().every(p => p.hasAbility(Abilities.STEELY_SPIRIT))).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, moveToCheck)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(enemyToCheck.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, moveToCheck)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(enemyToCheck.getBattlerIndex()); + game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex()); + game.move.select(moveToCheck, 1, enemyToCheck.getBattlerIndex()); await game.phaseInterceptor.to(MoveEffectPhase); expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower * Math.pow(steelySpiritMultiplier, 2)); @@ -90,10 +82,8 @@ describe("Abilities - Steely Spirit", () => { expect(boostSource.hasAbility(Abilities.STEELY_SPIRIT)).toBe(false); expect(boostSource.summonData.abilitySuppressed).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, moveToCheck)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(enemyToCheck.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower); diff --git a/src/test/abilities/sturdy.test.ts b/src/test/abilities/sturdy.test.ts index 602b2c04eb1..dc9f774cc5b 100644 --- a/src/test/abilities/sturdy.test.ts +++ b/src/test/abilities/sturdy.test.ts @@ -1,13 +1,12 @@ -import { EnemyPokemon } from "#app/field/pokemon.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { EnemyPokemon } from "#app/field/pokemon"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -42,7 +41,7 @@ describe("Abilities - Sturdy", () => { "Sturdy activates when user is at full HP", async () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CLOSE_COMBAT)); + game.move.select(Moves.CLOSE_COMBAT); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.getEnemyParty()[0].hp).toBe(1); }, @@ -57,7 +56,7 @@ describe("Abilities - Sturdy", () => { const enemyPokemon: EnemyPokemon = game.scene.getEnemyParty()[0]; enemyPokemon.hp = enemyPokemon.getMaxHp() - 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.CLOSE_COMBAT)); + game.move.select(Moves.CLOSE_COMBAT); await game.phaseInterceptor.to(DamagePhase); expect(enemyPokemon.hp).toBe(0); @@ -70,7 +69,7 @@ describe("Abilities - Sturdy", () => { "Sturdy pokemon should be immune to OHKO moves", async () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); await game.phaseInterceptor.to(MoveEndPhase); const enemyPokemon: EnemyPokemon = game.scene.getEnemyParty()[0]; @@ -85,7 +84,7 @@ describe("Abilities - Sturdy", () => { game.override.ability(Abilities.MOLD_BREAKER); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CLOSE_COMBAT)); + game.move.select(Moves.CLOSE_COMBAT); await game.phaseInterceptor.to(DamagePhase); const enemyPokemon: EnemyPokemon = game.scene.getEnemyParty()[0]; diff --git a/src/test/abilities/sweet_veil.test.ts b/src/test/abilities/sweet_veil.test.ts index 8ab384ae59e..5de3c7285a9 100644 --- a/src/test/abilities/sweet_veil.test.ts +++ b/src/test/abilities/sweet_veil.test.ts @@ -1,16 +1,14 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { CommandPhase } from "#app/phases/command-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerIndex } from "#app/battle.js"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { MovePhase } from "#app/phases/move-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Abilities - Sweet Veil", () => { let phaserGame: Phaser.Game; @@ -29,7 +27,7 @@ describe("Abilities - Sweet Veil", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override.battleType("double"); - game.override.moveset([Moves.SPLASH, Moves.REST]); + game.override.moveset([Moves.SPLASH, Moves.REST, Moves.YAWN]); game.override.enemySpecies(Species.MAGIKARP); game.override.enemyAbility(Abilities.BALL_FETCH); game.override.enemyMoveset([Moves.POWDER, Moves.POWDER, Moves.POWDER, Moves.POWDER]); @@ -38,8 +36,8 @@ describe("Abilities - Sweet Veil", () => { it("prevents the user and its allies from falling asleep", async () => { await game.startBattle([Species.SWIRLIX, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -50,8 +48,8 @@ describe("Abilities - Sweet Veil", () => { game.override.enemyMoveset(SPLASH_ONLY); await game.startBattle([Species.SWIRLIX, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.REST)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.REST, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -62,8 +60,8 @@ describe("Abilities - Sweet Veil", () => { game.override.enemyMoveset([Moves.YAWN, Moves.YAWN, Moves.YAWN, Moves.YAWN]); await game.startBattle([Species.SWIRLIX, Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -74,28 +72,19 @@ describe("Abilities - Sweet Veil", () => { game.override.enemySpecies(Species.PIKACHU); game.override.enemyLevel(5); game.override.startingLevel(5); - game.override.enemyMoveset([Moves.YAWN, Moves.YAWN, Moves.YAWN, Moves.YAWN]); + game.override.enemyMoveset(SPLASH_ONLY); await game.startBattle([Species.SHUCKLE, Species.SHUCKLE, Species.SWIRLIX]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.YAWN, 1, BattlerIndex.PLAYER); - // First pokemon move - await game.move.forceHit(); - - // Second pokemon move - await game.phaseInterceptor.to(MovePhase, false); - await game.move.forceHit(); + await game.phaseInterceptor.to("BerryPhase"); expect(game.scene.getPlayerField().some(p => !!p.getTag(BattlerTagType.DROWSY))).toBe(true); - await game.phaseInterceptor.to(TurnEndPhase); - - const drowsyMon = game.scene.getPlayerField().find(p => !!p.getTag(BattlerTagType.DROWSY))!; - await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, (drowsyMon.getBattlerIndex() as BattlerIndex.PLAYER | BattlerIndex.PLAYER_2), Moves.SPLASH)); + game.move.select(Moves.SPLASH); game.doSwitchPokemon(2); expect(game.scene.getPlayerField().every(p => p.status?.effect)).toBe(false); diff --git a/src/test/abilities/unseen_fist.test.ts b/src/test/abilities/unseen_fist.test.ts index 7d47d73bb16..ea1996ec66b 100644 --- a/src/test/abilities/unseen_fist.test.ts +++ b/src/test/abilities/unseen_fist.test.ts @@ -1,11 +1,10 @@ +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -80,7 +79,7 @@ async function testUnseenFistHitResult(game: GameManager, attackMove: Moves, pro const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, attackMove)); + game.move.select(attackMove); await game.phaseInterceptor.to(TurnEndPhase, false); if (shouldSucceed) { diff --git a/src/test/abilities/volt_absorb.test.ts b/src/test/abilities/volt_absorb.test.ts index 0e3d5c9792f..d9c3fe34c24 100644 --- a/src/test/abilities/volt_absorb.test.ts +++ b/src/test/abilities/volt_absorb.test.ts @@ -1,11 +1,10 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -42,7 +41,7 @@ describe("Abilities - Volt Absorb", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/wind_power.test.ts b/src/test/abilities/wind_power.test.ts index 24f01cceebc..c944e01b43a 100644 --- a/src/test/abilities/wind_power.test.ts +++ b/src/test/abilities/wind_power.test.ts @@ -1,13 +1,12 @@ -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Wind Power", () => { let phaserGame: Phaser.Game; @@ -38,7 +37,7 @@ describe("Abilities - Wind Power", () => { expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PETAL_BLIZZARD)); + game.move.select(Moves.PETAL_BLIZZARD); await game.phaseInterceptor.to(TurnEndPhase); expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeDefined(); @@ -53,7 +52,7 @@ describe("Abilities - Wind Power", () => { expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeDefined(); @@ -70,7 +69,7 @@ describe("Abilities - Wind Power", () => { expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); expect(magikarp.getTag(BattlerTagType.CHARGED)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); @@ -86,7 +85,7 @@ describe("Abilities - Wind Power", () => { expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SANDSTORM)); + game.move.select(Moves.SANDSTORM); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/wind_rider.test.ts b/src/test/abilities/wind_rider.test.ts index 92c38507e4f..97e2e6456dc 100644 --- a/src/test/abilities/wind_rider.test.ts +++ b/src/test/abilities/wind_rider.test.ts @@ -1,13 +1,12 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Wind Rider", () => { let phaserGame: Phaser.Game; @@ -38,7 +37,7 @@ describe("Abilities - Wind Rider", () => { expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.PETAL_BLIZZARD)); + game.move.select(Moves.PETAL_BLIZZARD); await game.phaseInterceptor.to(TurnEndPhase); @@ -55,7 +54,7 @@ describe("Abilities - Wind Rider", () => { expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); @@ -73,7 +72,7 @@ describe("Abilities - Wind Rider", () => { expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); @@ -91,7 +90,7 @@ describe("Abilities - Wind Rider", () => { expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); @@ -108,7 +107,7 @@ describe("Abilities - Wind Rider", () => { expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(shiftry.isFullHp()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SANDSTORM)); + game.move.select(Moves.SANDSTORM); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/wonder_skin.test.ts b/src/test/abilities/wonder_skin.test.ts index d6e2b2443c4..0c2aedc8ce8 100644 --- a/src/test/abilities/wonder_skin.test.ts +++ b/src/test/abilities/wonder_skin.test.ts @@ -1,14 +1,13 @@ -import { allAbilities } from "#app/data/ability.js"; -import { allMoves } from "#app/data/move.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allAbilities } from "#app/data/ability"; +import { allMoves } from "#app/data/move"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Abilities - Wonder Skin", () => { let phaserGame: Phaser.Game; @@ -40,7 +39,7 @@ describe("Abilities - Wonder Skin", () => { vi.spyOn(moveToCheck, "calculateBattleAccuracy"); await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM)); + game.move.select(Moves.CHARM); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(50); @@ -52,7 +51,7 @@ describe("Abilities - Wonder Skin", () => { vi.spyOn(moveToCheck, "calculateBattleAccuracy"); await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(100); @@ -68,7 +67,7 @@ describe("Abilities - Wonder Skin", () => { vi.spyOn(moveToCheck, "calculateBattleAccuracy"); await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM)); + game.move.select(Moves.CHARM); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(100); diff --git a/src/test/abilities/zen_mode.test.ts b/src/test/abilities/zen_mode.test.ts index 72fdc5442c5..677d998e876 100644 --- a/src/test/abilities/zen_mode.test.ts +++ b/src/test/abilities/zen_mode.test.ts @@ -1,26 +1,23 @@ +import { BattlerIndex } from "#app/battle"; import { Stat } from "#app/data/pokemon-stat"; -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; +import { Status, StatusEffect } from "#app/data/status-effect"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { PostSummonPhase } from "#app/phases/post-summon-phase"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { SwitchPhase } from "#app/phases/switch-phase"; +import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import { BattlerIndex } from "#app/battle.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js"; -import { MessagePhase } from "#app/phases/message-phase.js"; -import { PostSummonPhase } from "#app/phases/post-summon-phase.js"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase.js"; -import { SwitchPhase } from "#app/phases/switch-phase.js"; -import { SwitchSummonPhase } from "#app/phases/switch-summon-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; -import { TurnStartPhase } from "#app/phases/turn-start-phase.js"; const TIMEOUT = 20 * 1000; @@ -59,13 +56,7 @@ describe("Abilities - ZEN MODE", () => { game.scene.getParty()[0].hp = 100; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to(DamagePhase, false); @@ -88,13 +79,7 @@ describe("Abilities - ZEN MODE", () => { game.scene.getParty()[0].hp = 100; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to(QuietFormChangePhase); @@ -114,13 +99,7 @@ describe("Abilities - ZEN MODE", () => { game.scene.getParty()[0].hp = 100; expect(game.scene.getParty()[0].formIndex).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to(DamagePhase, false); @@ -169,7 +148,7 @@ describe("Abilities - ZEN MODE", () => { darmanitan.status = new Status(StatusEffect.FAINT); expect(darmanitan.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); diff --git a/src/test/abilities/zero_to_hero.test.ts b/src/test/abilities/zero_to_hero.test.ts index ee6c07096a8..1a9697f974e 100644 --- a/src/test/abilities/zero_to_hero.test.ts +++ b/src/test/abilities/zero_to_hero.test.ts @@ -1,11 +1,10 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import { Status, StatusEffect } from "#app/data/status-effect"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; @@ -52,7 +51,7 @@ describe("Abilities - ZERO TO HERO", () => { palafin2.status = new Status(StatusEffect.FAINT); expect(palafin2.isFainted()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(TurnEndPhase); game.doSelectModifier(); @@ -80,7 +79,7 @@ describe("Abilities - ZERO TO HERO", () => { const palafin = game.scene.getPlayerPokemon()!; expect(palafin.formIndex).toBe(baseForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.killPokemon(palafin); game.doSelectPartyPokemon(1); await game.toNextTurn(); @@ -97,7 +96,7 @@ describe("Abilities - ZERO TO HERO", () => { const palafin = game.scene.getPlayerPokemon()!; expect(palafin.formIndex).toBe(heroForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.killPokemon(palafin); game.doSelectPartyPokemon(1); await game.toNextTurn(); diff --git a/src/test/account.spec.ts b/src/test/account.spec.ts index d5d0458c7e8..eb6002f3cf2 100644 --- a/src/test/account.spec.ts +++ b/src/test/account.spec.ts @@ -1,4 +1,4 @@ -import * as battleScene from "#app/battle-scene.js"; +import * as battleScene from "#app/battle-scene"; import { describe, expect, it, vi } from "vitest"; import { initLoggedInUser, loggedInUser, updateUserInfo } from "../account"; import * as utils from "../utils"; diff --git a/src/test/achievements/achievement.test.ts b/src/test/achievements/achievement.test.ts index 5cd9c4d4094..36c20ae2248 100644 --- a/src/test/achievements/achievement.test.ts +++ b/src/test/achievements/achievement.test.ts @@ -1,7 +1,7 @@ -import { TurnHeldItemTransferModifier } from "#app/modifier/modifier.js"; +import { TurnHeldItemTransferModifier } from "#app/modifier/modifier"; import { Achv, AchvTier, DamageAchv, HealAchv, LevelAchv, ModifierAchv, MoneyAchv, RibbonAchv, achvs } from "#app/system/achv"; +import { IntegerHolder, NumberHolder } from "#app/utils"; import GameManager from "#test/utils/gameManager"; -import { IntegerHolder, NumberHolder } from "#app/utils.js"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import BattleScene from "../../battle-scene"; diff --git a/src/test/arena/arena_gravity.test.ts b/src/test/arena/arena_gravity.test.ts index 8fad4dde83d..eda8c687ba1 100644 --- a/src/test/arena/arena_gravity.test.ts +++ b/src/test/arena/arena_gravity.test.ts @@ -6,7 +6,6 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; @@ -45,14 +44,14 @@ describe("Arena - Gravity", () => { // Setup Gravity on first turn await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY)); + game.move.select(Moves.GRAVITY); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined(); // Use non-OHKO move on second turn await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(100 * 1.67); @@ -69,14 +68,14 @@ describe("Arena - Gravity", () => { // Setup Gravity on first turn await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY)); + game.move.select(Moves.GRAVITY); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined(); // Use OHKO move on second turn await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(30); @@ -96,21 +95,21 @@ describe("Arena - Gravity", () => { vi.spyOn(pidgeot, "getAttackTypeEffectiveness"); // Try earthquake on 1st turn (fails!); - game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); + game.move.select(Moves.EARTHQUAKE); await game.phaseInterceptor.to(TurnEndPhase); expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(0); // Setup Gravity on 2nd turn await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY)); + game.move.select(Moves.GRAVITY); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined(); // Use ground move on 3rd turn await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); + game.move.select(Moves.EARTHQUAKE); await game.phaseInterceptor.to(TurnEndPhase); expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(1); @@ -129,14 +128,14 @@ describe("Arena - Gravity", () => { vi.spyOn(pidgeot, "getAttackTypeEffectiveness"); // Setup Gravity on 1st turn - game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY)); + game.move.select(Moves.GRAVITY); await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined(); // Use electric move on 2nd turn await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); + game.move.select(Moves.THUNDERBOLT); await game.phaseInterceptor.to(TurnEndPhase); expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(2); diff --git a/src/test/arena/weather_fog.test.ts b/src/test/arena/weather_fog.test.ts index 350007ae943..b36b0de2e06 100644 --- a/src/test/arena/weather_fog.test.ts +++ b/src/test/arena/weather_fog.test.ts @@ -1,11 +1,10 @@ -import { allMoves } from "#app/data/move.js"; -import { WeatherType } from "#app/data/weather.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { WeatherType } from "#app/data/weather"; +import { Abilities } from "#app/enums/abilities"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -41,7 +40,7 @@ describe("Weather - Fog", () => { vi.spyOn(moveToCheck, "calculateBattleAccuracy"); await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(100 * 0.9); diff --git a/src/test/arena/weather_strong_winds.test.ts b/src/test/arena/weather_strong_winds.test.ts index 79fba30c019..8b2d3e2547e 100644 --- a/src/test/arena/weather_strong_winds.test.ts +++ b/src/test/arena/weather_strong_winds.test.ts @@ -1,12 +1,11 @@ -import { allMoves } from "#app/data/move.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { TurnStartPhase } from "#app/phases/turn-start-phase.js"; describe("Weather - Strong Winds", () => { let phaserGame: Phaser.Game; @@ -38,7 +37,7 @@ describe("Weather - Strong Winds", () => { const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); + game.move.select(Moves.THUNDERBOLT); await game.phaseInterceptor.to(TurnStartPhase); expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.THUNDERBOLT].type, pikachu)).toBe(0.5); @@ -49,7 +48,7 @@ describe("Weather - Strong Winds", () => { const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); + game.move.select(Moves.THUNDERBOLT); await game.phaseInterceptor.to(TurnStartPhase); expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.THUNDERBOLT].type, pikachu)).toBe(1); @@ -60,7 +59,7 @@ describe("Weather - Strong Winds", () => { const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); + game.move.select(Moves.ICE_BEAM); await game.phaseInterceptor.to(TurnStartPhase); expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.ICE_BEAM].type, pikachu)).toBe(1); @@ -71,7 +70,7 @@ describe("Weather - Strong Winds", () => { const pikachu = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.ROCK_SLIDE)); + game.move.select(Moves.ROCK_SLIDE); await game.phaseInterceptor.to(TurnStartPhase); expect(enemy.getAttackTypeEffectiveness(allMoves[Moves.ROCK_SLIDE].type, pikachu)).toBe(1); diff --git a/src/test/battle-scene.test.ts b/src/test/battle-scene.test.ts index 21d3f689d1c..9e28ec99791 100644 --- a/src/test/battle-scene.test.ts +++ b/src/test/battle-scene.test.ts @@ -1,4 +1,4 @@ -import { LoadingScene } from "#app/loading-scene.js"; +import { LoadingScene } from "#app/loading-scene"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "./utils/gameManager"; diff --git a/src/test/battle-stat.spec.ts b/src/test/battle-stat.spec.ts index 775dd40ff34..16fce962838 100644 --- a/src/test/battle-stat.spec.ts +++ b/src/test/battle-stat.spec.ts @@ -1,4 +1,4 @@ -import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat.js"; +import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat"; import { describe, expect, it } from "vitest"; import { arrayOfRange, mockI18next } from "./utils/testUtils"; diff --git a/src/test/battle/battle-order.test.ts b/src/test/battle/battle-order.test.ts index 208b921b843..0129ecad254 100644 --- a/src/test/battle/battle-order.test.ts +++ b/src/test/battle/battle-order.test.ts @@ -1,19 +1,13 @@ import { Stat } from "#app/data/pokemon-stat"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import TargetSelectUiHandler from "#app/ui/target-select-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { SelectTargetPhase } from "#app/phases/select-target-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Abilities } from "#enums/abilities"; -import { Button } from "#enums/buttons"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js"; -import { SelectTargetPhase } from "#app/phases/select-target-phase.js"; -import { TurnStartPhase } from "#app/phases/turn-start-phase.js"; describe("Battle order", () => { @@ -39,20 +33,14 @@ describe("Battle order", () => { game.override.moveset([Moves.TACKLE]); }); - it("opponent faster than player 50 vs 150", async() => { + it("opponent faster than player 50 vs 150", async () => { await game.startBattle([ Species.BULBASAUR, ]); game.scene.getParty()[0].stats[Stat.SPD] = 50; game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 150; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.run(EnemyCommandPhase); const phase = game.scene.getCurrentPhase() as TurnStartPhase; const order = phase.getOrder(); @@ -60,20 +48,14 @@ describe("Battle order", () => { expect(order[1]).toBe(0); }, 20000); - it("Player faster than opponent 150 vs 50", async() => { + it("Player faster than opponent 150 vs 50", async () => { await game.startBattle([ Species.BULBASAUR, ]); game.scene.getParty()[0].stats[Stat.SPD] = 150; game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 50; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.run(EnemyCommandPhase); const phase = game.scene.getCurrentPhase() as TurnStartPhase; const order = phase.getOrder(); @@ -81,7 +63,7 @@ describe("Battle order", () => { expect(order[1]).toBe(2); }, 20000); - it("double - both opponents faster than player 50/50 vs 150/150", async() => { + it("double - both opponents faster than player 50/50 vs 150/150", async () => { game.override.battleType("double"); await game.startBattle([ Species.BULBASAUR, @@ -92,28 +74,8 @@ describe("Battle order", () => { game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 150; game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); + game.move.select(Moves.TACKLE); + game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); const phase = game.scene.getCurrentPhase() as TurnStartPhase; const order = phase.getOrder(); @@ -123,7 +85,7 @@ describe("Battle order", () => { expect(order.indexOf(1)).toBeGreaterThan(order.indexOf(3)); }, 20000); - it("double - speed tie except 1 - 100/100 vs 100/150", async() => { + it("double - speed tie except 1 - 100/100 vs 100/150", async () => { game.override.battleType("double"); await game.startBattle([ Species.BULBASAUR, @@ -134,28 +96,8 @@ describe("Battle order", () => { game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 100; game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); + game.move.select(Moves.TACKLE); + game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); const phase = game.scene.getCurrentPhase() as TurnStartPhase; const order = phase.getOrder(); @@ -164,7 +106,7 @@ describe("Battle order", () => { expect(order.indexOf(3)).toBeLessThan(order.indexOf(2)); }, 20000); - it("double - speed tie 100/150 vs 100/150", async() => { + it("double - speed tie 100/150 vs 100/150", async () => { game.override.battleType("double"); await game.startBattle([ Species.BULBASAUR, @@ -175,28 +117,8 @@ describe("Battle order", () => { game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 100; game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = game.scene.ui.getHandler() as TargetSelectUiHandler; - handler.processInput(Button.ACTION); - }); + game.move.select(Moves.TACKLE); + game.move.select(Moves.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); const phase = game.scene.getCurrentPhase() as TurnStartPhase; const order = phase.getOrder(); diff --git a/src/test/battle/battle.test.ts b/src/test/battle/battle.test.ts index 43d8ddce4b0..c79eee63a7c 100644 --- a/src/test/battle/battle.test.ts +++ b/src/test/battle/battle.test.ts @@ -1,10 +1,23 @@ import { allSpecies } from "#app/data/pokemon-species"; -import { TempBattleStat } from "#app/data/temp-battle-stat.js"; -import { GameModes } from "#app/game-mode"; -import { getGameMode } from "#app/game-mode.js"; +import { TempBattleStat } from "#app/data/temp-battle-stat"; +import { GameModes, getGameMode } from "#app/game-mode"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { LoginPhase } from "#app/phases/login-phase"; +import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; +import { SelectGenderPhase } from "#app/phases/select-gender-phase"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { SummonPhase } from "#app/phases/summon-phase"; +import { SwitchPhase } from "#app/phases/switch-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { VictoryPhase } from "#app/phases/victory-phase"; import GameManager from "#app/test/utils/gameManager"; -import { generateStarter, getMovePosition, } from "#app/test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; +import { generateStarter } from "#app/test/utils/gameManagerUtils"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; @@ -13,21 +26,6 @@ import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { BattleEndPhase } from "#app/phases/battle-end-phase.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { EncounterPhase } from "#app/phases/encounter-phase.js"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js"; -import { LoginPhase } from "#app/phases/login-phase.js"; -import { NextEncounterPhase } from "#app/phases/next-encounter-phase.js"; -import { SelectGenderPhase } from "#app/phases/select-gender-phase.js"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase.js"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase.js"; -import { SummonPhase } from "#app/phases/summon-phase.js"; -import { SwitchPhase } from "#app/phases/switch-phase.js"; -import { TitlePhase } from "#app/phases/title-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; -import { VictoryPhase } from "#app/phases/victory-phase.js"; describe("Test Battle Phase", () => { let phaserGame: Phaser.Game; @@ -47,7 +45,7 @@ describe("Test Battle Phase", () => { game = new GameManager(phaserGame); }); - it("test phase interceptor with prompt", async() => { + it("test phase interceptor with prompt", async () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { @@ -65,7 +63,7 @@ describe("Test Battle Phase", () => { expect(game.scene.gameData.gender).toBe(PlayerGender.MALE); }, 20000); - it("test phase interceptor with prompt with preparation for a future prompt", async() => { + it("test phase interceptor with prompt with preparation for a future prompt", async () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { @@ -87,13 +85,13 @@ describe("Test Battle Phase", () => { expect(game.scene.gameData.gender).toBe(PlayerGender.MALE); }, 20000); - it("newGame one-liner", async() => { + it("newGame one-liner", async () => { await game.startBattle(); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); - it("do attack wave 3 - single battle - regular - OHKO", async() => { + it("do attack wave 3 - single battle - regular - OHKO", async () => { game.override.starterSpecies(Species.MEWTWO); game.override.enemySpecies(Species.RATTATA); game.override.startingLevel(2000); @@ -104,17 +102,11 @@ describe("Test Battle Phase", () => { game.override.enemyAbility(Abilities.HYDRATION); game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle(); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(SelectModifierPhase, false); }, 20000); - it("do attack wave 3 - single battle - regular - NO OHKO with opponent using non damage attack", async() => { + it("do attack wave 3 - single battle - regular - NO OHKO with opponent using non damage attack", async () => { game.override.starterSpecies(Species.MEWTWO); game.override.enemySpecies(Species.RATTATA); game.override.startingLevel(5); @@ -124,17 +116,11 @@ describe("Test Battle Phase", () => { game.override.enemyMoveset([Moves.TAIL_WHIP, Moves.TAIL_WHIP, Moves.TAIL_WHIP, Moves.TAIL_WHIP]); game.override.battleType("single"); await game.startBattle(); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase, false); }, 20000); - it("load 100% data file", async() => { + it("load 100% data file", async () => { await game.importData("src/test/utils/saves/everything.prsv"); const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => { const species = game.scene.gameData.dexData[key]; @@ -143,7 +129,7 @@ describe("Test Battle Phase", () => { expect(caughtCount).toBe(Object.keys(allSpecies).length); }, 20000); - it("start battle with selected team", async() => { + it("start battle with selected team", async () => { await game.startBattle([ Species.CHARIZARD, Species.CHANSEY, @@ -154,26 +140,26 @@ describe("Test Battle Phase", () => { expect(game.scene.getParty()[2].species.speciesId).toBe(Species.MEW); }, 20000); - it("test remove random battle seed int", async() => { - for (let i=0; i<10; i++) { + it("test remove random battle seed int", async () => { + for (let i = 0; i < 10; i++) { const rand = game.scene.randBattleSeedInt(16); expect(rand).toBe(15); } }); - it("wrong phase", async() => { + it("wrong phase", async () => { await game.phaseInterceptor.run(LoginPhase); await game.phaseInterceptor.run(LoginPhase).catch((e) => { expect(e).toBe("Wrong phase: this is SelectGenderPhase and not LoginPhase"); }); }, 20000); - it("wrong phase but skip", async() => { + it("wrong phase but skip", async () => { await game.phaseInterceptor.run(LoginPhase); await game.phaseInterceptor.run(LoginPhase, () => game.isCurrentPhase(SelectGenderPhase)); }, 20000); - it("good run", async() => { + it("good run", async () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { game.scene.gameData.gender = PlayerGender.MALE; @@ -183,7 +169,7 @@ describe("Test Battle Phase", () => { await game.phaseInterceptor.run(TitlePhase); }, 20000); - it("good run from select gender to title", async() => { + it("good run from select gender to title", async () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { game.scene.gameData.gender = PlayerGender.MALE; @@ -192,7 +178,7 @@ describe("Test Battle Phase", () => { await game.phaseInterceptor.runFrom(SelectGenderPhase).to(TitlePhase); }, 20000); - it("good run to SummonPhase phase", async() => { + it("good run to SummonPhase phase", async () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { game.scene.gameData.gender = PlayerGender.MALE; @@ -208,7 +194,7 @@ describe("Test Battle Phase", () => { await game.phaseInterceptor.runFrom(SelectGenderPhase).to(SummonPhase); }, 20000); - it("2vs1", async() => { + it("2vs1", async () => { game.override.battleType("single"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); @@ -221,7 +207,7 @@ describe("Test Battle Phase", () => { expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); - it("1vs1", async() => { + it("1vs1", async () => { game.override.battleType("single"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); @@ -233,7 +219,7 @@ describe("Test Battle Phase", () => { expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); - it("2vs2", async() => { + it("2vs2", async () => { game.override.battleType("double"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); @@ -247,7 +233,7 @@ describe("Test Battle Phase", () => { expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); - it("4vs2", async() => { + it("4vs2", async () => { game.override.battleType("double"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); @@ -263,7 +249,7 @@ describe("Test Battle Phase", () => { expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); - it("kill opponent pokemon", async() => { + it("kill opponent pokemon", async () => { const moveToUse = Moves.SPLASH; game.override.battleType("single"); game.override.starterSpecies(Species.MEWTWO); @@ -273,26 +259,20 @@ describe("Test Battle Phase", () => { game.override.startingLevel(2000); game.override.startingWave(3); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle([ Species.DARMANITAN, Species.CHARIZARD, ]); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.to(DamagePhase, false); await game.killPokemon(game.scene.currentBattle.enemyParty[0]); expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true); await game.phaseInterceptor.to(VictoryPhase, false); }, 200000); - it("to next turn", async() => { + it("to next turn", async () => { const moveToUse = Moves.SPLASH; game.override.battleType("single"); game.override.starterSpecies(Species.MEWTWO); @@ -302,15 +282,15 @@ describe("Test Battle Phase", () => { game.override.startingLevel(2000); game.override.startingWave(3); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle(); const turn = game.scene.currentBattle.turn; - game.doAttack(0); + game.move.select(moveToUse); await game.toNextTurn(); expect(game.scene.currentBattle.turn).toBeGreaterThan(turn); }, 20000); - it("to next wave with pokemon killed, single", async() => { + it("to next wave with pokemon killed, single", async () => { const moveToUse = Moves.SPLASH; game.override.battleType("single"); game.override.starterSpecies(Species.MEWTWO); @@ -320,10 +300,10 @@ describe("Test Battle Phase", () => { game.override.startingLevel(2000); game.override.startingWave(3); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); await game.startBattle(); const waveIndex = game.scene.currentBattle.waveIndex; - game.doAttack(0); + game.move.select(moveToUse); await game.doKillOpponents(); await game.toNextWave(); expect(game.scene.currentBattle.waveIndex).toBeGreaterThan(waveIndex); @@ -343,7 +323,7 @@ describe("Test Battle Phase", () => { await game.startBattle(); game.scene.getPlayerPokemon()!.hp = 1; - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(BattleEndPhase); game.doRevivePokemon(0); // pretend max revive was picked diff --git a/src/test/battle/damage_calculation.test.ts b/src/test/battle/damage_calculation.test.ts index 9b13a266d33..665000450be 100644 --- a/src/test/battle/damage_calculation.test.ts +++ b/src/test/battle/damage_calculation.test.ts @@ -1,14 +1,13 @@ import { DamagePhase } from "#app/phases/damage-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { ArenaTagType } from "#enums/arena-tag-type"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { toDmgValue } from "#app/utils"; describe("Round Down and Minimun 1 test in Damage Calculation", () => { let phaserGame: Phaser.Game; @@ -41,7 +40,7 @@ describe("Round Down and Minimun 1 test in Damage Calculation", () => { const shedinja = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.JUMP_KICK)); + game.move.select(Moves.JUMP_KICK); await game.phaseInterceptor.to(DamagePhase); diff --git a/src/test/battle/double_battle.test.ts b/src/test/battle/double_battle.test.ts index d2ee3812b3e..d264a29ef9b 100644 --- a/src/test/battle/double_battle.test.ts +++ b/src/test/battle/double_battle.test.ts @@ -1,13 +1,12 @@ -import GameManager from "#test/utils/gameManager"; -import { getMovePosition, } from "#test/utils/gameManagerUtils"; +import { Status, StatusEffect } from "#app/data/status-effect"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { BattleEndPhase } from "#app/phases/battle-end-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; describe("Double Battles", () => { let phaserGame: Phaser.Game; @@ -29,7 +28,7 @@ describe("Double Battles", () => { // double-battle player's pokemon both fainted in same round, then revive one, and next double battle summons two player's pokemon successfully. // (There were bugs that either only summon one when can summon two, player stuck in switchPhase etc) - it("3v2 edge case: player summons 2 pokemon on the next battle after being fainted and revived", async() => { + it("3v2 edge case: player summons 2 pokemon on the next battle after being fainted and revived", async () => { game.override.battleType("double").enemyMoveset(SPLASH_ONLY).moveset(SPLASH_ONLY); await game.startBattle([ Species.BULBASAUR, @@ -37,8 +36,8 @@ describe("Double Battles", () => { Species.SQUIRTLE, ]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH, 1); for (const pokemon of game.scene.getPlayerField()) { pokemon.hp = 0; diff --git a/src/test/battle/error-handling.test.ts b/src/test/battle/error-handling.test.ts index a88d7cd8c18..da5cc4d1969 100644 --- a/src/test/battle/error-handling.test.ts +++ b/src/test/battle/error-handling.test.ts @@ -1,13 +1,14 @@ -import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Error Handling", () => { let phaserGame: Phaser.Game; let game: GameManager; + const moveToUse = Moves.SPLASH; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -21,7 +22,6 @@ describe("Error Handling", () => { beforeEach(() => { game = new GameManager(phaserGame); - const moveToUse = Moves.SPLASH; game.override .battleType("single") .startingWave(3); @@ -31,13 +31,13 @@ describe("Error Handling", () => { game.override.ability(Abilities.ZEN_MODE); game.override.startingLevel(2000); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it.skip("to next turn", async() => { + it.skip("to next turn", async () => { await game.startBattle(); const turn = game.scene.currentBattle.turn; - game.doAttack(0); + game.move.select(moveToUse); await game.toNextTurn(); expect(game.scene.currentBattle.turn).toBeGreaterThan(turn); }, 20000); diff --git a/src/test/battle/special_battle.test.ts b/src/test/battle/special_battle.test.ts index 9b0fd1b3ab1..1d319bea372 100644 --- a/src/test/battle/special_battle.test.ts +++ b/src/test/battle/special_battle.test.ts @@ -1,9 +1,9 @@ -import { CommandPhase } from "#app/phases/command-phase.js"; -import GameManager from "#test/utils/gameManager"; +import { CommandPhase } from "#app/phases/command-phase"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/src/test/battlerTags/octolock.test.ts b/src/test/battlerTags/octolock.test.ts index a69b45cdfd2..fa491589f09 100644 --- a/src/test/battlerTags/octolock.test.ts +++ b/src/test/battlerTags/octolock.test.ts @@ -1,10 +1,10 @@ +import BattleScene from "#app/battle-scene"; +import { BattleStat } from "#app/data/battle-stat"; +import { BattlerTag, BattlerTagLapseType, OctolockTag, TrappedTag } from "#app/data/battler-tags"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import Pokemon from "#app/field/pokemon"; +import { StatChangePhase } from "#app/phases/stat-change-phase"; import { describe, expect, it, vi } from "vitest"; -import Pokemon from "#app/field/pokemon.js"; -import BattleScene from "#app/battle-scene.js"; -import { BattlerTag, BattlerTagLapseType, OctolockTag, TrappedTag } from "#app/data/battler-tags.js"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { StatChangePhase } from "#app/phases/stat-change-phase.js"; vi.mock("#app/battle-scene.js"); diff --git a/src/test/battlerTags/stockpiling.test.ts b/src/test/battlerTags/stockpiling.test.ts index 1a39d11e1bd..fef1e938c09 100644 --- a/src/test/battlerTags/stockpiling.test.ts +++ b/src/test/battlerTags/stockpiling.test.ts @@ -1,10 +1,10 @@ +import BattleScene from "#app/battle-scene"; +import { BattleStat } from "#app/data/battle-stat"; +import { StockpilingTag } from "#app/data/battler-tags"; +import Pokemon, { PokemonSummonData } from "#app/field/pokemon"; +import * as messages from "#app/messages"; +import { StatChangePhase } from "#app/phases/stat-change-phase"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import Pokemon, { PokemonSummonData } from "#app/field/pokemon.js"; -import BattleScene from "#app/battle-scene.js"; -import { StockpilingTag } from "#app/data/battler-tags.js"; -import { BattleStat } from "#app/data/battle-stat.js"; -import * as messages from "#app/messages.js"; -import { StatChangePhase } from "#app/phases/stat-change-phase.js"; beforeEach(() => { vi.spyOn(messages, "getPokemonNameWithAffix").mockImplementation(() => ""); diff --git a/src/test/eggs/egg.test.ts b/src/test/eggs/egg.test.ts index 0bc2972e2dc..a01d2257099 100644 --- a/src/test/eggs/egg.test.ts +++ b/src/test/eggs/egg.test.ts @@ -1,14 +1,14 @@ +import { Egg, getLegendaryGachaSpeciesForTimestamp } from "#app/data/egg"; +import { EggSourceType } from "#app/enums/egg-source-types"; +import { EggTier } from "#app/enums/egg-type"; +import { VariantTier } from "#app/enums/variant-tiers"; +import EggData from "#app/system/egg-data"; +import * as Utils from "#app/utils"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import BattleScene from "../../battle-scene"; -import { Egg, getLegendaryGachaSpeciesForTimestamp } from "#app/data/egg.js"; -import { Species } from "#enums/species"; -import Phaser from "phaser"; -import { EggSourceType } from "#app/enums/egg-source-types.js"; -import { EggTier } from "#app/enums/egg-type.js"; -import { VariantTier } from "#app/enums/variant-tiers.js"; -import GameManager from "#test/utils/gameManager"; -import EggData from "#app/system/egg-data.js"; -import * as Utils from "#app/utils.js"; describe("Egg Generation Tests", () => { let phaserGame: Phaser.Game; diff --git a/src/test/evolution.test.ts b/src/test/evolution.test.ts index b54deaa4611..41088c17bcb 100644 --- a/src/test/evolution.test.ts +++ b/src/test/evolution.test.ts @@ -1,6 +1,6 @@ -import { pokemonEvolutions, SpeciesFormEvolution, SpeciesWildEvolutionDelay } from "#app/data/pokemon-evolutions.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Species } from "#app/enums/species.js"; +import { pokemonEvolutions, SpeciesFormEvolution, SpeciesWildEvolutionDelay } from "#app/data/pokemon-evolutions"; +import { Abilities } from "#app/enums/abilities"; +import { Species } from "#app/enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/src/test/evolutions/evolutions.test.ts b/src/test/evolutions/evolutions.test.ts index af43e91b059..2028764115c 100644 --- a/src/test/evolutions/evolutions.test.ts +++ b/src/test/evolutions/evolutions.test.ts @@ -1,8 +1,8 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; import * as Utils from "#app/utils"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Evolution tests", () => { let phaserGame: Phaser.Game; diff --git a/src/test/field/pokemon.test.ts b/src/test/field/pokemon.test.ts index ee8e41e8b42..d597cd5219c 100644 --- a/src/test/field/pokemon.test.ts +++ b/src/test/field/pokemon.test.ts @@ -1,4 +1,4 @@ -import { Species } from "#app/enums/species.js"; +import { Species } from "#app/enums/species"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "../utils/gameManager"; diff --git a/src/test/final_boss.test.ts b/src/test/final_boss.test.ts index a57d71534a3..0f59572619b 100644 --- a/src/test/final_boss.test.ts +++ b/src/test/final_boss.test.ts @@ -1,8 +1,8 @@ -import { Biome } from "#app/enums/biome.js"; -import { Species } from "#app/enums/species.js"; +import { Biome } from "#app/enums/biome"; +import { Species } from "#app/enums/species"; +import { GameModes } from "#app/game-mode"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "./utils/gameManager"; -import { GameModes } from "#app/game-mode"; const FinalWave = { Classic: 200, diff --git a/src/test/game-mode.test.ts b/src/test/game-mode.test.ts index 4a1960a05ff..ccec3a3aa16 100644 --- a/src/test/game-mode.test.ts +++ b/src/test/game-mode.test.ts @@ -1,7 +1,7 @@ -import { GameMode, GameModes, getGameMode } from "#app/game-mode.js"; +import { GameMode, GameModes, getGameMode } from "#app/game-mode"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import GameManager from "./utils/gameManager"; import * as Utils from "../utils"; +import GameManager from "./utils/gameManager"; describe("game-mode", () => { let phaserGame: Phaser.Game; let game: GameManager; diff --git a/src/test/imports.test.ts b/src/test/imports.test.ts index 69c145236bc..305eccdc465 100644 --- a/src/test/imports.test.ts +++ b/src/test/imports.test.ts @@ -1,5 +1,5 @@ -import { describe, expect, it } from "vitest"; import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; +import { describe, expect, it } from "vitest"; async function importModule() { try { diff --git a/src/test/inputs/inputs.test.ts b/src/test/inputs/inputs.test.ts index 7182ac2c02c..6306c1b9da6 100644 --- a/src/test/inputs/inputs.test.ts +++ b/src/test/inputs/inputs.test.ts @@ -1,9 +1,9 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty"; +import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; +import GameManager from "#test/utils/gameManager"; import InputsHandler from "#test/utils/inputsHandler"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Inputs", () => { diff --git a/src/test/internals.test.ts b/src/test/internals.test.ts index 0ecd156431d..3c76b40e901 100644 --- a/src/test/internals.test.ts +++ b/src/test/internals.test.ts @@ -1,8 +1,8 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; +import { Abilities } from "#app/enums/abilities"; +import { Species } from "#app/enums/species"; import GameManager from "#test/utils/gameManager"; -import { Species } from "#app/enums/species.js"; -import { Abilities } from "#app/enums/abilities.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Internals", () => { let phaserGame: Phaser.Game; diff --git a/src/test/items/eviolite.test.ts b/src/test/items/eviolite.test.ts index 0fe90866de8..e491784acec 100644 --- a/src/test/items/eviolite.test.ts +++ b/src/test/items/eviolite.test.ts @@ -2,9 +2,9 @@ import { Stat } from "#app/data/pokemon-stat"; import { EvolutionStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/src/test/items/exp_booster.test.ts b/src/test/items/exp_booster.test.ts index 2b700c92086..9a7464e4866 100644 --- a/src/test/items/exp_booster.test.ts +++ b/src/test/items/exp_booster.test.ts @@ -1,7 +1,7 @@ -import { Abilities } from "#app/enums/abilities.js"; -import { PokemonExpBoosterModifier } from "#app/modifier/modifier.js"; -import GameManager from "#test/utils/gameManager"; +import { Abilities } from "#app/enums/abilities"; +import { PokemonExpBoosterModifier } from "#app/modifier/modifier"; import * as Utils from "#app/utils"; +import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/src/test/items/grip_claw.test.ts b/src/test/items/grip_claw.test.ts index ecf144c96c5..09afa9aea0b 100644 --- a/src/test/items/grip_claw.test.ts +++ b/src/test/items/grip_claw.test.ts @@ -1,16 +1,14 @@ -import { BattlerIndex } from "#app/battle.js"; -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BerryType } from "#app/enums/berry-type.js"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { BerryType } from "#app/enums/berry-type"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { SelectTargetPhase } from "#app/phases/select-target-phase.js"; +import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; // 20 seconds @@ -35,12 +33,12 @@ describe("Items - Grip Claw", () => { .battleType("double") .moveset([Moves.POPULATION_BOMB, Moves.SPLASH]) .startingHeldItems([ - { name: "GRIP_CLAW", count: 5 }, + { name: "GRIP_CLAW", count: 5 }, // TODO: Find a way to mock the steal chance of grip claw { name: "MULTI_LENS", count: 3 }, ]) .enemySpecies(Species.SNORLAX) .ability(Abilities.KLUTZ) - .enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]) + .enemyMoveset(SPLASH_ONLY) .enemyHeldItems([ { name: "BERRY", type: BerryType.SITRUS, count: 2 }, { name: "BERRY", type: BerryType.LUM, count: 2 }, @@ -54,19 +52,14 @@ describe("Items - Grip Claw", () => { it( "should only steal items from the attack target", async () => { - await game.startBattle([Species.PANSEAR, Species.ROWLET, Species.PANPOUR, Species.PANSAGE, Species.CHARMANDER, Species.SQUIRTLE]); + await game.startBattle([Species.PANSEAR, Species.ROWLET]); const enemyPokemon = game.scene.getEnemyField(); const enemyHeldItemCt = enemyPokemon.map(p => p.getHeldItems.length); - game.doAttack(getMovePosition(game.scene, 0, Moves.POPULATION_BOMB)); - - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - - await game.phaseInterceptor.to(CommandPhase, false); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.POPULATION_BOMB, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEndPhase, false); diff --git a/src/test/items/leek.test.ts b/src/test/items/leek.test.ts index 1e46bda9f0f..7505b6374a0 100644 --- a/src/test/items/leek.test.ts +++ b/src/test/items/leek.test.ts @@ -1,11 +1,11 @@ import { BattlerIndex } from "#app/battle"; import { CritBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import GameManager from "#test/utils/gameManager"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -27,21 +27,21 @@ describe("Items - Leek", () => { game = new GameManager(phaserGame); game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); game.override.disableCrits(); game.override.battleType("single"); }); - it("LEEK activates in battle correctly", async() => { + it("LEEK activates in battle correctly", async () => { game.override.startingHeldItems([{ name: "LEEK" }]); - game.override.moveset([ Moves.POUND ]); + game.override.moveset([Moves.POUND]); const consoleSpy = vi.spyOn(console, "log"); await game.startBattle([ Species.FARFETCHD ]); - game.doAttack(0); + game.move.select(Moves.POUND); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -50,7 +50,7 @@ describe("Items - Leek", () => { expect(consoleSpy).toHaveBeenCalledWith("Applied", "Leek", ""); }, 20000); - it("LEEK held by FARFETCHD", async() => { + it("LEEK held by FARFETCHD", async () => { await game.startBattle([ Species.FARFETCHD ]); @@ -70,7 +70,7 @@ describe("Items - Leek", () => { expect(critLevel.value).toBe(2); }, 20000); - it("LEEK held by GALAR_FARFETCHD", async() => { + it("LEEK held by GALAR_FARFETCHD", async () => { await game.startBattle([ Species.GALAR_FARFETCHD ]); @@ -90,7 +90,7 @@ describe("Items - Leek", () => { expect(critLevel.value).toBe(2); }, 20000); - it("LEEK held by SIRFETCHD", async() => { + it("LEEK held by SIRFETCHD", async () => { await game.startBattle([ Species.SIRFETCHD ]); @@ -110,9 +110,9 @@ describe("Items - Leek", () => { expect(critLevel.value).toBe(2); }, 20000); - it("LEEK held by fused FARFETCHD line (base)", async() => { + it("LEEK held by fused FARFETCHD line (base)", async () => { // Randomly choose from the Farfetch'd line - const species = [ Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD ]; + const species = [Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD]; await game.startBattle([ species[Utils.randInt(species.length)], @@ -145,9 +145,9 @@ describe("Items - Leek", () => { expect(critLevel.value).toBe(2); }, 20000); - it("LEEK held by fused FARFETCHD line (part)", async() => { + it("LEEK held by fused FARFETCHD line (part)", async () => { // Randomly choose from the Farfetch'd line - const species = [ Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD ]; + const species = [Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD]; await game.startBattle([ Species.PIKACHU, @@ -180,7 +180,7 @@ describe("Items - Leek", () => { expect(critLevel.value).toBe(2); }, 20000); - it("LEEK not held by FARFETCHD line", async() => { + it("LEEK not held by FARFETCHD line", async () => { await game.startBattle([ Species.PIKACHU ]); diff --git a/src/test/items/leftovers.test.ts b/src/test/items/leftovers.test.ts index 1a1c95ad9e6..8e548542436 100644 --- a/src/test/items/leftovers.test.ts +++ b/src/test/items/leftovers.test.ts @@ -1,12 +1,11 @@ -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Items - Leftovers", () => { @@ -32,10 +31,10 @@ describe("Items - Leftovers", () => { game.override.enemySpecies(Species.SHUCKLE); game.override.enemyAbility(Abilities.UNNERVE); game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); - game.override.startingHeldItems([{name: "LEFTOVERS", count: 1}]); + game.override.startingHeldItems([{ name: "LEFTOVERS", count: 1 }]); }); - it("leftovers works", async() => { + it("leftovers works", async () => { await game.startBattle([Species.ARCANINE]); // Make sure leftovers are there @@ -46,7 +45,7 @@ describe("Items - Leftovers", () => { // We should have full hp expect(leadPokemon.isFullHp()).toBe(true); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); // We should have less hp after the attack await game.phaseInterceptor.to(DamagePhase, false); diff --git a/src/test/items/light_ball.test.ts b/src/test/items/light_ball.test.ts index ff7dfa4eba5..cf4f5c9e22f 100644 --- a/src/test/items/light_ball.test.ts +++ b/src/test/items/light_ball.test.ts @@ -2,9 +2,9 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/src/test/items/lock_capsule.test.ts b/src/test/items/lock_capsule.test.ts index 0909e51ea2c..bc4ca1cb014 100644 --- a/src/test/items/lock_capsule.test.ts +++ b/src/test/items/lock_capsule.test.ts @@ -1,11 +1,10 @@ +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { Abilities } from "#app/enums/abilities.js"; -import { Moves } from "#app/enums/moves.js"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type.js"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase.js"; describe("Items - Lock Capsule", () => { let phaserGame: Phaser.Game; @@ -29,13 +28,13 @@ describe("Items - Lock Capsule", () => { .startingLevel(200) .moveset([Moves.SURF]) .enemyAbility(Abilities.BALL_FETCH) - .startingModifier([{name: "LOCK_CAPSULE"}]); + .startingModifier([{ name: "LOCK_CAPSULE" }]); }); - it("doesn't set the cost of common tier items to 0", async() => { + it("doesn't set the cost of common tier items to 0", async () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); + game.move.select(Moves.SURF); await game.phaseInterceptor.to(SelectModifierPhase, false); const rewards = game.scene.getCurrentPhase() as SelectModifierPhase; diff --git a/src/test/items/metal_powder.test.ts b/src/test/items/metal_powder.test.ts index 966762e4175..a3a4936532f 100644 --- a/src/test/items/metal_powder.test.ts +++ b/src/test/items/metal_powder.test.ts @@ -2,9 +2,9 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/src/test/items/quick_powder.test.ts b/src/test/items/quick_powder.test.ts index d2435dab431..53521ba78f1 100644 --- a/src/test/items/quick_powder.test.ts +++ b/src/test/items/quick_powder.test.ts @@ -2,9 +2,9 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/src/test/items/scope_lens.test.ts b/src/test/items/scope_lens.test.ts index fa605ca7129..85673218762 100644 --- a/src/test/items/scope_lens.test.ts +++ b/src/test/items/scope_lens.test.ts @@ -1,11 +1,11 @@ import { BattlerIndex } from "#app/battle"; import { CritBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import GameManager from "#test/utils/gameManager"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import * as Utils from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -27,30 +27,30 @@ describe("Items - Scope Lens", () => { game = new GameManager(phaserGame); game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); game.override.disableCrits(); game.override.battleType("single"); }, 20000); - it("SCOPE_LENS activates in battle correctly", async() => { + it("SCOPE_LENS activates in battle correctly", async () => { game.override.startingHeldItems([{ name: "SCOPE_LENS" }]); - game.override.moveset([ Moves.POUND ]); + game.override.moveset([Moves.POUND]); const consoleSpy = vi.spyOn(console, "log"); await game.startBattle([ Species.GASTLY ]); - game.doAttack(0); + game.move.select(Moves.POUND); - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase); expect(consoleSpy).toHaveBeenCalledWith("Applied", "Scope Lens", ""); }, 20000); - it("SCOPE_LENS held by random pokemon", async() => { + it("SCOPE_LENS held by random pokemon", async () => { await game.startBattle([ Species.GASTLY ]); diff --git a/src/test/items/thick_club.test.ts b/src/test/items/thick_club.test.ts index 841cd7c90ac..347921446e6 100644 --- a/src/test/items/thick_club.test.ts +++ b/src/test/items/thick_club.test.ts @@ -2,9 +2,9 @@ import { Stat } from "#app/data/pokemon-stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; import * as Utils from "#app/utils"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/src/test/items/toxic_orb.test.ts b/src/test/items/toxic_orb.test.ts index dc54a5a1c36..95336c0793e 100644 --- a/src/test/items/toxic_orb.test.ts +++ b/src/test/items/toxic_orb.test.ts @@ -1,18 +1,14 @@ import { StatusEffect } from "#app/data/status-effect"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import i18next, { initI18n } from "#app/plugins/i18n"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js"; -import { MessagePhase } from "#app/phases/message-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Items - Toxic orb", () => { @@ -45,7 +41,7 @@ describe("Items - Toxic orb", () => { }]); }); - it("TOXIC ORB", async() => { + it("TOXIC ORB", async () => { initI18n(); i18next.changeLanguage("en"); const moveToUse = Moves.GROWTH; @@ -55,15 +51,7 @@ describe("Items - Toxic orb", () => { ]); expect(game.scene.modifiers[0].type.id).toBe("TOXIC_ORB"); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - // Select Attack - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - // Select Move Growth - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); // will run the 13 phase from enemyCommandPhase to TurnEndPhase await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); diff --git a/src/test/localization/battle-stat.test.ts b/src/test/localization/battle-stat.test.ts index b99ed2b7064..e8fc20ab5a4 100644 --- a/src/test/localization/battle-stat.test.ts +++ b/src/test/localization/battle-stat.test.ts @@ -1,26 +1,25 @@ -import { beforeAll, describe, expect, it } from "vitest"; -import { getBattleStatName, getBattleStatLevelChangeDescription } from "#app/data/battle-stat.js"; -import { BattleStat} from "#app/data/battle-stat.js"; -import { pokemonInfo as enPokemonInfo } from "#app/locales/en/pokemon-info.js"; -import { battle as enBattleStat } from "#app/locales/en/battle.js"; -import { pokemonInfo as dePokemonInfo } from "#app/locales/de/pokemon-info.js"; -import { battle as deBattleStat } from "#app/locales/de/battle.js"; -import { pokemonInfo as esPokemonInfo } from "#app/locales/es/pokemon-info.js"; -import { battle as esBattleStat } from "#app/locales/es/battle.js"; -import { pokemonInfo as frPokemonInfo } from "#app/locales/fr/pokemon-info.js"; -import { battle as frBattleStat } from "#app/locales/fr/battle.js"; -import { pokemonInfo as itPokemonInfo } from "#app/locales/it/pokemon-info.js"; -import { battle as itBattleStat } from "#app/locales/it/battle.js"; -import { pokemonInfo as koPokemonInfo } from "#app/locales/ko/pokemon-info.js"; -import { battle as koBattleStat } from "#app/locales/ko/battle.js"; -import { pokemonInfo as ptBrPokemonInfo } from "#app/locales/pt_BR/pokemon-info.js"; -import { battle as ptBrBattleStat } from "#app/locales/pt_BR/battle.js"; -import { pokemonInfo as zhCnPokemonInfo } from "#app/locales/zh_CN/pokemon-info.js"; -import { battle as zhCnBattleStat } from "#app/locales/zh_CN/battle.js"; -import { pokemonInfo as zhTwPokemonInfo } from "#app/locales/zh_TW/pokemon-info.js"; -import { battle as zhTwBattleStat } from "#app/locales/zh_TW/battle.js"; +import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "#app/data/battle-stat"; +import { battle as deBattleStat } from "#app/locales/de/battle"; +import { pokemonInfo as dePokemonInfo } from "#app/locales/de/pokemon-info"; +import { battle as enBattleStat } from "#app/locales/en/battle"; +import { pokemonInfo as enPokemonInfo } from "#app/locales/en/pokemon-info"; +import { battle as esBattleStat } from "#app/locales/es/battle"; +import { pokemonInfo as esPokemonInfo } from "#app/locales/es/pokemon-info"; +import { battle as frBattleStat } from "#app/locales/fr/battle"; +import { pokemonInfo as frPokemonInfo } from "#app/locales/fr/pokemon-info"; +import { battle as itBattleStat } from "#app/locales/it/battle"; +import { pokemonInfo as itPokemonInfo } from "#app/locales/it/pokemon-info"; +import { battle as koBattleStat } from "#app/locales/ko/battle"; +import { pokemonInfo as koPokemonInfo } from "#app/locales/ko/pokemon-info"; +import { battle as ptBrBattleStat } from "#app/locales/pt_BR/battle"; +import { pokemonInfo as ptBrPokemonInfo } from "#app/locales/pt_BR/pokemon-info"; +import { battle as zhCnBattleStat } from "#app/locales/zh_CN/battle"; +import { pokemonInfo as zhCnPokemonInfo } from "#app/locales/zh_CN/pokemon-info"; +import { battle as zhTwBattleStat } from "#app/locales/zh_TW/battle"; +import { pokemonInfo as zhTwPokemonInfo } from "#app/locales/zh_TW/pokemon-info"; import i18next, { initI18n } from "#app/plugins/i18n"; import { KoreanPostpositionProcessor } from "i18next-korean-postposition-processor"; +import { beforeAll, describe, expect, it } from "vitest"; interface BattleStatTestUnit { stat: BattleStat, diff --git a/src/test/localization/french.test.ts b/src/test/localization/french.test.ts index b03a8ee64e8..92b4c82d7cb 100644 --- a/src/test/localization/french.test.ts +++ b/src/test/localization/french.test.ts @@ -1,9 +1,9 @@ -import { afterEach, beforeAll, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; -import i18next from "i18next"; import { initI18n } from "#app/plugins/i18n"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import i18next from "i18next"; +import Phaser from "phaser"; +import { afterEach, beforeAll, describe, expect, it } from "vitest"; describe("Lokalization - french", () => { let phaserGame: Phaser.Game; diff --git a/src/test/localization/status-effect.test.ts b/src/test/localization/status-effect.test.ts index 8a9effe1672..9dcab5aeb5f 100644 --- a/src/test/localization/status-effect.test.ts +++ b/src/test/localization/status-effect.test.ts @@ -1,7 +1,7 @@ -import { beforeAll, describe, afterEach, expect, it, vi } from "vitest"; import { StatusEffect, getStatusEffectActivationText, getStatusEffectDescriptor, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "#app/data/status-effect"; -import i18next from "i18next"; import { mockI18next } from "#test/utils/testUtils"; +import i18next from "i18next"; +import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; const pokemonName = "PKM"; const sourceText = "SOURCE"; diff --git a/src/test/localization/terrain.test.ts b/src/test/localization/terrain.test.ts index c072f9cc9ab..ed280177a06 100644 --- a/src/test/localization/terrain.test.ts +++ b/src/test/localization/terrain.test.ts @@ -1,11 +1,11 @@ import { TerrainType, getTerrainName } from "#app/data/terrain"; import { getTerrainBlockMessage, getTerrainClearMessage, getTerrainStartMessage } from "#app/data/weather"; -import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { mockI18next } from "#test/utils/testUtils"; import i18next from "i18next"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { mockI18next } from "#test/utils/testUtils"; describe("terrain", () => { let phaserGame: Phaser.Game; diff --git a/src/test/moves/astonish.test.ts b/src/test/moves/astonish.test.ts index 21a82f09d33..b21e2a06051 100644 --- a/src/test/moves/astonish.test.ts +++ b/src/test/moves/astonish.test.ts @@ -1,16 +1,15 @@ -import { allMoves } from "#app/data/move.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; +import { allMoves } from "#app/data/move"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -50,7 +49,7 @@ describe("Moves - Astonish", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.ASTONISH)); + game.move.select(Moves.ASTONISH); await game.phaseInterceptor.to(MoveEndPhase, false); @@ -63,7 +62,7 @@ describe("Moves - Astonish", () => { await game.phaseInterceptor.to(CommandPhase, false); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/aurora_veil.test.ts b/src/test/moves/aurora_veil.test.ts index 5429efec2bf..fec280debf4 100644 --- a/src/test/moves/aurora_veil.test.ts +++ b/src/test/moves/aurora_veil.test.ts @@ -1,15 +1,14 @@ -import { ArenaTagSide } from "#app/data/arena-tag.js"; -import Move, { allMoves } from "#app/data/move.js"; -import { WeatherType } from "#app/data/weather.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import Pokemon from "#app/field/pokemon.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { NumberHolder } from "#app/utils.js"; +import { ArenaTagSide } from "#app/data/arena-tag"; +import Move, { allMoves } from "#app/data/move"; +import { WeatherType } from "#app/data/weather"; +import { Abilities } from "#app/enums/abilities"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import Pokemon from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { NumberHolder } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -18,7 +17,7 @@ describe("Moves - Aurora Veil", () => { let phaserGame: Phaser.Game; let game: GameManager; const singleBattleMultiplier = 0.5; - const doubleBattleMultiplier = 2732/4096; + const doubleBattleMultiplier = 2732 / 4096; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -42,11 +41,11 @@ describe("Moves - Aurora Veil", () => { game.override.weather(WeatherType.HAIL); }); - it("reduces damage of physical attacks by half in a single battle", async() => { + it("reduces damage of physical attacks by half in a single battle", async () => { const moveToUse = Moves.TACKLE; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); @@ -54,14 +53,14 @@ describe("Moves - Aurora Veil", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); - it("reduces damage of physical attacks by a third in a double battle", async() => { + it("reduces damage of physical attacks by a third in a double battle", async () => { game.override.battleType("double"); const moveToUse = Moves.ROCK_SLIDE; await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); - game.doAttack(getMovePosition(game.scene, 1, moveToUse)); + game.move.select(moveToUse); + game.move.select(moveToUse, 1); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); @@ -69,11 +68,11 @@ describe("Moves - Aurora Veil", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); - it("reduces damage of special attacks by half in a single battle", async() => { + it("reduces damage of special attacks by half in a single battle", async () => { const moveToUse = Moves.ABSORB; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -82,14 +81,14 @@ describe("Moves - Aurora Veil", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); - it("reduces damage of special attacks by a third in a double battle", async() => { + it("reduces damage of special attacks by a third in a double battle", async () => { game.override.battleType("double"); const moveToUse = Moves.DAZZLING_GLEAM; await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); - game.doAttack(getMovePosition(game.scene, 1, moveToUse)); + game.move.select(moveToUse); + game.move.select(moveToUse, 1); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); diff --git a/src/test/moves/baton_pass.test.ts b/src/test/moves/baton_pass.test.ts index 790eddbf45c..602da9e37f8 100644 --- a/src/test/moves/baton_pass.test.ts +++ b/src/test/moves/baton_pass.test.ts @@ -1,13 +1,12 @@ -import { BattleStat } from "#app/data/battle-stat.js"; +import { BattleStat } from "#app/data/battle-stat"; +import { PostSummonPhase } from "#app/phases/post-summon-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { PostSummonPhase } from "#app/phases/post-summon-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Moves - Baton Pass", () => { @@ -36,7 +35,7 @@ describe("Moves - Baton Pass", () => { .disableCrits(); }); - it("passes stat stage buffs when player uses it", async() => { + it("passes stat stage buffs when player uses it", async () => { // arrange await game.startBattle([ Species.RAICHU, @@ -44,12 +43,12 @@ describe("Moves - Baton Pass", () => { ]); // round 1 - buff - game.doAttack(getMovePosition(game.scene, 0, Moves.NASTY_PLOT)); + game.move.select(Moves.NASTY_PLOT); await game.toNextTurn(); expect(game.scene.getPlayerPokemon()!.summonData.battleStats[BattleStat.SPATK]).toEqual(2); // round 2 - baton pass - game.doAttack(getMovePosition(game.scene, 0, Moves.BATON_PASS)); + game.move.select(Moves.BATON_PASS); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to(TurnEndPhase); @@ -59,7 +58,7 @@ describe("Moves - Baton Pass", () => { expect(playerPkm.summonData.battleStats[BattleStat.SPATK]).toEqual(2); }, 20000); - it("passes stat stage buffs when AI uses it", async() => { + it("passes stat stage buffs when AI uses it", async () => { // arrange game.override .startingWave(5) @@ -70,13 +69,13 @@ describe("Moves - Baton Pass", () => { ]); // round 1 - ai buffs - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); // round 2 - baton pass game.scene.getEnemyPokemon()!.hp = 100; game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS)); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(PostSummonPhase, false); // assert diff --git a/src/test/moves/beak_blast.test.ts b/src/test/moves/beak_blast.test.ts index 8938b4c7af8..2a93dc00a54 100644 --- a/src/test/moves/beak_blast.test.ts +++ b/src/test/moves/beak_blast.test.ts @@ -1,15 +1,14 @@ -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { StatusEffect } from "#app/enums/status-effect"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { MovePhase } from "#app/phases/move-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { MovePhase } from "#app/phases/move-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const TIMEOUT = 20 * 1000; @@ -48,7 +47,7 @@ describe("Moves - Beak Blast", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAK_BLAST)); + game.move.select(Moves.BEAK_BLAST); await game.phaseInterceptor.to(MovePhase, false); expect(leadPokemon.getTag(BattlerTagType.BEAK_BLAST_CHARGING)).toBeDefined(); @@ -68,7 +67,7 @@ describe("Moves - Beak Blast", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAK_BLAST)); + game.move.select(Moves.BEAK_BLAST); await game.phaseInterceptor.to(MovePhase, false); expect(leadPokemon.getTag(BattlerTagType.BEAK_BLAST_CHARGING)).toBeDefined(); @@ -88,7 +87,7 @@ describe("Moves - Beak Blast", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAK_BLAST)); + game.move.select(Moves.BEAK_BLAST); await game.phaseInterceptor.to(MovePhase, false); expect(leadPokemon.getTag(BattlerTagType.BEAK_BLAST_CHARGING)).toBeDefined(); @@ -101,13 +100,13 @@ describe("Moves - Beak Blast", () => { it( "should only hit twice with Multi-Lens", async () => { - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.BLASTOISE]); const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAK_BLAST)); + game.move.select(Moves.BEAK_BLAST); await game.phaseInterceptor.to(BerryPhase, false); expect(leadPokemon.turnData.hitCount).toBe(2); @@ -124,7 +123,7 @@ describe("Moves - Beak Blast", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAK_BLAST)); + game.move.select(Moves.BEAK_BLAST); await game.phaseInterceptor.to(MovePhase, false); expect(leadPokemon.getTag(BattlerTagType.BEAK_BLAST_CHARGING)).toBeDefined(); diff --git a/src/test/moves/beat_up.test.ts b/src/test/moves/beat_up.test.ts index a0f168ea30f..ce1598a49b4 100644 --- a/src/test/moves/beat_up.test.ts +++ b/src/test/moves/beat_up.test.ts @@ -1,12 +1,11 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { StatusEffect } from "#app/enums/status-effect"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/utils/gameManager"; -import { Species } from "#app/enums/species.js"; -import { Moves } from "#app/enums/moves.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { StatusEffect } from "#app/enums/status-effect.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const TIMEOUT = 20 * 1000; // 20 sec timeout @@ -46,7 +45,7 @@ describe("Moves - Beat Up", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; let enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAT_UP)); + game.move.select(Moves.BEAT_UP); await game.phaseInterceptor.to(MoveEffectPhase); @@ -70,7 +69,7 @@ describe("Moves - Beat Up", () => { game.scene.getParty()[1].trySetStatus(StatusEffect.BURN); - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAT_UP)); + game.move.select(Moves.BEAT_UP); await game.phaseInterceptor.to(MoveEffectPhase); @@ -81,14 +80,14 @@ describe("Moves - Beat Up", () => { it( "should hit twice for each player Pokemon if the user has Multi-Lens", async () => { - game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.startBattle([Species.MAGIKARP, Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.PIKACHU, Species.EEVEE]); const playerPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; let enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.BEAT_UP)); + game.move.select(Moves.BEAT_UP); await game.phaseInterceptor.to(MoveEffectPhase); diff --git a/src/test/moves/belly_drum.test.ts b/src/test/moves/belly_drum.test.ts index 229314c96e6..631de952a58 100644 --- a/src/test/moves/belly_drum.test.ts +++ b/src/test/moves/belly_drum.test.ts @@ -1,12 +1,11 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { BattleStat } from "#app/data/battle-stat"; -import { toDmgValue } from "#app/utils"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; // RATIO : HP Cost of Move @@ -41,13 +40,13 @@ describe("Moves - BELLY DRUM", () => { // Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Belly_Drum_(move) test("Belly Drum raises the user's Attack to its max, at the cost of 1/2 of its maximum HP", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); - game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); + game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -56,7 +55,7 @@ describe("Moves - BELLY DRUM", () => { ); test("Belly Drum will still take effect if an uninvolved stat is at max", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -66,7 +65,7 @@ describe("Moves - BELLY DRUM", () => { leadPokemon.summonData.battleStats[BattleStat.ATK] = -3; leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; - game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); + game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -76,14 +75,14 @@ describe("Moves - BELLY DRUM", () => { ); test("Belly Drum fails if the pokemon's attack stat is at its maximum", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; - game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); + game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); @@ -92,14 +91,14 @@ describe("Moves - BELLY DRUM", () => { ); test("Belly Drum fails if the user's health is less than 1/2", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; - game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); + game.move.select(Moves.BELLY_DRUM); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE); diff --git a/src/test/moves/ceaseless_edge.test.ts b/src/test/moves/ceaseless_edge.test.ts index c8291a99b59..34ecf8f39f6 100644 --- a/src/test/moves/ceaseless_edge.test.ts +++ b/src/test/moves/ceaseless_edge.test.ts @@ -2,14 +2,13 @@ import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; import { allMoves } from "#app/data/move"; import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -35,8 +34,8 @@ describe("Moves - Ceaseless Edge", () => { game.override.enemyPassiveAbility(Abilities.RUN_AWAY); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.CEASELESS_EDGE, Moves.SPLASH, Moves.ROAR ]); - game.override.enemyMoveset([Moves.SPLASH,Moves.SPLASH,Moves.SPLASH,Moves.SPLASH]); + game.override.moveset([Moves.CEASELESS_EDGE, Moves.SPLASH, Moves.ROAR]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); vi.spyOn(allMoves[Moves.CEASELESS_EDGE], "accuracy", "get").mockReturnValue(100); }); @@ -44,13 +43,13 @@ describe("Moves - Ceaseless Edge", () => { test( "move should hit and apply spikes", async () => { - await game.startBattle([ Species.ILLUMISE ]); + await game.startBattle([Species.ILLUMISE]); const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.CEASELESS_EDGE)); + game.move.select(Moves.CEASELESS_EDGE); await game.phaseInterceptor.to(MoveEffectPhase, false); // Spikes should not have any layers before move effect is applied @@ -68,14 +67,14 @@ describe("Moves - Ceaseless Edge", () => { test( "move should hit twice with multi lens and apply two layers of spikes", async () => { - game.override.startingHeldItems([{name: "MULTI_LENS"}]); - await game.startBattle([ Species.ILLUMISE ]); + game.override.startingHeldItems([{ name: "MULTI_LENS" }]); + await game.startBattle([Species.ILLUMISE]); const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.CEASELESS_EDGE)); + game.move.select(Moves.CEASELESS_EDGE); await game.phaseInterceptor.to(MoveEffectPhase, false); // Spikes should not have any layers before move effect is applied @@ -93,12 +92,12 @@ describe("Moves - Ceaseless Edge", () => { test( "trainer - move should hit twice, apply two layers of spikes, force switch opponent - opponent takes damage", async () => { - game.override.startingHeldItems([{name: "MULTI_LENS"}]); + game.override.startingHeldItems([{ name: "MULTI_LENS" }]); game.override.startingWave(5); - await game.startBattle([ Species.ILLUMISE ]); + await game.startBattle([Species.ILLUMISE]); - game.doAttack(getMovePosition(game.scene, 0, Moves.CEASELESS_EDGE)); + game.move.select(Moves.CEASELESS_EDGE); await game.phaseInterceptor.to(MoveEffectPhase, false); // Spikes should not have any layers before move effect is applied const tagBefore = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag; @@ -112,7 +111,7 @@ describe("Moves - Ceaseless Edge", () => { const hpBeforeSpikes = game.scene.currentBattle.enemyParty[1].hp; // Check HP of pokemon that WILL BE switched in (index 1) game.forceOpponentToSwitch(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase, false); expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(hpBeforeSpikes); }, TIMEOUT diff --git a/src/test/moves/clangorous_soul.test.ts b/src/test/moves/clangorous_soul.test.ts index afab4c2e9be..9ea6da91595 100644 --- a/src/test/moves/clangorous_soul.test.ts +++ b/src/test/moves/clangorous_soul.test.ts @@ -1,13 +1,12 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { BattleStat } from "#app/data/battle-stat"; +import GameManager from "#test/utils/gameManager"; import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { toDmgValue } from "#app/utils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; /** HP Cost of Move */ @@ -42,13 +41,13 @@ describe("Moves - CLANGOROUS_SOUL", () => { //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Clangorous_Soul_(move) test("Clangorous Soul raises the user's Attack, Defense, Special Attack, Special Defense and Speed by one stage each, at the cost of 1/3 of its maximum HP", - async() => { - await game.startBattle([Species.MAGIKARP]); + async () => { + await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); - game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); + game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -57,11 +56,11 @@ describe("Moves - CLANGOROUS_SOUL", () => { expect(leadPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(1); expect(leadPokemon.summonData.battleStats[BattleStat.SPDEF]).toBe(1); expect(leadPokemon.summonData.battleStats[BattleStat.SPD]).toBe(1); - }, TIMEOUT + }, TIMEOUT ); test("Clangorous Soul will still take effect if one or more of the involved stats are not at max", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -73,7 +72,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; leadPokemon.summonData.battleStats[BattleStat.SPDEF] = 4; - game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); + game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -86,7 +85,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { ); test("Clangorous Soul fails if all stats involved are at max", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -97,7 +96,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { leadPokemon.summonData.battleStats[BattleStat.SPDEF] = 6; leadPokemon.summonData.battleStats[BattleStat.SPD] = 6; - game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); + game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); @@ -110,14 +109,14 @@ describe("Moves - CLANGOROUS_SOUL", () => { ); test("Clangorous Soul fails if the user's health is less than 1/3", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; - game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); + game.move.select(Moves.CLANGOROUS_SOUL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE); diff --git a/src/test/moves/crafty_shield.test.ts b/src/test/moves/crafty_shield.test.ts index c3e50bc52c2..a341a50b0b9 100644 --- a/src/test/moves/crafty_shield.test.ts +++ b/src/test/moves/crafty_shield.test.ts @@ -1,14 +1,13 @@ +import { BattleStat } from "#app/data/battle-stat"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; -import { Species } from "#enums/species"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; const TIMEOUT = 20 * 1000; @@ -48,11 +47,11 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CRAFTY_SHIELD)); + game.move.select(Moves.CRAFTY_SHIELD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -69,11 +68,11 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CRAFTY_SHIELD)); + game.move.select(Moves.CRAFTY_SHIELD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -91,11 +90,11 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CRAFTY_SHIELD)); + game.move.select(Moves.CRAFTY_SHIELD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -110,11 +109,11 @@ describe("Moves - Crafty Shield", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CRAFTY_SHIELD)); + game.move.select(Moves.CRAFTY_SHIELD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SWORDS_DANCE)); + game.move.select(Moves.SWORDS_DANCE, 1); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/double_team.test.ts b/src/test/moves/double_team.test.ts index 1c89d5b6350..c45c8bd8516 100644 --- a/src/test/moves/double_team.test.ts +++ b/src/test/moves/double_team.test.ts @@ -1,10 +1,9 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { Abilities } from "#app/enums/abilities"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -42,7 +41,7 @@ describe("Moves - Double Team", () => { vi.spyOn(enemy, "getAccuracyMultiplier"); expect(ally.summonData.battleStats[BattleStat.EVA]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_TEAM)); + game.move.select(Moves.DOUBLE_TEAM); await game.phaseInterceptor.to(TurnEndPhase); await game.toNextTurn(); diff --git a/src/test/moves/dragon_rage.test.ts b/src/test/moves/dragon_rage.test.ts index 8a27f4006f4..223635575ab 100644 --- a/src/test/moves/dragon_rage.test.ts +++ b/src/test/moves/dragon_rage.test.ts @@ -1,17 +1,16 @@ import { BattleStat } from "#app/data/battle-stat"; import { Type } from "#app/data/type"; -import { Species } from "#app/enums/species.js"; +import { Species } from "#app/enums/species"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Dragon Rage", () => { let phaserGame: Phaser.Game; @@ -62,7 +61,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); vi.spyOn(enemyPokemon, "getTypes").mockReturnValue([Type.DRAGON]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -73,7 +72,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); vi.spyOn(enemyPokemon, "getTypes").mockReturnValue([Type.STEEL]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -84,7 +83,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); partyPokemon.summonData.battleStats[BattleStat.SPATK] = 2; - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -95,7 +94,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); vi.spyOn(partyPokemon, "getTypes").mockReturnValue([Type.DRAGON]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -105,7 +104,7 @@ describe("Moves - Dragon Rage", () => { it("ignores criticals", async () => { partyPokemon.addTag(BattlerTagType.ALWAYS_CRIT, 99); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -116,7 +115,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); game.override.enemyAbility(Abilities.ICE_SCALES); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; @@ -127,7 +126,7 @@ describe("Moves - Dragon Rage", () => { game.override.disableCrits(); game.scene.addModifier(modifierTypes.MULTI_LENS().newModifier(partyPokemon), false); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_RAGE)); + game.move.select(Moves.DRAGON_RAGE); await game.phaseInterceptor.to(TurnEndPhase); const damageDealt = enemyPokemon.getMaxHp() - enemyPokemon.hp; diff --git a/src/test/moves/dragon_tail.test.ts b/src/test/moves/dragon_tail.test.ts index 28c47a83454..362383e2fe3 100644 --- a/src/test/moves/dragon_tail.test.ts +++ b/src/test/moves/dragon_tail.test.ts @@ -1,16 +1,15 @@ -import { allMoves } from "#app/data/move.js"; -import { SPLASH_ONLY } from "../utils/testUtils"; +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BattlerIndex } from "#app/battle.js"; -import { BattleEndPhase } from "#app/phases/battle-end-phase.js"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -46,9 +45,8 @@ describe("Moves - Dragon Tail", () => { await game.startBattle([Species.DRATINI]); const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); + game.move.select(Moves.DRAGON_TAIL); await game.phaseInterceptor.to(BerryPhase); @@ -68,12 +66,9 @@ describe("Moves - Dragon Tail", () => { await game.startBattle([Species.DRATINI]); const leadPokemon = game.scene.getPlayerPokemon()!; - expect(leadPokemon).toBeDefined(); - const enemyPokemon = game.scene.getEnemyPokemon()!; - expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); + game.move.select(Moves.DRAGON_TAIL); await game.phaseInterceptor.to(BerryPhase); @@ -85,7 +80,7 @@ describe("Moves - Dragon Tail", () => { ); test( - "Double battles should proceed without crashing" , + "Double battles should proceed without crashing", async () => { game.override.battleType("double").enemyMoveset(SPLASH_ONLY); game.override.moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER]) @@ -93,19 +88,12 @@ describe("Moves - Dragon Tail", () => { await game.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]); const leadPokemon = game.scene.getParty()[0]!; - const secPokemon = game.scene.getParty()[1]!; - expect(leadPokemon).toBeDefined(); - expect(secPokemon).toBeDefined(); - const enemyLeadPokemon = game.scene.currentBattle.enemyParty[0]!; - const enemySecPokemon = game.scene.currentBattle.enemyParty[1]!; - expect(enemyLeadPokemon).toBeDefined(); - expect(enemySecPokemon).toBeDefined(); + const enemyLeadPokemon = game.scene.getEnemyParty()[0]!; + const enemySecPokemon = game.scene.getEnemyParty()[1]!; - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); - game.doSelectTarget(BattlerIndex.ENEMY); - - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.DRAGON_TAIL, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -117,10 +105,8 @@ describe("Moves - Dragon Tail", () => { expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp()); // second turn - - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAMETHROWER)); - game.doSelectTarget(BattlerIndex.ENEMY_2); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLAMETHROWER, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase); expect(enemySecPokemon.hp).toBeLessThan(enemySecPokemon.getMaxHp()); @@ -128,7 +114,7 @@ describe("Moves - Dragon Tail", () => { ); test( - "Flee move redirection works" , + "Flee move redirection works", async () => { game.override.battleType("double").enemyMoveset(SPLASH_ONLY); game.override.moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER]); @@ -137,20 +123,13 @@ describe("Moves - Dragon Tail", () => { const leadPokemon = game.scene.getParty()[0]!; const secPokemon = game.scene.getParty()[1]!; - expect(leadPokemon).toBeDefined(); - expect(secPokemon).toBeDefined(); - const enemyLeadPokemon = game.scene.currentBattle.enemyParty[0]!; - const enemySecPokemon = game.scene.currentBattle.enemyParty[1]!; - expect(enemyLeadPokemon).toBeDefined(); - expect(enemySecPokemon).toBeDefined(); - - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); - game.doSelectTarget(BattlerIndex.ENEMY); + const enemyLeadPokemon = game.scene.getEnemyParty()[0]!; + const enemySecPokemon = game.scene.getEnemyParty()[1]!; + game.move.select(Moves.DRAGON_TAIL, 0, BattlerIndex.ENEMY); // target the same pokemon, second move should be redirected after first flees - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_TAIL)); - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(Moves.DRAGON_TAIL, 1, BattlerIndex.ENEMY); await game.phaseInterceptor.to(BerryPhase); diff --git a/src/test/moves/dynamax_cannon.test.ts b/src/test/moves/dynamax_cannon.test.ts index 5e81241ef46..6ac0befdb36 100644 --- a/src/test/moves/dynamax_cannon.test.ts +++ b/src/test/moves/dynamax_cannon.test.ts @@ -1,14 +1,12 @@ import { BattlerIndex } from "#app/battle"; import { allMoves } from "#app/data/move"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { TurnStartPhase } from "#app/phases/turn-start-phase.js"; describe("Moves - Dynamax Cannon", () => { let phaserGame: Phaser.Game; @@ -29,7 +27,7 @@ describe("Moves - Dynamax Cannon", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.moveset([ dynamaxCannon.id ]); + game.override.moveset([dynamaxCannon.id]); game.override.startingLevel(200); // Note that, for Waves 1-10, the level cap is 10 @@ -38,18 +36,18 @@ describe("Moves - Dynamax Cannon", () => { game.override.disableCrits(); game.override.enemySpecies(Species.MAGIKARP); - game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); vi.spyOn(dynamaxCannon, "calculateBattlePower"); }); - it("should return 100 power against an enemy below level cap", async() => { + it("should return 100 power against an enemy below level cap", async () => { game.override.enemyLevel(1); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id); @@ -57,13 +55,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); - it("should return 100 power against an enemy at level cap", async() => { + it("should return 100 power against an enemy at level cap", async () => { game.override.enemyLevel(10); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id); @@ -71,13 +69,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); - it("should return 120 power against an enemy 1% above level cap", async() => { + it("should return 120 power against an enemy 1% above level cap", async () => { game.override.enemyLevel(101); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -88,13 +86,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(120); }, 20000); - it("should return 140 power against an enemy 2% above level capp", async() => { + it("should return 140 power against an enemy 2% above level capp", async () => { game.override.enemyLevel(102); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -105,13 +103,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(140); }, 20000); - it("should return 160 power against an enemy 3% above level cap", async() => { + it("should return 160 power against an enemy 3% above level cap", async () => { game.override.enemyLevel(103); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -122,13 +120,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(160); }, 20000); - it("should return 180 power against an enemy 4% above level cap", async() => { + it("should return 180 power against an enemy 4% above level cap", async () => { game.override.enemyLevel(104); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -139,13 +137,13 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(180); }, 20000); - it("should return 200 power against an enemy 5% above level cap", async() => { + it("should return 200 power against an enemy 5% above level cap", async () => { game.override.enemyLevel(105); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); + game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; @@ -156,17 +154,14 @@ describe("Moves - Dynamax Cannon", () => { expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("should return 200 power against an enemy way above level cap", async() => { + it("should return 200 power against an enemy way above level cap", async () => { game.override.enemyLevel(999); await game.startBattle([ Species.ETERNATUS, ]); - game.doAttack(getMovePosition(game.scene, 0, dynamaxCannon.id)); - - await game.phaseInterceptor.to(TurnStartPhase, false); - // Force user to act before enemy - vi.spyOn((game.scene.getCurrentPhase() as TurnStartPhase), "getOrder").mockReturnValue([ BattlerIndex.PLAYER, BattlerIndex. ENEMY]); + game.move.select(dynamaxCannon.id); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id); diff --git a/src/test/moves/fillet_away.test.ts b/src/test/moves/fillet_away.test.ts index fc87d600eb5..b2ff9e25dba 100644 --- a/src/test/moves/fillet_away.test.ts +++ b/src/test/moves/fillet_away.test.ts @@ -1,13 +1,12 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { BattleStat } from "#app/data/battle-stat"; +import GameManager from "#test/utils/gameManager"; import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { toDmgValue } from "#app/utils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; const TIMEOUT = 20 * 1000; /** HP Cost of Move */ @@ -42,13 +41,13 @@ describe("Moves - FILLET AWAY", () => { //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/fillet_away_(move) test("Fillet Away raises the user's Attack, Special Attack, and Speed by two stages each, at the cost of 1/2 of its maximum HP", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); - game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); + game.move.select(Moves.FILLET_AWAY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -59,7 +58,7 @@ describe("Moves - FILLET AWAY", () => { ); test("Fillet Away will still take effect if one or more of the involved stats are not at max", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -69,7 +68,7 @@ describe("Moves - FILLET AWAY", () => { leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; leadPokemon.summonData.battleStats[BattleStat.SPATK] = 3; - game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); + game.move.select(Moves.FILLET_AWAY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp() - hpLost); @@ -80,7 +79,7 @@ describe("Moves - FILLET AWAY", () => { ); test("Fillet Away fails if all stats involved are at max", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -89,7 +88,7 @@ describe("Moves - FILLET AWAY", () => { leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; leadPokemon.summonData.battleStats[BattleStat.SPD] = 6; - game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); + game.move.select(Moves.FILLET_AWAY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); @@ -100,14 +99,14 @@ describe("Moves - FILLET AWAY", () => { ); test("Fillet Away fails if the user's health is less than 1/2", - async() => { + async () => { await game.startBattle([Species.MAGIKARP]); const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; - game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); + game.move.select(Moves.FILLET_AWAY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.hp).toBe(hpLost - PREDAMAGE); diff --git a/src/test/moves/fissure.test.ts b/src/test/moves/fissure.test.ts index 65d692a5cc1..51122b269b8 100644 --- a/src/test/moves/fissure.test.ts +++ b/src/test/moves/fissure.test.ts @@ -1,15 +1,14 @@ import { BattleStat } from "#app/data/battle-stat"; -import { Species } from "#app/enums/species.js"; +import { Species } from "#app/enums/species"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Moves - Fissure", () => { let phaserGame: Phaser.Game; @@ -57,7 +56,7 @@ describe("Moves - Fissure", () => { game.override.ability(Abilities.NO_GUARD); game.override.enemyAbility(Abilities.FUR_COAT); - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); await game.phaseInterceptor.to(DamagePhase, true); expect(enemyPokemon.isFainted()).toBe(true); @@ -68,7 +67,7 @@ describe("Moves - Fissure", () => { enemyPokemon.summonData.battleStats[BattleStat.ACC] = -6; - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); // wait for TurnEndPhase instead of DamagePhase as fissure might not actually inflict damage await game.phaseInterceptor.to(TurnEndPhase); @@ -81,7 +80,7 @@ describe("Moves - Fissure", () => { enemyPokemon.summonData.battleStats[BattleStat.EVA] = 6; - game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + game.move.select(Moves.FISSURE); // wait for TurnEndPhase instead of DamagePhase as fissure might not actually inflict damage await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/moves/flame_burst.test.ts b/src/test/moves/flame_burst.test.ts index d6679f921df..2777b8178b8 100644 --- a/src/test/moves/flame_burst.test.ts +++ b/src/test/moves/flame_burst.test.ts @@ -1,14 +1,12 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; +import { allAbilities } from "#app/data/ability"; +import { Abilities } from "#app/enums/abilities"; +import Pokemon from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Abilities } from "#app/enums/abilities.js"; -import { allAbilities } from "#app/data/ability.js"; -import Pokemon from "#app/field/pokemon.js"; -import { SelectTargetPhase } from "#app/phases/select-target-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Flame Burst", () => { let phaserGame: Phaser.Game; @@ -22,7 +20,7 @@ describe("Moves - Flame Burst", () => { * @returns Effect damage of Flame Burst */ const getEffectDamage = (pokemon: Pokemon): number => { - return Math.max(1, Math.floor(pokemon.getMaxHp() * 1/16)); + return Math.max(1, Math.floor(pokemon.getMaxHp() * 1 / 16)); }; beforeAll(() => { @@ -49,12 +47,10 @@ describe("Moves - Flame Burst", () => { it("inflicts damage to the target's ally equal to 1/16 of its max HP", async () => { await game.startBattle([Species.PIKACHU, Species.PIKACHU]); - const [ leftEnemy, rightEnemy ] = game.scene.getEnemyField(); + const [leftEnemy, rightEnemy] = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAME_BURST)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(leftEnemy.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLAME_BURST, 0, leftEnemy.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(leftEnemy.hp).toBeLessThan(leftEnemy.getMaxHp()); @@ -65,12 +61,10 @@ describe("Moves - Flame Burst", () => { game.override.enemyAbility(Abilities.FLASH_FIRE); await game.startBattle([Species.PIKACHU, Species.PIKACHU]); - const [ leftEnemy, rightEnemy ] = game.scene.getEnemyField(); + const [leftEnemy, rightEnemy] = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAME_BURST)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(leftEnemy.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLAME_BURST, 0, leftEnemy.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(leftEnemy.hp).toBe(leftEnemy.getMaxHp()); @@ -79,14 +73,12 @@ describe("Moves - Flame Burst", () => { it("does not interact with the target ally's abilities", async () => { await game.startBattle([Species.PIKACHU, Species.PIKACHU]); - const [ leftEnemy, rightEnemy ] = game.scene.getEnemyField(); + const [leftEnemy, rightEnemy] = game.scene.getEnemyField(); vi.spyOn(rightEnemy, "getAbility").mockReturnValue(allAbilities[Abilities.FLASH_FIRE]); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAME_BURST)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(leftEnemy.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLAME_BURST, 0, leftEnemy.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(leftEnemy.hp).toBeLessThan(leftEnemy.getMaxHp()); @@ -95,14 +87,12 @@ describe("Moves - Flame Burst", () => { it("effect damage is prevented by Magic Guard", async () => { await game.startBattle([Species.PIKACHU, Species.PIKACHU]); - const [ leftEnemy, rightEnemy ] = game.scene.getEnemyField(); + const [leftEnemy, rightEnemy] = game.scene.getEnemyField(); vi.spyOn(rightEnemy, "getAbility").mockReturnValue(allAbilities[Abilities.MAGIC_GUARD]); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLAME_BURST)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(leftEnemy.getBattlerIndex()); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLAME_BURST, 0, leftEnemy.getBattlerIndex()); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); expect(leftEnemy.hp).toBeLessThan(leftEnemy.getMaxHp()); diff --git a/src/test/moves/flower_shield.test.ts b/src/test/moves/flower_shield.test.ts index 9001e8ceacb..b3e50219aec 100644 --- a/src/test/moves/flower_shield.test.ts +++ b/src/test/moves/flower_shield.test.ts @@ -1,16 +1,15 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { SemiInvulnerableTag } from "#app/data/battler-tags.js"; -import { Type } from "#app/data/type.js"; -import { Biome } from "#app/enums/biome.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { SemiInvulnerableTag } from "#app/data/battler-tags"; +import { Type } from "#app/data/type"; +import { Biome } from "#app/enums/biome"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Flower Shield", () => { let phaserGame: Phaser.Game; @@ -45,7 +44,7 @@ describe("Moves - Flower Shield", () => { expect(magikarp.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD)); + game.move.select(Moves.FLOWER_SHIELD); await game.phaseInterceptor.to(TurnEndPhase); expect(magikarp.summonData.battleStats[BattleStat.DEF]).toBe(0); @@ -64,8 +63,8 @@ describe("Moves - Flower Shield", () => { grassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(0)); nonGrassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(0)); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FLOWER_SHIELD); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); grassPokemons.forEach(p => expect(p.summonData.battleStats[BattleStat.DEF]).toBe(1)); @@ -88,7 +87,7 @@ describe("Moves - Flower Shield", () => { expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(paras.getTag(SemiInvulnerableTag)).toBeUndefined; - game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD)); + game.move.select(Moves.FLOWER_SHIELD); await game.phaseInterceptor.to(TurnEndPhase); expect(paras.getTag(SemiInvulnerableTag)).toBeDefined(); @@ -106,7 +105,7 @@ describe("Moves - Flower Shield", () => { expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(ally.summonData.battleStats[BattleStat.DEF]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLOWER_SHIELD)); + game.move.select(Moves.FLOWER_SHIELD); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(0); diff --git a/src/test/moves/focus_punch.test.ts b/src/test/moves/focus_punch.test.ts index 385234f0b71..99399623a1c 100644 --- a/src/test/moves/focus_punch.test.ts +++ b/src/test/moves/focus_punch.test.ts @@ -1,16 +1,15 @@ -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { Species } from "#enums/species"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { MoveHeaderPhase } from "#app/phases/move-header-phase"; +import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { MessagePhase } from "#app/phases/message-phase.js"; -import { MoveHeaderPhase } from "#app/phases/move-header-phase.js"; -import { SwitchSummonPhase } from "#app/phases/switch-summon-phase.js"; -import { TurnStartPhase } from "#app/phases/turn-start-phase.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const TIMEOUT = 20 * 1000; @@ -51,7 +50,7 @@ describe("Moves - Focus Punch", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + game.move.select(Moves.FOCUS_PUNCH); await game.phaseInterceptor.to(MessagePhase); @@ -78,7 +77,7 @@ describe("Moves - Focus Punch", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + game.move.select(Moves.FOCUS_PUNCH); await game.phaseInterceptor.to(MessagePhase); @@ -103,7 +102,7 @@ describe("Moves - Focus Punch", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + game.move.select(Moves.FOCUS_PUNCH); await game.phaseInterceptor.to(MessagePhase); // Header message @@ -125,7 +124,7 @@ describe("Moves - Focus Punch", () => { await game.startBattle([Species.CHARIZARD]); game.forceOpponentToSwitch(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FOCUS_PUNCH)); + game.move.select(Moves.FOCUS_PUNCH); await game.phaseInterceptor.to(TurnStartPhase); diff --git a/src/test/moves/follow_me.test.ts b/src/test/moves/follow_me.test.ts index a0fff9afbf8..d7ef199df3e 100644 --- a/src/test/moves/follow_me.test.ts +++ b/src/test/moves/follow_me.test.ts @@ -1,15 +1,12 @@ -import { BattlerIndex } from "#app/battle.js"; +import { BattlerIndex } from "#app/battle"; import { Stat } from "#app/data/pokemon-stat"; -import { Abilities } from "#app/enums/abilities.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { Abilities } from "#app/enums/abilities"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { SelectTargetPhase } from "#app/phases/select-target-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -34,32 +31,21 @@ describe("Moves - Follow Me", () => { game.override.enemySpecies(Species.SNORLAX); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK ]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); test( "move should redirect enemy attacks to the user", async () => { - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); - - const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const playerStartingHp = playerPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.FOLLOW_ME)); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(Moves.FOLLOW_ME); + game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY); await game.phaseInterceptor.to(TurnEndPhase, false); expect(playerPokemon[0].hp).toBeLessThan(playerStartingHp[0]); @@ -70,22 +56,14 @@ describe("Moves - Follow Me", () => { test( "move should redirect enemy attacks to the first ally that uses it", async () => { - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); - - const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const playerStartingHp = playerPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.FOLLOW_ME)); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.FOLLOW_ME)); + game.move.select(Moves.FOLLOW_ME); + game.move.select(Moves.FOLLOW_ME, 1); await game.phaseInterceptor.to(TurnEndPhase, false); playerPokemon.sort((a, b) => a.getBattleStat(Stat.SPD) - b.getBattleStat(Stat.SPD)); @@ -99,29 +77,17 @@ describe("Moves - Follow Me", () => { "move effect should be bypassed by Stalwart", async () => { game.override.ability(Abilities.STALWART); - game.override.moveset([ Moves.QUICK_ATTACK ]); - game.override.enemyMoveset([ Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME ]); + game.override.moveset([Moves.QUICK_ATTACK]); + game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]); - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY); + game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to(TurnEndPhase, false); // If redirection was bypassed, both enemies should be damaged @@ -133,29 +99,17 @@ describe("Moves - Follow Me", () => { test( "move effect should be bypassed by Snipe Shot", async () => { - game.override.moveset([ Moves.SNIPE_SHOT ]); - game.override.enemyMoveset([ Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME ]); + game.override.moveset([Moves.SNIPE_SHOT]); + game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]); - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.SNIPE_SHOT)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.SNIPE_SHOT)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.SNIPE_SHOT, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SNIPE_SHOT, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to(TurnEndPhase, false); // If redirection was bypassed, both enemies should be damaged diff --git a/src/test/moves/foresight.test.ts b/src/test/moves/foresight.test.ts index 91d3e3c37e0..b856ec0f852 100644 --- a/src/test/moves/foresight.test.ts +++ b/src/test/moves/foresight.test.ts @@ -1,11 +1,10 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/utils/gameManager"; -import { Species } from "#app/enums/species.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { Moves } from "#app/enums/moves.js"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; describe("Moves - Foresight", () => { let phaserGame: Phaser.Game; @@ -37,19 +36,19 @@ describe("Moves - Foresight", () => { const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.toNextTurn(); expect(enemy.hp).toBe(enemy.getMaxHp()); - game.doAttack(getMovePosition(game.scene, 0, Moves.FORESIGHT)); + game.move.select(Moves.FORESIGHT); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.toNextTurn(); expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); enemy.hp = enemy.getMaxHp(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MACH_PUNCH)); + game.move.select(Moves.MACH_PUNCH); await game.phaseInterceptor.to(MoveEffectPhase); expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); @@ -62,9 +61,9 @@ describe("Moves - Foresight", () => { const pokemon = game.scene.getPlayerPokemon()!; vi.spyOn(pokemon, "getAccuracyMultiplier"); - game.doAttack(getMovePosition(game.scene, 0, Moves.FORESIGHT)); + game.move.select(Moves.FORESIGHT); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); + game.move.select(Moves.QUICK_ATTACK); await game.phaseInterceptor.to(MoveEffectPhase); expect(pokemon.getAccuracyMultiplier).toHaveReturnedWith(1); diff --git a/src/test/moves/freezy_frost.test.ts b/src/test/moves/freezy_frost.test.ts index b4c30279c21..00d7104d373 100644 --- a/src/test/moves/freezy_frost.test.ts +++ b/src/test/moves/freezy_frost.test.ts @@ -1,15 +1,14 @@ import { BattleStat } from "#app/data/battle-stat"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { allMoves } from "#app/data/move.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; describe("Moves - Freezy Frost", () => { describe("integration tests", () => { @@ -47,17 +46,17 @@ describe("Moves - Freezy Frost", () => { expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE)); + game.move.select(Moves.SWORDS_DANCE); await game.phaseInterceptor.to(TurnInitPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM)); + game.move.select(Moves.CHARM); await game.phaseInterceptor.to(TurnInitPhase); const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; const enemyAtkBefore = enemy.summonData.battleStats[BattleStat.ATK]; expect(userAtkBefore).toBe(2); expect(enemyAtkBefore).toBe(-2); - game.doAttack(getMovePosition(game.scene, 0, Moves.FREEZY_FROST)); + game.move.select(Moves.FREEZY_FROST); await game.phaseInterceptor.to(TurnInitPhase); expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -69,13 +68,13 @@ describe("Moves - Freezy Frost", () => { const user = game.scene.getPlayerPokemon()!; expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE)); + game.move.select(Moves.SWORDS_DANCE); await game.phaseInterceptor.to(TurnInitPhase); const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; expect(userAtkBefore).toBe(2); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(MoveEndPhase); expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); }); diff --git a/src/test/moves/fusion_bolt.test.ts b/src/test/moves/fusion_bolt.test.ts index c7a21e2c736..db31863ad03 100644 --- a/src/test/moves/fusion_bolt.test.ts +++ b/src/test/moves/fusion_bolt.test.ts @@ -1,10 +1,9 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Species } from "#enums/species"; -import { Moves } from "#enums/moves"; import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Fusion Bolt", () => { let phaserGame: Phaser.Game; @@ -24,19 +23,19 @@ describe("Moves - Fusion Bolt", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.moveset([ fusionBolt ]); + game.override.moveset([fusionBolt]); game.override.startingLevel(1); game.override.enemySpecies(Species.RESHIRAM); game.override.enemyAbility(Abilities.ROUGH_SKIN); - game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); game.override.battleType("single"); game.override.startingWave(97); game.override.disableCrits(); }); - it("should not make contact", async() => { + it("should not make contact", async () => { await game.startBattle([ Species.ZEKROM, ]); @@ -44,7 +43,7 @@ describe("Moves - Fusion Bolt", () => { const partyMember = game.scene.getPlayerPokemon()!; const initialHp = partyMember.hp; - game.doAttack(getMovePosition(game.scene, 0, fusionBolt)); + game.move.select(fusionBolt); await game.toNextTurn(); diff --git a/src/test/moves/fusion_flare.test.ts b/src/test/moves/fusion_flare.test.ts index aa38357ddd3..471f6a2ac7b 100644 --- a/src/test/moves/fusion_flare.test.ts +++ b/src/test/moves/fusion_flare.test.ts @@ -1,11 +1,10 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { StatusEffect } from "#app/data/status-effect"; -import { Species } from "#enums/species"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Moves } from "#enums/moves"; -import { TurnStartPhase } from "#app/phases/turn-start-phase.js"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Fusion Flare", () => { let phaserGame: Phaser.Game; @@ -25,25 +24,25 @@ describe("Moves - Fusion Flare", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.moveset([ fusionFlare ]); + game.override.moveset([fusionFlare]); game.override.startingLevel(1); game.override.enemySpecies(Species.RESHIRAM); - game.override.enemyMoveset([ Moves.REST, Moves.REST, Moves.REST, Moves.REST ]); + game.override.enemyMoveset([Moves.REST, Moves.REST, Moves.REST, Moves.REST]); game.override.battleType("single"); game.override.startingWave(97); game.override.disableCrits(); }); - it("should thaw freeze status condition", async() => { + it("should thaw freeze status condition", async () => { await game.startBattle([ Species.RESHIRAM, ]); const partyMember = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, fusionFlare)); + game.move.select(fusionFlare); await game.phaseInterceptor.to(TurnStartPhase, false); diff --git a/src/test/moves/fusion_flare_bolt.test.ts b/src/test/moves/fusion_flare_bolt.test.ts index 1b95062ee81..ebef5148778 100644 --- a/src/test/moves/fusion_flare_bolt.test.ts +++ b/src/test/moves/fusion_flare_bolt.test.ts @@ -1,16 +1,15 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Stat } from "#app/data/pokemon-stat"; -import { allMoves } from "#app/data/move"; import { BattlerIndex } from "#app/battle"; -import { Species } from "#enums/species"; +import { allMoves } from "#app/data/move"; +import { Stat } from "#app/data/pokemon-stat"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { MovePhase } from "#app/phases/move-phase"; import { Moves } from "#enums/moves"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { MovePhase } from "#app/phases/move-phase.js"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Fusion Flare and Fusion Bolt", () => { let phaserGame: Phaser.Game; @@ -31,11 +30,11 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.moveset([ fusionFlare.id, fusionBolt.id ]); + game.override.moveset([fusionFlare.id, fusionBolt.id]); game.override.startingLevel(1); game.override.enemySpecies(Species.RESHIRAM); - game.override.enemyMoveset([ Moves.REST, Moves.REST, Moves.REST, Moves.REST ]); + game.override.enemyMoveset([Moves.REST, Moves.REST, Moves.REST, Moves.REST]); game.override.battleType("double"); game.override.startingWave(97); @@ -45,20 +44,17 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { vi.spyOn(fusionBolt, "calculateBattlePower"); }); - it("FUSION_FLARE should double power of subsequent FUSION_BOLT", async() => { + it("FUSION_FLARE should double power of subsequent FUSION_BOLT", async () => { await game.startBattle([ Species.ZEKROM, Species.ZEKROM ]); - game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); - game.doSelectTarget(BattlerIndex.ENEMY); - - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(fusionFlare.id, 0, BattlerIndex.ENEMY); + game.move.select(fusionBolt.id, 1, BattlerIndex.ENEMY); // Force user party to act before enemy party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); @@ -71,20 +67,17 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("FUSION_BOLT should double power of subsequent FUSION_FLARE", async() => { + it("FUSION_BOLT should double power of subsequent FUSION_FLARE", async () => { await game.startBattle([ Species.ZEKROM, Species.ZEKROM ]); - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.ENEMY); - - game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(fusionBolt.id, 0, BattlerIndex.ENEMY); + game.move.select(fusionFlare.id, 1, BattlerIndex.ENEMY); // Force user party to act before enemy party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); @@ -97,20 +90,17 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("FUSION_FLARE should double power of subsequent FUSION_BOLT if a move failed in between", async() => { + it("FUSION_FLARE should double power of subsequent FUSION_BOLT if a move failed in between", async () => { await game.startBattle([ Species.ZEKROM, Species.ZEKROM ]); - game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); - game.doSelectTarget(0); - - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(0); + game.move.select(fusionFlare.id, 0, BattlerIndex.PLAYER); + game.move.select(fusionBolt.id, 1, BattlerIndex.PLAYER); // Force first enemy to act (and fail) in between party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); @@ -128,21 +118,18 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("FUSION_FLARE should not double power of subsequent FUSION_BOLT if a move succeeded in between", async() => { - game.override.enemyMoveset([ Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH ]); + it("FUSION_FLARE should not double power of subsequent FUSION_BOLT if a move succeeded in between", async () => { + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); await game.startBattle([ Species.ZEKROM, Species.ZEKROM ]); - game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); - game.doSelectTarget(BattlerIndex.ENEMY); - - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(fusionFlare.id, 0, BattlerIndex.ENEMY); + game.move.select(fusionBolt.id, 1, BattlerIndex.ENEMY); // Force first enemy to act in between party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id); @@ -159,20 +146,17 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); - it("FUSION_FLARE should double power of subsequent FUSION_BOLT if moves are aimed at allies", async() => { + it("FUSION_FLARE should double power of subsequent FUSION_BOLT if moves are aimed at allies", async () => { await game.startBattle([ Species.ZEKROM, Species.RESHIRAM ]); - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.PLAYER_2); - - game.doAttack(getMovePosition(game.scene, 0, fusionFlare.id)); - game.doSelectTarget(BattlerIndex.PLAYER); + game.move.select(fusionBolt.id, 0, BattlerIndex.PLAYER_2); + game.move.select(fusionFlare.id, 1, BattlerIndex.PLAYER); // Force user party to act before enemy party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); @@ -185,8 +169,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves", async() => { - game.override.enemyMoveset([ fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id ]); + it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves", async () => { + game.override.enemyMoveset([fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id]); await game.startBattle([ Species.ZEKROM, Species.ZEKROM @@ -217,14 +201,11 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { vi.spyOn(party[1], "stats", "get").mockReturnValue(stats.player[0].map((val, i) => (i === Stat.SPDEF ? 250 : val))); vi.spyOn(party[1], "stats", "get").mockReturnValue(stats.player[1].map((val, i) => (i === Stat.SPDEF ? 250 : val))); - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.ENEMY); - - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.ENEMY); + game.move.select(fusionBolt.id, 0, BattlerIndex.ENEMY); + game.move.select(fusionBolt.id, 1, BattlerIndex.ENEMY); // Force first enemy to act in between party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); @@ -247,8 +228,8 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); - it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves if moves are aimed at allies", async() => { - game.override.enemyMoveset([ fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id ]); + it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves if moves are aimed at allies", async () => { + game.override.enemyMoveset([fusionFlare.id, fusionFlare.id, fusionFlare.id, fusionFlare.id]); await game.startBattle([ Species.ZEKROM, Species.ZEKROM @@ -279,14 +260,11 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { vi.spyOn(party[1], "stats", "get").mockReturnValue(stats.player[0].map((val, i) => (i === Stat.SPDEF ? 250 : val))); vi.spyOn(party[1], "stats", "get").mockReturnValue(stats.player[1].map((val, i) => (i === Stat.SPDEF ? 250 : val))); - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.PLAYER_2); - - game.doAttack(getMovePosition(game.scene, 0, fusionBolt.id)); - game.doSelectTarget(BattlerIndex.PLAYER); + game.move.select(fusionBolt.id, 0, BattlerIndex.PLAYER_2); + game.move.select(fusionBolt.id, 1, BattlerIndex.PLAYER); // Force first enemy to act in between party - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id); diff --git a/src/test/moves/gastro_acid.test.ts b/src/test/moves/gastro_acid.test.ts index cc247890754..67fd3464cf9 100644 --- a/src/test/moves/gastro_acid.test.ts +++ b/src/test/moves/gastro_acid.test.ts @@ -1,12 +1,11 @@ -import { BattlerIndex } from "#app/battle.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; -import { MoveResult } from "#app/field/pokemon.js"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#app/enums/abilities"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { MoveResult } from "#app/field/pokemon"; import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { SPLASH_ONLY } from "#test/utils/testUtils"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; const TIMEOUT = 20 * 1000; @@ -46,10 +45,8 @@ describe("Moves - Gastro Acid", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.GASTRO_ACID)); - game.doSelectTarget(BattlerIndex.ENEMY); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doSelectTarget(BattlerIndex.PLAYER_2); + game.move.select(Moves.GASTRO_ACID, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to("TurnInitPhase"); @@ -57,10 +54,8 @@ describe("Moves - Gastro Acid", () => { expect(enemyField[0].summonData.abilitySuppressed).toBe(true); expect(enemyField[1].summonData.abilitySuppressed).toBe(false); - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); - game.doSelectTarget(BattlerIndex.ENEMY); - game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.WATER_GUN, 0, BattlerIndex.ENEMY); + game.move.select(Moves.WATER_GUN, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to("TurnEndPhase"); @@ -73,13 +68,13 @@ describe("Moves - Gastro Acid", () => { await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CORE_ENFORCER)); + game.move.select(Moves.CORE_ENFORCER); // Force player to be slower to enable Core Enforcer to proc its suppression effect await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("TurnInitPhase"); - game.doAttack(getMovePosition(game.scene, 0, Moves.GASTRO_ACID)); + game.move.select(Moves.GASTRO_ACID); await game.phaseInterceptor.to("TurnInitPhase"); diff --git a/src/test/moves/glaive_rush.test.ts b/src/test/moves/glaive_rush.test.ts index f97ba1f0367..1eac3c32bb4 100644 --- a/src/test/moves/glaive_rush.test.ts +++ b/src/test/moves/glaive_rush.test.ts @@ -1,13 +1,12 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Moves - Glaive Rush", () => { @@ -37,36 +36,36 @@ describe("Moves - Glaive Rush", () => { game.override.moveset([Moves.SHADOW_SNEAK, Moves.AVALANCHE, Moves.SPLASH, Moves.GLAIVE_RUSH]); }); - it("takes double damage from attacks", async() => { + it("takes double damage from attacks", async () => { await game.startBattle(); const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; vi.spyOn(game.scene, "randBattleSeedInt").mockReturnValue(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SHADOW_SNEAK)); + game.move.select(Moves.SHADOW_SNEAK); await game.phaseInterceptor.to(DamagePhase); const damageDealt = 1000 - enemy.hp; await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.SHADOW_SNEAK)); + game.move.select(Moves.SHADOW_SNEAK); await game.phaseInterceptor.to(DamagePhase); expect(enemy.hp).toBeLessThanOrEqual(1001 - (damageDealt * 3)); }, 5000); // TODO: revert back to 20s - it("always gets hit by attacks", async() => { + it("always gets hit by attacks", async () => { await game.startBattle(); const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; allMoves[Moves.AVALANCHE].accuracy = 0; - game.doAttack(getMovePosition(game.scene, 0, Moves.AVALANCHE)); + game.move.select(Moves.AVALANCHE); await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.hp).toBeLessThan(1000); }, 20000); - it("interacts properly with multi-lens", async() => { - game.override.startingHeldItems([{name: "MULTI_LENS", count: 2}]); + it("interacts properly with multi-lens", async () => { + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }]); game.override.enemyMoveset(Array(4).fill(Moves.AVALANCHE)); await game.startBattle(); const player = game.scene.getPlayerPokemon()!; @@ -75,17 +74,17 @@ describe("Moves - Glaive Rush", () => { player.hp = 1000; allMoves[Moves.AVALANCHE].accuracy = 0; - game.doAttack(getMovePosition(game.scene, 0, Moves.GLAIVE_RUSH)); + game.move.select(Moves.GLAIVE_RUSH); await game.phaseInterceptor.to(TurnEndPhase); expect(player.hp).toBeLessThan(1000); player.hp = 1000; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(player.hp).toBe(1000); }, 20000); - it("secondary effects only last until next move", async() => { + it("secondary effects only last until next move", async () => { game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); await game.startBattle(); const player = game.scene.getPlayerPokemon()!; @@ -94,22 +93,22 @@ describe("Moves - Glaive Rush", () => { player.hp = 1000; allMoves[Moves.SHADOW_SNEAK].accuracy = 0; - game.doAttack(getMovePosition(game.scene, 0, Moves.GLAIVE_RUSH)); + game.move.select(Moves.GLAIVE_RUSH); await game.phaseInterceptor.to(TurnEndPhase); expect(player.hp).toBe(1000); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); const damagedHp = player.hp; expect(player.hp).toBeLessThan(1000); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(player.hp).toBe(damagedHp); }, 20000); - it("secondary effects are removed upon switching", async() => { + it("secondary effects are removed upon switching", async () => { game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); game.override.starterSpecies(0); await game.startBattle([Species.KLINK, Species.FEEBAS]); @@ -118,7 +117,7 @@ describe("Moves - Glaive Rush", () => { enemy.hp = 1000; allMoves[Moves.SHADOW_SNEAK].accuracy = 0; - game.doAttack(getMovePosition(game.scene, 0, Moves.GLAIVE_RUSH)); + game.move.select(Moves.GLAIVE_RUSH); await game.phaseInterceptor.to(TurnEndPhase); expect(player.hp).toBe(player.getMaxHp()); @@ -130,7 +129,7 @@ describe("Moves - Glaive Rush", () => { }, 20000); - it("secondary effects don't activate if move fails", async() => { + it("secondary effects don't activate if move fails", async () => { game.override.moveset([Moves.SHADOW_SNEAK, Moves.PROTECT, Moves.SPLASH, Moves.GLAIVE_RUSH]); await game.startBattle(); const player = game.scene.getPlayerPokemon()!; @@ -138,16 +137,16 @@ describe("Moves - Glaive Rush", () => { enemy.hp = 1000; player.hp = 1000; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.SHADOW_SNEAK)); + game.move.select(Moves.SHADOW_SNEAK); await game.phaseInterceptor.to(TurnEndPhase); game.override.enemyMoveset(Array(4).fill(Moves.SPLASH)); const damagedHP1 = 1000 - enemy.hp; enemy.hp = 1000; - game.doAttack(getMovePosition(game.scene, 0, Moves.SHADOW_SNEAK)); + game.move.select(Moves.SHADOW_SNEAK); await game.phaseInterceptor.to(TurnEndPhase); const damagedHP2 = 1000 - enemy.hp; diff --git a/src/test/moves/growth.test.ts b/src/test/moves/growth.test.ts index 0c60bb723f4..dfbf5406351 100644 --- a/src/test/moves/growth.test.ts +++ b/src/test/moves/growth.test.ts @@ -1,17 +1,13 @@ import { BattleStat } from "#app/data/battle-stat"; import { Stat } from "#app/data/pokemon-stat"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; describe("Moves - Growth", () => { @@ -37,10 +33,10 @@ describe("Moves - Growth", () => { game.override.ability(Abilities.INSOMNIA); game.override.startingLevel(2000); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("GROWTH", async() => { + it("GROWTH", async () => { const moveToUse = Moves.GROWTH; await game.startBattle([ Species.MIGHTYENA, @@ -52,13 +48,7 @@ describe("Moves - Growth", () => { const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.SPATK]).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase); battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats; expect(battleStatsPokemon[BattleStat.SPATK]).toBe(1); diff --git a/src/test/moves/hard_press.test.ts b/src/test/moves/hard_press.test.ts index 255b9f1f4b1..70c78490269 100644 --- a/src/test/moves/hard_press.test.ts +++ b/src/test/moves/hard_press.test.ts @@ -1,13 +1,12 @@ -import { allMoves } from "#app/data/move.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { allMoves } from "#app/data/move"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Hard Press", () => { let phaserGame: Phaser.Game; @@ -39,7 +38,7 @@ describe("Moves - Hard Press", () => { it("should return 100 power if target HP ratio is at 100%", async () => { await game.startBattle([Species.PIKACHU]); - game.doAttack(getMovePosition(game.scene, 0, Moves.HARD_PRESS)); + game.move.select(Moves.HARD_PRESS); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(100); @@ -52,7 +51,7 @@ describe("Moves - Hard Press", () => { vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); - game.doAttack(getMovePosition(game.scene, 0, Moves.HARD_PRESS)); + game.move.select(Moves.HARD_PRESS); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(50); @@ -65,7 +64,7 @@ describe("Moves - Hard Press", () => { vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); - game.doAttack(getMovePosition(game.scene, 0, Moves.HARD_PRESS)); + game.move.select(Moves.HARD_PRESS); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(1); @@ -78,7 +77,7 @@ describe("Moves - Hard Press", () => { vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); - game.doAttack(getMovePosition(game.scene, 0, Moves.HARD_PRESS)); + game.move.select(Moves.HARD_PRESS); await game.phaseInterceptor.to(MoveEffectPhase); expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(1); diff --git a/src/test/moves/haze.test.ts b/src/test/moves/haze.test.ts index d5e3efcbd9d..8a32a40cb32 100644 --- a/src/test/moves/haze.test.ts +++ b/src/test/moves/haze.test.ts @@ -1,14 +1,13 @@ import { BattleStat } from "#app/data/battle-stat"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; describe("Moves - Haze", () => { describe("integration tests", () => { @@ -45,17 +44,17 @@ describe("Moves - Haze", () => { expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE)); + game.move.select(Moves.SWORDS_DANCE); await game.phaseInterceptor.to(TurnInitPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.CHARM)); + game.move.select(Moves.CHARM); await game.phaseInterceptor.to(TurnInitPhase); const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; const enemyAtkBefore = enemy.summonData.battleStats[BattleStat.ATK]; expect(userAtkBefore).toBe(2); expect(enemyAtkBefore).toBe(-2); - game.doAttack(getMovePosition(game.scene, 0, Moves.HAZE)); + game.move.select(Moves.HAZE); await game.phaseInterceptor.to(TurnInitPhase); expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(enemy.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -67,13 +66,13 @@ describe("Moves - Haze", () => { const user = game.scene.getPlayerPokemon()!; expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SWORDS_DANCE)); + game.move.select(Moves.SWORDS_DANCE); await game.phaseInterceptor.to(TurnInitPhase); const userAtkBefore = user.summonData.battleStats[BattleStat.ATK]; expect(userAtkBefore).toBe(2); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(MoveEndPhase); expect(user.summonData.battleStats[BattleStat.ATK]).toBe(0); }); diff --git a/src/test/moves/hyper_beam.test.ts b/src/test/moves/hyper_beam.test.ts index ac8075081fb..1280d8b429a 100644 --- a/src/test/moves/hyper_beam.test.ts +++ b/src/test/moves/hyper_beam.test.ts @@ -1,14 +1,13 @@ -import { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; // 20 sec timeout for all tests @@ -48,7 +47,7 @@ describe("Moves - Hyper Beam", () => { const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); + game.move.select(Moves.HYPER_BEAM); await game.phaseInterceptor.to(TurnEndPhase); @@ -63,7 +62,7 @@ describe("Moves - Hyper Beam", () => { expect(enemyPokemon.hp).toBe(enemyPostAttackHp); expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/jaw_lock.test.ts b/src/test/moves/jaw_lock.test.ts index 4fe996588e4..42f7a244977 100644 --- a/src/test/moves/jaw_lock.test.ts +++ b/src/test/moves/jaw_lock.test.ts @@ -1,17 +1,16 @@ +import { BattlerIndex } from "#app/battle"; import { Abilities } from "#app/enums/abilities"; import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { SPLASH_ONLY } from "#app/test/utils/testUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#app/test/utils/testUtils"; -import { BattlerIndex } from "#app/battle"; -import { FaintPhase } from "#app/phases/faint-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { BerryPhase } from "#app/phases/berry-phase"; const TIMEOUT = 20 * 1000; @@ -46,12 +45,13 @@ describe("Moves - Jaw Lock", () => { it( "should trap the move's user and target", async () => { - await game.startBattle([ Species.BULBASAUR ]); + await game.startBattle([Species.BULBASAUR]); const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); + game.move.select(Moves.JAW_LOCK); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -69,12 +69,13 @@ describe("Moves - Jaw Lock", () => { "should not trap either pokemon if the target faints", async () => { game.override.enemyLevel(1); - await game.startBattle([ Species.BULBASAUR ]); + await game.startBattle([Species.BULBASAUR]); const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); + game.move.select(Moves.JAW_LOCK); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -96,12 +97,13 @@ describe("Moves - Jaw Lock", () => { it( "should only trap the user until the target faints", async () => { - await game.startBattle([ Species.BULBASAUR ]); + await game.startBattle([Species.BULBASAUR]); const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); + game.move.select(Moves.JAW_LOCK); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase); @@ -121,15 +123,14 @@ describe("Moves - Jaw Lock", () => { async () => { game.override.battleType("double"); - await game.startBattle([ Species.CHARMANDER, Species.BULBASAUR ]); + await game.startBattle([Species.CHARMANDER, Species.BULBASAUR]); const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); - game.doSelectTarget(BattlerIndex.ENEMY); - - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.JAW_LOCK, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.phaseInterceptor.to(MoveEffectPhase); @@ -138,10 +139,8 @@ describe("Moves - Jaw Lock", () => { await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); - game.doSelectTarget(BattlerIndex.ENEMY_2); - - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.JAW_LOCK, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEffectPhase); @@ -156,12 +155,12 @@ describe("Moves - Jaw Lock", () => { async () => { game.override.enemyMoveset(Array(4).fill(Moves.PROTECT)); - await game.startBattle([ Species.BULBASAUR ]); + await game.startBattle([Species.BULBASAUR]); const playerPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.JAW_LOCK)); + game.move.select(Moves.JAW_LOCK); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/light_screen.test.ts b/src/test/moves/light_screen.test.ts index 4577ffc574a..e94dc4a299e 100644 --- a/src/test/moves/light_screen.test.ts +++ b/src/test/moves/light_screen.test.ts @@ -1,14 +1,13 @@ -import { ArenaTagSide } from "#app/data/arena-tag.js"; -import Move, { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import Pokemon from "#app/field/pokemon.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { NumberHolder } from "#app/utils.js"; +import { ArenaTagSide } from "#app/data/arena-tag"; +import Move, { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import Pokemon from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { NumberHolder } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -17,7 +16,7 @@ describe("Moves - Light Screen", () => { let phaserGame: Phaser.Game; let game: GameManager; const singleBattleMultiplier = 0.5; - const doubleBattleMultiplier = 2732/4096; + const doubleBattleMultiplier = 2732 / 4096; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -40,11 +39,11 @@ describe("Moves - Light Screen", () => { game.override.disableCrits(); }); - it("reduces damage of special attacks by half in a single battle", async() => { + it("reduces damage of special attacks by half in a single battle", async () => { const moveToUse = Moves.ABSORB; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); @@ -53,14 +52,14 @@ describe("Moves - Light Screen", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); - it("reduces damage of special attacks by a third in a double battle", async() => { + it("reduces damage of special attacks by a third in a double battle", async () => { game.override.battleType("double"); const moveToUse = Moves.DAZZLING_GLEAM; await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); - game.doAttack(getMovePosition(game.scene, 1, moveToUse)); + game.move.select(moveToUse); + game.move.select(moveToUse, 1); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); @@ -68,11 +67,11 @@ describe("Moves - Light Screen", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); - it("does not affect physical attacks", async() => { + it("does not affect physical attacks", async () => { const moveToUse = Moves.TACKLE; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); diff --git a/src/test/moves/lucky_chant.test.ts b/src/test/moves/lucky_chant.test.ts index 643a5eddb00..7d5bfe02476 100644 --- a/src/test/moves/lucky_chant.test.ts +++ b/src/test/moves/lucky_chant.test.ts @@ -1,12 +1,11 @@ +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; 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 { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -44,13 +43,13 @@ describe("Moves - Lucky Chant", () => { const playerPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); const firstTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.LUCKY_CHANT)); + game.move.select(Moves.LUCKY_CHANT); await game.phaseInterceptor.to(BerryPhase, false); @@ -68,15 +67,15 @@ describe("Moves - Lucky Chant", () => { const playerPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.FOLLOW_ME)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.FOLLOW_ME); + game.move.select(Moves.SPLASH, 1); 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)); + game.move.select(Moves.FOLLOW_ME); + game.move.select(Moves.LUCKY_CHANT, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -97,13 +96,13 @@ describe("Moves - Lucky Chant", () => { enemyPokemon.addTag(BattlerTagType.ALWAYS_CRIT, 2, Moves.NONE, 0); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); const firstTurnDamage = playerPokemon.getMaxHp() - playerPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.LUCKY_CHANT)); + game.move.select(Moves.LUCKY_CHANT); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/magnet_rise.test.ts b/src/test/moves/magnet_rise.test.ts index 4ab32b5d048..9037e377090 100644 --- a/src/test/moves/magnet_rise.test.ts +++ b/src/test/moves/magnet_rise.test.ts @@ -1,14 +1,15 @@ -import GameManager from "#test/utils/gameManager"; +import { CommandPhase } from "#app/phases/command-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Moves - Magnet Rise", () => { let phaserGame: Phaser.Game; let game: GameManager; + const moveToUse = Moves.MAGNET_RISE; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -22,7 +23,6 @@ describe("Moves - Magnet Rise", () => { beforeEach(() => { game = new GameManager(phaserGame); - const moveToUse = Moves.MAGNET_RISE; game.override.battleType("single"); game.override.starterSpecies(Species.MAGNEZONE); game.override.enemySpecies(Species.RATTATA); @@ -36,7 +36,7 @@ describe("Moves - Magnet Rise", () => { await game.startBattle(); const startingHp = game.scene.getParty()[0].hp; - game.doAttack(0); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); const finalHp = game.scene.getParty()[0].hp; const hpLost = finalHp - startingHp; @@ -47,12 +47,12 @@ describe("Moves - Magnet Rise", () => { await game.startBattle(); const startingHp = game.scene.getParty()[0].hp; - game.doAttack(0); + game.move.select(moveToUse); await game.phaseInterceptor.to(CommandPhase); let finalHp = game.scene.getParty()[0].hp; let hpLost = finalHp - startingHp; expect(hpLost).toBe(0); - game.doAttack(2); + game.move.select(Moves.GRAVITY); await game.phaseInterceptor.to(TurnEndPhase); finalHp = game.scene.getParty()[0].hp; hpLost = finalHp - startingHp; diff --git a/src/test/moves/make_it_rain.test.ts b/src/test/moves/make_it_rain.test.ts index 5b0a8c6d62a..0af7763f175 100644 --- a/src/test/moves/make_it_rain.test.ts +++ b/src/test/moves/make_it_rain.test.ts @@ -1,14 +1,13 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { StatChangePhase } from "#app/phases/stat-change-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { StatChangePhase } from "#app/phases/stat-change-phase.js"; const TIMEOUT = 20 * 1000; @@ -42,8 +41,8 @@ describe("Moves - Make It Rain", () => { const playerPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.MAKE_IT_RAIN); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(MoveEndPhase); @@ -59,7 +58,7 @@ describe("Moves - Make It Rain", () => { const playerPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); + game.move.select(Moves.MAKE_IT_RAIN); await game.phaseInterceptor.to(StatChangePhase); @@ -75,8 +74,8 @@ describe("Moves - Make It Rain", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.MAKE_IT_RAIN); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(StatChangePhase); @@ -89,8 +88,8 @@ describe("Moves - Make It Rain", () => { const playerPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.MAKE_IT_RAIN); + game.move.select(Moves.SPLASH, 1); // Make Make It Rain miss the first target await game.move.forceMiss(true); diff --git a/src/test/moves/mat_block.test.ts b/src/test/moves/mat_block.test.ts index 27a55cab289..29a97806242 100644 --- a/src/test/moves/mat_block.test.ts +++ b/src/test/moves/mat_block.test.ts @@ -1,14 +1,13 @@ +import { BattleStat } from "#app/data/battle-stat"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; -import { Species } from "#enums/species"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -48,11 +47,11 @@ describe("Moves - Mat Block", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAT_BLOCK)); + game.move.select(Moves.MAT_BLOCK); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -69,11 +68,11 @@ describe("Moves - Mat Block", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAT_BLOCK)); + game.move.select(Moves.MAT_BLOCK); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -88,18 +87,18 @@ describe("Moves - Mat Block", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); const leadStartingHp = leadPokemon.map(p => p.hp); await game.phaseInterceptor.to(CommandPhase, false); - game.doAttack(getMovePosition(game.scene, 0, Moves.MAT_BLOCK)); + game.move.select(Moves.MAT_BLOCK); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.MAT_BLOCK)); + game.move.select(Moves.MAT_BLOCK, 1); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/miracle_eye.test.ts b/src/test/moves/miracle_eye.test.ts index 3e1e151e7d4..f47e4ce0c16 100644 --- a/src/test/moves/miracle_eye.test.ts +++ b/src/test/moves/miracle_eye.test.ts @@ -1,12 +1,11 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; +import { BattlerIndex } from "#app/battle"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/utils/gameManager"; -import { Species } from "#app/enums/species.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { Moves } from "#app/enums/moves.js"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BattlerIndex } from "#app/battle.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; describe("Moves - Miracle Eye", () => { let phaserGame: Phaser.Game; @@ -38,14 +37,14 @@ describe("Moves - Miracle Eye", () => { const enemy = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.CONFUSION)); + game.move.select(Moves.CONFUSION); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.toNextTurn(); expect(enemy.hp).toBe(enemy.getMaxHp()); - game.doAttack(getMovePosition(game.scene, 0, Moves.MIRACLE_EYE)); + game.move.select(Moves.MIRACLE_EYE); await game.toNextTurn(); - game.doAttack(getMovePosition(game.scene, 0, Moves.CONFUSION)); + game.move.select(Moves.CONFUSION); await game.phaseInterceptor.to(MoveEffectPhase); expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); diff --git a/src/test/moves/multi_target.test.ts b/src/test/moves/multi_target.test.ts index 6e8a7c99e9b..b8c1f67b3df 100644 --- a/src/test/moves/multi_target.test.ts +++ b/src/test/moves/multi_target.test.ts @@ -1,13 +1,12 @@ -import { getMoveTargets } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { Species } from "#app/enums/species.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; +import { getMoveTargets } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { Species } from "#app/enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -95,8 +94,8 @@ async function checkDamageDecrease(game: GameManager, attackMove: Moves, killAll game.scene.getEnemyField()[1].abilityIndex = ability; } - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -105,9 +104,9 @@ async function checkDamageDecrease(game: GameManager, attackMove: Moves, killAll await game.toNextTurn(); const initialHp = game.scene.getEnemyField()[0].hp; - game.doAttack(getMovePosition(game.scene, 0, attackMove)); + game.move.select(attackMove); if (!killAlly) { - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); } await game.phaseInterceptor.to(TurnEndPhase); @@ -119,7 +118,7 @@ async function checkDamageDecrease(game: GameManager, attackMove: Moves, killAll game.scene.getEnemyField()[0].hp = initialHp; const initialHp1v1 = game.scene.getEnemyField()[0].hp; - game.doAttack(getMovePosition(game.scene, 0, attackMove)); + game.move.select(attackMove); await game.phaseInterceptor.to(TurnEndPhase); const afterHp1v1 = game.scene.getEnemyField()[0].hp; diff --git a/src/test/moves/octolock.test.ts b/src/test/moves/octolock.test.ts index fcd68446eff..389e4a4c4cf 100644 --- a/src/test/moves/octolock.test.ts +++ b/src/test/moves/octolock.test.ts @@ -1,16 +1,15 @@ import { BattleStat } from "#app/data/battle-stat"; -import { TrappedTag } from "#app/data/battler-tags.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { TrappedTag } from "#app/data/battler-tags"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; describe("Moves - Octolock", () => { describe("integration tests", () => { @@ -47,7 +46,7 @@ describe("Moves - Octolock", () => { const enemyPokemon = game.scene.getEnemyField(); // use Octolock and advance to init phase of next turn to check for stat changes - game.doAttack(getMovePosition(game.scene, 0, Moves.OCTOLOCK)); + game.move.select(Moves.OCTOLOCK); await game.phaseInterceptor.to(TurnInitPhase); expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(-1); @@ -55,7 +54,7 @@ describe("Moves - Octolock", () => { // take a second turn to make sure stat changes occur again await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnInitPhase); expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(-2); @@ -70,7 +69,7 @@ describe("Moves - Octolock", () => { // before Octolock - enemy should not be trapped expect(enemyPokemon[0].findTag(t => t instanceof TrappedTag)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.OCTOLOCK)); + game.move.select(Moves.OCTOLOCK); // after Octolock - enemy should be trapped await game.phaseInterceptor.to(MoveEndPhase); diff --git a/src/test/moves/parting_shot.test.ts b/src/test/moves/parting_shot.test.ts index 32995d2d563..7c2ca3f334c 100644 --- a/src/test/moves/parting_shot.test.ts +++ b/src/test/moves/parting_shot.test.ts @@ -1,16 +1,15 @@ -import { SPLASH_ONLY } from "../utils/testUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, test } from "vitest"; import GameManager from "../utils/gameManager"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BattleStat } from "#app/data/battle-stat"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { FaintPhase } from "#app/phases/faint-phase.js"; -import { MessagePhase } from "#app/phases/message-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; +import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; @@ -49,7 +48,7 @@ describe("Moves - Parting Shot", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = enemyPokemon.summonData.battleStats; @@ -70,7 +69,7 @@ describe("Moves - Parting Shot", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = enemyPokemon.summonData.battleStats; @@ -87,19 +86,19 @@ describe("Moves - Parting Shot", () => { await game.startBattle([Species.MEOWTH, Species.MEOWTH, Species.MEOWTH, Species.MURKROW, Species.ABRA]); // use Memento 3 times to debuff enemy - game.doAttack(getMovePosition(game.scene, 0, Moves.MEMENTO)); + game.move.select(Moves.MEMENTO); await game.phaseInterceptor.to(FaintPhase); expect(game.scene.getParty()[0].isFainted()).toBe(true); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to(TurnInitPhase, false); - game.doAttack(getMovePosition(game.scene, 0, Moves.MEMENTO)); + game.move.select(Moves.MEMENTO); await game.phaseInterceptor.to(FaintPhase); expect(game.scene.getParty()[0].isFainted()).toBe(true); game.doSelectPartyPokemon(2); await game.phaseInterceptor.to(TurnInitPhase, false); - game.doAttack(getMovePosition(game.scene, 0, Moves.MEMENTO)); + game.move.select(Moves.MEMENTO); await game.phaseInterceptor.to(FaintPhase); expect(game.scene.getParty()[0].isFainted()).toBe(true); game.doSelectPartyPokemon(3); @@ -114,7 +113,7 @@ describe("Moves - Parting Shot", () => { expect(battleStatsOpponent[BattleStat.SPATK]).toBe(-6); // now parting shot should fail - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); expect(battleStatsOpponent[BattleStat.ATK]).toBe(-6); @@ -135,7 +134,7 @@ describe("Moves - Parting Shot", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = enemyPokemon.summonData.battleStats; @@ -156,7 +155,7 @@ describe("Moves - Parting Shot", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = enemyPokemon.summonData.battleStats; @@ -174,7 +173,7 @@ describe("Moves - Parting Shot", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = enemyPokemon.summonData.battleStats; @@ -188,7 +187,7 @@ describe("Moves - Parting Shot", () => { "Parting shot regularly not fail if no party available to switch - party fainted", async () => { await game.startBattle([Species.MURKROW, Species.MEOWTH]); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); // intentionally kill party pokemon, switch to second slot (now 1 party mon is fainted) await game.killPokemon(game.scene.getParty()[0]); @@ -197,7 +196,7 @@ describe("Moves - Parting Shot", () => { game.doSelectPartyPokemon(1); await game.phaseInterceptor.to(TurnInitPhase, false); - game.doAttack(getMovePosition(game.scene, 0, Moves.PARTING_SHOT)); + game.move.select(Moves.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; diff --git a/src/test/moves/protect.test.ts b/src/test/moves/protect.test.ts index 4d97ef5ce82..3fd51f4bc93 100644 --- a/src/test/moves/protect.test.ts +++ b/src/test/moves/protect.test.ts @@ -1,14 +1,13 @@ +import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; +import { BattleStat } from "#app/data/battle-stat"; +import { allMoves } from "#app/data/move"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import GameManager from "../utils/gameManager"; -import { Species } from "#enums/species"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { allMoves } from "#app/data/move.js"; -import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag.js"; -import { BerryPhase } from "#app/phases/berry-phase.js"; const TIMEOUT = 20 * 1000; @@ -48,7 +47,7 @@ describe("Moves - Protect", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(BerryPhase, false); @@ -66,7 +65,7 @@ describe("Moves - Protect", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(BerryPhase, false); @@ -84,7 +83,7 @@ describe("Moves - Protect", () => { const leadPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(BerryPhase, false); @@ -103,7 +102,7 @@ describe("Moves - Protect", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + game.move.select(Moves.PROTECT); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/purify.test.ts b/src/test/moves/purify.test.ts index 3020e4b47ac..15d684b2d60 100644 --- a/src/test/moves/purify.test.ts +++ b/src/test/moves/purify.test.ts @@ -1,13 +1,12 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattlerIndex } from "#app/battle"; +import { Status, StatusEffect } from "#app/data/status-effect"; +import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import { BattlerIndex } from "#app/battle.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -49,7 +48,7 @@ describe("Moves - Purify", () => { playerPokemon.hp = playerPokemon.getMaxHp() - 1; enemyPokemon.status = new Status(StatusEffect.BURN); - game.doAttack(getMovePosition(game.scene, 0, Moves.PURIFY)); + game.move.select(Moves.PURIFY); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEndPhase); @@ -69,7 +68,7 @@ describe("Moves - Purify", () => { playerPokemon.hp = playerPokemon.getMaxHp() - 1; const playerInitialHp = playerPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.PURIFY)); + game.move.select(Moves.PURIFY); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEndPhase); diff --git a/src/test/moves/quick_guard.test.ts b/src/test/moves/quick_guard.test.ts index 8bf647f2027..26d9a74e9fd 100644 --- a/src/test/moves/quick_guard.test.ts +++ b/src/test/moves/quick_guard.test.ts @@ -1,13 +1,12 @@ +import { BattleStat } from "#app/data/battle-stat"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; -import { Species } from "#enums/species"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; const TIMEOUT = 20 * 1000; @@ -47,11 +46,11 @@ describe("Moves - Quick Guard", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_GUARD)); + game.move.select(Moves.QUICK_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -69,11 +68,11 @@ describe("Moves - Quick Guard", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_GUARD)); + game.move.select(Moves.QUICK_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -91,11 +90,11 @@ describe("Moves - Quick Guard", () => { const leadPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_GUARD)); + game.move.select(Moves.QUICK_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.FOLLOW_ME)); + game.move.select(Moves.FOLLOW_ME, 1); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/rage_powder.test.ts b/src/test/moves/rage_powder.test.ts index 17b687feead..3e78c6fe0c9 100644 --- a/src/test/moves/rage_powder.test.ts +++ b/src/test/moves/rage_powder.test.ts @@ -1,14 +1,11 @@ -import { BattlerIndex } from "#app/battle.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattlerIndex } from "#app/battle"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { SelectTargetPhase } from "#app/phases/select-target-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -33,35 +30,23 @@ describe("Moves - Rage Powder", () => { game.override.enemySpecies(Species.SNORLAX); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK ]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); test( "move effect should be bypassed by Grass type", async () => { - game.override.enemyMoveset([ Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER ]); + game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]); - await game.startBattle([ Species.AMOONGUSS, Species.VENUSAUR ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.AMOONGUSS, Species.VENUSAUR]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY); + game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to(TurnEndPhase, false); // If redirection was bypassed, both enemies should be damaged @@ -74,29 +59,17 @@ describe("Moves - Rage Powder", () => { "move effect should be bypassed by Overcoat", async () => { game.override.ability(Abilities.OVERCOAT); - game.override.enemyMoveset([ Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER ]); + game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]); // Test with two non-Grass type player Pokemon - await game.startBattle([ Species.BLASTOISE, Species.CHARIZARD ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY); + game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to(TurnEndPhase, false); // If redirection was bypassed, both enemies should be damaged diff --git a/src/test/moves/reflect.test.ts b/src/test/moves/reflect.test.ts index 79dd4f8202b..9780ede3c55 100644 --- a/src/test/moves/reflect.test.ts +++ b/src/test/moves/reflect.test.ts @@ -1,14 +1,13 @@ -import { ArenaTagSide } from "#app/data/arena-tag.js"; -import Move, { allMoves } from "#app/data/move.js"; -import { Abilities } from "#app/enums/abilities.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import Pokemon from "#app/field/pokemon.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { NumberHolder } from "#app/utils.js"; +import { ArenaTagSide } from "#app/data/arena-tag"; +import Move, { allMoves } from "#app/data/move"; +import { Abilities } from "#app/enums/abilities"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import Pokemon from "#app/field/pokemon"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { NumberHolder } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -17,7 +16,7 @@ describe("Moves - Reflect", () => { let phaserGame: Phaser.Game; let game: GameManager; const singleBattleMultiplier = 0.5; - const doubleBattleMultiplier = 2732/4096; + const doubleBattleMultiplier = 2732 / 4096; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -40,11 +39,11 @@ describe("Moves - Reflect", () => { game.override.disableCrits(); }); - it("reduces damage of physical attacks by half in a single battle", async() => { + it("reduces damage of physical attacks by half in a single battle", async () => { const moveToUse = Moves.TACKLE; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); @@ -52,14 +51,14 @@ describe("Moves - Reflect", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); - it("reduces damage of physical attacks by a third in a double battle", async() => { + it("reduces damage of physical attacks by a third in a double battle", async () => { game.override.battleType("double"); const moveToUse = Moves.ROCK_SLIDE; await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); - game.doAttack(getMovePosition(game.scene, 1, moveToUse)); + game.move.select(moveToUse); + game.move.select(moveToUse, 1); await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); @@ -67,11 +66,11 @@ describe("Moves - Reflect", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); - it("does not affect special attacks", async() => { + it("does not affect special attacks", async () => { const moveToUse = Moves.ABSORB; await game.startBattle([Species.SHUCKLE]); - game.doAttack(getMovePosition(game.scene, 0, moveToUse)); + game.move.select(moveToUse); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/moves/rollout.test.ts b/src/test/moves/rollout.test.ts index cad65768a1c..ddb0b22e642 100644 --- a/src/test/moves/rollout.test.ts +++ b/src/test/moves/rollout.test.ts @@ -1,10 +1,9 @@ -import { allMoves } from "#app/data/move.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; +import { allMoves } from "#app/data/move"; +import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -12,7 +11,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite describe("Moves - Rollout", () => { let phaserGame: Phaser.Game; let game: GameManager; - const TIMEOUT = 20 * 1000; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -58,7 +56,7 @@ describe("Moves - Rollout", () => { let previousHp = enemyPkm.hp; for (let i = 0; i < turns; i++) { - game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT)); + game.move.select(Moves.ROLLOUT); await game.phaseInterceptor.to(CommandPhase); dmgHistory.push(previousHp - enemyPkm.hp); @@ -78,5 +76,5 @@ describe("Moves - Rollout", () => { // reset expect(turn6Dmg).toBeGreaterThanOrEqual(turn1Dmg - variance); expect(turn6Dmg).toBeLessThanOrEqual(turn1Dmg + variance); - }, TIMEOUT); + }); }); diff --git a/src/test/moves/roost.test.ts b/src/test/moves/roost.test.ts index c40bb18cdb1..cf07a3485e7 100644 --- a/src/test/moves/roost.test.ts +++ b/src/test/moves/roost.test.ts @@ -1,13 +1,12 @@ -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -32,8 +31,8 @@ describe("Moves - Roost", () => { game.override.enemyAbility(Abilities.INSOMNIA); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.STOMPING_TANTRUM ]); - game.override.enemyMoveset([Moves.ROOST,Moves.ROOST,Moves.ROOST,Moves.ROOST]); + game.override.moveset([Moves.STOMPING_TANTRUM]); + game.override.enemyMoveset([Moves.ROOST, Moves.ROOST, Moves.ROOST, Moves.ROOST]); }); test( @@ -45,7 +44,7 @@ describe("Moves - Roost", () => { const enemyStartingHp = enemyPokemon.hp; - game.doAttack(getMovePosition(game.scene, 0, Moves.STOMPING_TANTRUM)); + game.move.select(Moves.STOMPING_TANTRUM); await game.phaseInterceptor.to(MoveEffectPhase); diff --git a/src/test/moves/shell_trap.test.ts b/src/test/moves/shell_trap.test.ts index c600b1ee1cc..4549a8b2b73 100644 --- a/src/test/moves/shell_trap.test.ts +++ b/src/test/moves/shell_trap.test.ts @@ -1,16 +1,15 @@ +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { MoveResult } from "#app/field/pokemon"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { MovePhase } from "#app/phases/move-phase"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import GameManager from "#test/utils/gameManager"; -import { Moves } from "#app/enums/moves.js"; -import { Species } from "#app/enums/species.js"; -import { allMoves } from "#app/data/move.js"; -import { BattlerIndex } from "#app/battle.js"; -import { getMovePosition } from "../utils/gameManagerUtils"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { MoveResult } from "#app/field/pokemon.js"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { MovePhase } from "#app/phases/move-phase.js"; const TIMEOUT = 20 * 1000; @@ -49,8 +48,8 @@ describe("Moves - Shell Trap", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SHELL_TRAP)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SHELL_TRAP, 1); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2]); @@ -75,8 +74,8 @@ describe("Moves - Shell Trap", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SHELL_TRAP)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SHELL_TRAP, 1); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2]); @@ -101,8 +100,8 @@ describe("Moves - Shell Trap", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SHELL_TRAP)); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SHELL_TRAP, 1); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2]); @@ -127,8 +126,8 @@ describe("Moves - Shell Trap", () => { const playerPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SHELL_TRAP)); - game.doAttack(getMovePosition(game.scene, 1, Moves.BULLDOZE)); + game.move.select(Moves.SHELL_TRAP); + game.move.select(Moves.BULLDOZE, 1); await game.phaseInterceptor.to(MoveEndPhase); @@ -154,7 +153,7 @@ describe("Moves - Shell Trap", () => { const playerPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.SHELL_TRAP)); + game.move.select(Moves.SHELL_TRAP); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/spikes.test.ts b/src/test/moves/spikes.test.ts index ae3c676b893..c4096111c6f 100644 --- a/src/test/moves/spikes.test.ts +++ b/src/test/moves/spikes.test.ts @@ -1,8 +1,8 @@ -import { CommandPhase } from "#app/phases/command-phase.js"; -import GameManager from "#test/utils/gameManager"; +import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -31,14 +31,11 @@ describe("Moves - Spikes", () => { game.override.ability(Abilities.HYDRATION); game.override.passiveAbility(Abilities.HYDRATION); game.override.startingWave(3); - game.override.enemyMoveset([Moves.SPLASH,Moves.SPLASH,Moves.SPLASH,Moves.SPLASH]); - game.override.moveset([Moves.SPIKES,Moves.SPLASH, Moves.ROAR]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); + game.override.moveset([Moves.SPIKES, Moves.SPLASH, Moves.ROAR]); }); - it("single - wild - stay on field - no damage", async() => { - // player set spikes on the field and do splash for 3 turns - // opponent do splash for 4 turns - // nobody should take damage + it("single - wild - stay on field - no damage", async () => { await game.classicMode.runToSummon([ Species.MIGHTYENA, Species.POOCHYENA, @@ -46,21 +43,14 @@ describe("Moves - Spikes", () => { await game.phaseInterceptor.to(CommandPhase, true); const initialHp = game.scene.getParty()[0].hp; expect(game.scene.getParty()[0].hp).toBe(initialHp); - game.doAttack(0); + game.move.select(Moves.SPIKES); await game.toNextTurn(); - game.doAttack(1); - await game.toNextTurn(); - game.doAttack(1); - await game.toNextTurn(); - game.doAttack(1); - await game.toNextTurn(); - game.doAttack(1); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.getParty()[0].hp).toBe(initialHp); - console.log(game.textInterceptor.logs); }, 20000); - it("single - wild - take some damage", async() => { + it("single - wild - take some damage", async () => { // player set spikes on the field and switch back to back // opponent do splash for 2 turns // nobody should take damage @@ -82,7 +72,7 @@ describe("Moves - Spikes", () => { expect(game.scene.getParty()[0].hp).toBe(initialHp); }, 20000); - it("trainer - wild - force switch opponent - should take damage", async() => { + it("trainer - wild - force switch opponent - should take damage", async () => { game.override.startingWave(5); // player set spikes on the field and do splash for 3 turns // opponent do splash for 4 turns @@ -93,14 +83,14 @@ describe("Moves - Spikes", () => { ]); await game.phaseInterceptor.to(CommandPhase, true); const initialHpOpponent = game.scene.currentBattle.enemyParty[1].hp; - game.doAttack(0); + game.move.select(Moves.SPIKES); await game.toNextTurn(); - game.doAttack(2); + game.move.select(Moves.ROAR); await game.toNextTurn(); expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(initialHpOpponent); }, 20000); - it("trainer - wild - force switch by himself opponent - should take damage", async() => { + it("trainer - wild - force switch by himself opponent - should take damage", async () => { game.override.startingWave(5); game.override.startingLevel(5000); game.override.enemySpecies(0); @@ -113,11 +103,11 @@ describe("Moves - Spikes", () => { ]); await game.phaseInterceptor.to(CommandPhase, true); const initialHpOpponent = game.scene.currentBattle.enemyParty[1].hp; - game.doAttack(0); + game.move.select(Moves.SPIKES); await game.toNextTurn(); game.forceOpponentToSwitch(); - game.doAttack(1); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(initialHpOpponent); }, 20000); diff --git a/src/test/moves/spit_up.test.ts b/src/test/moves/spit_up.test.ts index 51d84a5e151..ab47e65d653 100644 --- a/src/test/moves/spit_up.test.ts +++ b/src/test/moves/spit_up.test.ts @@ -1,17 +1,17 @@ import { BattleStat } from "#app/data/battle-stat"; -import { StockpilingTag } from "#app/data/battler-tags.js"; -import { allMoves } from "#app/data/move.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { MoveResult, TurnMove } from "#app/field/pokemon.js"; -import GameManager from "#test/utils/gameManager"; +import { StockpilingTag } from "#app/data/battler-tags"; +import { allMoves } from "#app/data/move"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { MoveResult, TurnMove } from "#app/field/pokemon"; +import { MovePhase } from "#app/phases/move-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { MovePhase } from "#app/phases/move-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; describe("Moves - Spit Up", () => { let phaserGame: Phaser.Game; @@ -55,7 +55,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce(); @@ -80,7 +80,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce(); @@ -106,7 +106,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(allMoves[Moves.SPIT_UP].calculateBattlePower).toHaveBeenCalledOnce(); @@ -126,7 +126,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SPIT_UP, result: MoveResult.FAIL }); @@ -146,7 +146,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(MovePhase); expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1); @@ -186,7 +186,7 @@ describe("Moves - Spit Up", () => { vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); - game.doAttack(0); + game.move.select(Moves.SPIT_UP); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS }); diff --git a/src/test/moves/spotlight.test.ts b/src/test/moves/spotlight.test.ts index 40ab78471ae..e5f4719d1d3 100644 --- a/src/test/moves/spotlight.test.ts +++ b/src/test/moves/spotlight.test.ts @@ -1,14 +1,11 @@ -import { BattlerIndex } from "#app/battle.js"; +import { BattlerIndex } from "#app/battle"; import { Stat } from "#app/data/pokemon-stat"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { SelectTargetPhase } from "#app/phases/select-target-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; const TIMEOUT = 20 * 1000; @@ -33,33 +30,21 @@ describe("Moves - Spotlight", () => { game.override.enemySpecies(Species.SNORLAX); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK ]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); test( "move should redirect attacks to the target", async () => { - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPOTLIGHT)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(BattlerIndex.ENEMY_2); + game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY); + game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2); await game.phaseInterceptor.to(TurnEndPhase, false); expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]); @@ -70,17 +55,11 @@ describe("Moves - Spotlight", () => { test( "move should cause other redirection moves to fail", async () => { - game.override.enemyMoveset([ Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME ]); + game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]); - await game.startBattle([ Species.AMOONGUSS, Species.CHARIZARD ]); - - const playerPokemon = game.scene.getPlayerField(); - expect(playerPokemon.length).toBe(2); - playerPokemon.forEach(p => expect(p).not.toBe(undefined)); + await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyField(); - expect(enemyPokemon.length).toBe(2); - enemyPokemon.forEach(p => expect(p).not.toBe(undefined)); /** * Spotlight will target the slower enemy. In this situation without Spotlight being used, @@ -92,14 +71,8 @@ describe("Moves - Spotlight", () => { const enemyStartingHp = enemyPokemon.map(p => p.hp); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPOTLIGHT)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(spotTarget); - await game.phaseInterceptor.to(CommandPhase); - - game.doAttack(getMovePosition(game.scene, 1, Moves.QUICK_ATTACK)); - await game.phaseInterceptor.to(SelectTargetPhase, false); - game.doSelectTarget(attackTarget); + game.move.select(Moves.SPOTLIGHT, 0, spotTarget); + game.move.select(Moves.QUICK_ATTACK, 1, attackTarget); await game.phaseInterceptor.to(TurnEndPhase, false); expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]); diff --git a/src/test/moves/stockpile.test.ts b/src/test/moves/stockpile.test.ts index 0b208e20f81..b1941b9f9b3 100644 --- a/src/test/moves/stockpile.test.ts +++ b/src/test/moves/stockpile.test.ts @@ -1,16 +1,15 @@ import { BattleStat } from "#app/data/battle-stat"; -import { StockpilingTag } from "#app/data/battler-tags.js"; -import { MoveResult, TurnMove } from "#app/field/pokemon.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { StockpilingTag } from "#app/data/battler-tags"; +import { MoveResult, TurnMove } from "#app/field/pokemon"; +import { CommandPhase } from "#app/phases/command-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; describe("Moves - Stockpile", () => { describe("integration tests", () => { @@ -57,7 +56,7 @@ describe("Moves - Stockpile", () => { await game.phaseInterceptor.to(CommandPhase); } - game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); + game.move.select(Moves.STOCKPILE); await game.phaseInterceptor.to(TurnInitPhase); const stockpilingTag = user.getTag(StockpilingTag)!; @@ -92,7 +91,7 @@ describe("Moves - Stockpile", () => { expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6); expect(user.summonData.battleStats[BattleStat.SPDEF]).toBe(6); - game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); + game.move.select(Moves.STOCKPILE); await game.phaseInterceptor.to(TurnInitPhase); const stockpilingTag = user.getTag(StockpilingTag)!; @@ -104,7 +103,7 @@ describe("Moves - Stockpile", () => { // do it again, just for good measure await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); + game.move.select(Moves.STOCKPILE); await game.phaseInterceptor.to(TurnInitPhase); const stockpilingTagAgain = user.getTag(StockpilingTag)!; diff --git a/src/test/moves/swallow.test.ts b/src/test/moves/swallow.test.ts index 6a054393acc..202f25fee74 100644 --- a/src/test/moves/swallow.test.ts +++ b/src/test/moves/swallow.test.ts @@ -1,16 +1,16 @@ import { BattleStat } from "#app/data/battle-stat"; -import { StockpilingTag } from "#app/data/battler-tags.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import { MoveResult, TurnMove } from "#app/field/pokemon.js"; -import GameManager from "#test/utils/gameManager"; +import { StockpilingTag } from "#app/data/battler-tags"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { MoveResult, TurnMove } from "#app/field/pokemon"; +import { MovePhase } from "#app/phases/move-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { MovePhase } from "#app/phases/move-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; describe("Moves - Swallow", () => { let phaserGame: Phaser.Game; @@ -57,7 +57,7 @@ describe("Moves - Swallow", () => { vi.spyOn(pokemon, "heal"); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.heal).toHaveBeenCalledOnce(); @@ -85,7 +85,7 @@ describe("Moves - Swallow", () => { vi.spyOn(pokemon, "heal"); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.heal).toHaveBeenCalledOnce(); @@ -114,7 +114,7 @@ describe("Moves - Swallow", () => { vi.spyOn(pokemon, "heal"); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.heal).toHaveBeenCalledOnce(); @@ -132,7 +132,7 @@ describe("Moves - Swallow", () => { const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeUndefined(); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SWALLOW, result: MoveResult.FAIL }); @@ -148,7 +148,7 @@ describe("Moves - Swallow", () => { const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(MovePhase); expect(pokemon.summonData.battleStats[BattleStat.DEF]).toBe(1); @@ -184,7 +184,7 @@ describe("Moves - Swallow", () => { [BattleStat.SPDEF]: 2, }); - game.doAttack(0); + game.move.select(Moves.SWALLOW); await game.phaseInterceptor.to(TurnInitPhase); expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: Moves.SWALLOW, result: MoveResult.SUCCESS }); diff --git a/src/test/moves/tackle.test.ts b/src/test/moves/tackle.test.ts index 3da8bc6f978..5eca9e344c8 100644 --- a/src/test/moves/tackle.test.ts +++ b/src/test/moves/tackle.test.ts @@ -1,15 +1,11 @@ import { Stat } from "#app/data/pokemon-stat"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Moves - Tackle", () => { @@ -34,30 +30,24 @@ describe("Moves - Tackle", () => { game.override.startingLevel(1); game.override.startingWave(97); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.GROWTH,Moves.GROWTH,Moves.GROWTH,Moves.GROWTH]); + game.override.enemyMoveset([Moves.GROWTH, Moves.GROWTH, Moves.GROWTH, Moves.GROWTH]); game.override.disableCrits(); }); - it("TACKLE against ghost", async() => { + it("TACKLE against ghost", async () => { const moveToUse = Moves.TACKLE; game.override.enemySpecies(Species.GENGAR); await game.startBattle([ Species.MIGHTYENA, ]); const hpOpponent = game.scene.currentBattle.enemyParty[0].hp; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp; expect(hpLost).toBe(0); }, 20000); - it("TACKLE against not resistant", async() => { + it("TACKLE against not resistant", async () => { const moveToUse = Moves.TACKLE; await game.startBattle([ Species.MIGHTYENA, @@ -68,13 +58,7 @@ describe("Moves - Tackle", () => { const hpOpponent = game.scene.currentBattle.enemyParty[0].hp; - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp; expect(hpLost).toBeGreaterThan(0); diff --git a/src/test/moves/tail_whip.test.ts b/src/test/moves/tail_whip.test.ts index ba4a7459094..0a999fe1920 100644 --- a/src/test/moves/tail_whip.test.ts +++ b/src/test/moves/tail_whip.test.ts @@ -1,16 +1,12 @@ import { BattleStat } from "#app/data/battle-stat"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; describe("Moves - Tail whip", () => { @@ -36,10 +32,10 @@ describe("Moves - Tail whip", () => { game.override.ability(Abilities.INSOMNIA); game.override.startingLevel(2000); game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); }); - it("TAIL_WHIP", async() => { + it("TAIL_WHIP", async () => { const moveToUse = Moves.TAIL_WHIP; await game.startBattle([ Species.MIGHTYENA, @@ -49,13 +45,7 @@ describe("Moves - Tail whip", () => { let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.DEF]).toBe(0); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - const movePosition = getMovePosition(game.scene, 0, moveToUse); - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); + game.move.select(moveToUse); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase); battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; expect(battleStatsOpponent[BattleStat.DEF]).toBe(-1); diff --git a/src/test/moves/tailwind.test.ts b/src/test/moves/tailwind.test.ts index 115a97f3be4..6b70122d08d 100644 --- a/src/test/moves/tailwind.test.ts +++ b/src/test/moves/tailwind.test.ts @@ -1,14 +1,13 @@ -import { ArenaTagSide } from "#app/data/arena-tag.js"; -import { Stat } from "#app/data/pokemon-stat.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { ArenaTagSide } from "#app/data/arena-tag"; +import { Stat } from "#app/data/pokemon-stat"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; describe("Moves - Tailwind", () => { let phaserGame: Phaser.Game; @@ -42,8 +41,8 @@ describe("Moves - Tailwind", () => { expect(magikarp.getBattleStat(Stat.SPD)).equal(magikarpSpd); expect(meowth.getBattleStat(Stat.SPD)).equal(meowthSpd); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.TAILWIND); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(TurnEndPhase); @@ -57,19 +56,19 @@ describe("Moves - Tailwind", () => { await game.startBattle([Species.MAGIKARP]); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.toNextTurn(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeDefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + game.move.select(Moves.SPLASH); await game.toNextTurn(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeUndefined(); @@ -92,7 +91,7 @@ describe("Moves - Tailwind", () => { expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)).toBeUndefined(); expect(game.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.ENEMY)).toBeUndefined(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TAILWIND)); + game.move.select(Moves.TAILWIND); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/moves/tera_blast.test.ts b/src/test/moves/tera_blast.test.ts index 0bd2ad24e23..d261d4b856b 100644 --- a/src/test/moves/tera_blast.test.ts +++ b/src/test/moves/tera_blast.test.ts @@ -1,17 +1,16 @@ +import { BattlerIndex } from "#app/battle"; +import { BattleStat } from "#app/data/battle-stat"; import { allMoves } from "#app/data/move"; -import GameManager from "#test/utils/gameManager"; +import { Type } from "#app/data/type"; +import { Abilities } from "#app/enums/abilities"; +import { Stat } from "#app/enums/stat"; +import { HitResult } from "#app/field/pokemon"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { Abilities } from "#app/enums/abilities"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { Type } from "#app/data/type"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BattleStat } from "#app/data/battle-stat"; -import { Stat } from "#app/enums/stat"; -import { BattlerIndex } from "#app/battle"; -import { HitResult } from "#app/field/pokemon"; describe("Moves - Tera Blast", () => { let phaserGame: Phaser.Game; @@ -37,7 +36,7 @@ describe("Moves - Tera Blast", () => { .starterSpecies(Species.FEEBAS) .moveset([Moves.TERA_BLAST]) .ability(Abilities.BALL_FETCH) - .startingHeldItems([{name: "TERA_SHARD", type: Type.FIRE}]) + .startingHeldItems([{ name: "TERA_SHARD", type: Type.FIRE }]) .enemySpecies(Species.MAGIKARP) .enemyMoveset(SPLASH_ONLY) .enemyAbility(Abilities.BALL_FETCH) @@ -46,30 +45,30 @@ describe("Moves - Tera Blast", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); }); - it("changes type to match user's tera type", async() => { + it("changes type to match user's tera type", async () => { game.override .enemySpecies(Species.FURRET) - .startingHeldItems([{name: "TERA_SHARD", type: Type.FIGHTING}]); + .startingHeldItems([{ name: "TERA_SHARD", type: Type.FIGHTING }]); await game.startBattle(); const enemyPokemon = game.scene.getEnemyPokemon()!; vi.spyOn(enemyPokemon, "apply"); - game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST)); + game.move.select(Moves.TERA_BLAST); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEffectPhase"); expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.SUPER_EFFECTIVE); }, 20000); - it("increases power if user is Stellar tera type", async() => { - game.override.startingHeldItems([{name: "TERA_SHARD", type: Type.STELLAR}]); + it("increases power if user is Stellar tera type", async () => { + game.override.startingHeldItems([{ name: "TERA_SHARD", type: Type.STELLAR }]); const stellarTypeMultiplier = 2; const stellarTypeDmgBonus = 20; const basePower = moveToCheck.power; await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST)); + game.move.select(Moves.TERA_BLAST); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEffectPhase"); @@ -77,7 +76,7 @@ describe("Moves - Tera Blast", () => { }, 20000); // Currently abilities are bugged and can't see when a move's category is changed - it.skip("uses the higher stat of the user's Atk and SpAtk for damage calculation", async() => { + it.skip("uses the higher stat of the user's Atk and SpAtk for damage calculation", async () => { game.override.enemyAbility(Abilities.TOXIC_DEBRIS); await game.startBattle(); @@ -85,18 +84,18 @@ describe("Moves - Tera Blast", () => { playerPokemon.stats[Stat.ATK] = 100; playerPokemon.stats[Stat.SPATK] = 1; - game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST)); + game.move.select(Moves.TERA_BLAST); await game.phaseInterceptor.to("TurnEndPhase"); expect(game.scene.getEnemyPokemon()!.battleData.abilityRevealed).toBe(true); }, 20000); - it("causes stat drops if user is Stellar tera type", async() => { - game.override.startingHeldItems([{name: "TERA_SHARD", type: Type.STELLAR}]); + it("causes stat drops if user is Stellar tera type", async () => { + game.override.startingHeldItems([{ name: "TERA_SHARD", type: Type.STELLAR }]); await game.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.TERA_BLAST)); + game.move.select(Moves.TERA_BLAST); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEndPhase"); diff --git a/src/test/moves/thousand_arrows.test.ts b/src/test/moves/thousand_arrows.test.ts index d72f3ed3fac..8d1d6ee5f4a 100644 --- a/src/test/moves/thousand_arrows.test.ts +++ b/src/test/moves/thousand_arrows.test.ts @@ -1,13 +1,12 @@ -import { Abilities } from "#app/enums/abilities.js"; -import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { Abilities } from "#app/enums/abilities"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; const TIMEOUT = 20 * 1000; @@ -31,18 +30,18 @@ describe("Moves - Thousand Arrows", () => { game.override.enemySpecies(Species.TOGETIC); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.moveset([ Moves.THOUSAND_ARROWS ]); - game.override.enemyMoveset([Moves.SPLASH,Moves.SPLASH,Moves.SPLASH,Moves.SPLASH]); + game.override.moveset([Moves.THOUSAND_ARROWS]); + game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); }); it( "move should hit and ground Flying-type targets", async () => { - await game.startBattle([ Species.ILLUMISE ]); + await game.startBattle([Species.ILLUMISE]); const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); + game.move.select(Moves.THOUSAND_ARROWS); await game.phaseInterceptor.to(MoveEffectPhase, false); // Enemy should not be grounded before move effect is applied @@ -61,11 +60,11 @@ describe("Moves - Thousand Arrows", () => { game.override.enemySpecies(Species.SNORLAX); game.override.enemyAbility(Abilities.LEVITATE); - await game.startBattle([ Species.ILLUMISE ]); + await game.startBattle([Species.ILLUMISE]); const enemyPokemon = game.scene.getEnemyPokemon()!; - game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); + game.move.select(Moves.THOUSAND_ARROWS); await game.phaseInterceptor.to(MoveEffectPhase, false); // Enemy should not be grounded before move effect is applied @@ -83,13 +82,13 @@ describe("Moves - Thousand Arrows", () => { async () => { game.override.enemySpecies(Species.SNORLAX); - await game.startBattle([ Species.ILLUMISE ]); + await game.startBattle([Species.ILLUMISE]); const enemyPokemon = game.scene.getEnemyPokemon()!; enemyPokemon.addTag(BattlerTagType.MAGNET_RISEN, undefined, Moves.MAGNET_RISE); - game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); + game.move.select(Moves.THOUSAND_ARROWS); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/moves/tidy_up.test.ts b/src/test/moves/tidy_up.test.ts index 64a63df08df..1ef7933c114 100644 --- a/src/test/moves/tidy_up.test.ts +++ b/src/test/moves/tidy_up.test.ts @@ -1,15 +1,14 @@ -import { BattleStat } from "#app/data/battle-stat.js"; -import { ArenaTagType } from "#app/enums/arena-tag-type.js"; -import GameManager from "#test/utils/gameManager"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { BattleStat } from "#app/data/battle-stat"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { SPLASH_ONLY } from "#test/utils/testUtils"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Moves - Tidy Up", () => { @@ -38,81 +37,81 @@ describe("Moves - Tidy Up", () => { game.override.startingLevel(50); }); - it("spikes are cleared", async() => { + it("spikes are cleared", async () => { game.override.moveset([Moves.SPIKES, Moves.TIDY_UP]); game.override.enemyMoveset([Moves.SPIKES, Moves.SPIKES, Moves.SPIKES, Moves.SPIKES]); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPIKES)); + game.move.select(Moves.SPIKES); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.arena.getTag(ArenaTagType.SPIKES)).toBeUndefined(); }, 20000); - it("stealth rocks are cleared", async() => { + it("stealth rocks are cleared", async () => { game.override.moveset([Moves.STEALTH_ROCK, Moves.TIDY_UP]); game.override.enemyMoveset([Moves.STEALTH_ROCK, Moves.STEALTH_ROCK, Moves.STEALTH_ROCK, Moves.STEALTH_ROCK]); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.STEALTH_ROCK)); + game.move.select(Moves.STEALTH_ROCK); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.arena.getTag(ArenaTagType.STEALTH_ROCK)).toBeUndefined(); }, 20000); - it("toxic spikes are cleared", async() => { + it("toxic spikes are cleared", async () => { game.override.moveset([Moves.TOXIC_SPIKES, Moves.TIDY_UP]); game.override.enemyMoveset([Moves.TOXIC_SPIKES, Moves.TOXIC_SPIKES, Moves.TOXIC_SPIKES, Moves.TOXIC_SPIKES]); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.TOXIC_SPIKES)); + game.move.select(Moves.TOXIC_SPIKES); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.arena.getTag(ArenaTagType.TOXIC_SPIKES)).toBeUndefined(); }, 20000); - it("sticky webs are cleared", async() => { + it("sticky webs are cleared", async () => { game.override.moveset([Moves.STICKY_WEB, Moves.TIDY_UP]); game.override.enemyMoveset([Moves.STICKY_WEB, Moves.STICKY_WEB, Moves.STICKY_WEB, Moves.STICKY_WEB]); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.STICKY_WEB)); + game.move.select(Moves.STICKY_WEB); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); expect(game.scene.arena.getTag(ArenaTagType.STICKY_WEB)).toBeUndefined(); }, 20000); - it.skip("substitutes are cleared", async() => { + it.skip("substitutes are cleared", async () => { game.override.moveset([Moves.SUBSTITUTE, Moves.TIDY_UP]); game.override.enemyMoveset([Moves.SUBSTITUTE, Moves.SUBSTITUTE, Moves.SUBSTITUTE, Moves.SUBSTITUTE]); await game.startBattle(); - game.doAttack(getMovePosition(game.scene, 0, Moves.SUBSTITUTE)); + game.move.select(Moves.SUBSTITUTE); await game.phaseInterceptor.to(TurnEndPhase); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); // TODO: check for subs here once the move is implemented }, 20000); - it("user's stats are raised with no traps set", async() => { + it("user's stats are raised with no traps set", async () => { await game.startBattle(); const player = game.scene.getPlayerPokemon()!.summonData.battleStats; expect(player[BattleStat.ATK]).toBe(0); expect(player[BattleStat.SPD]).toBe(0); - game.doAttack(getMovePosition(game.scene, 0, Moves.TIDY_UP)); + game.move.select(Moves.TIDY_UP); await game.phaseInterceptor.to(TurnEndPhase); expect(player[BattleStat.ATK]).toBe(+1); diff --git a/src/test/moves/u_turn.test.ts b/src/test/moves/u_turn.test.ts index b93f997c487..ae55302bb42 100644 --- a/src/test/moves/u_turn.test.ts +++ b/src/test/moves/u_turn.test.ts @@ -1,14 +1,13 @@ -import { Abilities } from "#app/enums/abilities.js"; +import { Abilities } from "#app/enums/abilities"; +import { StatusEffect } from "#app/enums/status-effect"; +import { SwitchPhase } from "#app/phases/switch-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; import GameManager from "#app/test/utils/gameManager"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { StatusEffect } from "#app/enums/status-effect.js"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { SwitchPhase } from "#app/phases/switch-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; describe("Moves - U-turn", () => { let phaserGame: Phaser.Game; @@ -36,7 +35,7 @@ describe("Moves - U-turn", () => { .disableCrits(); }); - it("triggers regenerator a single time when a regenerator user switches out with u-turn", async() => { + it("triggers regenerator a single time when a regenerator user switches out with u-turn", async () => { // arrange const playerHp = 1; game.override.ability(Abilities.REGENERATOR); @@ -47,7 +46,7 @@ describe("Moves - U-turn", () => { game.scene.getPlayerPokemon()!.hp = playerHp; // act - game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); + game.move.select(Moves.U_TURN); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to(TurnEndPhase); @@ -57,7 +56,7 @@ describe("Moves - U-turn", () => { expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.SHUCKLE); }, 20000); - it("triggers rough skin on the u-turn user before a new pokemon is switched in", async() => { + it("triggers rough skin on the u-turn user before a new pokemon is switched in", async () => { // arrange game.override.enemyAbility(Abilities.ROUGH_SKIN); await game.startBattle([ @@ -66,7 +65,7 @@ describe("Moves - U-turn", () => { ]); // act - game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); + game.move.select(Moves.U_TURN); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to(SwitchPhase, false); @@ -78,7 +77,7 @@ describe("Moves - U-turn", () => { expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); }, 20000); - it("triggers contact abilities on the u-turn user (eg poison point) before a new pokemon is switched in", async() => { + it("triggers contact abilities on the u-turn user (eg poison point) before a new pokemon is switched in", async () => { // arrange game.override.enemyAbility(Abilities.POISON_POINT); await game.startBattle([ @@ -88,7 +87,7 @@ describe("Moves - U-turn", () => { vi.spyOn(game.scene.getEnemyPokemon()!, "randSeedInt").mockReturnValue(0); // act - game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); + game.move.select(Moves.U_TURN); await game.phaseInterceptor.to(SwitchPhase, false); // assert diff --git a/src/test/moves/wide_guard.test.ts b/src/test/moves/wide_guard.test.ts index 1f22428de4b..616972de01b 100644 --- a/src/test/moves/wide_guard.test.ts +++ b/src/test/moves/wide_guard.test.ts @@ -1,13 +1,12 @@ +import { BattleStat } from "#app/data/battle-stat"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; -import { Species } from "#enums/species"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { getMovePosition } from "../utils/gameManagerUtils"; -import { BattleStat } from "#app/data/battle-stat.js"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; const TIMEOUT = 20 * 1000; @@ -47,11 +46,11 @@ describe("Moves - Wide Guard", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.WIDE_GUARD)); + game.move.select(Moves.WIDE_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -68,11 +67,11 @@ describe("Moves - Wide Guard", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.WIDE_GUARD)); + game.move.select(Moves.WIDE_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -89,11 +88,11 @@ describe("Moves - Wide Guard", () => { const leadPokemon = game.scene.getPlayerField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.WIDE_GUARD)); + game.move.select(Moves.WIDE_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH)); + game.move.select(Moves.SPLASH, 1); await game.phaseInterceptor.to(BerryPhase, false); @@ -111,11 +110,11 @@ describe("Moves - Wide Guard", () => { const leadPokemon = game.scene.getPlayerField(); const enemyPokemon = game.scene.getEnemyField(); - game.doAttack(getMovePosition(game.scene, 0, Moves.WIDE_GUARD)); + game.move.select(Moves.WIDE_GUARD); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, 1, Moves.SURF)); + game.move.select(Moves.SURF, 1); await game.phaseInterceptor.to(BerryPhase, false); diff --git a/src/test/phases/phases.test.ts b/src/test/phases/phases.test.ts index 2ed1e48c706..5ef25361a3f 100644 --- a/src/test/phases/phases.test.ts +++ b/src/test/phases/phases.test.ts @@ -1,11 +1,11 @@ -import BattleScene from "#app/battle-scene.js"; -import { Mode } from "#app/ui/ui.js"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; +import BattleScene from "#app/battle-scene"; +import { LoginPhase } from "#app/phases/login-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { UnavailablePhase } from "#app/phases/unavailable-phase"; +import { Mode } from "#app/ui/ui"; import GameManager from "#test/utils/gameManager"; -import { LoginPhase } from "#app/phases/login-phase.js"; -import { TitlePhase } from "#app/phases/title-phase.js"; -import { UnavailablePhase } from "#app/phases/unavailable-phase.js"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Phases", () => { let phaserGame: Phaser.Game; diff --git a/src/test/settingMenu/helpers/inGameManip.ts b/src/test/settingMenu/helpers/inGameManip.ts index e18a82ca571..b81e577f5b9 100644 --- a/src/test/settingMenu/helpers/inGameManip.ts +++ b/src/test/settingMenu/helpers/inGameManip.ts @@ -1,6 +1,6 @@ import { getIconForLatestInput, getSettingNameWithKeycode } from "#app/configs/inputs/configHandler"; -import { expect } from "vitest"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; +import { expect } from "vitest"; export class InGameManip { private config; diff --git a/src/test/settingMenu/helpers/menuManip.ts b/src/test/settingMenu/helpers/menuManip.ts index 4fd5f526897..90b3f1e96e6 100644 --- a/src/test/settingMenu/helpers/menuManip.ts +++ b/src/test/settingMenu/helpers/menuManip.ts @@ -1,6 +1,6 @@ -import { expect } from "vitest"; -import { deleteBind, getIconWithKeycode, getIconWithSettingName, getKeyWithKeycode, getKeyWithSettingName, assign, getSettingNameWithKeycode, canIAssignThisKey, canIDeleteThisKey, canIOverrideThisSetting } from "#app/configs/inputs/configHandler"; +import { assign, canIAssignThisKey, canIDeleteThisKey, canIOverrideThisSetting, deleteBind, getIconWithKeycode, getIconWithSettingName, getKeyWithKeycode, getKeyWithSettingName, getSettingNameWithKeycode } from "#app/configs/inputs/configHandler"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; +import { expect } from "vitest"; export class MenuManip { private config; diff --git a/src/test/settingMenu/rebinding_setting.test.ts b/src/test/settingMenu/rebinding_setting.test.ts index eead23972c2..ec2343cfb41 100644 --- a/src/test/settingMenu/rebinding_setting.test.ts +++ b/src/test/settingMenu/rebinding_setting.test.ts @@ -1,13 +1,13 @@ -import { beforeEach, describe, expect, it } from "vitest"; -import { deepCopy } from "#app/utils"; -import { getKeyWithKeycode, getKeyWithSettingName } from "#app/configs/inputs/configHandler"; -import { MenuManip } from "#test/settingMenu/helpers/menuManip"; -import { InGameManip } from "#test/settingMenu/helpers/inGameManip"; -import { InterfaceConfig } from "#app/inputs-controller"; import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty"; +import { getKeyWithKeycode, getKeyWithSettingName } from "#app/configs/inputs/configHandler"; +import { InterfaceConfig } from "#app/inputs-controller"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { Device } from "#enums/devices"; +import { deepCopy } from "#app/utils"; import { Button } from "#enums/buttons"; +import { Device } from "#enums/devices"; +import { InGameManip } from "#test/settingMenu/helpers/inGameManip"; +import { MenuManip } from "#test/settingMenu/helpers/menuManip"; +import { beforeEach, describe, expect, it } from "vitest"; describe("Test Rebinding", () => { diff --git a/src/test/sprites/pokemonSprite.test.ts b/src/test/sprites/pokemonSprite.test.ts index deb5844d677..faf0626b365 100644 --- a/src/test/sprites/pokemonSprite.test.ts +++ b/src/test/sprites/pokemonSprite.test.ts @@ -1,8 +1,8 @@ -import { beforeAll, describe, expect, it } from "vitest"; -import _masterlist from "../../../public/images/pokemon/variant/_masterlist.json"; +import { getAppRootDir } from "#test/sprites/spritesUtils"; import fs from "fs"; import path from "path"; -import { getAppRootDir } from "#test/sprites/spritesUtils"; +import { beforeAll, describe, expect, it } from "vitest"; +import _masterlist from "../../../public/images/pokemon/variant/_masterlist.json"; type PokemonVariantMasterlist = typeof _masterlist; diff --git a/src/test/ui/starter-select.test.ts b/src/test/ui/starter-select.test.ts index dbbdb1999b9..8ef1ea16b4a 100644 --- a/src/test/ui/starter-select.test.ts +++ b/src/test/ui/starter-select.test.ts @@ -1,21 +1,21 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; -import { Mode } from "#app/ui/ui"; -import { GameModes } from "#app/game-mode"; -import StarterSelectUiHandler from "#app/ui/starter-select-ui-handler"; -import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; -import SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler"; -import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { Gender } from "#app/data/gender"; +import { Nature } from "#app/data/nature"; import { allSpecies } from "#app/data/pokemon-species"; -import { Nature} from "#app/data/nature"; -import { Button } from "#enums/buttons"; +import { GameModes } from "#app/game-mode"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; +import SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler"; +import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; +import StarterSelectUiHandler from "#app/ui/starter-select-ui-handler"; +import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; +import { Button } from "#enums/buttons"; import { Species } from "#enums/species"; -import { EncounterPhase } from "#app/phases/encounter-phase.js"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase.js"; -import { TitlePhase } from "#app/phases/title-phase.js"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("UI - Starter select", () => { diff --git a/src/test/ui/transfer-item.test.ts b/src/test/ui/transfer-item.test.ts index 21aed9b5b87..f7dea463574 100644 --- a/src/test/ui/transfer-item.test.ts +++ b/src/test/ui/transfer-item.test.ts @@ -2,16 +2,15 @@ import { BerryType } from "#app/enums/berry-type"; import { Button } from "#app/enums/buttons"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; -import GameManager from "#test/utils/gameManager"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; import { Mode } from "#app/ui/ui"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { BattleEndPhase } from "#app/phases/battle-end-phase.js"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase.js"; describe("UI - Transfer Items", () => { @@ -44,7 +43,7 @@ describe("UI - Transfer Items", () => { await game.startBattle([Species.RAYQUAZA, Species.RAYQUAZA, Species.RAYQUAZA]); - game.doAttack(getMovePosition(game.scene, 0, Moves.DRAGON_CLAW)); + game.move.select(Moves.DRAGON_CLAW); game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { expect(game.scene.ui.getHandler()).toBeInstanceOf(ModifierSelectUiHandler); @@ -88,6 +87,7 @@ describe("UI - Transfer Items", () => { handler.processInput(Button.ACTION); // select Pokemon expect(handler.optionsContainer.list.some((option) => (option as BBCodeText).text?.includes("Transfer"))).toBe(true); + game.phaseInterceptor.unlock(); }); diff --git a/src/test/ui/type-hints.test.ts b/src/test/ui/type-hints.test.ts index f93260f15b7..ccab02b82bf 100644 --- a/src/test/ui/type-hints.test.ts +++ b/src/test/ui/type-hints.test.ts @@ -1,14 +1,14 @@ -import { Button } from "#app/enums/buttons.js"; +import { Button } from "#app/enums/buttons"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; -import FightUiHandler from "#app/ui/fight-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import { CommandPhase } from "#app/phases/command-phase"; +import FightUiHandler from "#app/ui/fight-ui-handler"; +import { Mode } from "#app/ui/ui"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import MockText from "../utils/mocks/mocksContainer/mockText"; import { SPLASH_ONLY } from "../utils/testUtils"; -import { CommandPhase } from "#app/phases/command-phase.js"; describe("UI - Type Hints", () => { let phaserGame: Phaser.Game; diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index d60cbd62836..cb3c547744b 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -1,47 +1,47 @@ -import GameWrapper from "#test/utils/gameWrapper"; -import { Mode } from "#app/ui/ui"; -import { generateStarter, waitUntil } from "#test/utils/gameManagerUtils"; -import BattleScene from "#app/battle-scene.js"; -import PhaseInterceptor from "#test/utils/phaseInterceptor"; -import TextInterceptor from "#test/utils/TextInterceptor"; -import { GameModes, getGameMode } from "#app/game-mode"; -import fs from "fs"; -import { AES, enc } from "crypto-js"; import { updateUserInfo } from "#app/account"; -import InputsHandler from "#app/test/utils/inputsHandler"; -import ErrorInterceptor from "#app/test/utils/errorInterceptor"; +import { BattlerIndex } from "#app/battle"; +import BattleScene from "#app/battle-scene"; import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import { MockClock } from "#app/test/utils/mocks/mockClock"; -import PartyUiHandler from "#app/ui/party-ui-handler"; -import CommandUiHandler, { Command } from "#app/ui/command-ui-handler"; import Trainer from "#app/field/trainer"; +import { GameModes, getGameMode } from "#app/game-mode"; +import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type"; +import { CommandPhase } from "#app/phases/command-phase"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { LoginPhase } from "#app/phases/login-phase"; +import { MovePhase } from "#app/phases/move-phase"; +import { NewBattlePhase } from "#app/phases/new-battle-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { SelectTargetPhase } from "#app/phases/select-target-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; +import ErrorInterceptor from "#app/test/utils/errorInterceptor"; +import InputsHandler from "#app/test/utils/inputsHandler"; +import { MockClock } from "#app/test/utils/mocks/mockClock"; +import CommandUiHandler from "#app/ui/command-ui-handler"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import PartyUiHandler from "#app/ui/party-ui-handler"; +import TargetSelectUiHandler from "#app/ui/target-select-ui-handler"; +import { Mode } from "#app/ui/ui"; +import { Button } from "#enums/buttons"; import { ExpNotification } from "#enums/exp-notification"; import { GameDataType } from "#enums/game-data-type"; import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; -import { Button } from "#enums/buttons"; -import { BattlerIndex } from "#app/battle.js"; -import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.js"; -import { OverridesHelper } from "./helpers/overridesHelper"; -import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type.js"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler.js"; -import { MoveHelper } from "./helpers/moveHelper"; +import { generateStarter, waitUntil } from "#test/utils/gameManagerUtils"; +import GameWrapper from "#test/utils/gameWrapper"; +import PhaseInterceptor from "#test/utils/phaseInterceptor"; +import TextInterceptor from "#test/utils/TextInterceptor"; +import { AES, enc } from "crypto-js"; +import fs from "fs"; import { vi } from "vitest"; import { ClassicModeHelper } from "./helpers/classicModeHelper"; import { DailyModeHelper } from "./helpers/dailyModeHelper"; +import { MoveHelper } from "./helpers/moveHelper"; +import { OverridesHelper } from "./helpers/overridesHelper"; import { SettingsHelper } from "./helpers/settingsHelper"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { EncounterPhase } from "#app/phases/encounter-phase.js"; -import { FaintPhase } from "#app/phases/faint-phase.js"; -import { LoginPhase } from "#app/phases/login-phase.js"; -import { MovePhase } from "#app/phases/move-phase.js"; -import { NewBattlePhase } from "#app/phases/new-battle-phase.js"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase.js"; -import { SelectTargetPhase } from "#app/phases/select-target-phase.js"; -import { TitlePhase } from "#app/phases/title-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; -import { TurnStartPhase } from "#app/phases/turn-start-phase.js"; /** * Class to manage the game state and transitions between phases. @@ -192,38 +192,23 @@ export default class GameManager { } /** - * Emulate a player attack - * @param movePosition the index of the move in the pokemon's moveset array + * Emulate a player's target selection after a move is chosen, usually called automatically by {@linkcode MoveHelper.select}. + * Will trigger during the next {@linkcode SelectTargetPhase} + * @param {BattlerIndex} targetIndex The index of the attack target, or `undefined` for multi-target attacks + * @param movePosition The index of the move in the pokemon's moveset array */ - doAttack(movePosition: integer) { - this.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - this.scene.ui.setMode(Mode.FIGHT, (this.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); - }); - this.onNextPrompt("CommandPhase", Mode.FIGHT, () => { - (this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); - }); - - // Confirm target selection if move is multi-target + selectTarget(movePosition: integer, targetIndex?: BattlerIndex) { this.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { const handler = this.scene.ui.getHandler() as TargetSelectUiHandler; const move = (this.scene.getCurrentPhase() as SelectTargetPhase).getPokemon().getMoveset()[movePosition]!.getMove(); // TODO: is the bang correct? - if (move.isMultiTarget()) { - handler.processInput(Button.ACTION); + if (!move.isMultiTarget()) { + handler.setCursor(targetIndex !== undefined ? targetIndex : BattlerIndex.ENEMY); + } + if (move.isMultiTarget() && targetIndex !== undefined) { + throw new Error(`targetIndex was passed to selectMove() but move ("${move.name}") is not targetted`); } - }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(MovePhase) || this.isCurrentPhase(TurnEndPhase)); - } - - /** - * Emulate a player's target selection after an attack is chosen, - * usually called after {@linkcode doAttack} in a double battle. - * @param {BattlerIndex} targetIndex the index of the attack target - */ - doSelectTarget(targetIndex: BattlerIndex) { - this.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { - const handler = this.scene.ui.getHandler() as TargetSelectUiHandler; - handler.setCursor(targetIndex); handler.processInput(Button.ACTION); - }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(TurnStartPhase)); + }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(MovePhase) || this.isCurrentPhase(TurnStartPhase) || this.isCurrentPhase(TurnEndPhase)); } /** Faint all opponents currently on the field */ @@ -321,7 +306,7 @@ export default class GameManager { */ async importData(path): Promise<[boolean, integer]> { const saveKey = "x0i2O7WRiANTqPmZ"; - const dataRaw = fs.readFileSync(path, {encoding: "utf8", flag: "r"}); + const dataRaw = fs.readFileSync(path, { encoding: "utf8", flag: "r" }); let dataStr = AES.decrypt(dataRaw, saveKey).toString(enc.Utf8); dataStr = this.scene.gameData.convertSystemDataStr(dataStr); const systemData = this.scene.gameData.parseSystemData(dataStr); @@ -335,7 +320,7 @@ export default class GameManager { async killPokemon(pokemon: PlayerPokemon | EnemyPokemon) { (this.scene.time as MockClock).overrideDelay = 0.01; - return new Promise(async(resolve, reject) => { + return new Promise(async (resolve, reject) => { pokemon.hp = 0; this.scene.pushPhase(new FaintPhase(this.scene, pokemon.getBattlerIndex(), true)); await this.phaseInterceptor.to(FaintPhase).catch((e) => reject(e)); diff --git a/src/test/utils/gameManagerUtils.ts b/src/test/utils/gameManagerUtils.ts index dfba55fc75c..20a3fd179fd 100644 --- a/src/test/utils/gameManagerUtils.ts +++ b/src/test/utils/gameManagerUtils.ts @@ -1,12 +1,12 @@ +import BattleScene from "#app/battle-scene"; import { getDailyRunStarters } from "#app/data/daily-run"; import { Gender } from "#app/data/gender"; -import { Species } from "#enums/species"; -import { Starter } from "#app/ui/starter-select-ui-handler"; -import { GameModes, getGameMode } from "#app/game-mode"; import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; -import { PlayerPokemon } from "#app/field/pokemon"; import { Moves } from "#app/enums/moves"; -import BattleScene from "#app/battle-scene"; +import { PlayerPokemon } from "#app/field/pokemon"; +import { GameModes, getGameMode } from "#app/game-mode"; +import { Starter } from "#app/ui/starter-select-ui-handler"; +import { Species } from "#enums/species"; /** Function to convert Blob to string */ export function blobToString(blob) { diff --git a/src/test/utils/gameWrapper.ts b/src/test/utils/gameWrapper.ts index 49044c260fa..f3098fa9b71 100644 --- a/src/test/utils/gameWrapper.ts +++ b/src/test/utils/gameWrapper.ts @@ -1,31 +1,28 @@ /* eslint-disable */ // @ts-nocheck -import * as main from "#app/main"; +import BattleScene, * as battleScene from "#app/battle-scene"; +import { MoveAnim } from "#app/data/battle-anims"; +import Pokemon from "#app/field/pokemon"; +import * as Utils from "#app/utils"; +import { blobToString } from "#test/utils/gameManagerUtils"; +import { MockClock } from "#test/utils/mocks/mockClock"; +import mockConsoleLog from "#test/utils/mocks/mockConsoleLog"; +import { MockFetch } from "#test/utils/mocks/mockFetch"; +import MockLoader from "#test/utils/mocks/mockLoader"; +import mockLocalStorage from "#test/utils/mocks/mockLocalStorage"; +import MockImage from "#test/utils/mocks/mocksContainer/mockImage"; +import MockTextureManager from "#test/utils/mocks/mockTextureManager"; import fs from "fs"; +import Phaser from "phaser"; +import InputText from "phaser3-rex-plugins/plugins/inputtext"; +import { vi } from "vitest"; +import { MockGameObjectCreator } from "./mocks/mockGameObjectCreator"; import InputManager = Phaser.Input.InputManager; import KeyboardManager = Phaser.Input.Keyboard.KeyboardManager; import KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin; import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin; import EventEmitter = Phaser.Events.EventEmitter; import UpdateList = Phaser.GameObjects.UpdateList; -import MockGraphics from "#test/utils/mocks/mocksContainer/mockGraphics"; -import MockTextureManager from "#test/utils/mocks/mockTextureManager"; -import Phaser from "phaser"; -import { blobToString } from "#test/utils/gameManagerUtils"; -import { vi } from "vitest"; -import mockLocalStorage from "#test/utils/mocks/mockLocalStorage"; -import mockConsoleLog from "#test/utils/mocks/mockConsoleLog"; -import MockLoader from "#test/utils/mocks/mockLoader"; -import { MockFetch } from "#test/utils/mocks/mockFetch"; -import * as Utils from "#app/utils"; -import InputText from "phaser3-rex-plugins/plugins/inputtext"; -import { MockClock } from "#test/utils/mocks/mockClock"; -import BattleScene from "#app/battle-scene.js"; -import { MoveAnim } from "#app/data/battle-anims"; -import Pokemon from "#app/field/pokemon"; -import * as battleScene from "#app/battle-scene"; -import MockImage from "#test/utils/mocks/mocksContainer/mockImage.js"; -import { MockGameObjectCreator } from "./mocks/mockGameObjectCreator"; Object.defineProperty(window, "localStorage", { value: mockLocalStorage(), diff --git a/src/test/utils/helpers/classicModeHelper.ts b/src/test/utils/helpers/classicModeHelper.ts index cf59dd81183..f41472303b4 100644 --- a/src/test/utils/helpers/classicModeHelper.ts +++ b/src/test/utils/helpers/classicModeHelper.ts @@ -1,9 +1,9 @@ -import { Species } from "#app/enums/species.js"; -import { GameModes, getGameMode } from "#app/game-mode.js"; -import overrides from "#app/overrides.js"; -import { EncounterPhase } from "#app/phases/encounter-phase.js"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase.js"; -import { Mode } from "#app/ui/ui.js"; +import { Species } from "#app/enums/species"; +import { GameModes, getGameMode } from "#app/game-mode"; +import overrides from "#app/overrides"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { Mode } from "#app/ui/ui"; import { generateStarter } from "../gameManagerUtils"; import { GameManagerHelper } from "./gameManagerHelper"; diff --git a/src/test/utils/helpers/dailyModeHelper.ts b/src/test/utils/helpers/dailyModeHelper.ts index a143e212fcb..8f60981f4d8 100644 --- a/src/test/utils/helpers/dailyModeHelper.ts +++ b/src/test/utils/helpers/dailyModeHelper.ts @@ -1,9 +1,9 @@ -import { Button } from "#app/enums/buttons.js"; -import overrides from "#app/overrides.js"; -import { EncounterPhase } from "#app/phases/encounter-phase.js"; -import { TitlePhase } from "#app/phases/title-phase.js"; -import SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler.js"; -import { Mode } from "#app/ui/ui.js"; +import { Button } from "#app/enums/buttons"; +import overrides from "#app/overrides"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler"; +import { Mode } from "#app/ui/ui"; import { GameManagerHelper } from "./gameManagerHelper"; /** diff --git a/src/test/utils/helpers/moveHelper.ts b/src/test/utils/helpers/moveHelper.ts index 3179e63a6d0..a53fa521785 100644 --- a/src/test/utils/helpers/moveHelper.ts +++ b/src/test/utils/helpers/moveHelper.ts @@ -1,6 +1,12 @@ +import { BattlerIndex } from "#app/battle"; +import { Moves } from "#app/enums/moves"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { Command } from "#app/ui/command-ui-handler"; +import { Mode } from "#app/ui/ui"; import { vi } from "vitest"; +import { getMovePosition } from "../gameManagerUtils"; import { GameManagerHelper } from "./gameManagerHelper"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; /** * Helper to handle a Pokemon's move @@ -32,4 +38,25 @@ export class MoveHelper extends GameManagerHelper { hitCheck.mockReturnValue(false); } } + + /** + * Select the move to be used by the given Pokemon(-index). Triggers during the next {@linkcode CommandPhase} + * @param move the move to use + * @param pkmIndex the pokemon index. Relevant for double-battles only (defaults to 0) + * @param targetIndex The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required + */ + select(move: Moves, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null) { + const movePosition = getMovePosition(this.game.scene, pkmIndex, move); + + this.game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + this.game.scene.ui.setMode(Mode.FIGHT, (this.game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); + }); + this.game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { + (this.game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); + }); + + if (targetIndex !== null) { + this.game.selectTarget(movePosition, targetIndex); + } + } } diff --git a/src/test/utils/inputsHandler.ts b/src/test/utils/inputsHandler.ts index 148329ada32..30dd101f43d 100644 --- a/src/test/utils/inputsHandler.ts +++ b/src/test/utils/inputsHandler.ts @@ -1,11 +1,11 @@ import BattleScene from "#app/battle-scene"; -import Phaser from "phaser"; -import { InputsController } from "#app/inputs-controller"; import pad_xbox360 from "#app/configs/inputs/pad_xbox360"; -import { holdOn } from "#test/utils/gameManagerUtils"; +import { InputsController } from "#app/inputs-controller"; import TouchControl from "#app/touch-controls"; -import { JSDOM } from "jsdom"; +import { holdOn } from "#test/utils/gameManagerUtils"; import fs from "fs"; +import { JSDOM } from "jsdom"; +import Phaser from "phaser"; interface LogEntry { type: string; diff --git a/src/test/utils/misc.test.ts b/src/test/utils/misc.test.ts index c1947dbe8a2..a49b2894ca2 100644 --- a/src/test/utils/misc.test.ts +++ b/src/test/utils/misc.test.ts @@ -1,8 +1,8 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/utils/gameManager"; import { apiFetch } from "#app/utils"; +import GameManager from "#test/utils/gameManager"; import { waitUntil } from "#test/utils/gameManagerUtils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Test misc", () => { let phaserGame: Phaser.Game; diff --git a/src/test/utils/mocks/mockTextureManager.ts b/src/test/utils/mocks/mockTextureManager.ts index 330409e9776..16d94da28ad 100644 --- a/src/test/utils/mocks/mockTextureManager.ts +++ b/src/test/utils/mocks/mockTextureManager.ts @@ -1,12 +1,12 @@ import MockContainer from "#test/utils/mocks/mocksContainer/mockContainer"; -import MockSprite from "#test/utils/mocks/mocksContainer/mockSprite"; -import MockRectangle from "#test/utils/mocks/mocksContainer/mockRectangle"; -import MockNineslice from "#test/utils/mocks/mocksContainer/mockNineslice"; import MockImage from "#test/utils/mocks/mocksContainer/mockImage"; -import MockText from "#test/utils/mocks/mocksContainer/mockText"; +import MockNineslice from "#test/utils/mocks/mocksContainer/mockNineslice"; import MockPolygon from "#test/utils/mocks/mocksContainer/mockPolygon"; -import { MockGameObject } from "./mockGameObject"; +import MockRectangle from "#test/utils/mocks/mocksContainer/mockRectangle"; +import MockSprite from "#test/utils/mocks/mocksContainer/mockSprite"; +import MockText from "#test/utils/mocks/mocksContainer/mockText"; import MockTexture from "#test/utils/mocks/mocksContainer/mockTexture"; +import { MockGameObject } from "./mockGameObject"; /** * Stub class for Phaser.Textures.TextureManager diff --git a/src/test/utils/mocks/mocksContainer/mockSprite.ts b/src/test/utils/mocks/mocksContainer/mockSprite.ts index 9c566fc4bcb..35cd2d5faab 100644 --- a/src/test/utils/mocks/mocksContainer/mockSprite.ts +++ b/src/test/utils/mocks/mocksContainer/mockSprite.ts @@ -1,7 +1,7 @@ +import Phaser from "phaser"; import { MockGameObject } from "../mockGameObject"; import Sprite = Phaser.GameObjects.Sprite; import Frame = Phaser.Textures.Frame; -import Phaser from "phaser"; export default class MockSprite implements MockGameObject { diff --git a/src/test/utils/mocks/mocksContainer/mockTexture.ts b/src/test/utils/mocks/mocksContainer/mockTexture.ts index 03bedb4751b..cb31480cc60 100644 --- a/src/test/utils/mocks/mocksContainer/mockTexture.ts +++ b/src/test/utils/mocks/mocksContainer/mockTexture.ts @@ -1,5 +1,5 @@ -import { MockGameObject } from "../mockGameObject"; import MockTextureManager from "#test/utils/mocks/mockTextureManager"; +import { MockGameObject } from "../mockGameObject"; /** diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index 2304d726757..ca3d55137fa 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -1,42 +1,42 @@ -import UI, { Mode } from "#app/ui/ui"; import { Phase } from "#app/phase"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { DamagePhase } from "#app/phases/damage-phase"; +import { EggLapsePhase } from "#app/phases/egg-lapse-phase"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { LoginPhase } from "#app/phases/login-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { MovePhase } from "#app/phases/move-phase"; +import { NewBattlePhase } from "#app/phases/new-battle-phase"; +import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; +import { PartyHealPhase } from "#app/phases/party-heal-phase"; +import { PostSummonPhase } from "#app/phases/post-summon-phase"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { SelectGenderPhase } from "#app/phases/select-gender-phase"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { SelectTargetPhase } from "#app/phases/select-target-phase"; +import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase"; +import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; +import { StatChangePhase } from "#app/phases/stat-change-phase"; +import { SummonPhase } from "#app/phases/summon-phase"; +import { SwitchPhase } from "#app/phases/switch-phase"; +import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; +import { UnavailablePhase } from "#app/phases/unavailable-phase"; +import { VictoryPhase } from "#app/phases/victory-phase"; import ErrorInterceptor from "#app/test/utils/errorInterceptor"; -import { BattleEndPhase } from "#app/phases/battle-end-phase.js"; -import { BerryPhase } from "#app/phases/berry-phase.js"; -import { CheckSwitchPhase } from "#app/phases/check-switch-phase.js"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { DamagePhase } from "#app/phases/damage-phase.js"; -import { EggLapsePhase } from "#app/phases/egg-lapse-phase.js"; -import { EncounterPhase } from "#app/phases/encounter-phase.js"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js"; -import { FaintPhase } from "#app/phases/faint-phase.js"; -import { LoginPhase } from "#app/phases/login-phase.js"; -import { MessagePhase } from "#app/phases/message-phase.js"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase.js"; -import { MoveEndPhase } from "#app/phases/move-end-phase.js"; -import { MovePhase } from "#app/phases/move-phase.js"; -import { NewBattlePhase } from "#app/phases/new-battle-phase.js"; -import { NextEncounterPhase } from "#app/phases/next-encounter-phase.js"; -import { PostSummonPhase } from "#app/phases/post-summon-phase.js"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase.js"; -import { SelectGenderPhase } from "#app/phases/select-gender-phase.js"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase.js"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase.js"; -import { SelectTargetPhase } from "#app/phases/select-target-phase.js"; -import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase.js"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase.js"; -import { StatChangePhase } from "#app/phases/stat-change-phase.js"; -import { SummonPhase } from "#app/phases/summon-phase.js"; -import { SwitchPhase } from "#app/phases/switch-phase.js"; -import { SwitchSummonPhase } from "#app/phases/switch-summon-phase.js"; -import { TitlePhase } from "#app/phases/title-phase.js"; -import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; -import { TurnInitPhase } from "#app/phases/turn-init-phase.js"; -import { TurnStartPhase } from "#app/phases/turn-start-phase.js"; -import { UnavailablePhase } from "#app/phases/unavailable-phase.js"; -import { VictoryPhase } from "#app/phases/victory-phase.js"; -import { PartyHealPhase } from "#app/phases/party-heal-phase.js"; +import UI, { Mode } from "#app/ui/ui"; export default class PhaseInterceptor { public scene; diff --git a/src/test/vitest.setup.ts b/src/test/vitest.setup.ts index b2861b7071c..eaa987c1a66 100644 --- a/src/test/vitest.setup.ts +++ b/src/test/vitest.setup.ts @@ -9,8 +9,8 @@ import { initMoves } from "#app/data/move"; import { initPokemonPrevolutions } from "#app/data/pokemon-evolutions"; import { initPokemonForms } from "#app/data/pokemon-forms"; import { initSpecies } from "#app/data/pokemon-species"; -import { initAchievements } from "#app/system/achv.js"; -import { initVouchers } from "#app/system/voucher.js"; +import { initAchievements } from "#app/system/achv"; +import { initVouchers } from "#app/system/voucher"; import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; import { beforeAll, vi } from "vitest"; From 0cd52b86d27d8eca77bf1a710b9a88a793f5f115 Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Thu, 22 Aug 2024 09:20:14 -0700 Subject: [PATCH 28/30] [Refactor] Move Daily Pokerus Start Generation to its own function in data/pokemon-species (#3501) * Moving daily Pokerus generation to game-data * Moved pokerus starter generation to pokemon-species * Added JsDocs * Update src/data/pokemon-species.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * boo typedocs boo --------- Co-authored-by: Frutescens Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/pokemon-species.ts | 22 ++++++++++++++++++ src/ui/starter-select-ui-handler.ts | 36 +++-------------------------- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index aa2c29a9725..da2892f8128 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -3317,6 +3317,28 @@ export function getStarterValueFriendshipCap(value: integer): integer { } } +/** +* Method to get the daily list of starters with Pokerus. +* @param scene {@linkcode BattleScene} used as part of RNG +* @returns A list of starters with Pokerus +*/ +export function getPokerusStarters(scene: BattleScene): PokemonSpecies[] { + const pokerusStarters: PokemonSpecies[] = []; + const date = new Date(); + const starterCount = 3; //for easy future adjustment! + date.setUTCHours(0, 0, 0, 0); + scene.executeWithSeedOffset(() => { + while (pokerusStarters.length < starterCount) { + const randomSpeciesId = parseInt(Utils.randSeedItem(Object.keys(speciesStarters)), 10); + const species = getPokemonSpecies(randomSpeciesId); + if (!pokerusStarters.includes(species)) { + pokerusStarters.push(species); + } + } + }, 0, date.getTime().toString()); + return pokerusStarters; +} + export const starterPassiveAbilities = { [Species.BULBASAUR]: Abilities.GRASSY_SURGE, [Species.CHARMANDER]: Abilities.BEAST_BOOST, diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 67e870838a2..bc809d8c686 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -13,7 +13,7 @@ import { allMoves } from "../data/move"; import { Nature, getNatureName } from "../data/nature"; import { pokemonFormChanges } from "../data/pokemon-forms"; import { LevelMoves, pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "../data/pokemon-level-moves"; -import PokemonSpecies, { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species"; +import PokemonSpecies, { allSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities, getPokerusStarters } from "../data/pokemon-species"; import { Type } from "../data/type"; import { GameModes } from "../game-mode"; import { AbilityAttr, DexAttr, DexAttrProps, DexEntry, StarterMoveset, StarterAttributes, StarterPreferences, StarterPrefs } from "../system/game-data"; @@ -872,38 +872,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.message.setOrigin(0, 0); this.starterSelectMessageBoxContainer.add(this.message); - const date = new Date(); - date.setUTCHours(0, 0, 0, 0); - - this.scene.executeWithSeedOffset(() => { - for (let c = 0; c < 3; c++) { - let randomSpeciesId: Species; - let species: PokemonSpecies | undefined; - - const generateSpecies = () => { - randomSpeciesId = Utils.randSeedItem(starterSpecies); - species = getPokemonSpecies(randomSpeciesId); - }; - - let dupe = false; - - do { - dupe = false; - - generateSpecies(); - - for (let ps = 0; ps < c; ps++) { - if (this.pokerusSpecies[ps] === species) { - dupe = true; - break; - } - } - } while (dupe); - - this.pokerusSpecies.push(species!); // TODO: is the bang correct? - } - }, 0, date.getTime().toString()); - this.statsContainer = new StatsContainer(this.scene, 6, 16); this.scene.add.existing(this.statsContainer); @@ -934,6 +902,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.starterPreferences = StarterPrefs.load(); } this.moveInfoOverlay.clear(); // clear this when removing a menu; the cancel button doesn't seem to trigger this automatically on controllers + this.pokerusSpecies = getPokerusStarters(this.scene); + if (args.length >= 1 && args[0] instanceof Function) { super.show(args); this.starterSelectCallback = args[0] as StarterSelectCallback; From a7acf752db6ca3c95226fa7caeefa736e513ccde Mon Sep 17 00:00:00 2001 From: Enoch Date: Fri, 23 Aug 2024 01:23:47 +0900 Subject: [PATCH 29/30] [Localization] Add localization hard-coded message in ability (AIR_LOCK, CLOUD_NINE) (#3641) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * localize ability message "The effects of the weather disappeared." * Update src/locales/de/ability-trigger.ts Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com> * Update src/locales/it/ability-trigger.ts Co-authored-by: Niccolò <123510358+NicusPulcis@users.noreply.github.com> * Update src/locales/fr/ability-trigger.ts Co-authored-by: Lugiad' * Apply suggestions from code review Co-authored-by: Lugiad' Co-authored-by: José Ricardo Fleury Oliveira * Update src/locales/es/ability-trigger.ts Co-authored-by: Asdar --------- Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com> Co-authored-by: Niccolò <123510358+NicusPulcis@users.noreply.github.com> Co-authored-by: Lugiad' Co-authored-by: José Ricardo Fleury Oliveira Co-authored-by: Asdar --- src/data/ability.ts | 4 ++-- src/locales/ca_ES/ability-trigger.ts | 1 + src/locales/de/ability-trigger.ts | 1 + src/locales/en/ability-trigger.ts | 1 + src/locales/es/ability-trigger.ts | 1 + src/locales/fr/ability-trigger.ts | 1 + src/locales/it/ability-trigger.ts | 1 + src/locales/ja/ability-trigger.ts | 1 + src/locales/ko/ability-trigger.ts | 1 + src/locales/pt_BR/ability-trigger.ts | 1 + src/locales/zh_CN/ability-trigger.ts | 1 + src/locales/zh_TW/ability-trigger.ts | 1 + 12 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 284a9cb4e91..8b220b14cf1 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4591,7 +4591,7 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.CLOUD_NINE, 3) .attr(SuppressWeatherEffectAbAttr, true) - .attr(PostSummonUnnamedMessageAbAttr, "The effects of the weather disappeared."), + .attr(PostSummonUnnamedMessageAbAttr, i18next.t("abilityTriggers:weatherEffectDisappeared")), new Ability(Abilities.COMPOUND_EYES, 3) .attr(BattleStatMultiplierAbAttr, BattleStat.ACC, 1.3), new Ability(Abilities.INSOMNIA, 3) @@ -4786,7 +4786,7 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.AIR_LOCK, 3) .attr(SuppressWeatherEffectAbAttr, true) - .attr(PostSummonUnnamedMessageAbAttr, "The effects of the weather disappeared."), + .attr(PostSummonUnnamedMessageAbAttr, i18next.t("abilityTriggers:weatherEffectDisappeared")), new Ability(Abilities.TANGLED_FEET, 4) .conditionalAttr(pokemon => !!pokemon.getTag(BattlerTagType.CONFUSED), BattleStatMultiplierAbAttr, BattleStat.EVA, 2) .ignorable(), diff --git a/src/locales/ca_ES/ability-trigger.ts b/src/locales/ca_ES/ability-trigger.ts index 2bdd17baa56..0b7fe8bd0bc 100644 --- a/src/locales/ca_ES/ability-trigger.ts +++ b/src/locales/ca_ES/ability-trigger.ts @@ -47,6 +47,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", + "weatherEffectDisappeared": "The effects of the weather disappeared.", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", diff --git a/src/locales/de/ability-trigger.ts b/src/locales/de/ability-trigger.ts index 72023a842b3..9e4ef9df6ed 100644 --- a/src/locales/de/ability-trigger.ts +++ b/src/locales/de/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{abilityName}} von {{pokemonNameWithAffix}} schadet seinem Angreifer!", "postFaintHpDamage": "{{abilityName}} von {{pokemonNameWithAffix}} schadet seinem Angreifer!", "postSummonPressure": "{{pokemonNameWithAffix}} setzt Gegner mit Erzwinger unter Druck!", + "weatherEffectDisappeared": "Jegliche wetterbedingten Effekte wurden aufgehoben!", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} gelingt es, gegnerische Fähigkeiten zu überbrücken!", "postSummonAnticipation": "{{pokemonNameWithAffix}} erschaudert!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} strahlt eine lodernde Aura aus!", diff --git a/src/locales/en/ability-trigger.ts b/src/locales/en/ability-trigger.ts index 035fe8371be..2a0e0df255a 100644 --- a/src/locales/en/ability-trigger.ts +++ b/src/locales/en/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", + "weatherEffectDisappeared": "The effects of the weather disappeared.", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", diff --git a/src/locales/es/ability-trigger.ts b/src/locales/es/ability-trigger.ts index 60bc186e99d..99ebfe3bd4c 100644 --- a/src/locales/es/ability-trigger.ts +++ b/src/locales/es/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", + "weatherEffectDisappeared": "El tiempo atmosférico ya no ejerce ninguna influencia.", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", diff --git a/src/locales/fr/ability-trigger.ts b/src/locales/fr/ability-trigger.ts index cd077993b4e..92e02b82414 100644 --- a/src/locales/fr/ability-trigger.ts +++ b/src/locales/fr/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}} est blessé\npar son talent {{abilityName}} !", "postFaintHpDamage": "{{pokemonNameWithAffix}} est blessé\npar son talent {{abilityName}} !", "postSummonPressure": "{{pokemonNameWithAffix}}\naugmente la pression !", + "weatherEffectDisappeared": "Les effets de la météo se dissipent !", "postSummonMoldBreaker": "{{pokemonNameWithAffix}}\nbrise le moule !", "postSummonAnticipation": "{{pokemonNameWithAffix}}\nest tout tremblant !", "postSummonTurboblaze": "{{pokemonNameWithAffix}} dégage\nune aura de flammes incandescentes !", diff --git a/src/locales/it/ability-trigger.ts b/src/locales/it/ability-trigger.ts index c834fa28fbe..6ac5e76ee3e 100644 --- a/src/locales/it/ability-trigger.ts +++ b/src/locales/it/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{abilityName}} di {{pokemonNameWithAffix}}\nferisce il Pokémon che lo ha attaccato!", "postFaintHpDamage": "{{abilityName}} di {{pokemonNameWithAffix}}\nferisce il Pokémon che lo ha attaccato!", "postSummonPressure": "{{pokemonNameWithAffix}} fa pressione!", + "weatherEffectDisappeared": "Le condizioni atmosferiche non hanno alcun effetto.", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} ha l’abilità Rompiforma!", "postSummonAnticipation": "{{pokemonNameWithAffix}} rabbrividisce!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} emana un’aura infuocata!", diff --git a/src/locales/ja/ability-trigger.ts b/src/locales/ja/ability-trigger.ts index 7c7d081f645..cf4c89ff5a4 100644 --- a/src/locales/ja/ability-trigger.ts +++ b/src/locales/ja/ability-trigger.ts @@ -47,6 +47,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}は {{abilityName}}で\n相手に ダメージを 与えた!", "postFaintHpDamage": "{{pokemonNameWithAffix}}は {{abilityName}}で\n相手に ダメージを 与えた!", "postSummonPressure": "{{pokemonNameWithAffix}}は\nプレッシャーを 放っている!", + "weatherEffectDisappeared": "天候の影響が なくなった!", "postSummonMoldBreaker": "{{pokemonNameWithAffix}}は\nかたやぶりだ!", "postSummonAnticipation": "{{pokemonNameWithAffix}}は\nみぶるいした!", "postSummonTurboblaze": "{{pokemonNameWithAffix}}は\n燃え盛(もえさか)る オーラを 放っている!", diff --git a/src/locales/ko/ability-trigger.ts b/src/locales/ko/ability-trigger.ts index 974e6970569..9e330c176e7 100644 --- a/src/locales/ko/ability-trigger.ts +++ b/src/locales/ko/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}[[는]] {{abilityName}}[[로]]\n상대에게 데미지를 입혔다!", "postFaintHpDamage": "{{pokemonNameWithAffix}}[[는]] {{abilityName}}[[로]]\n상대에게 데미지를 입혔다!", "postSummonPressure": "{{pokemonNameWithAffix}}[[는]]\n프레셔를 발산하고 있다!", + "weatherEffectDisappeared": "날씨의 영향이 없어졌다!", "postSummonMoldBreaker": "{{pokemonNameWithAffix}}의\n틀깨기!", "postSummonAnticipation": "{{pokemonNameWithAffix}}[[는]]\n몸을 떨었다!", "postSummonTurboblaze": "{{pokemonNameWithAffix}}[[는]]\n활활 타오르는 오라를 발산하고 있다!", diff --git a/src/locales/pt_BR/ability-trigger.ts b/src/locales/pt_BR/ability-trigger.ts index da91fa3213f..9cfa42edce3 100644 --- a/src/locales/pt_BR/ability-trigger.ts +++ b/src/locales/pt_BR/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{abilityName}} de {{pokemonNameWithAffix}}\nferiu seu adversário!", "postFaintHpDamage": "{{abilityName}} de {{pokemonNameWithAffix}}\nferiu seu adversário!", "postSummonPressure": "{{pokemonNameWithAffix}} está exercendo sua pressão!", + "weatherEffectDisappeared": "Os efeitos do clima desapareceram.", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} quebra o molde!", "postSummonAnticipation": "{{pokemonNameWithAffix}} se arrepiou!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} está irradiando uma aura ardente!", diff --git a/src/locales/zh_CN/ability-trigger.ts b/src/locales/zh_CN/ability-trigger.ts index 0d69a78f0f7..337c0655c87 100644 --- a/src/locales/zh_CN/ability-trigger.ts +++ b/src/locales/zh_CN/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}的{{abilityName}}\n使对方受到了伤害!", "postFaintHpDamage": "{{pokemonNameWithAffix}}的{{abilityName}}\n使对方受到了伤害!", "postSummonPressure": "从{{pokemonNameWithAffix}}的身上\n感到了一种压迫感!", + "weatherEffectDisappeared": "天气的影响消失了!", "postSummonMoldBreaker": "{{pokemonNameWithAffix}}\n打破了常规!", "postSummonAnticipation": "{{pokemonNameWithAffix}}\n发抖了!", "postSummonTurboblaze": "{{pokemonNameWithAffix}}\n正在释放炽焰气场!", diff --git a/src/locales/zh_TW/ability-trigger.ts b/src/locales/zh_TW/ability-trigger.ts index c0d348633bc..14aa6de1af7 100644 --- a/src/locales/zh_TW/ability-trigger.ts +++ b/src/locales/zh_TW/ability-trigger.ts @@ -46,6 +46,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "postFaintContactDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postFaintHpDamage": "{{pokemonNameWithAffix}}'s {{abilityName}}\nhurt its attacker!", "postSummonPressure": "{{pokemonNameWithAffix}} is exerting its Pressure!", + "weatherEffectDisappeared": "天氣的影響消失了!", "postSummonMoldBreaker": "{{pokemonNameWithAffix}} breaks the mold!", "postSummonAnticipation": "{{pokemonNameWithAffix}} shuddered!", "postSummonTurboblaze": "{{pokemonNameWithAffix}} is radiating a blazing aura!", From 03ee764e230626b1a175795c938b8e38a2d1ddc8 Mon Sep 17 00:00:00 2001 From: "Adrian T." <68144167+torranx@users.noreply.github.com> Date: Fri, 23 Aug 2024 00:26:10 +0800 Subject: [PATCH 30/30] [QoL] Settings Option: Shop Cursor Target (#3666) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [QoL] Post Reroll UI Targeting * removed hasRolled from battlescene and handled in UI * Added getter for reroll count and ternary * Explicit catch instead of falsy * Fixed Settings errors with proper labeling * changed setting label to translate as well * translations and fixes * move option further down * change integer to number * change integer to number * update implementation * remove unused method * change name set to update * Update src/ui/modifier-select-ui-handler.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> * Update src/system/settings/settings.ts Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> * Update src/locales/fr/settings.ts Co-authored-by: Lugiad' * Update src/locales/pt_BR/settings.ts Co-authored-by: José Ricardo Fleury Oliveira * Update src/locales/ko/settings.ts Co-authored-by: sodam <66295123+sodaMelon@users.noreply.github.com> * Update src/locales/zh_CN/settings.ts Co-authored-by: Yonmaru40 <47717431+40chyan@users.noreply.github.com> * Update src/locales/it/settings.ts Co-authored-by: Niccolò <123510358+NicusPulcis@users.noreply.github.com> * Revert "Update src/locales/ko/settings.ts" This reverts commit 08c0b89f4caf7c33f49722200d7aeb20b79b0ba5. * Update src/locales/ko/settings.ts Co-authored-by: sodam <66295123+sodaMelon@users.noreply.github.com> * Revert "Revert "Update src/locales/ko/settings.ts"" This reverts commit a57d8777f2e0538ee34764c91d6841bd3ef0dc60. --------- Co-authored-by: Matthew Kroeger Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> Co-authored-by: Lugiad' Co-authored-by: José Ricardo Fleury Oliveira Co-authored-by: sodam <66295123+sodaMelon@users.noreply.github.com> Co-authored-by: Yonmaru40 <47717431+40chyan@users.noreply.github.com> Co-authored-by: Niccolò <123510358+NicusPulcis@users.noreply.github.com> --- src/battle-scene.ts | 2 ++ src/enums/shop-cursor-target.ts | 13 +++++++++++++ src/locales/ca_ES/settings.ts | 7 ++++++- src/locales/de/settings.ts | 5 +++++ src/locales/en/settings.ts | 7 ++++++- src/locales/es/settings.ts | 7 ++++++- src/locales/fr/settings.ts | 7 ++++++- src/locales/it/settings.ts | 7 ++++++- src/locales/ja/settings.ts | 5 +++++ src/locales/ko/settings.ts | 7 ++++++- src/locales/pt_BR/settings.ts | 7 ++++++- src/locales/zh_CN/settings.ts | 7 ++++++- src/locales/zh_TW/settings.ts | 7 ++++++- src/phases/select-modifier-phase.ts | 2 +- src/system/settings/settings.ts | 28 ++++++++++++++++++++++++++++ src/ui/modifier-select-ui-handler.ts | 20 ++++++++++++++++---- 16 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 src/enums/shop-cursor-target.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index af63f0b0a39..eaf550e7d10 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -83,6 +83,7 @@ import { SwitchPhase } from "./phases/switch-phase"; import { TitlePhase } from "./phases/title-phase"; import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase"; import { TurnInitPhase } from "./phases/turn-init-phase"; +import { ShopCursorTarget } from "./enums/shop-cursor-target"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -127,6 +128,7 @@ export default class BattleScene extends SceneBase { public gameSpeed: integer = 1; public damageNumbersMode: integer = 0; public reroll: boolean = false; + public shopCursorTarget: number = ShopCursorTarget.CHECK_TEAM; public showMovesetFlyout: boolean = true; public showArenaFlyout: boolean = true; public showTimeOfDayWidget: boolean = true; diff --git a/src/enums/shop-cursor-target.ts b/src/enums/shop-cursor-target.ts new file mode 100644 index 00000000000..d2f72fed0d6 --- /dev/null +++ b/src/enums/shop-cursor-target.ts @@ -0,0 +1,13 @@ +/** + * Determines the cursor target when entering the shop phase. + */ +export enum ShopCursorTarget { + /** Cursor points to Reroll */ + REROLL, + /** Cursor points to Items */ + ITEMS, + /** Cursor points to Shop */ + SHOP, + /** Cursor points to Check Team */ + CHECK_TEAM +} diff --git a/src/locales/ca_ES/settings.ts b/src/locales/ca_ES/settings.ts index 491bfa4a481..9c0b3f36365 100644 --- a/src/locales/ca_ES/settings.ts +++ b/src/locales/ca_ES/settings.ts @@ -96,5 +96,10 @@ export const settings: SimpleTranslationEntries = { "controller": "Controller", "gamepadSupport": "Gamepad Support", "showBgmBar": "Show Music Names", - "shopOverlayOpacity": "Shop Overlay Opacity" + "shopOverlayOpacity": "Shop Overlay Opacity", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/locales/de/settings.ts b/src/locales/de/settings.ts index 7d4523c8cbd..3942e470e3f 100644 --- a/src/locales/de/settings.ts +++ b/src/locales/de/settings.ts @@ -99,4 +99,9 @@ export const settings: SimpleTranslationEntries = { "showBgmBar": "Musiknamen anzeigen", "moveTouchControls": "Bewegung Touch Steuerung", "shopOverlayOpacity": "Shop Overlay Deckkraft", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/locales/en/settings.ts b/src/locales/en/settings.ts index c63f9de6049..ad2ea914dc9 100644 --- a/src/locales/en/settings.ts +++ b/src/locales/en/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "Gamepad Support", "showBgmBar": "Show Music Names", "moveTouchControls": "Move Touch Controls", - "shopOverlayOpacity": "Shop Overlay Opacity" + "shopOverlayOpacity": "Shop Overlay Opacity", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/locales/es/settings.ts b/src/locales/es/settings.ts index c7f723fe80c..fbc56d92fe5 100644 --- a/src/locales/es/settings.ts +++ b/src/locales/es/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "Gamepad Support", "showBgmBar": "Show Music Names", "moveTouchControls": "Move Touch Controls", - "shopOverlayOpacity": "Opacidad de la fase de compra" + "shopOverlayOpacity": "Opacidad de la fase de compra", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/locales/fr/settings.ts b/src/locales/fr/settings.ts index 95246ccb7d0..d5e4047bb12 100644 --- a/src/locales/fr/settings.ts +++ b/src/locales/fr/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "Gamepad Support", "showBgmBar": "Titre de la musique", "moveTouchControls": "Déplacer les contrôles tactiles", - "shopOverlayOpacity": "Opacité boutique" + "shopOverlayOpacity": "Opacité boutique", + "shopCursorTarget": "Choix après relance", + "items": "Obj. gratuits", + "reroll": "Relance", + "shop": "Boutique", + "checkTeam": "Équipe" } as const; diff --git a/src/locales/it/settings.ts b/src/locales/it/settings.ts index 0d05d01ba1c..278a02bef52 100644 --- a/src/locales/it/settings.ts +++ b/src/locales/it/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "Supporto Gamepad", "showBgmBar": "Mostra Nomi Musica", "moveTouchControls": "Move Touch Controls", - "shopOverlayOpacity": "Opacità Finestra Negozio" + "shopOverlayOpacity": "Opacità Finestra Negozio", + "shopCursorTarget": "Target Cursore Negozio", + "items": "Oggetti", + "reroll": "Rerolla", + "shop": "Negozio", + "checkTeam": "Squadra" } as const; diff --git a/src/locales/ja/settings.ts b/src/locales/ja/settings.ts index ef20d071d2d..3be237e26ae 100644 --- a/src/locales/ja/settings.ts +++ b/src/locales/ja/settings.ts @@ -98,4 +98,9 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "コントローラーサポート", "showBgmBar": "Show Music Names", "shopOverlayOpacity": "Shop Overlay Opacity", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/locales/ko/settings.ts b/src/locales/ko/settings.ts index aa4adfc4e41..38b0679d911 100644 --- a/src/locales/ko/settings.ts +++ b/src/locales/ko/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "게임패드 지원", "showBgmBar": "BGM 제목 보여주기", "moveTouchControls": "터치 컨트롤 이동", - "shopOverlayOpacity": "상점 오버레이 투명도" + "shopOverlayOpacity": "상점 오버레이 투명도", + "shopCursorTarget": "상점 커서 위치", + "items": "아이템", + "reroll": "갱신", + "shop": "상점", + "checkTeam": "파티 확인" } as const; diff --git a/src/locales/pt_BR/settings.ts b/src/locales/pt_BR/settings.ts index 14646b59742..e34fdfa0e5d 100644 --- a/src/locales/pt_BR/settings.ts +++ b/src/locales/pt_BR/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "Suporte para Controle", "showBgmBar": "Exibir Nomes das Músicas", "moveTouchControls": "Move Touch Controls", - "shopOverlayOpacity": "Opacidade da Loja" + "shopOverlayOpacity": "Opacidade da Loja", + "shopCursorTarget": "Alvo do Cursor da Loja", + "items": "Itens", + "reroll": "Atualizar", + "shop": "Loja", + "checkTeam": "Checar Time" } as const; diff --git a/src/locales/zh_CN/settings.ts b/src/locales/zh_CN/settings.ts index 422b1bd591d..d727ac2bef5 100644 --- a/src/locales/zh_CN/settings.ts +++ b/src/locales/zh_CN/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "手柄支持", "showBgmBar": "显示音乐名称", "moveTouchControls": "移动触摸控制", - "shopOverlayOpacity": "商店显示不透明度" + "shopOverlayOpacity": "商店显示不透明度", + "shopCursorTarget": "商店指针位置", + "items": "道具", + "reroll": "刷新", + "shop": "购买", + "checkTeam": "检查队伍" } as const; diff --git a/src/locales/zh_TW/settings.ts b/src/locales/zh_TW/settings.ts index 35499f45d15..7e6bf25bd7a 100644 --- a/src/locales/zh_TW/settings.ts +++ b/src/locales/zh_TW/settings.ts @@ -98,5 +98,10 @@ export const settings: SimpleTranslationEntries = { "gamepadSupport": "手柄支持", "showBgmBar": "Show Music Names", "moveTouchControls": "移動觸控控制", - "shopOverlayOpacity": "Shop Overlay Opacity" + "shopOverlayOpacity": "Shop Overlay Opacity", + "shopCursorTarget": "Shop Cursor Target", + "items": "Items", + "reroll": "Reroll", + "shop": "Shop", + "checkTeam": "Check Team" } as const; diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 67ae904fb58..57b842e7b38 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -205,7 +205,7 @@ export class SelectModifierPhase extends BattlePhase { return true; } - getRerollCost(typeOptions: ModifierTypeOption[], lockRarities: boolean): integer { + getRerollCost(typeOptions: ModifierTypeOption[], lockRarities: boolean): number { let baseValue = 0; if (Overrides.WAIVE_ROLL_FEE_OVERRIDE) { return baseValue; diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index 7263ae3a3de..4bd69788f04 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -9,6 +9,7 @@ import { EaseType } from "#enums/ease-type"; import { MoneyFormat } from "#enums/money-format"; import { PlayerGender } from "#enums/player-gender"; import { getIsInitialized, initI18n } from "#app/plugins/i18n.js"; +import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; function getTranslation(key: string): string { if (!getIsInitialized()) { @@ -102,6 +103,7 @@ export const SettingKeys = { Damage_Numbers: "DAMAGE_NUMBERS", Move_Animations: "MOVE_ANIMATIONS", Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS", + Reroll_Target: "REROLL_TARGET", Candy_Upgrade_Notification: "CANDY_UPGRADE_NOTIFICATION", Candy_Upgrade_Display: "CANDY_UPGRADE_DISPLAY", Move_Info: "MOVE_INFO", @@ -577,6 +579,30 @@ export const Setting: Array = [ activatable: true, isHidden: () => !hasTouchscreen() }, + { + key: SettingKeys.Reroll_Target, + label: i18next.t("settings:shopCursorTarget"), + options: [ + { + value:"Reroll", + label: i18next.t("settings:reroll") + }, + { + value:"Items", + label: i18next.t("settings:items") + }, + { + value:"Shop", + label: i18next.t("settings:shop") + }, + { + value:"Check Team", + label: i18next.t("settings:checkTeam") + } + ], + default: ShopCursorTarget.CHECK_TEAM, + type: SettingType.DISPLAY + }, { key: SettingKeys.Shop_Overlay_Opacity, label: i18next.t("settings:shopOverlayOpacity"), @@ -709,6 +735,8 @@ export function setSetting(scene: BattleScene, setting: string, value: integer): case SettingKeys.Show_Stats_on_Level_Up: scene.showLevelUpStats = Setting[index].options[value].value === "On"; break; + case SettingKeys.Reroll_Target: + scene.shopCursorTarget = value; case SettingKeys.EXP_Gains_Speed: scene.expGainsSpeed = value; break; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 016708027ca..bb1f970fe1c 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -12,6 +12,7 @@ import { allMoves } from "../data/move"; import * as Utils from "./../utils"; import Overrides from "#app/overrides"; import i18next from "i18next"; +import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; export const SHOP_OPTIONS_ROW_LIMIT = 6; @@ -249,11 +250,22 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { duration: 250 }); - this.setCursor(0); - this.setRowCursor(1); + const updateCursorTarget = () => { + if (this.scene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) { + this.setRowCursor(0); + this.setCursor(2); + } else { + this.setRowCursor(this.scene.shopCursorTarget); + this.setCursor(0); + } + }; - handleTutorial(this.scene, Tutorial.Select_Item).then(() => { - this.setCursor(0); + updateCursorTarget(); + + handleTutorial(this.scene, Tutorial.Select_Item).then((res) => { + if (res) { + updateCursorTarget(); + } this.awaitingActionInput = true; this.onActionInput = args[2]; });