Compare commits

...

7 Commits

Author SHA1 Message Date
ImperialSympathizer
b794662776
[Test] Fix final boss test (#3444)
Co-authored-by: ImperialSympathizer <imperialsympathizer@gmail.com>
2024-08-08 17:06:57 -04:00
ImperialSympathizer
ba9378d1d8
disables final boss passive (#3393)
Co-authored-by: ImperialSympathizer <imperialsympathizer@gmail.com>
2024-08-08 20:59:23 +01:00
Leo Kim
b54a255c15
[Enhancement] Add go filter shortcut (#3345)
* update goFilter shortcut

* fix filter instruction position

* remove unnecessary new lines

* update requested changes. add other language entries too

* remove unnecessary case code

* open dropdown when pressing goFilter shortcut

* add missing entry for `fr`

* Update src/locales/fr/starter-select-ui-handler.ts

Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr>

* Update src/locales/de/starter-select-ui-handler.ts

Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com>

* update instruction container position and text offset for more longer instruction label. decrease size of fr instruction font

* fixed de info font size

* Update src/locales/pt_BR/starter-select-ui-handler.ts

Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br>

* Update src/locales/ko/starter-select-ui-handler.ts

Co-authored-by: Enoch <enoch.jwsong@gmail.com>

* Update src/locales/zh_TW/starter-select-ui-handler.ts

Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com>

* Update src/locales/zh_CN/starter-select-ui-handler.ts

Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com>

* update font size, starterInfoXPos and starterInfoYOffset in zh. update starterInfoXPos in de

* Update src/locales/es/starter-select-ui-handler.ts

Co-authored-by: Asdar <asdargmng@gmail.com>

---------

Co-authored-by: Lugiad' <adrien.grivel@hotmail.fr>
Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com>
Co-authored-by: José Ricardo Fleury Oliveira <josefleury@discente.ufg.br>
Co-authored-by: Enoch <enoch.jwsong@gmail.com>
Co-authored-by: mercurius-00 <80205689+mercurius-00@users.noreply.github.com>
Co-authored-by: Asdar <asdargmng@gmail.com>
2024-08-08 20:34:20 +01:00
Chapybara-jp
cff5a670b1
[Localization(ja)] First draft translated achv.ts (#3408)
* First draft translated achv.ts

* Update achv.ts

Added Earth Monotype challenge translation 
じめんタイプチャレンジのギャグを加えました
Changed spacing size
空白を全角スペースに変えた

* Update src/locales/ja/achv.ts

Co-authored-by: Yonmaru40 <47717431+40chyan@users.noreply.github.com>

* Update achv.ts

* Update achv.ts

---------

Co-authored-by: Yonmaru40 <47717431+40chyan@users.noreply.github.com>
2024-08-08 15:30:42 -04:00
Chapybara-jp
6ad32f3c40
[Localisation(ja)] Translate challenges.ts (#3439)
Discordでこの質問を聞きましたが、ここでも聞いて置きます:
"Starters" をどう翻訳しましょうか?一番合ってる公式の用語は「最初のパートナー」だと思いますが、ちょっと長いです。
2024-08-08 15:28:26 -04:00
innerthunder
2e823b1914
[Documentation] Add comments to MoveEffectPhase (+ some style changes) (#3392)
* Document MoveEffectPhase

* Edits based on Temp's suggestions
2024-08-08 15:27:26 -04:00
innerthunder
7f5e873457
[Move] Fully Implement Gulp Missile (#3438)
* Fix Dive + Gulp Missile interaction

* Fix animation + remove tests for inaccurate behavior

* Fix strict-null issue in new test
2024-08-08 14:23:11 -04:00
20 changed files with 388 additions and 170 deletions

View File

@ -6,7 +6,7 @@ import { BattleStat, getBattleStatName } from "./battle-stat";
import { MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases"; import { MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases";
import { getPokemonNameWithAffix } from "../messages"; import { getPokemonNameWithAffix } from "../messages";
import { Weather, WeatherType } from "./weather"; import { Weather, WeatherType } from "./weather";
import { BattlerTag, GroundedTag, GulpMissileTag } from "./battler-tags"; import { BattlerTag, GroundedTag, GulpMissileTag, SemiInvulnerableTag } from "./battler-tags";
import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect"; import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect";
import { Gender } from "./gender"; import { Gender } from "./gender";
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit } from "./move"; import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit } from "./move";
@ -517,7 +517,7 @@ export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr {
*/ */
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise<boolean> { applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
const battlerTag = pokemon.getTag(GulpMissileTag); const battlerTag = pokemon.getTag(GulpMissileTag);
if (!battlerTag || move.category === MoveCategory.STATUS) { if (!battlerTag || move.category === MoveCategory.STATUS || pokemon.getTag(SemiInvulnerableTag)) {
return false; return false;
} }
@ -5138,9 +5138,7 @@ export function initAbilities() {
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(PostDefendGulpMissileAbAttr) .attr(PostDefendGulpMissileAbAttr),
// Does not transform when Surf/Dive misses/is protected
.partial(),
new Ability(Abilities.STALWART, 8) new Ability(Abilities.STALWART, 8)
.attr(BlockRedirectAbAttr), .attr(BlockRedirectAbAttr),
new Ability(Abilities.STEAM_ENGINE, 8) new Ability(Abilities.STEAM_ENGINE, 8)

View File

@ -4356,7 +4356,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
*/ */
export class GulpMissileTagAttr extends MoveEffectAttr { export class GulpMissileTagAttr extends MoveEffectAttr {
constructor() { constructor() {
super(true, MoveEffectTrigger.POST_APPLY); super(true);
} }
/** /**
@ -6954,7 +6954,7 @@ export function initMoves() {
.makesContact(false) .makesContact(false)
.partial(), .partial(),
new AttackMove(Moves.DIVE, Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 3) new AttackMove(Moves.DIVE, Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 3)
.attr(ChargeAttr, ChargeAnim.DIVE_CHARGING, i18next.t("moveTriggers:hidUnderwater", {pokemonName: "{USER}"}), BattlerTagType.UNDERWATER) .attr(ChargeAttr, ChargeAnim.DIVE_CHARGING, i18next.t("moveTriggers:hidUnderwater", {pokemonName: "{USER}"}), BattlerTagType.UNDERWATER, true)
.attr(GulpMissileTagAttr) .attr(GulpMissileTagAttr)
.ignoresVirtual(), .ignoresVirtual(),
new AttackMove(Moves.ARM_THRUST, Type.FIGHTING, MoveCategory.PHYSICAL, 15, 100, 20, -1, 0, 3) new AttackMove(Moves.ARM_THRUST, Type.FIGHTING, MoveCategory.PHYSICAL, 15, 100, 20, -1, 0, 3)

View File

@ -1081,6 +1081,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
(Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && !this.isPlayer())) { (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && !this.isPlayer())) {
return true; return true;
} }
// Final boss does not have passive
if (this.scene.currentBattle?.battleSpec === BattleSpec.FINAL_BOSS && this instanceof EnemyPokemon) {
return false;
}
return this.passive || this.isBoss(); return this.passive || this.isBoss();
} }

View File

@ -11,6 +11,7 @@ import { BattleSpec } from "#enums/battle-spec";
import { BattlePhase, MovePhase, PokemonHealPhase } from "./phases"; import { BattlePhase, MovePhase, PokemonHealPhase } from "./phases";
import { getTypeRgb } from "./data/type"; import { getTypeRgb } from "./data/type";
import { getPokemonNameWithAffix } from "./messages"; import { getPokemonNameWithAffix } from "./messages";
import { SemiInvulnerableTag } from "./data/battler-tags";
export class FormChangePhase extends EvolutionPhase { export class FormChangePhase extends EvolutionPhase {
private formChange: SpeciesFormChange; private formChange: SpeciesFormChange;
@ -194,7 +195,7 @@ export class QuietFormChangePhase extends BattlePhase {
const preName = getPokemonNameWithAffix(this.pokemon); const preName = getPokemonNameWithAffix(this.pokemon);
if (!this.pokemon.isOnField()) { if (!this.pokemon.isOnField() || this.pokemon.getTag(SemiInvulnerableTag)) {
this.pokemon.changeForm(this.formChange).then(() => { this.pokemon.changeForm(this.formChange).then(() => {
this.scene.ui.showText(getSpeciesFormChangeMessage(this.pokemon, this.formChange, preName), null, () => this.end(), 1500); this.scene.ui.showText(getSpeciesFormChangeMessage(this.pokemon, this.formChange, preName), null, () => this.end(), 1500);
}); });

View File

@ -41,6 +41,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
"cycleAbility": ": Fähigkeit", "cycleAbility": ": Fähigkeit",
"cycleNature": ": Wesen", "cycleNature": ": Wesen",
"cycleVariant": ": Seltenheit", "cycleVariant": ": Seltenheit",
"goFilter": ": Zu den Filtern",
"enablePassive": "Passiv-Skill aktivieren", "enablePassive": "Passiv-Skill aktivieren",
"disablePassive": "Passiv-Skill deaktivieren", "disablePassive": "Passiv-Skill deaktivieren",
"locked": "Gesperrt", "locked": "Gesperrt",

View File

@ -41,6 +41,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
"cycleAbility": ": Ability", "cycleAbility": ": Ability",
"cycleNature": ": Nature", "cycleNature": ": Nature",
"cycleVariant": ": Variant", "cycleVariant": ": Variant",
"goFilter": ": Go to filters",
"enablePassive": "Enable Passive", "enablePassive": "Enable Passive",
"disablePassive": "Disable Passive", "disablePassive": "Disable Passive",
"locked": "Locked", "locked": "Locked",

View File

@ -41,6 +41,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
"cycleAbility": ": Habilidad", "cycleAbility": ": Habilidad",
"cycleNature": ": Naturaleza", "cycleNature": ": Naturaleza",
"cycleVariant": ": Variante", "cycleVariant": ": Variante",
"goFilter": ": Ir a filtros",
"enablePassive": "Activar Pasiva", "enablePassive": "Activar Pasiva",
"disablePassive": "Desactivar Pasiva", "disablePassive": "Desactivar Pasiva",
"locked": "Bloqueado", "locked": "Bloqueado",

View File

@ -41,6 +41,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
"cycleAbility": ": Talent", "cycleAbility": ": Talent",
"cycleNature": ": Nature", "cycleNature": ": Nature",
"cycleVariant": ": Variant", "cycleVariant": ": Variant",
"goFilter": ": Aller aux filtres",
"enablePassive": "Activer Passif", "enablePassive": "Activer Passif",
"disablePassive": "Désactiver Passif", "disablePassive": "Désactiver Passif",
"locked": "Verrouillé", "locked": "Verrouillé",

View File

@ -41,6 +41,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
"cycleAbility": ": Abilità", "cycleAbility": ": Abilità",
"cycleNature": ": Natura", "cycleNature": ": Natura",
"cycleVariant": ": Variante", "cycleVariant": ": Variante",
"goFilter": ": Go to filters",
"enablePassive": "Attiva passiva", "enablePassive": "Attiva passiva",
"disablePassive": "Disattiva passiva", "disablePassive": "Disattiva passiva",
"locked": "Bloccato", "locked": "Bloccato",

View File

@ -3,171 +3,171 @@ import { AchievementTranslationEntries } from "#app/interfaces/locales.js";
// Achievement translations for the when the player character is male // Achievement translations for the when the player character is male
export const PGMachv: AchievementTranslationEntries = { export const PGMachv: AchievementTranslationEntries = {
"Achievements": { "Achievements": {
name: "Achievements", name: "実績",
}, },
"Locked": { "Locked": {
name: "Locked", name: "なし",
}, },
"MoneyAchv": { "MoneyAchv": {
description: "Accumulate a total of ₽{{moneyAmount}}", description: "一回の ランで ₽{{moneyAmount}}を 稼ぐ",
}, },
"10K_MONEY": { "10K_MONEY": {
name: "Money Haver", name: "お金を持つ人",
}, },
"100K_MONEY": { "100K_MONEY": {
name: "Rich", name: "富豪",
}, },
"1M_MONEY": { "1M_MONEY": {
name: "Millionaire", name: "百万長者",
}, },
"10M_MONEY": { "10M_MONEY": {
name: "One Percenter", name: "超富裕層",
}, },
"DamageAchv": { "DamageAchv": {
description: "Inflict {{damageAmount}} damage in one hit", description: "一撃で {{damageAmount}}ダメージを 与える",
}, },
"250_DMG": { "250_DMG": {
name: "Hard Hitter", name: "力持ち",
}, },
"1000_DMG": { "1000_DMG": {
name: "Harder Hitter", name: "強者",
}, },
"2500_DMG": { "2500_DMG": {
name: "That's a Lotta Damage!", name: "カカロット",
}, },
"10000_DMG": { "10000_DMG": {
name: "One Punch Man", name: "ワンパンマン",
}, },
"HealAchv": { "HealAchv": {
description: "Heal {{healAmount}} {{HP}} at once with a move, ability, or held item", description: "一つの 技や 特性や 持っているアイテムで {{healAmount}}{{HP}}を 一気に 回復する",
}, },
"250_HEAL": { "250_HEAL": {
name: "Novice Healer", name: "回復発見者",
}, },
"1000_HEAL": { "1000_HEAL": {
name: "Big Healer", name: "大いなる治療者",
}, },
"2500_HEAL": { "2500_HEAL": {
name: "Cleric", name: "回復達人",
}, },
"10000_HEAL": { "10000_HEAL": {
name: "Recovery Master", name: "ジョーイさん",
}, },
"LevelAchv": { "LevelAchv": {
description: "Level up a Pokémon to Lv{{level}}", description: "一つの ポケモンを Lv{{level}}まで レベルアップする",
}, },
"LV_100": { "LV_100": {
name: "But Wait, There's More!", name: "まだまだだよ",
}, },
"LV_250": { "LV_250": {
name: "Elite", name: "天王",
}, },
"LV_1000": { "LV_1000": {
name: "To Go Even Further Beyond", name: "向こうの向こうを超え",
}, },
"RibbonAchv": { "RibbonAchv": {
description: "Accumulate a total of {{ribbonAmount}} Ribbons", description: "{{ribbonAmount}}巻の リボンを 積もる",
}, },
"10_RIBBONS": { "10_RIBBONS": {
name: "Pokémon League Champion", name: "ポケモンリーグチャンピオン",
}, },
"25_RIBBONS": { "25_RIBBONS": {
name: "Great League Champion", name: "スーパーリーグチャンピオン",
}, },
"50_RIBBONS": { "50_RIBBONS": {
name: "Ultra League Champion", name: "ハイパーリーグチャンピオン",
}, },
"75_RIBBONS": { "75_RIBBONS": {
name: "Rogue League Champion", name: "ローグリーグチャンピオン",
}, },
"100_RIBBONS": { "100_RIBBONS": {
name: "Master League Champion", name: "マスターリーグチャンピオン",
}, },
"TRANSFER_MAX_BATTLE_STAT": { "TRANSFER_MAX_BATTLE_STAT": {
name: "Teamwork", name: "同力",
description: "Baton pass to another party member with at least one stat maxed out", description: "少なくとも 一つの 能力を 最大まで あげて 他の 手持ちポケモンに バトンタッチする",
}, },
"MAX_FRIENDSHIP": { "MAX_FRIENDSHIP": {
name: "Friendmaxxing", name: "マブ達",
description: "Reach max friendship on a Pokémon", description: "一つの 手持ちポケモンの 仲良し度を 最大に 上げる",
}, },
"MEGA_EVOLVE": { "MEGA_EVOLVE": {
name: "Megamorph", name: "ザ・アブソリュート",
description: "Mega evolve a Pokémon", description: "一つの 手持ちポケモンを メガシンカさせる",
}, },
"GIGANTAMAX": { "GIGANTAMAX": {
name: "Absolute Unit", name: "太―くて 堪らない",
description: "Gigantamax a Pokémon", description: "一つの 手持ちポケモンを キョダイマックスさせる",
}, },
"TERASTALLIZE": { "TERASTALLIZE": {
name: "STAB Enthusiast", name: "一致好き",
description: "Terastallize a Pokémon", description: "一つの 手持ちポケモンを テラスタルさせる",
}, },
"STELLAR_TERASTALLIZE": { "STELLAR_TERASTALLIZE": {
name: "The Hidden Type", name: "隠れたタイプ",
description: "Stellar Terastallize a Pokémon", description: "一つの 手持ちポケモンを ステラ・テラスタルさせる",
}, },
"SPLICE": { "SPLICE": {
name: "Infinite Fusion", name: "インフィニット・フュジョン",
description: "Splice two Pokémon together with DNA Splicers", description: "いでんしのくさびで 二つの ポケモンを 吸収合体させる",
}, },
"MINI_BLACK_HOLE": { "MINI_BLACK_HOLE": {
name: "A Hole Lot of Items", name: "アイテムホーリック",
description: "Acquire a Mini Black Hole", description: "ミニブラックホールを 手に入れる",
}, },
"CATCH_MYTHICAL": { "CATCH_MYTHICAL": {
name: "Mythical", name: "",
description: "Catch a mythical Pokémon", description: "幻の ポケモンを 捕まえる",
}, },
"CATCH_SUB_LEGENDARY": { "CATCH_SUB_LEGENDARY": {
name: "(Sub-)Legendary", name: "準・伝説",
description: "Catch a sub-legendary Pokémon", description: "準伝説の ポケモンを 捕まえる",
}, },
"CATCH_LEGENDARY": { "CATCH_LEGENDARY": {
name: "Legendary", name: "ザ・伝説",
description: "Catch a legendary Pokémon", description: "伝説の ポケモンを 捕まえる",
}, },
"SEE_SHINY": { "SEE_SHINY": {
name: "Shiny", name: "色とりどりに光る",
description: "Find a shiny Pokémon in the wild", description: "野生の 色違いポケモンを みつける",
}, },
"SHINY_PARTY": { "SHINY_PARTY": {
name: "That's Dedication", name: "きらきら努力家",
description: "Have a full party of shiny Pokémon", description: "手持ちポケモンは 全員 色違いポケモンに する",
}, },
"HATCH_MYTHICAL": { "HATCH_MYTHICAL": {
name: "Mythical Egg", name: "幻のタマゴ",
description: "Hatch a mythical Pokémon from an egg", description: "幻の ポケモンを タマゴから 生まれる",
}, },
"HATCH_SUB_LEGENDARY": { "HATCH_SUB_LEGENDARY": {
name: "Sub-Legendary Egg", name: "準伝説のタマゴ",
description: "Hatch a sub-legendary Pokémon from an egg", description: "準伝説の ポケモンを タマゴから 生まれる",
}, },
"HATCH_LEGENDARY": { "HATCH_LEGENDARY": {
name: "Legendary Egg", name: "伝説のタマゴ",
description: "Hatch a legendary Pokémon from an egg", description: "伝説の ポケモンを タマゴから 生まれる",
}, },
"HATCH_SHINY": { "HATCH_SHINY": {
name: "Shiny Egg", name: "色違いタマゴ",
description: "Hatch a shiny Pokémon from an egg", description: "色違いポケモンを タマゴから 生まれる",
}, },
"HIDDEN_ABILITY": { "HIDDEN_ABILITY": {
name: "Hidden Potential", name: "底力",
description: "Catch a Pokémon with a hidden ability", description: "隠れ特性がある ポケモンを 捕まえる",
}, },
"PERFECT_IVS": { "PERFECT_IVS": {
name: "Certificate of Authenticity", name: "個体値の賞状",
description: "Get perfect IVs on a Pokémon", description: "一つの ポケモンの 個体値を すべて 最大に する",
}, },
"CLASSIC_VICTORY": { "CLASSIC_VICTORY": {
name: "Undefeated", name: "無双",
description: "Beat the game in classic mode", description: "クラシックモードを クリアする",
}, },
"UNEVOLVED_CLASSIC_VICTORY": { "UNEVOLVED_CLASSIC_VICTORY": {
name: "Bring Your Child To Work Day", name: "Bring Your Child To Work Day",
@ -175,102 +175,102 @@ export const PGMachv: AchievementTranslationEntries = {
}, },
"MONO_GEN_ONE": { "MONO_GEN_ONE": {
name: "The Original Rival", name: "原始",
description: "Complete the generation one only challenge.", description: "1世代の 単一世代チャレンジを クリアする",
}, },
"MONO_GEN_TWO": { "MONO_GEN_TWO": {
name: "Generation 1.5", name: "懐かしいカンジョウ",
description: "Complete the generation two only challenge.", description: "2世代の 単一世代チャレンジを クリアする",
}, },
"MONO_GEN_THREE": { "MONO_GEN_THREE": {
name: "Too much water?", name: "水浸し",
description: "Complete the generation three only challenge.", description: "3世代の 単一世代チャレンジを クリアする",
}, },
"MONO_GEN_FOUR": { "MONO_GEN_FOUR": {
name: "Is she really the hardest?", name: "神々の地",
description: "Complete the generation four only challenge.", description: "4世代の 単一世代チャレンジを クリアする",
}, },
"MONO_GEN_FIVE": { "MONO_GEN_FIVE": {
name: "All Original", name: "ニューヨーカー",
description: "Complete the generation five only challenge.", description: "5世代の 単一世代チャレンジを クリアする",
}, },
"MONO_GEN_SIX": { "MONO_GEN_SIX": {
name: "Almost Royalty", name: "サヴァ・サヴァ",
description: "Complete the generation six only challenge.", description: "6世代の 単一世代チャレンジを クリアする",
}, },
"MONO_GEN_SEVEN": { "MONO_GEN_SEVEN": {
name: "Only Technically", name: "アローラ・オエ",
description: "Complete the generation seven only challenge.", description: "7世代の 単一世代チャレンジを クリアする",
}, },
"MONO_GEN_EIGHT": { "MONO_GEN_EIGHT": {
name: "A Champion Time!", name: "チャンピオン タイムを 楽しめ!",
description: "Complete the generation eight only challenge.", description: "8世代の 単一世代チャレンジを クリアする",
}, },
"MONO_GEN_NINE": { "MONO_GEN_NINE": {
name: "She was going easy on you", name: "ネモに甘えたでしょう",
description: "Complete the generation nine only challenge.", description: "9世代の 単一世代チャレンジを クリアする",
}, },
"MonoType": { "MonoType": {
description: "Complete the {{type}} monotype challenge.", description: "{{type}}タイプの 単一タイプチャレンジを クリアする",
}, },
"MONO_NORMAL": { "MONO_NORMAL": {
name: "Extra Ordinary", name: "凡人",
}, },
"MONO_FIGHTING": { "MONO_FIGHTING": {
name: "I Know Kung Fu", name: "八千以上だ!!",
}, },
"MONO_FLYING": { "MONO_FLYING": {
name: "Angry Birds", name: "翼をください",
}, },
"MONO_POISON": { "MONO_POISON": {
name: "Kanto's Favourite", name: "カントーの名物",
}, },
"MONO_GROUND": { "MONO_GROUND": {
name: "Forecast: Earthquakes", name: "自信でユラユラ",
}, },
"MONO_ROCK": { "MONO_ROCK": {
name: "Brock Hard", name: "タケシの挑戦状",
}, },
"MONO_BUG": { "MONO_BUG": {
name: "You Like Jazz?", name: "チョウチョウせん者",
}, },
"MONO_GHOST": { "MONO_GHOST": {
name: "Who You Gonna Call?", name: "貞子ちゃん",
}, },
"MONO_STEEL": { "MONO_STEEL": {
name: "Iron Giant", name: "ハガネーター",
}, },
"MONO_FIRE": { "MONO_FIRE": {
name: "I Cast Fireball!", name: "NIGHT OF FIRE",
}, },
"MONO_WATER": { "MONO_WATER": {
name: "When It Rains, It Pours", name: "土砂降リスト",
}, },
"MONO_GRASS": { "MONO_GRASS": {
name: "Can't Touch This", name: "",
}, },
"MONO_ELECTRIC": { "MONO_ELECTRIC": {
name: "Aim For The Horn!", name: "パチピカペコ",
}, },
"MONO_PSYCHIC": { "MONO_PSYCHIC": {
name: "Big Brain Energy", name: "陽キャ",
}, },
"MONO_ICE": { "MONO_ICE": {
name: "Walking On Thin Ice", name: "ありのまま",
}, },
"MONO_DRAGON": { "MONO_DRAGON": {
name: "Pseudo-Legend Club", name: "龍が如く",
}, },
"MONO_DARK": { "MONO_DARK": {
name: "It's Just A Phase", name: "陰キャ",
}, },
"MONO_FAIRY": { "MONO_FAIRY": {
name: "Hey! Listen!", name: "あらハート満タンになった",
}, },
"FRESH_START": { "FRESH_START": {
name: "First Try!", name: "一発で!",
description: "Complete the fresh start challenge." description: "出直しチャレンジを クリアする"
} }
} as const; } as const;

View File

@ -1,32 +1,32 @@
import { TranslationEntries } from "#app/interfaces/locales"; import { TranslationEntries } from "#app/interfaces/locales";
export const challenges: TranslationEntries = { export const challenges: TranslationEntries = {
"title": "チャレンジ じょうけん せってい", "title": "チャレンジを 設定",
"illegalEvolution": "{{pokemon}} changed into an ineligble pokémon\nfor this challenge!", "illegalEvolution": "{{pokemon}}は このチャレンジで\n対象外の ポケモンに なってしまった",
"singleGeneration": { "singleGeneration": {
"name": "Mono Gen", "name": "単一世代",
"desc": "You can only use Pokémon from Generation {{gen}}.", "desc": "{{gen}}世代からの ポケモンしか 使えません",
"desc_default": "You can only use Pokémon from the chosen generation.", "desc_default": "選んだ 世代からの ポケモンしか 使えません",
"gen_1": "one", "gen_1": "1",
"gen_2": "two", "gen_2": "2",
"gen_3": "three", "gen_3": "3",
"gen_4": "four", "gen_4": "4",
"gen_5": "five", "gen_5": "5",
"gen_6": "six", "gen_6": "6",
"gen_7": "seven", "gen_7": "7",
"gen_8": "eight", "gen_8": "8",
"gen_9": "nine", "gen_9": "9",
}, },
"singleType": { "singleType": {
"name": "Mono Type", "name": "単一タイプ",
"desc": "You can only use Pokémon with the {{type}} type.", "desc": "{{type}}タイプの ポケモンしか 使えません",
"desc_default": "You can only use Pokémon of the chosen type." "desc_default": "選んだ タイプの ポケモンしか 使えません"
//types in pokemon-info //types in pokemon-info
}, },
"freshStart": { "freshStart": {
"name": "Fresh Start", "name": "出直し",
"desc": "You can only use the original starters, and only as if you had just started pokerogue.", "desc": "ポケローグを 始めた ばかりの ような ままで ゲーム開始の 最初のパートナーしか 使えません",
"value.0": "Off", "value.0": "オフ",
"value.1": "On", "value.1": "オン",
}, },
} as const; } as const;

View File

@ -41,6 +41,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
"cycleAbility": ": 특성", "cycleAbility": ": 특성",
"cycleNature": ": 성격", "cycleNature": ": 성격",
"cycleVariant": ": 색상", "cycleVariant": ": 색상",
"goFilter": ": 필터로 이동",
"enablePassive": "패시브 활성화", "enablePassive": "패시브 활성화",
"disablePassive": "패시브 비활성화", "disablePassive": "패시브 비활성화",
"locked": "잠김", "locked": "잠김",

View File

@ -41,6 +41,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
"cycleAbility": ": » Habilidade", "cycleAbility": ": » Habilidade",
"cycleNature": ": » Natureza", "cycleNature": ": » Natureza",
"cycleVariant": ": » Variante", "cycleVariant": ": » Variante",
"goFilter": ": Ir para filtros",
"enablePassive": "Ativar Passiva", "enablePassive": "Ativar Passiva",
"disablePassive": "Desativar Passiva", "disablePassive": "Desativar Passiva",
"locked": "Bloqueada", "locked": "Bloqueada",

View File

@ -41,6 +41,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
"cycleAbility": ": 特性", "cycleAbility": ": 特性",
"cycleNature": ": 性格", "cycleNature": ": 性格",
"cycleVariant": ": 变种", "cycleVariant": ": 变种",
"goFilter": ": 转到筛选",
"enablePassive": "启用被动", "enablePassive": "启用被动",
"disablePassive": "禁用被动", "disablePassive": "禁用被动",
"locked": "未解锁", "locked": "未解锁",

View File

@ -42,6 +42,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
"cycleAbility": ": 特性", "cycleAbility": ": 特性",
"cycleNature": ": 性格", "cycleNature": ": 性格",
"cycleVariant": ": 變種", "cycleVariant": ": 變種",
"goFilter": ": 轉到篩選",
"enablePassive": "啟用被動", "enablePassive": "啟用被動",
"disablePassive": "禁用被動", "disablePassive": "禁用被動",
"locked": "未解鎖", "locked": "未解鎖",

View File

@ -2957,9 +2957,11 @@ export class MoveEffectPhase extends PokemonPhase {
constructor(scene: BattleScene, battlerIndex: BattlerIndex, targets: BattlerIndex[], move: PokemonMove) { constructor(scene: BattleScene, battlerIndex: BattlerIndex, targets: BattlerIndex[], move: PokemonMove) {
super(scene, battlerIndex); super(scene, battlerIndex);
this.move = move; this.move = move;
// In double battles, if the right Pokemon selects a spread move and the left Pokemon dies /**
// with no party members available to switch in, then the right Pokemon takes the index * In double battles, if the right Pokemon selects a spread move and the left Pokemon dies
// of the left Pokemon and gets hit unless this is checked. * with no party members available to switch in, then the right Pokemon takes the index
* of the left Pokemon and gets hit unless this is checked.
*/
if (targets.includes(battlerIndex) && this.move.getMove().moveTarget === MoveTarget.ALL_NEAR_OTHERS) { if (targets.includes(battlerIndex) && this.move.getMove().moveTarget === MoveTarget.ALL_NEAR_OTHERS) {
const i = targets.indexOf(battlerIndex); const i = targets.indexOf(battlerIndex);
targets.splice(i, i + 1); targets.splice(i, i + 1);
@ -2970,40 +2972,72 @@ export class MoveEffectPhase extends PokemonPhase {
start() { start() {
super.start(); super.start();
/** The Pokemon using this phase's invoked move */
const user = this.getUserPokemon(); const user = this.getUserPokemon();
/** All Pokemon targeted by this phase's invoked move */
const targets = this.getTargets(); const targets = this.getTargets();
/** If the user was somehow removed from the field, end this phase */
if (!user?.isOnField()) { if (!user?.isOnField()) {
return super.end(); return super.end();
} }
/**
* Does an effect from this move override other effects on this turn?
* e.g. Charging moves (Fly, etc.) on their first turn of use.
*/
const overridden = new Utils.BooleanHolder(false); const overridden = new Utils.BooleanHolder(false);
/** The {@linkcode Move} object from {@linkcode allMoves} invoked by this phase */
const move = this.move.getMove(); const move = this.move.getMove();
// Assume single target for override // Assume single target for override
applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget() ?? null, move, overridden, this.move.virtual).then(() => { applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget() ?? null, move, overridden, this.move.virtual).then(() => {
// If other effects were overriden, stop this phase before they can be applied
if (overridden.value) { if (overridden.value) {
return this.end(); return this.end();
} }
user.lapseTags(BattlerTagLapseType.MOVE_EFFECT); user.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
/**
* If this phase is for the first hit of the invoked move,
* resolve the move's total hit count. This block combines the
* effects of the move itself, Parental Bond, and Multi-Lens to do so.
*/
if (user.turnData.hitsLeft === undefined) { if (user.turnData.hitsLeft === undefined) {
const hitCount = new Utils.IntegerHolder(1); const hitCount = new Utils.IntegerHolder(1);
// Assume single target for multi hit // Assume single target for multi hit
applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount); 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, 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)) { if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) {
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0)); this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0));
} }
user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value; // Set the user's relevant turnData fields to reflect the final hit count
user.turnData.hitCount = hitCount.value;
user.turnData.hitsLeft = hitCount.value;
} }
/**
* Log to be entered into the user's move history once the move result is resolved.
* Note that `result` (a {@linkcode MoveResult}) logs whether the move was successfully
* used in the sense of it not failing or missing; it does not account for the move's
* effectiveness (which is logged as a {@linkcode HitResult}).
*/
const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual }; const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual };
/**
* Stores results of hit checks of the invoked move against all targets, organized by battler index.
* @see {@linkcode hitCheck}
*/
const targetHitChecks = Object.fromEntries(targets.map(p => [p.getBattlerIndex(), this.hitCheck(p)])); const targetHitChecks = Object.fromEntries(targets.map(p => [p.getBattlerIndex(), this.hitCheck(p)]));
const hasActiveTargets = targets.some(t => t.isActive(true)); const hasActiveTargets = targets.some(t => t.isActive(true));
/**
* If no targets are left for the move to hit (FAIL), or the invoked move is single-target
* (and not random target) and failed the hit check against its target (MISS), log the move
* as FAILed or MISSed (depending on the conditions above) and end this phase.
*/
if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) { if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) {
this.stopMultiHit(); this.stopMultiHit();
if (hasActiveTargets) { if (hasActiveTargets) {
@ -3018,6 +3052,7 @@ export class MoveEffectPhase extends PokemonPhase {
return this.end(); return this.end();
} }
/** All move effect attributes are chained together in this array to be applied asynchronously. */
const applyAttrs: Promise<void>[] = []; const applyAttrs: Promise<void>[] = [];
// Move animation only needs one target // Move animation only needs one target
@ -3025,6 +3060,10 @@ export class MoveEffectPhase extends PokemonPhase {
/** Has the move successfully hit a target (for damage) yet? */ /** Has the move successfully hit a target (for damage) yet? */
let hasHit: boolean = false; let hasHit: boolean = false;
for (const target of targets) { for (const target of targets) {
/**
* If the move missed a target, stop all future hits against that target
* and move on to the next target (if there is one).
*/
if (!targetHitChecks[target.getBattlerIndex()]) { if (!targetHitChecks[target.getBattlerIndex()]) {
this.stopMultiHit(target); this.stopMultiHit(target);
this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) }));
@ -3036,18 +3075,38 @@ export class MoveEffectPhase extends PokemonPhase {
continue; continue;
} }
/** Is the invoked move blocked by a protection effect on the target? */
const isProtected = !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target) && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType)); const isProtected = !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target) && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType));
/** Does this phase represent the invoked move's first strike? */
const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount); const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount);
// Only log the move's result on the first strike
if (firstHit) { if (firstHit) {
user.pushMoveHistory(moveHistoryEntry); user.pushMoveHistory(moveHistoryEntry);
} }
/**
* Since all fail/miss checks have applied, the move is considered successfully applied.
* It's worth noting that if the move has no effect or is protected against, it's move
* result is still logged as a SUCCESS.
*/
moveHistoryEntry.result = MoveResult.SUCCESS; moveHistoryEntry.result = MoveResult.SUCCESS;
/**
* Stores the result of applying the invoked move to the target.
* If the target is protected, the result is always `NO_EFFECT`.
* Otherwise, the hit result is based on type effectiveness, immunities,
* and other factors that may negate the attack or status application.
*
* Internally, the call to {@linkcode Pokemon.apply} is where damage is calculated
* (for attack moves) and the target's HP is updated. However, this isn't
* made visible to the user until the resulting {@linkcode DamagePhase}
* is invoked.
*/
const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT; const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT;
/** Does {@linkcode hitResult} indicate that damage was dealt to the target? */
const dealsDamage = [ const dealsDamage = [
HitResult.EFFECTIVE, HitResult.EFFECTIVE,
HitResult.SUPER_EFFECTIVE, HitResult.SUPER_EFFECTIVE,
@ -3055,28 +3114,53 @@ export class MoveEffectPhase extends PokemonPhase {
HitResult.ONE_HIT_KO HitResult.ONE_HIT_KO
].includes(hitResult); ].includes(hitResult);
/** Is this target the first one hit by the move on its current strike? */
const firstTarget = dealsDamage && !hasHit; const firstTarget = dealsDamage && !hasHit;
if (firstTarget) { if (firstTarget) {
hasHit = true; hasHit = true;
} }
/** Does this phase represent the invoked move's last strike? */
const lastHit = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()); const lastHit = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive());
/**
* If the user can change forms by using the invoked move,
* it only changes forms after the move's last hit
* (see Relic Song's interaction with Parental Bond when used by Meloetta).
*/
if (lastHit) { if (lastHit) {
this.scene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger); this.scene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger);
} }
/**
* Create a Promise that applys *all* effects from the invoked move's MoveEffectAttrs.
* These are ordered by trigger type (see {@linkcode MoveEffectTrigger}), and each trigger
* type requires different conditions to be met with respect to the move's hit result.
*/
applyAttrs.push(new Promise(resolve => { applyAttrs.push(new Promise(resolve => {
// Apply all effects with PRE_MOVE triggers (if the target isn't immune to the move)
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && hitResult !== HitResult.NO_EFFECT, applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && hitResult !== HitResult.NO_EFFECT,
user, target, move).then(() => { user, target, move).then(() => {
// All other effects require the move to not have failed or have been cancelled to trigger
if (hitResult !== HitResult.FAIL) { if (hitResult !== HitResult.FAIL) {
/** Are the move's effects tied to the first turn of a charge move? */
const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget() ?? null, move)); const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget() ?? null, move));
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present /**
* If the invoked move's effects are meant to trigger during the move's "charge turn,"
* ignore all effects after this point.
* Otherwise, apply all self-targeted POST_APPLY effects.
*/
Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY
&& attr.selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, move)).then(() => { && attr.selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, move)).then(() => {
// All effects past this point require the move to have hit the target
if (hitResult !== HitResult.NO_EFFECT) { if (hitResult !== HitResult.NO_EFFECT) {
// Apply all non-self-targeted POST_APPLY effects
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY
&& !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, this.move.getMove()).then(() => { && !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, this.move.getMove()).then(() => {
/**
* If the move hit, and the target doesn't have Shield Dust,
* apply the chance to flinch the target gained from King's Rock
*/
if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr)) { if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr)) {
const flinched = new Utils.BooleanHolder(false); const flinched = new Utils.BooleanHolder(false);
user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched); user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
@ -3084,15 +3168,23 @@ export class MoveEffectPhase extends PokemonPhase {
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id); target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
} }
} }
// If the move was not protected against, apply all HIT effects
Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT
&& (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && (!attr.firstTargetOnly || firstTarget), user, target, this.move.getMove()).then(() => { && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && (!attr.firstTargetOnly || firstTarget), user, target, this.move.getMove()).then(() => {
// Apply the target's post-defend ability effects (as long as the target is active or can otherwise apply them)
return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult).then(() => { return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult).then(() => {
// If the invoked move is an enemy attack, apply the enemy's status effect-inflicting tags and tokens
target.lapseTag(BattlerTagType.BEAK_BLAST_CHARGING); target.lapseTag(BattlerTagType.BEAK_BLAST_CHARGING);
if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) { if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) {
user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target); user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target);
} }
})).then(() => { })).then(() => {
// Apply the user's post-attack ability effects
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult).then(() => { applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult).then(() => {
/**
* If the invoked move is an attack, apply the user's chance to
* steal an item from the target granted by Grip Claw
*/
if (this.move.getMove() instanceof AttackMove) { if (this.move.getMove() instanceof AttackMove) {
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target); this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target);
} }
@ -3112,7 +3204,7 @@ export class MoveEffectPhase extends PokemonPhase {
}); });
})); }));
} }
// Trigger effect which should only apply one time on the last hit after all targeted effects have already applied // Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved
const postTarget = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()) ? const postTarget = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()) ?
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, user, null, move) : applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, user, null, move) :
null; null;
@ -3125,6 +3217,7 @@ export class MoveEffectPhase extends PokemonPhase {
} }
} }
// Wait for all move effects to finish applying, then end this phase
Promise.allSettled(applyAttrs).then(() => this.end()); Promise.allSettled(applyAttrs).then(() => this.end());
}); });
}); });
@ -3134,6 +3227,13 @@ export class MoveEffectPhase extends PokemonPhase {
const move = this.move.getMove(); const move = this.move.getMove();
move.type = move.defaultType; move.type = move.defaultType;
const user = this.getUserPokemon(); const user = this.getUserPokemon();
/**
* If this phase isn't for the invoked move's last strike,
* unshift another MoveEffectPhase for the next strike.
* Otherwise, queue a message indicating the number of times the move has struck
* (if the move has struck more than once), then apply the heal from Shell Bell
* to the user.
*/
if (user) { if (user) {
if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) { if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) {
this.scene.unshiftPhase(this.getNewHitPhase()); this.scene.unshiftPhase(this.getNewHitPhase());
@ -3152,6 +3252,11 @@ export class MoveEffectPhase extends PokemonPhase {
super.end(); super.end();
} }
/**
* Resolves whether this phase's invoked move hits or misses the given target
* @param target {@linkcode Pokemon} the Pokemon targeted by the invoked move
* @returns `true` if the move does not miss the target; `false` otherwise
*/
hitCheck(target: Pokemon): boolean { hitCheck(target: Pokemon): boolean {
// Moves targeting the user and entry hazards can't miss // Moves targeting the user and entry hazards can't miss
if ([MoveTarget.USER, MoveTarget.ENEMY_SIDE].includes(this.move.getMove().moveTarget)) { if ([MoveTarget.USER, MoveTarget.ENEMY_SIDE].includes(this.move.getMove().moveTarget)) {
@ -3182,8 +3287,8 @@ export class MoveEffectPhase extends PokemonPhase {
return true; return true;
} }
const hiddenTag = target.getTag(SemiInvulnerableTag); const semiInvulnerableTag = target.getTag(SemiInvulnerableTag);
if (hiddenTag && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === hiddenTag.tagType)) { if (semiInvulnerableTag && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType)) {
return false; return false;
} }
@ -3199,6 +3304,7 @@ export class MoveEffectPhase extends PokemonPhase {
return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct? return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct?
} }
/** Returns the {@linkcode Pokemon} using this phase's invoked move */
getUserPokemon(): Pokemon | undefined { getUserPokemon(): Pokemon | undefined {
if (this.battlerIndex > BattlerIndex.ENEMY_2) { if (this.battlerIndex > BattlerIndex.ENEMY_2) {
return this.scene.getPokemonById(this.battlerIndex) ?? undefined; return this.scene.getPokemonById(this.battlerIndex) ?? undefined;
@ -3206,14 +3312,20 @@ export class MoveEffectPhase extends PokemonPhase {
return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex]; return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex];
} }
/** Returns an array of all {@linkcode Pokemon} targeted by this phase's invoked move */
getTargets(): Pokemon[] { getTargets(): Pokemon[] {
return this.scene.getField(true).filter(p => this.targets.indexOf(p.getBattlerIndex()) > -1); return this.scene.getField(true).filter(p => this.targets.indexOf(p.getBattlerIndex()) > -1);
} }
/** Returns the first target of this phase's invoked move */
getTarget(): Pokemon | undefined { getTarget(): Pokemon | undefined {
return this.getTargets().find(() => true); return this.getTargets()[0];
} }
/**
* Removes the given {@linkcode Pokemon} from this phase's target list
* @param target {@linkcode Pokemon} the Pokemon to be removed
*/
removeTarget(target: Pokemon): void { removeTarget(target: Pokemon): void {
const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex()); const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex());
if (targetIndex !== -1) { if (targetIndex !== -1) {
@ -3221,6 +3333,11 @@ export class MoveEffectPhase extends PokemonPhase {
} }
} }
/**
* Prevents subsequent strikes of this phase's invoked move from occurring
* @param target {@linkcode Pokemon} if defined, only stop subsequent
* strikes against this Pokemon
*/
stopMultiHit(target?: Pokemon): void { stopMultiHit(target?: Pokemon): void {
/** If given a specific target, remove the target from subsequent strikes */ /** If given a specific target, remove the target from subsequent strikes */
if (target) { if (target) {
@ -3236,6 +3353,7 @@ export class MoveEffectPhase extends PokemonPhase {
} }
} }
/** Returns a new MoveEffectPhase with the same properties as this phase */
getNewHitPhase() { getNewHitPhase() {
return new MoveEffectPhase(this.scene, this.battlerIndex, this.targets, this.move); return new MoveEffectPhase(this.scene, this.battlerIndex, this.targets, this.move);
} }

View File

@ -1,5 +1,6 @@
import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { BattlerTagType } from "#app/enums/battler-tag-type.js";
import { import {
BerryPhase,
MoveEndPhase, MoveEndPhase,
TurnEndPhase, TurnEndPhase,
TurnStartPhase, TurnStartPhase,
@ -14,7 +15,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite
import { SPLASH_ONLY } from "../utils/testUtils"; import { SPLASH_ONLY } from "../utils/testUtils";
import { BattleStat } from "#app/data/battle-stat.js"; import { BattleStat } from "#app/data/battle-stat.js";
import { StatusEffect } from "#app/enums/status-effect.js"; import { StatusEffect } from "#app/enums/status-effect.js";
import { GulpMissileTag } from "#app/data/battler-tags.js";
import Pokemon from "#app/field/pokemon.js"; import Pokemon from "#app/field/pokemon.js";
describe("Abilities - Gulp Missile", () => { describe("Abilities - Gulp Missile", () => {
@ -84,6 +84,17 @@ describe("Abilities - Gulp Missile", () => {
expect(cramorant.formIndex).toBe(GORGING_FORM); expect(cramorant.formIndex).toBe(GORGING_FORM);
}); });
it("changes form during Dive's charge turn", async () => {
await game.startBattle([Species.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE));
await game.phaseInterceptor.to(MoveEndPhase);
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
expect(cramorant.formIndex).toBe(GULPING_FORM);
});
it("deals ¼ of the attacker's maximum HP when hit by a damaging attack", async () => { it("deals ¼ of the attacker's maximum HP when hit by a damaging attack", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
await game.startBattle([Species.CRAMORANT]); await game.startBattle([Species.CRAMORANT]);
@ -165,29 +176,16 @@ describe("Abilities - Gulp Missile", () => {
}); });
it("does not activate the ability when underwater", async () => { it("does not activate the ability when underwater", async () => {
game.override game.override.enemyMoveset(Array(4).fill(Moves.SURF));
.enemyMoveset(Array(4).fill(Moves.SURF))
.enemySpecies(Species.REGIELEKI)
.enemyAbility(Abilities.BALL_FETCH)
.enemyLevel(5);
await game.startBattle([Species.CRAMORANT]); await game.startBattle([Species.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!; const cramorant = game.scene.getPlayerPokemon()!;
game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE));
await game.toNextTurn(); await game.phaseInterceptor.to(BerryPhase, false);
// Turn 2 underwater, enemy moves first expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined();
game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); expect(cramorant.formIndex).toBe(GULPING_FORM);
await game.phaseInterceptor.to(MoveEndPhase);
expect(cramorant.formIndex).toBe(NORMAL_FORM);
expect(cramorant.getTag(GulpMissileTag)).toBeUndefined();
// Turn 2 Cramorant comes out and changes form
await game.phaseInterceptor.to(TurnEndPhase);
expect(cramorant.formIndex).not.toBe(NORMAL_FORM);
expect(cramorant.getTag(GulpMissileTag)).toBeDefined();
}); });
it("prevents effect damage but inflicts secondary effect on attacker with Magic Guard", async () => { it("prevents effect damage but inflicts secondary effect on attacker with Magic Guard", async () => {

View File

@ -56,6 +56,14 @@ describe("Final Boss", () => {
expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(Species.ETERNATUS); expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(Species.ETERNATUS);
}); });
it("should not have passive enabled on Eternatus", async () => {
await runToFinalBossEncounter(game, [Species.BIDOOF]);
const eternatus = game.scene.getEnemyPokemon();
expect(eternatus?.species.speciesId).toBe(Species.ETERNATUS);
expect(eternatus?.hasPassive()).toBe(false);
});
it.todo("should change form on direct hit down to last boss fragment", () => {}); it.todo("should change form on direct hit down to last boss fragment", () => {});
}); });

View File

@ -79,7 +79,7 @@ export class UiInputs {
[Button.ACTION]: () => this.buttonAb(Button.ACTION), [Button.ACTION]: () => this.buttonAb(Button.ACTION),
[Button.CANCEL]: () => this.buttonAb(Button.CANCEL), [Button.CANCEL]: () => this.buttonAb(Button.CANCEL),
[Button.MENU]: () => this.buttonMenu(), [Button.MENU]: () => this.buttonMenu(),
[Button.STATS]: () => this.buttonStats(true), [Button.STATS]: () => this.buttonGoToFilter(Button.STATS),
[Button.CYCLE_SHINY]: () => this.buttonCycleOption(Button.CYCLE_SHINY), [Button.CYCLE_SHINY]: () => this.buttonCycleOption(Button.CYCLE_SHINY),
[Button.CYCLE_FORM]: () => this.buttonCycleOption(Button.CYCLE_FORM), [Button.CYCLE_FORM]: () => this.buttonCycleOption(Button.CYCLE_FORM),
[Button.CYCLE_GENDER]: () => this.buttonCycleOption(Button.CYCLE_GENDER), [Button.CYCLE_GENDER]: () => this.buttonCycleOption(Button.CYCLE_GENDER),
@ -139,6 +139,17 @@ export class UiInputs {
p.toggleStats(pressed); p.toggleStats(pressed);
} }
} }
buttonGoToFilter(button: Button): void {
const whitelist = [StarterSelectUiHandler];
const uiHandler = this.scene.ui?.getHandler();
if (whitelist.some(handler => uiHandler instanceof handler)) {
this.scene.ui.processInput(button);
} else {
this.buttonStats(true);
}
}
buttonInfo(pressed: boolean = true): void { buttonInfo(pressed: boolean = true): void {
if (this.scene.showMovesetFlyout ) { if (this.scene.showMovesetFlyout ) {
for (const p of this.scene.getField().filter(p => p?.isActive(true))) { for (const p of this.scene.getField().filter(p => p?.isActive(true))) {

View File

@ -70,8 +70,9 @@ const languageSettings: { [key: string]: LanguageSetting } = {
instructionTextSize: "38px", instructionTextSize: "38px",
}, },
"de":{ "de":{
starterInfoTextSize: "56px", starterInfoTextSize: "48px",
instructionTextSize: "35px", instructionTextSize: "35px",
starterInfoXPos: 33,
}, },
"es":{ "es":{
starterInfoTextSize: "56px", starterInfoTextSize: "56px",
@ -79,7 +80,7 @@ const languageSettings: { [key: string]: LanguageSetting } = {
}, },
"fr":{ "fr":{
starterInfoTextSize: "54px", starterInfoTextSize: "54px",
instructionTextSize: "42px", instructionTextSize: "35px",
}, },
"it":{ "it":{
starterInfoTextSize: "56px", starterInfoTextSize: "56px",
@ -91,9 +92,10 @@ const languageSettings: { [key: string]: LanguageSetting } = {
starterInfoXPos: 33, starterInfoXPos: 33,
}, },
"zh":{ "zh":{
starterInfoTextSize: "40px", starterInfoTextSize: "47px",
instructionTextSize: "42px", instructionTextSize: "38px",
starterInfoYOffset: 2 starterInfoYOffset: 1,
starterInfoXPos: 24,
}, },
"pt":{ "pt":{
starterInfoTextSize: "48px", starterInfoTextSize: "48px",
@ -258,18 +260,21 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
private pokemonShinyIcon: Phaser.GameObjects.Sprite; private pokemonShinyIcon: Phaser.GameObjects.Sprite;
private instructionsContainer: Phaser.GameObjects.Container; private instructionsContainer: Phaser.GameObjects.Container;
private filterInstructionsContainer: Phaser.GameObjects.Container;
private shinyIconElement: Phaser.GameObjects.Sprite; private shinyIconElement: Phaser.GameObjects.Sprite;
private formIconElement: Phaser.GameObjects.Sprite; private formIconElement: Phaser.GameObjects.Sprite;
private abilityIconElement: Phaser.GameObjects.Sprite; private abilityIconElement: Phaser.GameObjects.Sprite;
private genderIconElement: Phaser.GameObjects.Sprite; private genderIconElement: Phaser.GameObjects.Sprite;
private natureIconElement: Phaser.GameObjects.Sprite; private natureIconElement: Phaser.GameObjects.Sprite;
private variantIconElement: Phaser.GameObjects.Sprite; private variantIconElement: Phaser.GameObjects.Sprite;
private goFilterIconElement: Phaser.GameObjects.Sprite;
private shinyLabel: Phaser.GameObjects.Text; private shinyLabel: Phaser.GameObjects.Text;
private formLabel: Phaser.GameObjects.Text; private formLabel: Phaser.GameObjects.Text;
private genderLabel: Phaser.GameObjects.Text; private genderLabel: Phaser.GameObjects.Text;
private abilityLabel: Phaser.GameObjects.Text; private abilityLabel: Phaser.GameObjects.Text;
private natureLabel: Phaser.GameObjects.Text; private natureLabel: Phaser.GameObjects.Text;
private variantLabel: Phaser.GameObjects.Text; private variantLabel: Phaser.GameObjects.Text;
private goFilterLabel: Phaser.GameObjects.Text;
private starterSelectMessageBox: Phaser.GameObjects.NineSlice; private starterSelectMessageBox: Phaser.GameObjects.NineSlice;
private starterSelectMessageBoxContainer: Phaser.GameObjects.Container; private starterSelectMessageBoxContainer: Phaser.GameObjects.Container;
@ -329,7 +334,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
//variables to keep track of the dynamically rendered list of instruction prompts for starter select //variables to keep track of the dynamically rendered list of instruction prompts for starter select
private instructionRowX = 0; private instructionRowX = 0;
private instructionRowY = 0; private instructionRowY = 0;
private instructionRowTextOffset = 12; private instructionRowTextOffset = 9;
private filterInstructionRowX = 0;
private filterInstructionRowY = 0;
private starterSelectCallback: StarterSelectCallback | null; private starterSelectCallback: StarterSelectCallback | null;
@ -825,8 +832,19 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.variantLabel = addTextObject(this.scene, this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleVariant"), TextStyle.PARTY, { fontSize: instructionTextSize }); this.variantLabel = addTextObject(this.scene, this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleVariant"), TextStyle.PARTY, { fontSize: instructionTextSize });
this.variantLabel.setName("text-variant-label"); this.variantLabel.setName("text-variant-label");
this.goFilterIconElement = new Phaser.GameObjects.Sprite(this.scene, this.filterInstructionRowX, this.filterInstructionRowY, "keyboard", "C.png");
this.goFilterIconElement.setName("sprite-goFilter-icon-element");
this.goFilterIconElement.setScale(0.675);
this.goFilterIconElement.setOrigin(0.0, 0.0);
this.goFilterLabel = addTextObject(this.scene, this.filterInstructionRowX + this.instructionRowTextOffset, this.filterInstructionRowY, i18next.t("starterSelectUiHandler:goFilter"), TextStyle.PARTY, { fontSize: instructionTextSize });
this.goFilterLabel.setName("text-goFilter-label");
this.hideInstructions(); this.hideInstructions();
this.filterInstructionsContainer = this.scene.add.container(50, 5);
this.filterInstructionsContainer.setVisible(true);
this.starterSelectContainer.add(this.filterInstructionsContainer);
this.starterSelectMessageBoxContainer = this.scene.add.container(0, this.scene.game.canvas.height / 6); this.starterSelectMessageBoxContainer = this.scene.add.container(0, this.scene.game.canvas.height / 6);
this.starterSelectMessageBoxContainer.setVisible(false); this.starterSelectMessageBoxContainer.setVisible(false);
this.starterSelectContainer.add(this.starterSelectMessageBoxContainer); this.starterSelectContainer.add(this.starterSelectMessageBoxContainer);
@ -1175,6 +1193,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.tryExit(); this.tryExit();
success = true; success = true;
} }
} else if (button === Button.STATS) {
// if stats button is pressed, go to filter directly
if (!this.filterMode) {
this.startCursorObj.setVisible(false);
this.starterIconsCursorObj.setVisible(false);
this.setSpecies(null);
this.filterBarCursor = 0;
this.setFilterMode(true);
this.filterBar.toggleDropDown(this.filterBarCursor);
}
} else if (this.startCursorObj.visible) { // this checks to see if the start button is selected } else if (this.startCursorObj.visible) { // this checks to see if the start button is selected
switch (button) { switch (button) {
case Button.ACTION: case Button.ACTION:
@ -2026,6 +2054,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
case SettingKeyboard.Button_Cycle_Variant: case SettingKeyboard.Button_Cycle_Variant:
iconPath = "V.png"; iconPath = "V.png";
break; break;
case SettingKeyboard.Button_Stats:
iconPath = "C.png";
break;
default: default:
break; break;
} }
@ -2045,11 +2076,36 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
} }
} }
updateFilterButtonIcon(iconSetting, gamepadType, iconElement, controlLabel): void {
let iconPath;
// touch controls cannot be rebound as is, and are just emulating a keyboard event.
// Additionally, since keyboard controls can be rebound (and will be displayed when they are), we need to have special handling for the touch controls
if (gamepadType === "touch") {
gamepadType = "keyboard";
iconPath = "C.png";
} else {
iconPath = this.scene.inputController?.getIconForLatestInputRecorded(iconSetting);
}
iconElement.setTexture(gamepadType, iconPath);
iconElement.setPosition(this.filterInstructionRowX, this.filterInstructionRowY);
controlLabel.setPosition(this.filterInstructionRowX + this.instructionRowTextOffset, this.filterInstructionRowY);
iconElement.setVisible(true);
controlLabel.setVisible(true);
this.filterInstructionsContainer.add([iconElement, controlLabel]);
this.filterInstructionRowY += 8;
if (this.filterInstructionRowY >= 24) {
this.filterInstructionRowY = 0;
this.filterInstructionRowX += 50;
}
}
updateInstructions(): void { updateInstructions(): void {
this.instructionRowX = 0; this.instructionRowX = 0;
this.instructionRowY = 0; this.instructionRowY = 0;
this.filterInstructionRowX = 0;
this.filterInstructionRowY = 0;
this.hideInstructions(); this.hideInstructions();
this.instructionsContainer.removeAll(); this.instructionsContainer.removeAll();
this.filterInstructionsContainer.removeAll();
let gamepadType; let gamepadType;
if (this.scene.inputMethod === "gamepad") { if (this.scene.inputMethod === "gamepad") {
gamepadType = this.scene.inputController.getConfig(this.scene.inputController.selectedDevice[Device.GAMEPAD]).padType; gamepadType = this.scene.inputController.getConfig(this.scene.inputController.selectedDevice[Device.GAMEPAD]).padType;
@ -2057,6 +2113,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
gamepadType = this.scene.inputMethod; gamepadType = this.scene.inputMethod;
} }
if (!gamepadType) {
return;
}
if (this.speciesStarterDexEntry?.caughtAttr) { if (this.speciesStarterDexEntry?.caughtAttr) {
if (this.canCycleShiny) { if (this.canCycleShiny) {
this.updateButtonIcon(SettingKeyboard.Button_Cycle_Shiny, gamepadType, this.shinyIconElement, this.shinyLabel); this.updateButtonIcon(SettingKeyboard.Button_Cycle_Shiny, gamepadType, this.shinyIconElement, this.shinyLabel);
@ -2077,6 +2137,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.updateButtonIcon(SettingKeyboard.Button_Cycle_Variant, gamepadType, this.variantIconElement, this.variantLabel); this.updateButtonIcon(SettingKeyboard.Button_Cycle_Variant, gamepadType, this.variantIconElement, this.variantLabel);
} }
} }
// if filter mode is inactivated and gamepadType is not undefined, update the button icons
if (!this.filterMode) {
this.updateFilterButtonIcon(SettingKeyboard.Button_Stats, gamepadType, this.goFilterIconElement, this.goFilterLabel);
}
} }
getValueLimit(): integer { getValueLimit(): integer {
@ -2398,6 +2464,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.setCursor(filterMode ? this.filterBarCursor : this.cursor); this.setCursor(filterMode ? this.filterBarCursor : this.cursor);
if (filterMode) { if (filterMode) {
this.setSpecies(null); this.setSpecies(null);
this.updateInstructions();
} }
return true; return true;
@ -3260,6 +3327,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.natureLabel.setVisible(false); this.natureLabel.setVisible(false);
this.variantIconElement.setVisible(false); this.variantIconElement.setVisible(false);
this.variantLabel.setVisible(false); this.variantLabel.setVisible(false);
this.goFilterIconElement.setVisible(false);
this.goFilterLabel.setVisible(false);
} }
clear(): void { clear(): void {