diff --git a/public/battle-anims/encounter-magma-bg.json b/public/battle-anims/encounter-magma-bg.json index 1e144b9d4f6..bb22f721d9a 100644 --- a/public/battle-anims/encounter-magma-bg.json +++ b/public/battle-anims/encounter-magma-bg.json @@ -1,5 +1,4 @@ { - "graphic": "Encounter Magma Bg", "frames": [ [], [], @@ -27,9 +26,6 @@ [], [], [], - [], - [], - [], [] ], "frameTimedEvents": { @@ -49,7 +45,7 @@ "bgX": 0, "bgY": 0, "opacity": 255, - "duration": 4, + "duration": 12, "eventType": "AnimTimedUpdateBgEvent" } ], @@ -60,7 +56,7 @@ "bgX": 0, "bgY": 0, "opacity": 0, - "duration": 7, + "duration": 8, "eventType": "AnimTimedUpdateBgEvent" } ] diff --git a/public/battle-anims/encounter-magma-spout.json b/public/battle-anims/encounter-magma-spout.json index 9e0acea61fa..21f3bec585f 100644 --- a/public/battle-anims/encounter-magma-spout.json +++ b/public/battle-anims/encounter-magma-spout.json @@ -11,7 +11,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -23,7 +23,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -35,6 +35,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -48,7 +49,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -60,7 +61,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -72,6 +73,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -85,7 +87,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -97,7 +99,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -109,6 +111,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -122,7 +125,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -134,7 +137,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -146,6 +149,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -159,7 +163,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -171,7 +175,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -183,6 +187,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -196,7 +201,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -208,7 +213,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -220,6 +225,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -233,7 +239,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -245,7 +251,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -257,6 +263,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -270,7 +277,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -282,7 +289,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -294,6 +301,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -307,7 +315,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -319,6 +327,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -330,6 +339,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -343,7 +353,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -355,6 +365,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -366,7 +377,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 } ], @@ -380,6 +391,7 @@ "target": 2, "graphicFrame": 10, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -391,6 +403,7 @@ "target": 2, "graphicFrame": 10, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -402,7 +415,7 @@ "target": 2, "graphicFrame": 10, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 } ], @@ -416,6 +429,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -427,7 +441,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -439,7 +453,7 @@ "target": 2, "graphicFrame": 7, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 } ], @@ -453,7 +467,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -465,7 +479,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -477,6 +491,7 @@ "target": 2, "graphicFrame": 8, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -490,7 +505,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -502,6 +517,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -513,6 +529,7 @@ "target": 2, "graphicFrame": 9, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -526,7 +543,7 @@ "target": 2, "graphicFrame": 10, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -538,6 +555,7 @@ "target": 2, "graphicFrame": 10, "opacity": 255, + "priority": 4, "focus": 1 }, { @@ -549,7 +567,8 @@ "target": 2, "graphicFrame": 10, "opacity": 255, - "priority": 1, + "priority": 4, + "priority": 4, "focus": 1 } ], @@ -563,7 +582,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -575,7 +594,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -587,6 +606,7 @@ "target": 2, "graphicFrame": 6, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -600,7 +620,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -612,7 +632,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -624,6 +644,7 @@ "target": 2, "graphicFrame": 5, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -637,7 +658,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -649,7 +670,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -661,6 +682,7 @@ "target": 2, "graphicFrame": 4, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -674,7 +696,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -686,7 +708,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -698,6 +720,7 @@ "target": 2, "graphicFrame": 3, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -711,7 +734,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -723,7 +746,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -735,6 +758,7 @@ "target": 2, "graphicFrame": 2, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -748,7 +772,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -760,7 +784,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -772,6 +796,7 @@ "target": 2, "graphicFrame": 1, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -785,7 +810,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -797,7 +822,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -809,6 +834,7 @@ "target": 2, "graphicFrame": 0, "opacity": 255, + "priority": 4, "focus": 1 } ], @@ -822,7 +848,7 @@ "target": 2, "graphicFrame": 0, "opacity": 130, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -834,7 +860,7 @@ "target": 2, "graphicFrame": 0, "opacity": 130, - "priority": 1, + "priority": 4, "focus": 1 }, { @@ -846,6 +872,7 @@ "target": 2, "graphicFrame": 0, "opacity": 140, + "priority": 4, "focus": 1 } ] diff --git a/public/images/mystery-encounters/exclaim.json b/public/images/mystery-encounters/exclaim.json new file mode 100644 index 00000000000..31231910097 --- /dev/null +++ b/public/images/mystery-encounters/exclaim.json @@ -0,0 +1,41 @@ +{ + "textures": [ + { + "image": "exclaim.png", + "format": "RGBA8888", + "size": { + "w": 32, + "h": 32 + }, + "scale": 1, + "frames": [ + { + "filename": "0001.png", + "rotated": false, + "trimmed": false, + "sourceSize": { + "w": 32, + "h": 32 + }, + "spriteSourceSize": { + "x": 0, + "y": 0, + "w": 32, + "h": 32 + }, + "frame": { + "x": 0, + "y": 0, + "w": 32, + "h": 32 + } + } + ] + } + ], + "meta": { + "app": "https://www.codeandweb.com/texturepacker", + "version": "3.0", + "smartupdate": "$TexturePacker:SmartUpdate:895f0a79b89fa0fb44167f4584fd9a22:357b46953b7e17c6b2f43a62d52855d8:cc1ed0e4f90aaa9dcf1b39a0af1283b0$" + } +} diff --git a/public/images/mystery-encounters/exclaim.png b/public/images/mystery-encounters/exclaim.png new file mode 100644 index 00000000000..a7727f4da2e Binary files /dev/null and b/public/images/mystery-encounters/exclaim.png differ diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 6cde5929308..954b24fd10b 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1,43 +1,42 @@ import Phaser from "phaser"; import UI from "./ui/ui"; -import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase } from "./phases"; -import Pokemon, { PlayerPokemon, EnemyPokemon } from "./field/pokemon"; -import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species"; -import {Constructor, isNullOrUndefined} from "#app/utils"; +import { LevelCapPhase, LoginPhase, MessagePhase, MovePhase, NewBiomeEncounterPhase, NextEncounterPhase, ReturnPhase, SelectBiomePhase, ShowTrainerPhase, SwitchPhase, TitlePhase, TurnInitPhase } from "./phases"; +import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon"; +import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "./data/pokemon-species"; +import { Constructor, isNullOrUndefined } from "#app/utils"; import * as Utils from "./utils"; -import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier"; +import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, TerastallizeModifier } from "./modifier/modifier"; import { PokeballType } from "./data/pokeball"; import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims"; import { Phase } from "./phase"; import { initGameSpeed } from "./system/game-speed"; import { Arena, ArenaBase } from "./field/arena"; import { GameData } from "./system/game-data"; -import { TextStyle, addTextObject, getTextColor } from "./ui/text"; +import { addTextObject, getTextColor, TextStyle } from "./ui/text"; import { allMoves } from "./data/move"; -import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getPartyLuckValue, PokemonHeldItemModifierType } from "./modifier/modifier-type"; +import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getPartyLuckValue, ModifierPoolType, PokemonHeldItemModifierType } from "./modifier/modifier-type"; import AbilityBar from "./ui/ability-bar"; -import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, PostBattleInitAbAttr, applyAbAttrs, applyPostBattleInitAbAttrs } from "./data/ability"; -import { allAbilities } from "./data/ability"; +import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, PostBattleInitAbAttr } from "./data/ability"; import Battle, { BattleType, FixedBattleConfig } from "./battle"; import { GameMode, GameModes, getGameMode } from "./game-mode"; import FieldSpritePipeline from "./pipelines/field-sprite"; import SpritePipeline from "./pipelines/sprite"; import PartyExpBar from "./ui/party-exp-bar"; -import { TrainerSlot, trainerConfigs } from "./data/trainer-config"; +import { trainerConfigs, TrainerSlot } from "./data/trainer-config"; import Trainer, { TrainerVariant } from "./field/trainer"; import TrainerData from "./system/trainer-data"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import { pokemonPrevolutions } from "./data/pokemon-evolutions"; import PokeballTray from "./ui/pokeball-tray"; import InvertPostFX from "./pipelines/invert"; -import { Achv, ModifierAchv, MoneyAchv, achvs } from "./system/achv"; +import { Achv, achvs, ModifierAchv, MoneyAchv } from "./system/achv"; import { Voucher, vouchers } from "./system/voucher"; import { Gender } from "./data/gender"; import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; import { addUiThemeOverrides } from "./ui/ui-theme"; import PokemonData from "./system/pokemon-data"; import { Nature } from "./data/nature"; -import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges } from "./data/pokemon-forms"; +import { pokemonFormChanges, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "./data/pokemon-forms"; import { FormChangePhase, QuietFormChangePhase } from "./form-change-phase"; import { getTypeRgb } from "./data/type"; import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler"; @@ -50,8 +49,8 @@ import CandyBar from "./ui/candy-bar"; import { Variant, variantData } from "./data/variant"; import { Localizable } from "#app/interfaces/locales"; import * as Overrides from "./overrides"; -import {InputsController} from "./inputs-controller"; -import {UiInputs} from "./ui-inputs"; +import { InputsController } from "./inputs-controller"; +import { UiInputs } from "./ui-inputs"; import { NewArenaEvent } from "./events/battle-scene"; import ArenaFlyout from "./ui/arena-flyout"; import { EaseType } from "#enums/ease-type"; @@ -68,7 +67,7 @@ import { UiTheme } from "#enums/ui-theme"; import { TimedEventManager } from "#app/timed-event-manager.js"; import i18next from "i18next"; import IMysteryEncounter, { MysteryEncounterTier, MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter"; -import { mysteryEncountersByBiome, allMysteryEncounters, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters"; +import { allMysteryEncounters, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, mysteryEncountersByBiome, WIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters"; import { MysteryEncounterData } from "#app/data/mystery-encounters/mystery-encounter-data"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; @@ -1039,6 +1038,8 @@ export default class BattleScene extends SceneBase { const playerField = this.getPlayerField(); + this.newArena(Biome.VOLCANO); + if (this.gameMode.isFixedBattle(newWaveIndex) && trainerData === undefined) { battleConfig = this.gameMode.getFixedBattle(newWaveIndex); newDouble = battleConfig.double; diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 234e827b648..81536d74771 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -718,14 +718,16 @@ export abstract class BattleAnim { public target: Pokemon; public sprites: Phaser.GameObjects.Sprite[]; public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle; + public playOnEmptyField: boolean; private srcLine: number[]; private dstLine: number[]; - constructor(user: Pokemon, target: Pokemon) { + constructor(user: Pokemon, target: Pokemon, playOnEmptyField: boolean = false) { this.user = user; this.target = target; this.sprites = []; + this.playOnEmptyField = playOnEmptyField; } abstract getAnim(): AnimConfig; @@ -799,7 +801,7 @@ export abstract class BattleAnim { const user = !isOppAnim ? this.user : this.target; const target = !isOppAnim ? this.target : this.user; - if (!target.isOnField()) { + if (!target.isOnField() && !this.playOnEmptyField) { if (callback) { callback(); } @@ -1097,10 +1099,7 @@ export abstract class BattleAnim { let r = anim.frames.length; let f = 0; - const fieldSprites = scene.field.getAll(); - const playerFieldSprite = fieldSprites[1]; - const enemyFieldSprite = fieldSprites[3]; - const trainerSprite = fieldSprites[5]; + const existingFieldSprites = [...scene.field.getAll()]; scene.tweens.addCounter({ duration: Utils.getFrameMs(3) * frameTimeMult, @@ -1127,41 +1126,24 @@ export abstract class BattleAnim { const graphicIndex = g++; const moveSprite = sprites[graphicIndex]; - if (spritePriorities[graphicIndex] !== frame.priority) { - spritePriorities[graphicIndex] = frame.priority; + spritePriorities[graphicIndex] = frame.priority; + if (!isNullOrUndefined(frame.priority)) { const setSpritePriority = (priority: integer) => { - if (priority < 0) { - // Move to top of scene - scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); - } else if (priority === 1) { - // Move above player field - if (playerFieldSprite) { - scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, playerFieldSprite); + try { + if (existingFieldSprites.length > priority) { + // Move to specified priority index + scene.field.moveTo(moveSprite, scene.field.getIndex(existingFieldSprites[priority])); } else { - setSpritePriority(-1); + // Move to top of scene + scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); } - } else if (priority === 3) { - // Move above player enemy field - if (enemyFieldSprite) { - scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, enemyFieldSprite); - } else { - setSpritePriority(-1); - } - } else if (priority === 5) { - // Move above player trainer sprite - if (trainerSprite) { - scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, trainerSprite); - } else { - setSpritePriority(-1); - } - } else { - setSpritePriority(-1); + } catch (ignored) { + console.log("index is no longer valid"); } }; setSpritePriority(frame.priority); } moveSprite.setFrame(frame.graphicFrame); - //console.log(AnimFocus[frame.focus]); const graphicFrameData = frameData.get(frame.target).get(graphicIndex); moveSprite.setPosition(graphicFrameData.x, graphicFrameData.y); @@ -1219,8 +1201,8 @@ export abstract class BattleAnim { export class CommonBattleAnim extends BattleAnim { public commonAnim: CommonAnim; - constructor(commonAnim: CommonAnim, user: Pokemon, target?: Pokemon) { - super(user, target || user); + constructor(commonAnim: CommonAnim, user: Pokemon, target?: Pokemon, playOnEmptyField: boolean = false) { + super(user, target || user, playOnEmptyField); this.commonAnim = commonAnim; } diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index ae025a909b1..2e8a5ff67bd 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -48,7 +48,7 @@ export const DepartmentStoreSaleEncounter: IMysteryEncounter = speaker: `${namespace}:speaker`, }, ]) - .withHideIntroVisuals(false) + .withAutoHideIntroVisuals(false) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index da0016db360..45ac52eb161 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -56,7 +56,7 @@ export const FieldTripEncounter: IMysteryEncounter = speaker: `${namespace}:speaker`, }, ]) - .withHideIntroVisuals(false) + .withAutoHideIntroVisuals(false) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index f4c9db5e4f8..2a7c26995d3 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -1,5 +1,5 @@ import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; -import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, initCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { applyDamageToPokemon, EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, initCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { modifierTypes, } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "../../../battle-scene"; @@ -14,25 +14,33 @@ import { PokemonMove } from "#app/field/pokemon"; import { Moves } from "#enums/moves"; import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims"; import { WeatherType } from "#app/data/weather"; -import { randSeedInt } from "#app/utils"; +import { isNullOrUndefined, randSeedInt } from "#app/utils"; +import { StatusEffect } from "#app/data/status-effect"; +import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:fieryFallout"; +/** + * Damage percentage taken when suffering the heat. + * Can be a number between `0` - `100`. + * The higher the more damage taken (100% = instant KO). + */ +const DAMAGE_PERCENTAGE: number = 20; + /** * Fiery Fallout encounter. * @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/88 | GitHub Issue #88} * @see For biome requirements check [mysteryEncountersByBiome](../mystery-encounters.ts) */ export const FieryFalloutEncounter: IMysteryEncounter = - MysteryEncounterBuilder.withEncounterType( - MysteryEncounterType.FIERY_FALLOUT - ) + MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIERY_FALLOUT) .withEncounterTier(MysteryEncounterTier.COMMON) .withSceneWaveRangeRequirement(40, 180) // waves 10 to 180 .withCatchAllowed(true) .withIntroSpriteConfigs([]) // Set in onInit() .withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT) + .withAutoHideIntroVisuals(false) .withIntroDialogue([ { text: `${namespace}:intro`, @@ -44,7 +52,6 @@ export const FieryFalloutEncounter: IMysteryEncounter = // Calculate boss mons const volcaronaSpecies = getPokemonSpecies(Species.VOLCARONA); const config: EnemyPartyConfig = { - levelAdditiveMultiplier: 0.25, pokemonConfigs: [ { species: volcaronaSpecies, @@ -62,12 +69,31 @@ export const FieryFalloutEncounter: IMysteryEncounter = }; encounter.enemyPartyConfigs = [config]; - // Sets weather for 5 turns - scene.arena.trySetWeather(WeatherType.SUNNY, true); + // Load hidden Volcarona sprites + encounter.spriteConfigs = [ + { + spriteKey: volcaronaSpecies.getSpriteId(false), + fileRoot: "pokemon", + repeat: true, + hidden: true, + hasShadow: true, + x: -20 + }, + { + spriteKey: volcaronaSpecies.getSpriteId(true ), + fileRoot: "pokemon", + repeat: true, + hidden: true, + hasShadow: true, + x: 20 + }, + ]; // Load animations/sfx for Volcarona moves initCustomMovesForEncounter(scene, [Moves.FIRE_SPIN, Moves.QUIVER_DANCE]); + scene.arena.trySetWeather(WeatherType.SUNNY, true); + return true; }) .withOnVisualsStart((scene: BattleScene) => { @@ -75,13 +101,13 @@ export const FieryFalloutEncounter: IMysteryEncounter = const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon(), scene.getPlayerPokemon()); background.playWithoutTargets(scene, 200, 70, 2, 3); const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon(), scene.getPlayerPokemon()); - animation.playWithoutTargets(scene, 100, 100, 2); - const increment = 600; - for (let i = 3; i < 6; i++) { - scene.time.delayedCall((increment) * (i - 2), () => { - animation.playWithoutTargets(scene, randSeedInt(12) * 15, 150 - randSeedInt(10) * 15, 2); - }); - } + animation.playWithoutTargets(scene, 80, 100, 2); + scene.time.delayedCall(600, () => { + animation.playWithoutTargets(scene, -20, 100, 2); + }); + scene.time.delayedCall(1200, () => { + animation.playWithoutTargets(scene, 140, 150, 2); + }); return true; }) @@ -142,15 +168,36 @@ export const FieryFalloutEncounter: IMysteryEncounter = ], }, async (scene: BattleScene) => { - // Damage party and burn 1 random member + // Damage non-fire types and burn 1 random non-fire type member + const encounter = scene.currentBattle.mysteryEncounter; + const nonFireTypes = scene.getParty().filter((p) => !p.getTypes().includes(Type.FIRE)); + + for (const pkm of nonFireTypes) { + const percentage = DAMAGE_PERCENTAGE / 100; + const damage = Math.floor(pkm.getMaxHp() * percentage); + applyDamageToPokemon(scene, pkm, damage); + } + + // Burn random member + const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status.effect) || p.status?.effect === StatusEffect.BURN); + if (burnable?.length > 0) { + const chosenPokemon = burnable[randSeedInt(burnable.length - 1)]; + if (chosenPokemon.trySetStatus(StatusEffect.BURN)) { + // Burn applied + encounter.setDialogueToken("burnedPokemon", chosenPokemon.name); + queueEncounterMessage(scene, `${namespace}:option:2:target_burned`); + } + } + // No rewards - leaveEncounterWithoutBattle(scene); + leaveEncounterWithoutBattle(scene, true); } ) .withOption( new MysteryEncounterOptionBuilder() .withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL) - .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,2)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically + .withPrimaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3PrimaryName dialogue token automatically + .withSecondaryPokemonRequirement(new TypeRequirement(Type.FIRE, true,1)) // Will set option3SecondaryName dialogue token automatically .withDialogue({ buttonLabel: `${namespace}:option:3:label`, buttonTooltip: `${namespace}:option:3:tooltip`, @@ -161,12 +208,20 @@ export const FieryFalloutEncounter: IMysteryEncounter = }, ], }) + .withPreOptionPhase(async (scene: BattleScene) => { + transitionMysteryEncounterIntroVisuals(scene, false, false, 2000); + }) .withOptionPhase(async (scene: BattleScene) => { // Fire types help calm the Volcarona - // const encounter = scene.currentBattle.mysteryEncounter; + const encounter = scene.currentBattle.mysteryEncounter; + transitionMysteryEncounterIntroVisuals(scene); const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]); setEncounterRewards(scene, { guaranteedModifierTypeOptions: [charcoal], fillRemaining: true }); - setEncounterExp(scene, scene.getParty().map(p => p.id), 500); + + const primary = encounter.options[2].primaryPokemon; + const secondary = encounter.options[2].secondaryPokemon[0]; + + setEncounterExp(scene, [primary.id, secondary.id], getPokemonSpecies(Species.VOLCARONA).baseExp * 2); leaveEncounterWithoutBattle(scene); }) .build() diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 67ca533e893..a93df6cb264 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -20,7 +20,7 @@ export const MysteriousChestEncounter: IMysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHEST) .withEncounterTier(MysteryEncounterTier.COMMON) .withSceneWaveRangeRequirement(10, 180) // waves 2 to 180 - .withHideIntroVisuals(false) + .withAutoHideIntroVisuals(false) .withIntroSpriteConfigs([ { spriteKey: "chest_blue", diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 5b65cb12260..289800d0b52 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -60,13 +60,23 @@ export default interface IMysteryEncounter { encounterTier?: MysteryEncounterTier; encounterAnimations?: EncounterAnim[]; hideBattleIntroMessage?: boolean; - hideIntroVisuals?: boolean; + autoHideIntroVisuals?: boolean; catchAllowed?: boolean; maxAllowedEncounters?: number; - doEncounterExp?: (scene: BattleScene) => boolean; - doEncounterRewards?: (scene: BattleScene) => boolean; + + /** + * Event callback functions + */ + /** Event when Encounter is first loaded, use it for data conditioning */ onInit?: (scene: BattleScene) => boolean; + /** Event when battlefield visuals have finished sliding in and the encounter dialogue begins */ onVisualsStart?: (scene: BattleScene) => boolean; + /** Event right before MysteryEncounterPhase begins. Use for unshifting any phases before the actual encounter */ + onPreMysteryEncounterPhase?: (scene: BattleScene) => boolean; + /** Will provide the player party EXP before rewards are displayed for that wave */ + doEncounterExp?: (scene: BattleScene) => boolean; + /** Will provide the player a rewards shop for that wave */ + doEncounterRewards?: (scene: BattleScene) => boolean; /** * Requirements @@ -171,7 +181,7 @@ export default class IMysteryEncounter implements IMysteryEncounter { this.encounterVariant = MysteryEncounterVariant.DEFAULT; this.requirements = this.requirements ? this.requirements : []; this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false; - this.hideIntroVisuals = !isNullOrUndefined(this.hideIntroVisuals) ? this.hideIntroVisuals : true; + this.autoHideIntroVisuals = !isNullOrUndefined(this.autoHideIntroVisuals) ? this.autoHideIntroVisuals : true; this.startOfBattleEffects = this.startOfBattleEffects ?? []; // Reset any dirty flags or encounter data @@ -377,10 +387,13 @@ export class MysteryEncounterBuilder implements Partial { secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = []; excludePrimaryFromSupportRequirements?: boolean; dialogueTokens?: Record; + doEncounterExp?: (scene: BattleScene) => boolean; doEncounterRewards?: (scene: BattleScene) => boolean; onInit?: (scene: BattleScene) => boolean; onVisualsStart?: (scene: BattleScene) => boolean; + onPreMysteryEncounterPhase?: (scene: BattleScene) => boolean; + hideBattleIntroMessage?: boolean; hideIntroVisuals?: boolean; enemyPartyConfigs?: EnemyPartyConfig[] = []; @@ -612,6 +625,18 @@ export class MysteryEncounterBuilder implements Partial { return Object.assign(this, { onInit: onInit }); } + /** + * Event callback right before MysteryEncounterPhase begins. + * Use for unshifting any last-minute phases before the actual encounter, as all phases are cleared and reset at that point. + * Example: set the weather before encounter begins + * + * @param onVisualsStart - synchronous callback function to perform immediately before MysteryEncounterPhase begins + * @returns + */ + withOnPreMysteryEncounterPhase(onPreMysteryEncounterPhase: (scene: BattleScene) => boolean): this & Required> { + return Object.assign(this, { onPreMysteryEncounterPhase: onPreMysteryEncounterPhase }); + } + /** * Can be used to perform some extra logic (usually animations) when the enemy field is finished sliding in * @@ -651,11 +676,11 @@ export class MysteryEncounterBuilder implements Partial { } /** - * @param hideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter + * @param autoHideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter * @returns */ - withHideIntroVisuals(hideIntroVisuals: boolean): this & Required> { - return Object.assign(this, { hideIntroVisuals: hideIntroVisuals }); + withAutoHideIntroVisuals(autoHideIntroVisuals: boolean): this & Required> { + return Object.assign(this, { autoHideIntroVisuals: autoHideIntroVisuals }); } /** diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 44b9f7c8c7e..ef542971057 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -30,6 +30,29 @@ import { Gender } from "#app/data/gender"; import { Moves } from "#enums/moves"; import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; +export function doTrainerExclamation(scene: BattleScene) { + const exclamationSprite = scene.addFieldSprite(0, 0, "exclaim"); + exclamationSprite.setName("exclamation"); + scene.field.add(exclamationSprite); + scene.field.moveTo(exclamationSprite, scene.field.list.length - 1); + exclamationSprite.setVisible(true); + exclamationSprite.setPosition(110, 68); + scene.tweens.add({ + targets: exclamationSprite, + y: "-=25", + ease: "Cubic.easeOut", + duration: 300, + yoyo: true, + onComplete: () => { + scene.time.delayedCall(800, () => { + scene.field.remove(exclamationSprite, true); + }); + } + }); + + scene.playSound("GEN8- Exclaim.wav"); +} + export interface EnemyPokemonConfig { species: PokemonSpecies; isBoss: boolean; @@ -452,7 +475,7 @@ export function setEncounterExp(scene: BattleScene, participantId: integer | int const nonFaintedPartyMembers = party.filter(p => p.hp); const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < scene.getMaxExpLevel()); const partyMemberExp = []; - let expValue = baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1); + let expValue = baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1) / 5 + 1; if (participantIds?.length > 0) { if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.TRAINER_BATTLE) { @@ -590,23 +613,40 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase: } } -export function hideMysteryEncounterIntroVisuals(scene: BattleScene): Promise { +/** + * + * @param scene + * @param hide - If true, performs ease out and hide visuals. If false, eases in visuals. Defaults to true + * @param destroy - If true, will destroy visuals ONLY ON HIDE TRANSITION. Does nothing on show. Defaults to true + * @param duration + */ +export function transitionMysteryEncounterIntroVisuals(scene: BattleScene, hide: boolean = true, destroy: boolean = true, duration: number = 750): Promise { return new Promise(resolve => { const introVisuals = scene.currentBattle.mysteryEncounter.introVisuals; if (introVisuals) { - // Hide + if (!hide) { + // Make sure visuals are in proper state for showing + introVisuals.setVisible(true); + introVisuals.x += 16; + introVisuals.y -= 16; + introVisuals.alpha = 0; + } + + // Transition scene.tweens.add({ targets: introVisuals, - x: "+=16", - y: "-=16", - alpha: 0, + x: hide ? "+=16" : "-=16", + y: hide ? "-=16" : "+=16", + alpha: hide ? 0 : 1, ease: "Sine.easeInOut", - duration: 750, + duration: duration, onComplete: () => { - scene.field.remove(introVisuals); - introVisuals.setVisible(false); - introVisuals.destroy(); - scene.currentBattle.mysteryEncounter.introVisuals = null; + if (hide && destroy) { + scene.field.remove(introVisuals); + introVisuals.setVisible(false); + introVisuals.destroy(); + scene.currentBattle.mysteryEncounter.introVisuals = null; + } resolve(true); } }); diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index 15bf282d4df..b505c4f971d 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -405,8 +405,9 @@ function removePb(scene: BattleScene, pokeball: Phaser.GameObjects.Sprite) { }); } -export function doPokemonFlee(scene: BattleScene, pokemon: EnemyPokemon): Promise { - return new Promise(resolve => { +export async function doPokemonFlee(scene: BattleScene, pokemon: EnemyPokemon): Promise { + await new Promise(resolve => { + scene.playSound("flee"); // Ease pokemon out scene.tweens.add({ targets: pokemon, diff --git a/src/field/arena.ts b/src/field/arena.ts index b4c474ce474..d767f84a135 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -317,7 +317,7 @@ export class Arena { this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType, this.weather?.turnsLeft)); if (this.weather) { - this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1))); + this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1), true)); this.scene.queueMessage(getWeatherStartMessage(weather)); } else { this.scene.queueMessage(getWeatherClearMessage(oldWeatherType)); diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index bacf107f482..fb894869adf 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -32,6 +32,8 @@ export class MysteryEncounterSpriteConfig { disableAnimation?: boolean = false; /** Repeat the animation. Defaults to `false` */ repeat?: boolean = false; + /** Hidden at start of encounter. Defaults to `false` */ + hidden?: boolean = false; /** Tint color. `0` - `1`. Higher means darker tint. */ tint?: number; /** X offset */ @@ -105,6 +107,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con tintSprite = getItemSprite(spriteKey); } + sprite.setVisible(!config.hidden); tintSprite.setVisible(false); if (scale) { @@ -345,6 +348,13 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con this.untint(tintSprite, duration, ease); }); } + + setVisible(value: boolean): this { + this.getSprites().forEach(sprite => { + sprite.setVisible(value); + }); + return super.setVisible(value); + } } export default interface MysteryEncounterIntroVisuals { diff --git a/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts index 59808bc35ac..6f93cb239b1 100644 --- a/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts +++ b/src/locales/en/mystery-encounters/fiery-fallout-dialogue.ts @@ -13,16 +13,16 @@ export const fieryFalloutDialogue = { 2: { label: "Hunker down", tooltip: "(-) Suffer the effects of the weather", - selected: `The weather effects cause significant harm as you struggle to find shelter! - $Your party takes 30% Max HP damage! - $Your {burnTarget} also becomes burned!`, + selected: `The weather effects cause significant\nharm as you struggle to find shelter! + $Your party takes 20% Max HP damage!`, + target_burned: "Your {{burnedPokemon}} also became burned!" }, 3: { label: "Your Fire types help", tooltip: "(+) End the conditions\n(+) Gain a Charcoal", disabled_tooltip: "You need at least 2 Fire Type Pokémon to choose this", - selected: `Your {{primaryPokemonName}} and {{secondaryPokemonName}} guide you to where two Volcarona are in the middle of a mating dance! - $Thankfully, your Pokémon are able to calm them, and they depart without issue.`, + selected: `Your {{option3PrimaryName}} and {{option3SecondaryName}} guide you to where two Volcarona are in the middle of a mating dance! + $Thankfully, your Pokémon are able to calm them,\nand they depart without issue.`, }, } }; diff --git a/src/phases.ts b/src/phases.ts index 5e4bdf29943..2e6c8224de2 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -67,7 +67,7 @@ import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; import { MysteryEncounterVariant } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { handleEncounterStartOfBattleEffects, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; +import { doTrainerExclamation, handleEncounterStartOfBattleEffects, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; @@ -901,6 +901,15 @@ export class EncounterPhase extends BattlePhase { battle.mysteryEncounter = newEncounter; } loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter.introVisuals.initSprite())); + // Load Mystery Encounter Exclamation bubble and sfx + loadEnemyAssets.push(new Promise(resolve => { + this.scene.loadSe("GEN8- Exclaim.wav", "battle_anims", "GEN8- Exclaim.wav"); + this.scene.loadAtlas("exclaim", "mystery-encounters"); + this.scene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve()); + if (!this.scene.load.isLoading()) { + this.scene.load.start(); + } + })); } else { // This block only applies for double battles to init the boss segments (idk why it's split up like this) if (battle.enemyParty.filter(p => p.isBoss()).length > 1) { @@ -1091,8 +1100,13 @@ export class EncounterPhase extends BattlePhase { const doShowEncounterOptions = () => { this.scene.ui.clearText(); this.scene.ui.getMessageHandler().hideNameText(); - this.scene.unshiftPhase(new MysteryEncounterPhase(this.scene)); + // Can add any additional unshift phases here before MysteryEncounterPhase begins (and all phase queues are cleared) + if (this.scene.currentBattle.mysteryEncounter.onPreMysteryEncounterPhase) { + this.scene.currentBattle.mysteryEncounter.onPreMysteryEncounterPhase(this.scene); + } + + this.scene.unshiftPhase(new MysteryEncounterPhase(this.scene)); this.end(); }; @@ -1125,6 +1139,7 @@ export class EncounterPhase extends BattlePhase { if (!encounterMessage) { doEncounter(); } else { + doTrainerExclamation(this.scene); this.scene.ui.showDialogue(encounterMessage, "???", null, () => { this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doEncounter())); }); @@ -2706,12 +2721,14 @@ export class NewBattlePhase extends BattlePhase { export class CommonAnimPhase extends PokemonPhase { private anim: CommonAnim; private targetIndex: integer; + private playOnEmptyField: boolean; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, targetIndex: BattlerIndex, anim: CommonAnim) { + constructor(scene: BattleScene, battlerIndex: BattlerIndex, targetIndex: BattlerIndex, anim: CommonAnim, playOnEmptyField: boolean = false) { super(scene, battlerIndex); this.anim = anim; this.targetIndex = targetIndex; + this.playOnEmptyField = playOnEmptyField; } setAnimation(anim: CommonAnim) { @@ -2719,7 +2736,8 @@ export class CommonAnimPhase extends PokemonPhase { } start() { - new CommonBattleAnim(this.anim, this.getPokemon(), this.targetIndex !== undefined ? (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex] : this.getPokemon()).play(this.scene, () => { + const target = this.targetIndex !== undefined ? (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex] : this.getPokemon(); + new CommonBattleAnim(this.anim, this.getPokemon(), target, this.playOnEmptyField).play(this.scene, () => { this.end(); }); } diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 6f474500838..1ac00cbea8e 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -2,7 +2,7 @@ import i18next from "i18next"; import BattleScene from "../battle-scene"; import { Phase } from "../phase"; import { Mode } from "../ui/ui"; -import { hideMysteryEncounterIntroVisuals, OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils"; +import { transitionMysteryEncounterIntroVisuals, OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils"; import { CheckSwitchPhase, NewBattlePhase, ReturnPhase, ScanIvsPhase, SelectModifierPhase, SummonPhase, ToggleDoublePositionPhase } from "../phases"; import MysteryEncounterOption, { OptionPhaseCallback } from "../data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterVariant } from "../data/mystery-encounters/mystery-encounter"; @@ -148,8 +148,8 @@ export class MysteryEncounterOptionSelectedPhase extends Phase { start() { super.start(); - if (this.scene.currentBattle.mysteryEncounter.hideIntroVisuals) { - hideMysteryEncounterIntroVisuals(this.scene).then(() => { + if (this.scene.currentBattle.mysteryEncounter.autoHideIntroVisuals) { + transitionMysteryEncounterIntroVisuals(this.scene).then(() => { this.scene.executeWithSeedOffset(() => { this.onOptionSelect(this.scene).finally(() => { this.end();