2024-05-23 16:03:10 +01:00
import BattleScene , { bypassLogin } from "./battle-scene" ;
2024-03-01 01:08:50 +00:00
import { default as Pokemon , PlayerPokemon , EnemyPokemon , PokemonMove , MoveResult , DamageResult , FieldPosition , HitResult , TurnMove } from "./field/pokemon" ;
2024-05-23 16:03:10 +01:00
import * as Utils from "./utils" ;
import { allMoves , applyMoveAttrs , BypassSleepAttr , ChargeAttr , applyFilteredMoveAttrs , HitsTagAttr , MissEffectAttr , MoveAttr , MoveEffectAttr , MoveFlags , MultiHitAttr , OverrideMoveEffectAttr , VariableAccuracyAttr , MoveTarget , getMoveTargets , MoveTargetSet , MoveEffectTrigger , CopyMoveAttr , AttackMove , SelfStatusMove , PreMoveMessageAttr , HealStatusEffectAttr , IgnoreOpponentStatChangesAttr , NoEffectAttr , BypassRedirectAttr , FixedDamageAttr , PostVictoryStatChangeAttr , OneHitKOAccuracyAttr , ForceSwitchOutAttr , VariableTargetAttr , IncrementMovePriorityAttr } from "./data/move" ;
import { Mode } from "./ui/ui" ;
2023-04-10 19:12:01 +01:00
import { Command } from "./ui/command-ui-handler" ;
2023-04-20 20:46:05 +01:00
import { Stat } from "./data/pokemon-stat" ;
2024-05-31 20:31:11 +01:00
import { BerryModifier , ContactHeldItemTransferChanceModifier , EnemyAttackStatusEffectChanceModifier , EnemyPersistentModifier , EnemyStatusEffectHealChanceModifier , EnemyTurnHealModifier , ExpBalanceModifier , ExpBoosterModifier , ExpShareModifier , ExtraModifierModifier , FlinchChanceModifier , HealingBoosterModifier , HitHealModifier , LapsingPersistentModifier , MapModifier , Modifier , MultipleParticipantExpBonusModifier , PersistentModifier , PokemonExpBoosterModifier , PokemonHeldItemModifier , PokemonInstantReviveModifier , SwitchEffectTransferModifier , TempBattleStatBoosterModifier , TurnHealModifier , TurnHeldItemTransferModifier , MoneyMultiplierModifier , MoneyInterestModifier , IvScannerModifier , LapsingPokemonHeldItemModifier , PokemonMultiHitModifier , PokemonMoveAccuracyBoosterModifier , overrideModifiers , overrideHeldItems , BypassSpeedChanceModifier , TurnStatusEffectModifier } from "./modifier/modifier" ;
2023-04-11 16:04:39 +01:00
import PartyUiHandler , { PartyOption , PartyUiMode } from "./ui/party-ui-handler" ;
2023-07-04 22:50:51 +01:00
import { doPokeballBounceAnim , getPokeballAtlasKey , getPokeballCatchMultiplier , getPokeballTintColor , PokeballType } from "./data/pokeball" ;
2023-04-20 20:46:05 +01:00
import { CommonAnim , CommonBattleAnim , MoveAnim , initMoveAnim , loadMoveAnimAssets } from "./data/battle-anims" ;
2023-05-19 21:13:11 +01:00
import { StatusEffect , getStatusEffectActivationText , getStatusEffectCatchRateMultiplier , getStatusEffectHealText , getStatusEffectObtainText , getStatusEffectOverlapText } from "./data/status-effect" ;
2023-04-10 19:12:01 +01:00
import { SummaryUiMode } from "./ui/summary-ui-handler" ;
import EvolutionSceneHandler from "./ui/evolution-scene-handler" ;
import { EvolutionPhase } from "./evolution-phase" ;
2024-02-22 04:57:49 +00:00
import { Phase } from "./phase" ;
2023-04-20 20:46:05 +01:00
import { BattleStat , getBattleStatLevelChangeDescription , getBattleStatName } from "./data/battle-stat" ;
2024-04-16 20:58:02 +01:00
import { biomeLinks , getBiomeName } from "./data/biomes" ;
2024-01-13 17:24:24 +00:00
import { ModifierTier } from "./modifier/modifier-tier" ;
2024-05-01 15:16:58 +01:00
import { FusePokemonModifierType , ModifierPoolType , ModifierType , ModifierTypeFunc , ModifierTypeOption , PokemonModifierType , PokemonMoveModifierType , PokemonPpRestoreModifierType , PokemonPpUpModifierType , RememberMoveModifierType , TmModifierType , getDailyRunStarterModifiers , getEnemyBuffModifierForWave , getModifierType , getPlayerModifierTypeOptions , getPlayerShopModifierTypeOptionsForWave , modifierTypes , regenerateModifierPoolThresholds } from "./modifier/modifier-type" ;
2023-04-13 00:09:15 +01:00
import SoundFade from "phaser3-rex-plugins/plugins/soundfade" ;
2024-03-13 21:09:23 +00:00
import { BattlerTagLapseType , EncoreTag , HideSpriteTag as HiddenTag , ProtectedTag , TrappedTag } from "./data/battler-tags" ;
2024-06-06 14:36:12 +01:00
import { getPokemonMessage , getPokemonNameWithAffix } from "./messages" ;
2023-04-18 03:44:41 +01:00
import { Starter } from "./ui/starter-select-ui-handler" ;
2023-04-20 20:46:05 +01:00
import { Gender } from "./data/gender" ;
2024-03-18 22:03:13 +00:00
import { Weather , WeatherType , getRandomWeatherType , getTerrainBlockMessage , getWeatherDamageMessage , getWeatherLapseMessage } from "./data/weather" ;
2023-04-20 20:46:05 +01:00
import { TempBattleStat } from "./data/temp-battle-stat" ;
2024-01-16 04:29:22 +00:00
import { ArenaTagSide , ArenaTrapTag , MistTag , TrickRoomTag } from "./data/arena-tag" ;
2024-06-15 17:06:32 +01:00
import { CheckTrappedAbAttr , IgnoreOpponentStatChangesAbAttr , IgnoreOpponentEvasionAbAttr , PostAttackAbAttr , PostBattleAbAttr , PostDefendAbAttr , PostSummonAbAttr , PostTurnAbAttr , PostWeatherLapseAbAttr , PreSwitchOutAbAttr , PreWeatherDamageAbAttr , ProtectStatAbAttr , RedirectMoveAbAttr , BlockRedirectAbAttr , RunSuccessAbAttr , StatChangeMultiplierAbAttr , SuppressWeatherEffectAbAttr , SyncEncounterNatureAbAttr , applyAbAttrs , applyCheckTrappedAbAttrs , applyPostAttackAbAttrs , applyPostBattleAbAttrs , applyPostDefendAbAttrs , applyPostSummonAbAttrs , applyPostTurnAbAttrs , applyPostWeatherLapseAbAttrs , applyPreStatChangeAbAttrs , applyPreSwitchOutAbAttrs , applyPreWeatherEffectAbAttrs , BattleStatMultiplierAbAttr , applyBattleStatMultiplierAbAttrs , IncrementMovePriorityAbAttr , applyPostVictoryAbAttrs , PostVictoryAbAttr , BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr , applyPostKnockOutAbAttrs , PostKnockOutAbAttr , PostBiomeChangeAbAttr , applyPostFaintAbAttrs , PostFaintAbAttr , IncreasePpAbAttr , PostStatChangeAbAttr , applyPostStatChangeAbAttrs , AlwaysHitAbAttr , PreventBerryUseAbAttr , StatChangeCopyAbAttr , PokemonTypeChangeAbAttr , applyPreAttackAbAttrs , applyPostMoveUsedAbAttrs , PostMoveUsedAbAttr , MaxMultiHitAbAttr , HealFromBerryUseAbAttr , WonderSkinAbAttr , applyPreDefendAbAttrs } from "./data/ability" ;
2023-04-29 06:40:24 +01:00
import { Unlockables , getUnlockableName } from "./system/unlockables" ;
2024-03-01 01:08:50 +00:00
import { getBiomeKey } from "./field/arena" ;
2023-10-07 21:08:33 +01:00
import { BattleType , BattlerIndex , TurnCommand } from "./battle" ;
2024-06-08 06:07:23 +01:00
import { ChallengeAchv , HealAchv , LevelAchv , achvs } from "./system/achv" ;
2024-05-23 16:03:10 +01:00
import { TrainerSlot , trainerConfigs } from "./data/trainer-config" ;
2023-12-20 04:51:48 +00:00
import { EggHatchPhase } from "./egg-hatch-phase" ;
import { Egg } from "./data/egg" ;
import { vouchers } from "./system/voucher" ;
2024-06-16 03:06:40 +01:00
import { clientSessionId , loggedInUser , updateUserInfo } from "./account" ;
2024-06-06 01:11:07 +01:00
import { SessionSaveData } from "./system/game-data" ;
2024-03-01 01:08:50 +00:00
import { addPokeballCaptureStars , addPokeballOpenParticles } from "./field/anims" ;
2024-03-30 22:46:33 +00:00
import { SpeciesFormChangeActiveTrigger , SpeciesFormChangeManualTrigger , SpeciesFormChangeMoveLearnedTrigger , SpeciesFormChangePostMoveTrigger , SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms" ;
2024-05-17 02:41:38 +01:00
import { battleSpecDialogue , getCharVariantFromDialogue , miscDialogue } from "./data/dialogue" ;
2024-02-04 05:30:19 +00:00
import ModifierSelectUiHandler , { SHOP_OPTIONS_ROW_LIMIT } from "./ui/modifier-select-ui-handler" ;
2024-06-04 00:57:47 +01:00
import { SettingKeys } from "./system/settings/settings" ;
2024-02-13 23:42:11 +00:00
import { Tutorial , handleTutorial } from "./tutorial" ;
2024-03-10 05:14:09 +00:00
import { TerrainType } from "./data/terrain" ;
2024-03-15 19:13:32 +00:00
import { OptionSelectConfig , OptionSelectItem } from "./ui/abstact-option-select-ui-handler" ;
import { SaveSlotUiMode } from "./ui/save-slot-select-ui-handler" ;
2024-03-17 02:06:56 +00:00
import { fetchDailyRunSeed , getDailyRunStarters } from "./data/daily-run" ;
2024-06-08 06:07:23 +01:00
import { GameMode , GameModes , getGameMode } from "./game-mode" ;
2024-05-23 16:03:10 +01:00
import PokemonSpecies , { getPokemonSpecies , speciesStarters } from "./data/pokemon-species" ;
import i18next from "./plugins/i18n" ;
import * as Overrides from "./overrides" ;
2024-05-17 02:41:38 +01:00
import { TextStyle , addTextObject } from "./ui/text" ;
2024-05-17 04:09:40 +01:00
import { Type } from "./data/type" ;
2024-06-11 02:19:07 +01:00
import { BerryUsedEvent , EncounterPhaseEvent , MoveUsedEvent , TurnEndEvent , TurnInitEvent } from "./events/battle-scene" ;
2024-06-13 23:44:23 +01:00
import { Abilities } from "#enums/abilities" ;
import { ArenaTagType } from "#enums/arena-tag-type" ;
import { BattleSpec } from "#enums/battle-spec" ;
import { BattleStyle } from "#enums/battle-style" ;
import { BattlerTagType } from "#enums/battler-tag-type" ;
import { Biome } from "#enums/biome" ;
import { ExpNotification } from "#enums/exp-notification" ;
import { Moves } from "#enums/moves" ;
import { PlayerGender } from "#enums/player-gender" ;
import { Species } from "#enums/species" ;
import { TrainerType } from "#enums/trainer-type" ;
2024-05-17 04:09:40 +01:00
2023-12-30 23:41:25 +00:00
2024-02-22 04:57:49 +00:00
export class LoginPhase extends Phase {
2023-12-30 23:41:25 +00:00
private showText : boolean ;
constructor ( scene : BattleScene , showText? : boolean ) {
super ( scene ) ;
this . showText = showText === undefined || ! ! showText ;
}
start ( ) : void {
super . start ( ) ;
2024-04-10 06:32:49 +01:00
const hasSession = ! ! Utils . getCookie ( Utils . sessionIdKey ) ;
2023-12-30 23:41:25 +00:00
this . scene . ui . setMode ( Mode . LOADING , { buttonActions : [ ] } ) ;
2024-04-10 06:32:49 +01:00
Utils . executeIf ( bypassLogin || hasSession , updateUserInfo ) . then ( response = > {
const success = response ? response [ 0 ] : false ;
const statusCode = response ? response [ 1 ] : null ;
2023-12-30 23:41:25 +00:00
if ( ! success ) {
2024-04-10 06:32:49 +01:00
if ( ! statusCode || statusCode === 400 ) {
2024-05-23 16:03:10 +01:00
if ( this . showText ) {
this . scene . ui . showText ( i18next . t ( "menu:logInOrCreateAccount" ) ) ;
}
2024-05-24 00:45:04 +01:00
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "menu_open" ) ;
2024-04-10 06:32:49 +01:00
const loadData = ( ) = > {
updateUserInfo ( ) . then ( ( ) = > this . scene . gameData . loadSystem ( ) . then ( ( ) = > this . end ( ) ) ) ;
} ;
2024-05-24 00:45:04 +01:00
2024-04-10 06:32:49 +01:00
this . scene . ui . setMode ( Mode . LOGIN_FORM , {
buttonActions : [
( ) = > {
this . scene . ui . playSelect ( ) ;
loadData ( ) ;
} , ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "menu_open" ) ;
2024-04-10 06:32:49 +01:00
this . scene . ui . setMode ( Mode . REGISTRATION_FORM , {
buttonActions : [
( ) = > {
this . scene . ui . playSelect ( ) ;
updateUserInfo ( ) . then ( ( ) = > this . end ( ) ) ;
} , ( ) = > {
this . scene . unshiftPhase ( new LoginPhase ( this . scene , false ) ) ;
this . end ( ) ;
}
]
} ) ;
}
]
} ) ;
} else {
this . scene . unshiftPhase ( new UnavailablePhase ( this . scene ) ) ;
super . end ( ) ;
}
2023-12-30 23:41:25 +00:00
return null ;
2024-02-20 01:36:10 +00:00
} else {
this . scene . gameData . loadSystem ( ) . then ( success = > {
2024-05-23 16:03:10 +01:00
if ( success || bypassLogin ) {
2024-02-20 01:36:10 +00:00
this . end ( ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-02-20 01:36:10 +00:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "menu:failedToLoadSaveData" ) ) ;
2024-02-20 01:36:10 +00:00
}
} ) ;
}
2023-12-30 23:41:25 +00:00
} ) ;
}
2024-01-01 03:49:50 +00:00
end ( ) : void {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-05-23 16:03:10 +01:00
if ( ! this . scene . gameData . gender ) {
2024-02-20 01:36:10 +00:00
this . scene . unshiftPhase ( new SelectGenderPhase ( this . scene ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-24 00:45:04 +01:00
2024-02-13 23:42:11 +00:00
handleTutorial ( this . scene , Tutorial . Intro ) . then ( ( ) = > super . end ( ) ) ;
2024-01-01 03:49:50 +00:00
}
2023-12-30 23:41:25 +00:00
}
2023-04-10 19:12:01 +01:00
2024-03-15 01:49:49 +00:00
export class TitlePhase extends Phase {
2023-04-28 20:03:42 +01:00
private loaded : boolean ;
2024-03-21 17:12:05 +00:00
private lastSessionData : SessionSaveData ;
2024-06-07 23:33:45 +01:00
public gameMode : GameModes ;
2023-04-28 20:03:42 +01:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
this . loaded = false ;
}
start ( ) : void {
2023-12-30 23:41:25 +00:00
super . start ( ) ;
2024-03-15 01:49:49 +00:00
2024-03-17 02:06:56 +00:00
this . scene . ui . clearText ( ) ;
2024-03-15 19:13:32 +00:00
this . scene . ui . fadeIn ( 250 ) ;
2024-05-23 16:03:10 +01:00
this . scene . playBgm ( "title" , true ) ;
2024-03-15 19:13:32 +00:00
2024-03-21 17:12:05 +00:00
this . scene . gameData . getSession ( loggedInUser . lastSessionSlot ) . then ( sessionData = > {
2024-03-21 17:17:43 +00:00
if ( sessionData ) {
this . lastSessionData = sessionData ;
const biomeKey = getBiomeKey ( sessionData . arena . biome ) ;
const bgTexture = ` ${ biomeKey } _bg ` ;
this . scene . arenaBg . setTexture ( bgTexture ) ;
}
2024-03-21 17:12:05 +00:00
this . showOptions ( ) ;
2024-03-22 00:45:15 +00:00
} ) . catch ( err = > {
console . error ( err ) ;
this . showOptions ( ) ;
2024-03-21 17:12:05 +00:00
} ) ;
2024-03-15 19:13:32 +00:00
}
showOptions ( ) : void {
2024-03-15 01:49:49 +00:00
const options : OptionSelectItem [ ] = [ ] ;
2024-03-22 00:45:15 +00:00
if ( loggedInUser . lastSessionSlot > - 1 ) {
2024-03-15 01:49:49 +00:00
options . push ( {
2024-05-23 16:03:10 +01:00
label : i18next.t ( "menu:continue" ) ,
2024-04-13 23:59:58 +01:00
handler : ( ) = > {
this . loadSaveSlot ( this . lastSessionData ? - 1 : loggedInUser.lastSessionSlot ) ;
return true ;
}
2024-03-15 01:49:49 +00:00
} ) ;
}
options . push ( {
2024-05-23 16:03:10 +01:00
label : i18next.t ( "menu:newGame" ) ,
2024-03-15 01:49:49 +00:00
handler : ( ) = > {
2024-04-07 04:50:26 +01:00
const setModeAndEnd = ( gameMode : GameModes ) = > {
this . gameMode = gameMode ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . clearText ( ) ;
this . end ( ) ;
} ;
if ( this . scene . gameData . unlocks [ Unlockables . ENDLESS_MODE ] ) {
2024-04-13 23:59:58 +01:00
const options : OptionSelectItem [ ] = [
2024-04-07 04:50:26 +01:00
{
2024-06-08 06:07:23 +01:00
label : GameMode.getModeName ( GameModes . CLASSIC ) ,
2024-04-13 23:59:58 +01:00
handler : ( ) = > {
setModeAndEnd ( GameModes . CLASSIC ) ;
return true ;
}
2024-04-07 04:50:26 +01:00
} ,
{
2024-06-08 06:07:23 +01:00
label : GameMode.getModeName ( GameModes . CHALLENGE ) ,
handler : ( ) = > {
setModeAndEnd ( GameModes . CHALLENGE ) ;
return true ;
}
} ,
{
label : GameMode.getModeName ( GameModes . ENDLESS ) ,
2024-04-13 23:59:58 +01:00
handler : ( ) = > {
setModeAndEnd ( GameModes . ENDLESS ) ;
return true ;
}
2024-04-07 04:50:26 +01:00
}
] ;
if ( this . scene . gameData . unlocks [ Unlockables . SPLICED_ENDLESS_MODE ] ) {
options . push ( {
2024-06-08 06:07:23 +01:00
label : GameMode.getModeName ( GameModes . SPLICED_ENDLESS ) ,
2024-04-13 23:59:58 +01:00
handler : ( ) = > {
setModeAndEnd ( GameModes . SPLICED_ENDLESS ) ;
return true ;
}
2024-04-07 04:50:26 +01:00
} ) ;
}
options . push ( {
2024-05-23 16:03:10 +01:00
label : i18next.t ( "menu:cancel" ) ,
2024-04-07 04:50:26 +01:00
handler : ( ) = > {
this . scene . clearPhaseQueue ( ) ;
this . scene . pushPhase ( new TitlePhase ( this . scene ) ) ;
super . end ( ) ;
2024-04-13 23:59:58 +01:00
return true ;
2024-04-07 04:50:26 +01:00
}
} ) ;
2024-04-12 04:47:03 +01:00
this . scene . ui . showText ( i18next . t ( "menu:selectGameMode" ) , null , ( ) = > this . scene . ui . setOverlayMode ( Mode . OPTION_SELECT , { options : options } ) ) ;
2024-04-07 05:06:44 +01:00
} else {
this . gameMode = GameModes . CLASSIC ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . clearText ( ) ;
2024-04-07 04:50:26 +01:00
this . end ( ) ;
2024-04-07 05:06:44 +01:00
}
2024-04-13 23:59:58 +01:00
return true ;
2024-03-15 01:49:49 +00:00
}
} ,
2024-03-15 19:13:32 +00:00
{
2024-05-23 16:03:10 +01:00
label : i18next.t ( "menu:loadGame" ) ,
2024-04-13 23:59:58 +01:00
handler : ( ) = > {
this . scene . ui . setOverlayMode ( Mode . SAVE_SLOT , SaveSlotUiMode . LOAD ,
( slotId : integer ) = > {
2024-05-23 16:03:10 +01:00
if ( slotId === - 1 ) {
2024-04-13 23:59:58 +01:00
return this . showOptions ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-13 23:59:58 +01:00
this . loadSaveSlot ( slotId ) ;
} ) ;
return true ;
}
2024-03-17 02:06:56 +00:00
} ,
2024-03-16 01:59:34 +00:00
{
2024-05-23 16:03:10 +01:00
label : i18next.t ( "menu:dailyRun" ) ,
2024-04-13 23:59:58 +01:00
handler : ( ) = > {
this . initDailyRun ( ) ;
return true ;
} ,
2024-03-15 01:49:49 +00:00
keepOpen : true
2024-05-28 21:51:33 +01:00
} ,
{
label : i18next.t ( "menu:settings" ) ,
handler : ( ) = > {
this . scene . ui . setOverlayMode ( Mode . SETTINGS ) ;
return true ;
} ,
keepOpen : true
2024-03-17 02:06:56 +00:00
} ) ;
2024-03-15 19:13:32 +00:00
const config : OptionSelectConfig = {
options : options ,
2024-03-21 17:12:05 +00:00
noCancel : true ,
yOffset : 47
2024-03-15 19:13:32 +00:00
} ;
2024-03-21 17:12:05 +00:00
this . scene . ui . setMode ( Mode . TITLE , config ) ;
2024-03-15 19:13:32 +00:00
}
loadSaveSlot ( slotId : integer ) : void {
2024-03-21 17:12:05 +00:00
this . scene . sessionSlotId = slotId > - 1 ? slotId : loggedInUser.lastSessionSlot ;
2024-03-15 19:13:32 +00:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-03-21 17:12:05 +00:00
this . scene . gameData . loadSession ( this . scene , slotId , slotId === - 1 ? this . lastSessionData : null ) . then ( ( success : boolean ) = > {
2024-03-15 19:13:32 +00:00
if ( success ) {
this . loaded = true ;
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "menu:sessionSuccess" ) , null , ( ) = > this . end ( ) ) ;
} else {
2024-03-15 19:13:32 +00:00
this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-15 19:13:32 +00:00
} ) . catch ( err = > {
console . error ( err ) ;
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "menu:failedToLoadSession" ) , null ) ;
2023-04-28 20:03:42 +01:00
} ) ;
}
2024-03-16 01:59:34 +00:00
initDailyRun ( ) : void {
this . scene . ui . setMode ( Mode . SAVE_SLOT , SaveSlotUiMode . SAVE , ( slotId : integer ) = > {
this . scene . clearPhaseQueue ( ) ;
if ( slotId === - 1 ) {
this . scene . pushPhase ( new TitlePhase ( this . scene ) ) ;
2024-03-17 21:30:07 +00:00
return super . end ( ) ;
2024-03-16 01:59:34 +00:00
}
this . scene . sessionSlotId = slotId ;
2024-03-17 02:06:56 +00:00
2024-05-14 20:42:30 +01:00
const generateDaily = ( seed : string ) = > {
2024-06-08 06:07:23 +01:00
this . scene . gameMode = getGameMode ( GameModes . DAILY ) ;
2024-03-25 15:00:42 +00:00
2024-03-17 02:06:56 +00:00
this . scene . setSeed ( seed ) ;
this . scene . resetSeed ( 1 ) ;
this . scene . money = this . scene . gameMode . getStartingMoney ( ) ;
const starters = getDailyRunStarters ( this . scene , seed ) ;
const startingLevel = this . scene . gameMode . getStartingLevel ( ) ;
const party = this . scene . getParty ( ) ;
const loadPokemonAssets : Promise < void > [ ] = [ ] ;
2024-05-23 16:03:10 +01:00
for ( const starter of starters ) {
2024-03-17 02:06:56 +00:00
const starterProps = this . scene . gameData . getSpeciesDexAttrProps ( starter . species , starter . dexAttr ) ;
const starterFormIndex = Math . min ( starterProps . formIndex , Math . max ( starter . species . forms . length - 1 , 0 ) ) ;
const starterGender = starter . species . malePercent !== null
? ! starterProps . female ? Gender.MALE : Gender.FEMALE
: Gender . GENDERLESS ;
2024-04-19 03:52:26 +01:00
const starterPokemon = this . scene . addPlayerPokemon ( starter . species , startingLevel , starter . abilityIndex , starterFormIndex , starterGender , starterProps . shiny , starterProps . variant , undefined , starter . nature ) ;
2024-03-17 02:06:56 +00:00
starterPokemon . setVisible ( false ) ;
party . push ( starterPokemon ) ;
loadPokemonAssets . push ( starterPokemon . loadAssets ( ) ) ;
}
2024-05-24 00:45:04 +01:00
2024-03-17 02:06:56 +00:00
regenerateModifierPoolThresholds ( party , ModifierPoolType . DAILY_STARTER ) ;
const modifiers : Modifier [ ] = Array ( 3 ) . fill ( null ) . map ( ( ) = > modifierTypes . EXP_SHARE ( ) . withIdFromFunc ( modifierTypes . EXP_SHARE ) . newModifier ( ) )
. concat ( Array ( 3 ) . fill ( null ) . map ( ( ) = > modifierTypes . GOLDEN_EXP_CHARM ( ) . withIdFromFunc ( modifierTypes . GOLDEN_EXP_CHARM ) . newModifier ( ) ) )
. concat ( getDailyRunStarterModifiers ( party ) ) ;
2024-05-23 16:03:10 +01:00
for ( const m of modifiers ) {
2024-03-17 02:06:56 +00:00
this . scene . addModifier ( m , true , false , false , true ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-17 02:06:56 +00:00
this . scene . updateModifiers ( true , true ) ;
Promise . all ( loadPokemonAssets ) . then ( ( ) = > {
this . scene . time . delayedCall ( 500 , ( ) = > this . scene . playBgm ( ) ) ;
this . scene . gameData . gameStats . dailyRunSessionsPlayed ++ ;
2024-04-01 17:48:35 +01:00
this . scene . newArena ( this . scene . gameMode . getStartingBiome ( this . scene ) ) ;
2024-03-17 02:06:56 +00:00
this . scene . newBattle ( ) ;
2024-04-01 17:48:35 +01:00
this . scene . arena . init ( ) ;
2024-03-17 02:06:56 +00:00
this . scene . sessionPlayTime = 0 ;
2024-05-15 05:52:06 +01:00
this . scene . lastSavePlayTime = 0 ;
2024-03-17 02:06:56 +00:00
this . end ( ) ;
} ) ;
2024-05-14 20:42:30 +01:00
} ;
2024-05-24 00:45:04 +01:00
2024-05-14 20:42:30 +01:00
// If Online, calls seed fetch from db to generate daily run. If Offline, generates a daily run based on current date.
if ( ! Utils . isLocal ) {
fetchDailyRunSeed ( ) . then ( seed = > {
generateDaily ( seed ) ;
} ) . catch ( err = > {
console . error ( "Failed to load daily run:\n" , err ) ;
} ) ;
} else {
generateDaily ( btoa ( new Date ( ) . toISOString ( ) . substring ( 0 , 10 ) ) ) ;
}
2024-03-16 01:59:34 +00:00
} ) ;
}
2023-04-28 20:03:42 +01:00
end ( ) : void {
2024-03-16 01:59:34 +00:00
if ( ! this . loaded && ! this . scene . gameMode . isDaily ) {
2023-04-28 20:03:42 +01:00
this . scene . arena . preloadBgm ( ) ;
2024-06-08 06:07:23 +01:00
this . scene . gameMode = getGameMode ( this . gameMode ) ;
if ( this . gameMode === GameModes . CHALLENGE ) {
this . scene . pushPhase ( new SelectChallengePhase ( this . scene ) ) ;
} else {
this . scene . pushPhase ( new SelectStarterPhase ( this . scene ) ) ;
}
2024-04-01 17:48:35 +01:00
this . scene . newArena ( this . scene . gameMode . getStartingBiome ( this . scene ) ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-10-10 01:20:02 +01:00
this . scene . playBgm ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-28 20:03:42 +01:00
this . scene . pushPhase ( new EncounterPhase ( this . scene , this . loaded ) ) ;
2023-12-02 15:30:23 +00:00
if ( this . loaded ) {
2024-06-08 06:07:23 +01:00
const availablePartyMembers = this . scene . getParty ( ) . filter ( p = > p . isAllowedInBattle ( ) ) . length ;
2023-12-02 15:30:23 +00:00
2024-04-11 01:57:22 +01:00
this . scene . pushPhase ( new SummonPhase ( this . scene , 0 , true , true ) ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . double && availablePartyMembers > 1 ) {
2024-04-11 01:57:22 +01:00
this . scene . pushPhase ( new SummonPhase ( this . scene , 1 , true , true ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-21 08:16:17 +01:00
if ( this . scene . currentBattle . battleType !== BattleType . TRAINER && ( this . scene . currentBattle . waveIndex > 1 || ! this . scene . gameMode . isDaily ) ) {
const minPartySize = this . scene . currentBattle . double ? 2 : 1 ;
if ( availablePartyMembers > minPartySize ) {
this . scene . pushPhase ( new CheckSwitchPhase ( this . scene , 0 , this . scene . currentBattle . double ) ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . double ) {
2024-05-21 08:16:17 +01:00
this . scene . pushPhase ( new CheckSwitchPhase ( this . scene , 1 , this . scene . currentBattle . double ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-21 08:16:17 +01:00
}
2023-12-02 15:30:23 +00:00
}
2023-10-03 17:50:31 +01:00
}
2023-04-28 20:03:42 +01:00
2024-05-23 16:03:10 +01:00
for ( const achv of Object . keys ( this . scene . gameData . achvUnlocks ) ) {
if ( vouchers . hasOwnProperty ( achv ) ) {
2023-12-20 04:51:48 +00:00
this . scene . validateVoucher ( vouchers [ achv ] ) ;
2024-05-23 16:03:10 +01:00
}
2023-12-20 04:51:48 +00:00
}
2023-04-28 20:03:42 +01:00
super . end ( ) ;
}
}
2024-04-10 06:32:49 +01:00
export class UnavailablePhase extends Phase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start ( ) : void {
this . scene . ui . setMode ( Mode . UNAVAILABLE , ( ) = > {
this . scene . unshiftPhase ( new LoginPhase ( this . scene , true ) ) ;
this . end ( ) ;
} ) ;
}
}
2024-04-21 15:59:50 +01:00
export class ReloadSessionPhase extends Phase {
2024-05-15 05:52:06 +01:00
private systemDataStr : string ;
constructor ( scene : BattleScene , systemDataStr? : string ) {
2024-04-21 15:59:50 +01:00
super ( scene ) ;
2024-05-15 05:52:06 +01:00
this . systemDataStr = systemDataStr ;
2024-04-21 15:59:50 +01:00
}
start ( ) : void {
this . scene . ui . setMode ( Mode . SESSION_RELOAD ) ;
let delayElapsed = false ;
let loaded = false ;
this . scene . time . delayedCall ( Utils . fixedInt ( 1500 ) , ( ) = > {
2024-05-23 16:03:10 +01:00
if ( loaded ) {
2024-04-21 15:59:50 +01:00
this . end ( ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-04-21 15:59:50 +01:00
delayElapsed = true ;
2024-05-23 16:03:10 +01:00
}
2024-04-21 15:59:50 +01:00
} ) ;
2024-05-15 05:52:06 +01:00
this . scene . gameData . clearLocalData ( ) ;
( this . systemDataStr ? this . scene . gameData . initSystem ( this . systemDataStr ) : this . scene . gameData . loadSystem ( ) ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
if ( delayElapsed ) {
2024-04-21 15:59:50 +01:00
this . end ( ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-04-21 15:59:50 +01:00
loaded = true ;
2024-05-23 16:03:10 +01:00
}
2024-04-21 15:59:50 +01:00
} ) ;
}
}
2024-04-15 15:09:51 +01:00
export class OutdatedPhase extends Phase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start ( ) : void {
this . scene . ui . setMode ( Mode . OUTDATED ) ;
}
}
2024-02-22 04:57:49 +00:00
export class SelectGenderPhase extends Phase {
2024-02-06 21:15:35 +00:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
2024-05-24 00:45:04 +01:00
2024-02-06 21:15:35 +00:00
start ( ) : void {
super . start ( ) ;
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "menu:boyOrGirl" ) , null , ( ) = > {
2024-02-06 21:15:35 +00:00
this . scene . ui . setMode ( Mode . OPTION_SELECT , {
options : [
{
2024-05-23 16:03:10 +01:00
label : i18next.t ( "menu:boy" ) ,
2024-02-06 21:15:35 +00:00
handler : ( ) = > {
this . scene . gameData . gender = PlayerGender . MALE ;
2024-06-04 00:57:47 +01:00
this . scene . gameData . saveSetting ( SettingKeys . Player_Gender , 0 ) ;
2024-02-06 21:15:35 +00:00
this . scene . gameData . saveSystem ( ) . then ( ( ) = > this . end ( ) ) ;
2024-04-13 23:59:58 +01:00
return true ;
2024-02-06 21:15:35 +00:00
}
} ,
{
2024-05-23 16:03:10 +01:00
label : i18next.t ( "menu:girl" ) ,
2024-02-06 21:15:35 +00:00
handler : ( ) = > {
this . scene . gameData . gender = PlayerGender . FEMALE ;
2024-06-04 00:57:47 +01:00
this . scene . gameData . saveSetting ( SettingKeys . Player_Gender , 1 ) ;
2024-02-06 21:15:35 +00:00
this . scene . gameData . saveSystem ( ) . then ( ( ) = > this . end ( ) ) ;
2024-04-13 23:59:58 +01:00
return true ;
2024-02-06 21:15:35 +00:00
}
}
]
} ) ;
} ) ;
}
2024-03-17 14:11:24 +00:00
end ( ) : void {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
super . end ( ) ;
}
2024-02-06 21:15:35 +00:00
}
2024-06-08 06:07:23 +01:00
export class SelectChallengePhase extends Phase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
super . start ( ) ;
this . scene . playBgm ( "menu" ) ;
this . scene . ui . setMode ( Mode . CHALLENGE_SELECT ) ;
}
}
2024-02-22 04:57:49 +00:00
export class SelectStarterPhase extends Phase {
2024-04-07 04:50:26 +01:00
2024-06-08 06:07:23 +01:00
constructor ( scene : BattleScene ) {
2023-04-10 19:12:01 +01:00
super ( scene ) ;
}
start() {
super . start ( ) ;
2024-05-23 16:03:10 +01:00
this . scene . playBgm ( "menu" ) ;
2023-04-13 00:09:15 +01:00
2023-04-18 03:44:41 +01:00
this . scene . ui . setMode ( Mode . STARTER_SELECT , ( starters : Starter [ ] ) = > {
2024-03-15 19:13:32 +00:00
this . scene . ui . clearText ( ) ;
this . scene . ui . setMode ( Mode . SAVE_SLOT , SaveSlotUiMode . SAVE , ( slotId : integer ) = > {
if ( slotId === - 1 ) {
this . scene . clearPhaseQueue ( ) ;
this . scene . pushPhase ( new TitlePhase ( this . scene ) ) ;
return this . end ( ) ;
}
this . scene . sessionSlotId = slotId ;
2024-06-07 23:33:45 +01:00
this . initBattle ( starters ) ;
2023-04-13 00:09:15 +01:00
} ) ;
2024-06-08 06:07:23 +01:00
} ) ;
2023-04-10 19:12:01 +01:00
}
2024-06-07 23:33:45 +01:00
2024-06-13 10:36:12 +01:00
/ * *
* Initialize starters before starting the first battle
* @param starters { @linkcode Pokemon } with which to start the first battle
* /
2024-06-07 23:33:45 +01:00
initBattle ( starters : Starter [ ] ) {
const party = this . scene . getParty ( ) ;
const loadPokemonAssets : Promise < void > [ ] = [ ] ;
starters . forEach ( ( starter : Starter , i : integer ) = > {
if ( ! i && Overrides . STARTER_SPECIES_OVERRIDE ) {
starter . species = getPokemonSpecies ( Overrides . STARTER_SPECIES_OVERRIDE as Species ) ;
}
const starterProps = this . scene . gameData . getSpeciesDexAttrProps ( starter . species , starter . dexAttr ) ;
let starterFormIndex = Math . min ( starterProps . formIndex , Math . max ( starter . species . forms . length - 1 , 0 ) ) ;
2024-06-13 10:36:12 +01:00
if (
starter . species . speciesId in Overrides . STARTER_FORM_OVERRIDES &&
starter . species . forms [ Overrides . STARTER_FORM_OVERRIDES [ starter . species . speciesId ] ]
) {
starterFormIndex = Overrides . STARTER_FORM_OVERRIDES [ starter . species . speciesId ] ;
2024-06-07 23:33:45 +01:00
}
2024-06-13 10:36:12 +01:00
2024-06-07 23:33:45 +01:00
let starterGender = starter . species . malePercent !== null
? ! starterProps . female ? Gender.MALE : Gender.FEMALE
: Gender . GENDERLESS ;
if ( Overrides . GENDER_OVERRIDE !== null ) {
starterGender = Overrides . GENDER_OVERRIDE ;
}
const starterIvs = this . scene . gameData . dexData [ starter . species . speciesId ] . ivs . slice ( 0 ) ;
const starterPokemon = this . scene . addPlayerPokemon ( starter . species , this . scene . gameMode . getStartingLevel ( ) , starter . abilityIndex , starterFormIndex , starterGender , starterProps . shiny , starterProps . variant , starterIvs , starter . nature ) ;
starterPokemon . tryPopulateMoveset ( starter . moveset ) ;
if ( starter . passive ) {
starterPokemon . passive = true ;
}
starterPokemon . luck = this . scene . gameData . getDexAttrLuck ( this . scene . gameData . dexData [ starter . species . speciesId ] . caughtAttr ) ;
if ( starter . pokerus ) {
starterPokemon . pokerus = true ;
}
if ( this . scene . gameMode . isSplicedOnly ) {
starterPokemon . generateFusionSpecies ( true ) ;
}
starterPokemon . setVisible ( false ) ;
party . push ( starterPokemon ) ;
loadPokemonAssets . push ( starterPokemon . loadAssets ( ) ) ;
} ) ;
overrideModifiers ( this . scene ) ;
overrideHeldItems ( this . scene , party [ 0 ] ) ;
Promise . all ( loadPokemonAssets ) . then ( ( ) = > {
SoundFade . fadeOut ( this . scene , this . scene . sound . get ( "menu" ) , 500 , true ) ;
this . scene . time . delayedCall ( 500 , ( ) = > this . scene . playBgm ( ) ) ;
if ( this . scene . gameMode . isClassic ) {
this . scene . gameData . gameStats . classicSessionsPlayed ++ ;
} else {
this . scene . gameData . gameStats . endlessSessionsPlayed ++ ;
}
this . scene . newBattle ( ) ;
this . scene . arena . init ( ) ;
this . scene . sessionPlayTime = 0 ;
this . scene . lastSavePlayTime = 0 ;
this . end ( ) ;
} ) ;
}
2023-04-10 19:12:01 +01:00
}
2024-02-22 04:57:49 +00:00
export class BattlePhase extends Phase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
2024-03-21 04:57:28 +00:00
showEnemyTrainer ( trainerSlot : TrainerSlot = TrainerSlot . NONE ) : void {
const sprites = this . scene . currentBattle . trainer . getSprites ( ) ;
const tintSprites = this . scene . currentBattle . trainer . getTintSprites ( ) ;
for ( let i = 0 ; i < sprites . length ; i ++ ) {
const visible = ! trainerSlot || ! i === ( trainerSlot === TrainerSlot . TRAINER ) || sprites . length < 2 ;
[ sprites [ i ] , tintSprites [ i ] ] . map ( sprite = > {
2024-05-23 16:03:10 +01:00
if ( visible ) {
2024-03-27 14:20:08 +00:00
sprite . x = trainerSlot || sprites . length < 2 ? 0 : i ? 16 : - 16 ;
2024-05-23 16:03:10 +01:00
}
2024-03-21 04:57:28 +00:00
sprite . setVisible ( visible ) ;
sprite . clearTint ( ) ;
2024-05-23 16:03:10 +01:00
} ) ;
2024-03-21 04:57:28 +00:00
sprites [ i ] . setVisible ( visible ) ;
tintSprites [ i ] . setVisible ( visible ) ;
sprites [ i ] . clearTint ( ) ;
tintSprites [ i ] . clearTint ( ) ;
}
2024-02-22 04:57:49 +00:00
this . scene . tweens . add ( {
targets : this.scene.currentBattle.trainer ,
2024-05-23 16:03:10 +01:00
x : "-=16" ,
y : "+=16" ,
2024-02-22 04:57:49 +00:00
alpha : 1 ,
2024-05-23 16:03:10 +01:00
ease : "Sine.easeInOut" ,
2024-02-22 04:57:49 +00:00
duration : 750
} ) ;
}
hideEnemyTrainer ( ) : void {
this . scene . tweens . add ( {
targets : this.scene.currentBattle.trainer ,
2024-05-23 16:03:10 +01:00
x : "+=16" ,
y : "-=16" ,
2024-02-22 04:57:49 +00:00
alpha : 0 ,
2024-05-23 16:03:10 +01:00
ease : "Sine.easeInOut" ,
2024-02-22 04:57:49 +00:00
duration : 750
} ) ;
}
}
2023-05-18 16:11:06 +01:00
type PokemonFunc = ( pokemon : Pokemon ) = > void ;
export abstract class FieldPhase extends BattlePhase {
getOrder ( ) : BattlerIndex [ ] {
const playerField = this . scene . getPlayerField ( ) . filter ( p = > p . isActive ( ) ) as Pokemon [ ] ;
const enemyField = this . scene . getEnemyField ( ) . filter ( p = > p . isActive ( ) ) as Pokemon [ ] ;
2024-06-07 04:01:13 +01:00
// We shuffle the list before sorting so speed ties produce random results
2024-06-11 00:42:39 +01:00
let orderedTargets : Pokemon [ ] = playerField . concat ( enemyField ) ;
// We seed it with the current turn to prevent an inconsistency where it
// was varying based on how long since you last reloaded
this . scene . executeWithSeedOffset ( ( ) = > {
orderedTargets = Utils . randSeedShuffle ( orderedTargets ) ;
} , this . scene . currentBattle . turn , this . scene . waveSeed ) ;
orderedTargets . sort ( ( a : Pokemon , b : Pokemon ) = > {
2023-05-18 16:11:06 +01:00
const aSpeed = a ? . getBattleStat ( Stat . SPD ) || 0 ;
const bSpeed = b ? . getBattleStat ( Stat . SPD ) || 0 ;
2024-06-07 04:01:13 +01:00
return bSpeed - aSpeed ;
2023-05-18 16:11:06 +01:00
} ) ;
const speedReversed = new Utils . BooleanHolder ( false ) ;
this . scene . arena . applyTags ( TrickRoomTag , speedReversed ) ;
2024-05-23 16:03:10 +01:00
if ( speedReversed . value ) {
2023-05-18 16:11:06 +01:00
orderedTargets = orderedTargets . reverse ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
return orderedTargets . map ( t = > t . getFieldIndex ( ) + ( ! t . isPlayer ( ) ? BattlerIndex.ENEMY : 0 ) ) ;
}
executeForAll ( func : PokemonFunc ) : void {
2024-03-12 00:55:41 +00:00
const field = this . scene . getField ( true ) . filter ( p = > p . summonData ) ;
2023-05-18 16:11:06 +01:00
field . forEach ( pokemon = > func ( pokemon ) ) ;
}
}
export abstract class PokemonPhase extends FieldPhase {
2023-12-04 05:09:38 +00:00
protected battlerIndex : BattlerIndex | integer ;
2024-04-11 17:35:22 +01:00
public player : boolean ;
public fieldIndex : integer ;
2023-05-18 16:11:06 +01:00
2023-12-04 05:09:38 +00:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex | integer ) {
2023-05-18 16:11:06 +01:00
super ( scene ) ;
2024-05-23 16:03:10 +01:00
if ( battlerIndex === undefined ) {
2023-05-18 16:11:06 +01:00
battlerIndex = scene . getField ( ) . find ( p = > p ? . isActive ( ) ) . getBattlerIndex ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
this . battlerIndex = battlerIndex ;
this . player = battlerIndex < 2 ;
this . fieldIndex = battlerIndex % 2 ;
}
getPokemon() {
2024-05-23 16:03:10 +01:00
if ( this . battlerIndex > BattlerIndex . ENEMY_2 ) {
2023-12-04 05:09:38 +00:00
return this . scene . getPokemonById ( this . battlerIndex ) ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
return this . scene . getField ( ) [ this . battlerIndex ] ;
}
}
export abstract class PartyMemberPokemonPhase extends FieldPhase {
protected partyMemberIndex : integer ;
protected fieldIndex : integer ;
2023-10-07 21:08:33 +01:00
protected player : boolean ;
2023-05-18 16:11:06 +01:00
2023-10-07 21:08:33 +01:00
constructor ( scene : BattleScene , partyMemberIndex : integer , player : boolean ) {
2023-05-18 16:11:06 +01:00
super ( scene ) ;
this . partyMemberIndex = partyMemberIndex ;
this . fieldIndex = partyMemberIndex < this . scene . currentBattle . getBattlerCount ( )
? partyMemberIndex
: - 1 ;
2023-10-07 21:08:33 +01:00
this . player = player ;
2023-05-18 16:11:06 +01:00
}
2023-10-07 21:08:33 +01:00
getParty ( ) : Pokemon [ ] {
return this . player ? this . scene . getParty ( ) : this . scene . getEnemyParty ( ) ;
}
getPokemon ( ) : Pokemon {
return this . getParty ( ) [ this . partyMemberIndex ] ;
}
}
export abstract class PlayerPartyMemberPokemonPhase extends PartyMemberPokemonPhase {
constructor ( scene : BattleScene , partyMemberIndex : integer ) {
super ( scene , partyMemberIndex , true ) ;
}
getPlayerPokemon ( ) : PlayerPokemon {
return super . getPokemon ( ) as PlayerPokemon ;
}
}
export abstract class EnemyPartyMemberPokemonPhase extends PartyMemberPokemonPhase {
constructor ( scene : BattleScene , partyMemberIndex : integer ) {
super ( scene , partyMemberIndex , false ) ;
}
getEnemyPokemon ( ) : EnemyPokemon {
return super . getPokemon ( ) as EnemyPokemon ;
2023-05-18 16:11:06 +01:00
}
}
2023-04-10 19:12:01 +01:00
export class EncounterPhase extends BattlePhase {
2023-04-28 20:03:42 +01:00
private loaded : boolean ;
constructor ( scene : BattleScene , loaded? : boolean ) {
2023-04-10 19:12:01 +01:00
super ( scene ) ;
2023-04-28 20:03:42 +01:00
this . loaded = ! ! loaded ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
2024-05-10 11:42:28 +01:00
this . scene . updateGameInfo ( ) ;
2024-01-12 01:27:50 +00:00
this . scene . initSession ( ) ;
2024-01-13 17:24:24 +00:00
2024-06-06 04:57:55 +01:00
this . scene . eventTarget . dispatchEvent ( new EncounterPhaseEvent ( ) ) ;
2024-05-25 10:36:30 +01:00
// Failsafe if players somehow skip floor 200 in classic mode
if ( this . scene . gameMode . isClassic && this . scene . currentBattle . waveIndex > 200 ) {
this . scene . unshiftPhase ( new GameOverPhase ( this . scene ) ) ;
}
2023-05-18 16:11:06 +01:00
const loadEnemyAssets = [ ] ;
2023-04-10 19:12:01 +01:00
const battle = this . scene . currentBattle ;
2024-03-29 14:37:28 +00:00
let totalBst = 0 ;
2023-05-18 16:11:06 +01:00
battle . enemyLevels . forEach ( ( level , e ) = > {
2023-10-18 23:01:15 +01:00
if ( ! this . loaded ) {
2024-05-23 16:03:10 +01:00
if ( battle . battleType === BattleType . TRAINER ) {
2023-10-18 23:01:15 +01:00
battle . enemyParty [ e ] = battle . trainer . genPartyMember ( e ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-11-08 23:36:30 +00:00
const enemySpecies = this . scene . randomSpecies ( battle . waveIndex , level , true ) ;
2024-03-21 04:57:28 +00:00
battle . enemyParty [ e ] = this . scene . addEnemyPokemon ( enemySpecies , level , TrainerSlot . NONE , ! ! this . scene . getEncounterBossSegments ( battle . waveIndex , level , enemySpecies ) ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . battleSpec === BattleSpec . FINAL_BOSS ) {
2024-02-20 03:58:25 +00:00
battle . enemyParty [ e ] . ivs = new Array ( 6 ) . fill ( 31 ) ;
2024-05-23 16:03:10 +01:00
}
2024-01-06 03:24:05 +00:00
this . scene . getParty ( ) . slice ( 0 , ! battle . double ? 1 : 2 ) . reverse ( ) . forEach ( playerPokemon = > {
applyAbAttrs ( SyncEncounterNatureAbAttr , playerPokemon , null , battle . enemyParty [ e ] ) ;
} ) ;
2023-10-18 23:01:15 +01:00
}
}
2023-10-07 21:08:33 +01:00
const enemyPokemon = this . scene . getEnemyParty ( ) [ e ] ;
if ( e < ( battle . double ? 2 : 1 ) ) {
enemyPokemon . setX ( - 66 + enemyPokemon . getFieldPositionOffset ( ) [ 0 ] ) ;
enemyPokemon . resetSummonData ( ) ;
}
2023-04-18 06:32:26 +01:00
2024-05-23 16:03:10 +01:00
if ( ! this . loaded ) {
2024-04-22 16:28:13 +01:00
this . scene . gameData . setPokemonSeen ( enemyPokemon , true , battle . battleType === BattleType . TRAINER ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-08 01:16:03 +01:00
2024-02-26 17:34:45 +00:00
if ( enemyPokemon . species . speciesId === Species . ETERNATUS ) {
2024-03-14 20:26:57 +00:00
if ( this . scene . gameMode . isClassic && ( battle . battleSpec === BattleSpec . FINAL_BOSS || this . scene . gameMode . isWaveFinal ( battle . waveIndex ) ) ) {
2024-02-28 03:50:27 +00:00
if ( battle . battleSpec !== BattleSpec . FINAL_BOSS ) {
2024-02-26 17:34:45 +00:00
enemyPokemon . formIndex = 1 ;
2024-02-28 03:50:27 +00:00
enemyPokemon . updateScale ( ) ;
}
2024-02-26 17:34:45 +00:00
enemyPokemon . setBoss ( ) ;
2024-02-28 03:50:27 +00:00
} else if ( ! ( battle . waveIndex % 1000 ) ) {
2024-01-13 17:24:24 +00:00
enemyPokemon . formIndex = 1 ;
2024-02-28 03:50:27 +00:00
enemyPokemon . updateScale ( ) ;
}
2024-01-08 04:17:24 +00:00
}
2024-05-24 00:45:04 +01:00
2024-03-29 14:37:28 +00:00
totalBst += enemyPokemon . getSpeciesForm ( ) . baseTotal ;
2023-10-24 04:20:05 +01:00
2023-05-18 16:11:06 +01:00
loadEnemyAssets . push ( enemyPokemon . loadAssets ( ) ) ;
2024-05-24 00:45:04 +01:00
2023-12-01 22:23:26 +00:00
console . log ( enemyPokemon . name , enemyPokemon . species . speciesId , enemyPokemon . stats ) ;
2023-05-18 16:11:06 +01:00
} ) ;
2023-04-10 19:12:01 +01:00
2024-05-23 16:03:10 +01:00
if ( this . scene . getParty ( ) . filter ( p = > p . isShiny ( ) ) . length === 6 ) {
2024-04-07 04:14:13 +01:00
this . scene . validateAchv ( achvs . SHINY_PARTY ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-07 04:14:13 +01:00
2024-05-23 16:03:10 +01:00
if ( battle . battleType === BattleType . TRAINER ) {
2023-10-18 23:01:15 +01:00
loadEnemyAssets . push ( battle . trainer . loadAssets ( ) . then ( ( ) = > battle . trainer . initSprite ( ) ) ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-03-29 14:37:28 +00:00
if ( battle . enemyParty . filter ( p = > p . isBoss ( ) ) . length > 1 ) {
2024-05-23 16:03:10 +01:00
for ( const enemyPokemon of battle . enemyParty ) {
2024-03-29 14:29:19 +00:00
if ( enemyPokemon . isBoss ( ) ) {
2024-03-29 14:37:28 +00:00
enemyPokemon . setBoss ( true , Math . ceil ( enemyPokemon . bossSegments * ( enemyPokemon . getSpeciesForm ( ) . baseTotal / totalBst ) ) ) ;
2024-03-29 14:29:19 +00:00
enemyPokemon . initBattleInfo ( ) ;
}
}
}
}
2023-10-07 21:08:33 +01:00
2023-05-18 16:11:06 +01:00
Promise . all ( loadEnemyAssets ) . then ( ( ) = > {
2023-10-07 21:08:33 +01:00
battle . enemyParty . forEach ( ( enemyPokemon , e ) = > {
if ( e < ( battle . double ? 2 : 1 ) ) {
if ( battle . battleType === BattleType . WILD ) {
this . scene . field . add ( enemyPokemon ) ;
2023-10-23 18:48:56 +01:00
battle . seenEnemyPartyMemberIds . add ( enemyPokemon . id ) ;
2023-10-07 21:08:33 +01:00
const playerPokemon = this . scene . getPlayerPokemon ( ) ;
2024-05-23 16:03:10 +01:00
if ( playerPokemon ? . visible ) {
2023-10-07 21:08:33 +01:00
this . scene . field . moveBelow ( enemyPokemon as Pokemon , playerPokemon ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-07 21:08:33 +01:00
enemyPokemon . tint ( 0 , 0.5 ) ;
} else if ( battle . battleType === BattleType . TRAINER ) {
enemyPokemon . setVisible ( false ) ;
this . scene . currentBattle . trainer . tint ( 0 , 0.5 ) ;
}
2024-05-23 16:03:10 +01:00
if ( battle . double ) {
2023-10-07 21:08:33 +01:00
enemyPokemon . setFieldPosition ( e ? FieldPosition.RIGHT : FieldPosition.LEFT ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-07 21:08:33 +01:00
}
2023-05-18 16:11:06 +01:00
} ) ;
2023-04-10 19:12:01 +01:00
2023-04-28 20:03:42 +01:00
if ( ! this . loaded ) {
2023-10-23 18:48:56 +01:00
regenerateModifierPoolThresholds ( this . scene . getEnemyField ( ) , battle . battleType === BattleType . TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD ) ;
2023-04-28 20:03:42 +01:00
this . scene . generateEnemyModifiers ( ) ;
}
2023-04-21 00:44:56 +01:00
2023-04-28 20:03:42 +01:00
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > {
2023-11-13 04:47:04 +00:00
if ( ! this . loaded ) {
2024-05-15 05:56:17 +01:00
this . scene . gameData . saveAll ( this . scene , true , battle . waveIndex % 10 === 1 || this . scene . lastSavePlayTime >= 300 ) . then ( success = > {
2024-04-17 20:56:29 +01:00
this . scene . disableMenu = false ;
2024-05-23 16:03:10 +01:00
if ( ! success ) {
2023-12-30 23:41:25 +00:00
return this . scene . reset ( true ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-13 04:01:05 +01:00
this . doEncounter ( ) ;
2023-12-30 23:41:25 +00:00
} ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-12-30 23:41:25 +00:00
this . doEncounter ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-28 20:03:42 +01:00
} ) ;
2023-04-10 19:12:01 +01:00
} ) ;
}
doEncounter() {
2023-10-10 01:20:02 +01:00
this . scene . playBgm ( undefined , true ) ;
2023-12-16 15:15:25 +00:00
this . scene . updateModifiers ( false ) ;
2024-01-15 01:47:08 +00:00
this . scene . setFieldScale ( 1 ) ;
2023-12-16 15:15:25 +00:00
2023-11-29 02:35:52 +00:00
/ * i f ( s t a r t i n g W a v e > 1 0 ) {
2023-05-30 14:46:42 +01:00
for ( let m = 0 ; m < Math . min ( Math . floor ( startingWave / 10 ) , 99 ) ; m ++ )
2023-11-01 01:43:22 +00:00
this . scene . addModifier ( getPlayerModifierTypeOptionsForWave ( ( m + 1 ) * 10 , 1 , this . scene . getParty ( ) ) [ 0 ] . type . newModifier ( ) , true ) ;
this . scene . updateModifiers ( true ) ;
2023-11-29 02:35:52 +00:00
} * /
2024-05-23 16:03:10 +01:00
for ( const pokemon of this . scene . getParty ( ) ) {
if ( pokemon ) {
2023-11-29 02:35:52 +00:00
pokemon . resetBattleData ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-19 19:07:38 +01:00
}
2024-05-23 16:03:10 +01:00
if ( ! this . loaded ) {
2024-04-21 17:43:50 +01:00
this . scene . arena . trySetWeather ( getRandomWeatherType ( this . scene . arena ) , false ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-19 19:07:38 +01:00
2023-05-18 16:11:06 +01:00
const enemyField = this . scene . getEnemyField ( ) ;
2023-04-10 19:12:01 +01:00
this . scene . tweens . add ( {
2023-10-07 21:08:33 +01:00
targets : [ this . scene . arenaEnemy , this . scene . currentBattle . trainer , enemyField , this . scene . arenaPlayer , this . scene . trainer ] . flat ( ) ,
x : ( _target , _key , value , fieldIndex : integer ) = > fieldIndex < 2 + ( enemyField . length ) ? value + 300 : value - 300 ,
2023-04-10 19:12:01 +01:00
duration : 2000 ,
2024-01-13 17:24:24 +00:00
onComplete : ( ) = > {
2024-05-23 16:03:10 +01:00
if ( ! this . tryOverrideForBattleSpec ( ) ) {
2024-01-13 17:24:24 +00:00
this . doEncounterCommon ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-01-13 17:24:24 +00:00
}
2023-04-10 19:12:01 +01:00
} ) ;
}
2024-01-13 17:24:24 +00:00
getEncounterMessage ( ) : string {
const enemyField = this . scene . getEnemyField ( ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . battleSpec === BattleSpec . FINAL_BOSS ) {
return i18next . t ( "battle:bossAppeared" , { bossName : enemyField [ 0 ] . name } ) ;
}
2024-01-13 17:24:24 +00:00
2024-05-16 12:09:12 +01:00
if ( this . scene . currentBattle . battleType === BattleType . TRAINER ) {
if ( this . scene . currentBattle . double ) {
2024-05-23 16:03:10 +01:00
return i18next . t ( "battle:trainerAppearedDouble" , { trainerName : this.scene.currentBattle.trainer.getName ( TrainerSlot . NONE , true ) } ) ;
2024-05-16 12:09:12 +01:00
2024-05-23 16:03:10 +01:00
} else {
return i18next . t ( "battle:trainerAppeared" , { trainerName : this.scene.currentBattle.trainer.getName ( TrainerSlot . NONE , true ) } ) ;
2024-05-16 12:09:12 +01:00
}
}
2024-01-13 17:24:24 +00:00
return enemyField . length === 1
2024-05-23 16:03:10 +01:00
? i18next . t ( "battle:singleWildAppeared" , { pokemonName : enemyField [ 0 ] . name } )
: i18next . t ( "battle:multiWildAppeared" , { pokemonName1 : enemyField [ 0 ] . name , pokemonName2 : enemyField [ 1 ] . name } ) ;
2024-01-13 17:24:24 +00:00
}
doEncounterCommon ( showEncounterMessage : boolean = true ) {
2023-10-07 21:08:33 +01:00
const enemyField = this . scene . getEnemyField ( ) ;
if ( this . scene . currentBattle . battleType === BattleType . WILD ) {
enemyField . forEach ( enemyPokemon = > {
2024-05-23 16:03:10 +01:00
enemyPokemon . untint ( 100 , "Sine.easeOut" ) ;
2023-10-07 21:08:33 +01:00
enemyPokemon . cry ( ) ;
enemyPokemon . showInfo ( ) ;
2024-05-23 16:03:10 +01:00
if ( enemyPokemon . isShiny ( ) ) {
2023-11-12 05:31:40 +00:00
this . scene . validateAchv ( achvs . SEE_SHINY ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-07 21:08:33 +01:00
} ) ;
2024-02-15 04:25:12 +00:00
this . scene . updateFieldScale ( ) ;
2024-05-23 16:03:10 +01:00
if ( showEncounterMessage ) {
2024-01-13 17:24:24 +00:00
this . scene . ui . showText ( this . getEncounterMessage ( ) , null , ( ) = > this . end ( ) , 1500 ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-01-13 17:24:24 +00:00
this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-07 21:08:33 +01:00
} else if ( this . scene . currentBattle . battleType === BattleType . TRAINER ) {
2023-10-18 23:01:15 +01:00
const trainer = this . scene . currentBattle . trainer ;
2024-05-23 16:03:10 +01:00
trainer . untint ( 100 , "Sine.easeOut" ) ;
2023-10-18 23:01:15 +01:00
trainer . playAnim ( ) ;
2024-05-24 00:45:04 +01:00
2023-10-18 23:01:15 +01:00
const doSummon = ( ) = > {
this . scene . currentBattle . started = true ;
this . scene . playBgm ( undefined ) ;
this . scene . pbTray . showPbTray ( this . scene . getParty ( ) ) ;
this . scene . pbTrayEnemy . showPbTray ( this . scene . getEnemyParty ( ) ) ;
2024-01-13 17:24:24 +00:00
const doTrainerSummon = ( ) = > {
2024-02-22 04:57:49 +00:00
this . hideEnemyTrainer ( ) ;
2023-12-02 15:30:23 +00:00
const availablePartyMembers = this . scene . getEnemyParty ( ) . filter ( p = > ! p . isFainted ( ) ) . length ;
2023-10-18 23:01:15 +01:00
this . scene . unshiftPhase ( new SummonPhase ( this . scene , 0 , false ) ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . double && availablePartyMembers > 1 ) {
2023-10-18 23:01:15 +01:00
this . scene . unshiftPhase ( new SummonPhase ( this . scene , 1 , false ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-18 23:01:15 +01:00
this . end ( ) ;
2024-01-13 17:24:24 +00:00
} ;
2024-05-23 16:03:10 +01:00
if ( showEncounterMessage ) {
2024-01-13 17:24:24 +00:00
this . scene . ui . showText ( this . getEncounterMessage ( ) , null , doTrainerSummon , 1500 , true ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-01-13 17:24:24 +00:00
doTrainerSummon ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-18 23:01:15 +01:00
} ;
2024-05-24 00:45:04 +01:00
2024-02-14 19:41:39 +00:00
const encounterMessages = this . scene . currentBattle . trainer . getEncounterMessages ( ) ;
2023-10-18 23:01:15 +01:00
2024-05-23 16:03:10 +01:00
if ( ! encounterMessages ? . length ) {
2023-10-18 23:01:15 +01:00
doSummon ( ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-06-03 11:49:13 +01:00
let message : string ;
this . scene . executeWithSeedOffset ( ( ) = > message = Utils . randSeedItem ( encounterMessages ) , this . scene . currentBattle . waveIndex ) ;
2024-02-22 23:03:36 +00:00
const showDialogueAndSummon = ( ) = > {
2024-05-19 12:23:24 +01:00
this . scene . ui . showDialogue ( message , trainer . getName ( TrainerSlot . NONE , true ) , null , ( ) = > {
2024-02-22 23:03:36 +00:00
this . scene . charSprite . hide ( ) . then ( ( ) = > this . scene . hideFieldOverlay ( 250 ) . then ( ( ) = > doSummon ( ) ) ) ;
} ) ;
} ;
2024-06-03 11:49:13 +01:00
if ( this . scene . currentBattle . trainer . config . hasCharSprite && ! this . scene . ui . shouldSkipDialogue ( message ) ) {
2024-02-26 00:09:24 +00:00
this . scene . showFieldOverlay ( 500 ) . then ( ( ) = > this . scene . charSprite . showCharacter ( trainer . getKey ( ) , getCharVariantFromDialogue ( encounterMessages [ 0 ] ) ) . then ( ( ) = > showDialogueAndSummon ( ) ) ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-02-22 23:03:36 +00:00
showDialogueAndSummon ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-18 23:01:15 +01:00
}
2023-10-07 21:08:33 +01:00
}
}
2023-04-10 19:12:01 +01:00
end() {
2023-05-18 16:11:06 +01:00
const enemyField = this . scene . getEnemyField ( ) ;
enemyField . forEach ( ( enemyPokemon , e ) = > {
2024-05-23 16:03:10 +01:00
if ( enemyPokemon . isShiny ( ) ) {
2023-05-18 16:11:06 +01:00
this . scene . unshiftPhase ( new ShinySparklePhase ( this . scene , BattlerIndex . ENEMY + e ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
} ) ;
2023-04-24 19:30:21 +01:00
2023-11-12 17:49:06 +00:00
if ( this . scene . currentBattle . battleType !== BattleType . TRAINER ) {
2024-06-11 23:55:16 +01:00
enemyField . map ( p = > this . scene . pushConditionalPhase ( new PostSummonPhase ( this . scene , p . getBattlerIndex ( ) ) , ( ) = > {
2024-06-13 13:42:25 +01:00
// if there is not a player party, we can't continue
if ( ! this . scene . getParty ( ) ? . length ) {
return false ;
}
2024-06-11 23:55:16 +01:00
// how many player pokemon are on the field ?
2024-06-13 13:42:25 +01:00
const pokemonsOnFieldCount = this . scene . getParty ( ) . filter ( p = > p . isOnField ( ) ) . length ;
// if it's a 2vs1, there will never be a 2nd pokemon on our field even
const requiredPokemonsOnField = Math . min ( this . scene . getParty ( ) . filter ( ( p ) = > ! p . isFainted ( ) ) . length , 2 ) ;
2024-06-11 23:55:16 +01:00
// if it's a double, there should be 2, otherwise 1
2024-06-13 13:42:25 +01:00
if ( this . scene . currentBattle . double ) {
return pokemonsOnFieldCount === requiredPokemonsOnField ;
}
return pokemonsOnFieldCount === 1 ;
2024-06-11 23:55:16 +01:00
} ) ) ;
2023-11-12 17:49:06 +00:00
const ivScannerModifier = this . scene . findModifier ( m = > m instanceof IvScannerModifier ) ;
2024-05-23 16:03:10 +01:00
if ( ivScannerModifier ) {
2024-04-06 15:37:54 +01:00
enemyField . map ( p = > this . scene . pushPhase ( new ScanIvsPhase ( this . scene , p . getBattlerIndex ( ) , Math . min ( ivScannerModifier . getStackCount ( ) * 2 , 6 ) ) ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-12 17:49:06 +00:00
}
2023-12-02 15:30:23 +00:00
if ( ! this . loaded ) {
2024-06-08 06:07:23 +01:00
const availablePartyMembers = this . scene . getParty ( ) . filter ( p = > p . isAllowedInBattle ( ) ) ;
2023-12-02 15:30:23 +00:00
2024-05-28 12:11:04 +01:00
if ( ! availablePartyMembers [ 0 ] . isOnField ( ) ) {
2023-12-02 15:30:23 +00:00
this . scene . pushPhase ( new SummonPhase ( this . scene , 0 ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-12-02 15:30:23 +00:00
if ( this . scene . currentBattle . double ) {
if ( availablePartyMembers . length > 1 ) {
this . scene . pushPhase ( new ToggleDoublePositionPhase ( this . scene , true ) ) ;
2024-05-28 12:11:04 +01:00
if ( ! availablePartyMembers [ 1 ] . isOnField ( ) ) {
2023-12-02 15:30:23 +00:00
this . scene . pushPhase ( new SummonPhase ( this . scene , 1 ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-12-02 15:30:23 +00:00
}
} else {
2024-05-23 16:03:10 +01:00
if ( availablePartyMembers . length > 1 && availablePartyMembers [ 1 ] . isOnField ( ) ) {
2023-12-02 15:30:23 +00:00
this . scene . pushPhase ( new ReturnPhase ( this . scene , 1 ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-12-02 15:30:23 +00:00
this . scene . pushPhase ( new ToggleDoublePositionPhase ( this . scene , false ) ) ;
}
2024-05-24 00:45:04 +01:00
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . battleType !== BattleType . TRAINER && ( this . scene . currentBattle . waveIndex > 1 || ! this . scene . gameMode . isDaily ) ) {
2024-05-21 08:16:17 +01:00
const minPartySize = this . scene . currentBattle . double ? 2 : 1 ;
if ( availablePartyMembers . length > minPartySize ) {
this . scene . pushPhase ( new CheckSwitchPhase ( this . scene , 0 , this . scene . currentBattle . double ) ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . double ) {
2024-05-21 08:16:17 +01:00
this . scene . pushPhase ( new CheckSwitchPhase ( this . scene , 1 , this . scene . currentBattle . double ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-21 08:16:17 +01:00
}
2023-12-02 15:30:23 +00:00
}
}
2024-02-14 15:44:55 +00:00
handleTutorial ( this . scene , Tutorial . Access_Menu ) . then ( ( ) = > super . end ( ) ) ;
2023-04-10 19:12:01 +01:00
}
2024-01-13 17:24:24 +00:00
tryOverrideForBattleSpec ( ) : boolean {
switch ( this . scene . currentBattle . battleSpec ) {
2024-05-23 16:03:10 +01:00
case BattleSpec . FINAL_BOSS :
const enemy = this . scene . getEnemyPokemon ( ) ;
this . scene . ui . showText ( this . getEncounterMessage ( ) , null , ( ) = > {
this . scene . ui . showDialogue ( battleSpecDialogue [ BattleSpec . FINAL_BOSS ] . encounter , enemy . species . name , null , ( ) = > {
this . doEncounterCommon ( false ) ;
} ) ;
} , 1500 , true ) ;
return true ;
2024-01-13 17:24:24 +00:00
}
return false ;
}
2023-04-10 19:12:01 +01:00
}
export class NextEncounterPhase extends EncounterPhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
2024-06-10 15:10:23 +01:00
start() {
super . start ( ) ;
}
2023-04-10 19:12:01 +01:00
doEncounter ( ) : void {
2023-10-10 01:20:02 +01:00
this . scene . playBgm ( undefined , true ) ;
2024-05-23 16:03:10 +01:00
for ( const pokemon of this . scene . getParty ( ) ) {
if ( pokemon ) {
2024-02-26 20:14:47 +00:00
pokemon . resetBattleData ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-02-26 20:14:47 +00:00
}
2024-04-01 17:48:35 +01:00
this . scene . arenaNextEnemy . setBiome ( this . scene . arena . biomeType ) ;
2024-01-15 01:47:08 +00:00
this . scene . arenaNextEnemy . setVisible ( true ) ;
2023-05-18 16:11:06 +01:00
const enemyField = this . scene . getEnemyField ( ) ;
2023-04-10 19:12:01 +01:00
this . scene . tweens . add ( {
2023-10-18 23:01:15 +01:00
targets : [ this . scene . arenaEnemy , this . scene . arenaNextEnemy , this . scene . currentBattle . trainer , enemyField , this . scene . lastEnemyTrainer ] . flat ( ) ,
2024-05-23 16:03:10 +01:00
x : "+=300" ,
2023-04-10 19:12:01 +01:00
duration : 2000 ,
onComplete : ( ) = > {
2024-04-01 17:48:35 +01:00
this . scene . arenaEnemy . setBiome ( this . scene . arena . biomeType ) ;
2023-04-10 19:12:01 +01:00
this . scene . arenaEnemy . setX ( this . scene . arenaNextEnemy . x ) ;
2023-05-07 22:05:19 +01:00
this . scene . arenaEnemy . setAlpha ( 1 ) ;
2023-04-10 19:12:01 +01:00
this . scene . arenaNextEnemy . setX ( this . scene . arenaNextEnemy . x - 300 ) ;
2024-01-15 01:47:08 +00:00
this . scene . arenaNextEnemy . setVisible ( false ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . lastEnemyTrainer ) {
2023-10-23 03:45:48 +01:00
this . scene . lastEnemyTrainer . destroy ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-24 00:45:04 +01:00
2024-05-23 16:03:10 +01:00
if ( ! this . tryOverrideForBattleSpec ( ) ) {
2024-01-13 17:24:24 +00:00
this . doEncounterCommon ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
}
} ) ;
}
}
export class NewBiomeEncounterPhase extends NextEncounterPhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
doEncounter ( ) : void {
2024-03-17 02:06:56 +00:00
this . scene . playBgm ( undefined , true ) ;
2024-05-23 16:03:10 +01:00
for ( const pokemon of this . scene . getParty ( ) ) {
if ( pokemon ) {
2023-11-29 02:35:52 +00:00
pokemon . resetBattleData ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-29 02:35:52 +00:00
}
2023-12-30 02:04:40 +00:00
this . scene . arena . trySetWeather ( getRandomWeatherType ( this . scene . arena ) , false ) ;
2023-04-19 05:35:06 +01:00
2024-05-23 16:03:10 +01:00
for ( const pokemon of this . scene . getParty ( ) . filter ( p = > p . isOnField ( ) ) ) {
2024-04-08 14:31:30 +01:00
applyAbAttrs ( PostBiomeChangeAbAttr , pokemon , null ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-08 14:31:30 +01:00
2023-05-18 16:11:06 +01:00
const enemyField = this . scene . getEnemyField ( ) ;
2023-04-10 19:12:01 +01:00
this . scene . tweens . add ( {
2023-05-18 16:11:06 +01:00
targets : [ this . scene . arenaEnemy , enemyField ] . flat ( ) ,
2024-05-23 16:03:10 +01:00
x : "+=300" ,
2023-04-10 19:12:01 +01:00
duration : 2000 ,
2024-01-13 17:24:24 +00:00
onComplete : ( ) = > {
2024-05-23 16:03:10 +01:00
if ( ! this . tryOverrideForBattleSpec ( ) ) {
2024-01-13 17:24:24 +00:00
this . doEncounterCommon ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-01-13 17:24:24 +00:00
}
2023-04-10 19:12:01 +01:00
} ) ;
}
}
2023-07-04 22:50:51 +01:00
export class PostSummonPhase extends PokemonPhase {
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex ) ;
}
start() {
super . start ( ) ;
const pokemon = this . getPokemon ( ) ;
2024-06-10 21:33:34 +01:00
if ( pokemon . status ? . effect === StatusEffect . TOXIC ) {
pokemon . status . turnCount = 0 ;
}
2023-07-04 22:50:51 +01:00
this . scene . arena . applyTags ( ArenaTrapTag , pokemon ) ;
2023-12-23 03:46:05 +00:00
applyPostSummonAbAttrs ( PostSummonAbAttr , pokemon ) . then ( ( ) = > this . end ( ) ) ;
2023-07-04 22:50:51 +01:00
}
}
2023-04-12 05:37:56 +01:00
export class SelectBiomePhase extends BattlePhase {
2023-04-10 19:12:01 +01:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
super . start ( ) ;
2023-04-12 05:37:56 +01:00
const currentBiome = this . scene . arena . biomeType ;
const setNextBiome = ( nextBiome : Biome ) = > {
2023-11-11 02:11:36 +00:00
if ( this . scene . currentBattle . waveIndex % 10 === 1 ) {
this . scene . applyModifiers ( MoneyInterestModifier , true , this . scene ) ;
2023-10-27 00:07:41 +01:00
this . scene . unshiftPhase ( new PartyHealPhase ( this . scene , false ) ) ;
2023-11-11 02:11:36 +00:00
}
2023-04-12 05:37:56 +01:00
this . scene . unshiftPhase ( new SwitchBiomePhase ( this . scene , nextBiome ) ) ;
this . end ( ) ;
} ;
2024-03-17 02:06:56 +00:00
if ( ( this . scene . gameMode . isClassic && this . scene . gameMode . isWaveFinal ( this . scene . currentBattle . waveIndex + 9 ) )
2024-04-23 01:30:46 +01:00
|| ( this . scene . gameMode . isDaily && this . scene . gameMode . isWaveFinal ( this . scene . currentBattle . waveIndex ) )
2024-05-23 16:03:10 +01:00
|| ( this . scene . gameMode . hasShortBiomes && ! ( this . scene . currentBattle . waveIndex % 50 ) ) ) {
2023-04-26 22:40:08 +01:00
setNextBiome ( Biome . END ) ;
2024-05-23 16:03:10 +01:00
} else if ( this . scene . gameMode . hasRandomBiomes ) {
2023-12-07 18:41:47 +00:00
setNextBiome ( this . generateNextBiome ( ) ) ;
2024-06-14 18:16:32 +01:00
} else if ( Array . isArray ( biomeLinks [ currentBiome ] ) ) {
2023-12-12 20:32:50 +00:00
let biomes : Biome [ ] ;
this . scene . executeWithSeedOffset ( ( ) = > {
biomes = ( biomeLinks [ currentBiome ] as ( Biome | [ Biome , integer ] ) [ ] )
. filter ( b = > ! Array . isArray ( b ) || ! Utils . randSeedInt ( b [ 1 ] ) )
. map ( b = > ! Array . isArray ( b ) ? b : b [ 0 ] ) ;
} , this . scene . currentBattle . waveIndex ) ;
if ( biomes . length > 1 && this . scene . findModifier ( m = > m instanceof MapModifier ) ) {
2024-04-16 20:58:02 +01:00
let biomeChoices : Biome [ ] ;
this . scene . executeWithSeedOffset ( ( ) = > {
2024-06-14 18:16:32 +01:00
biomeChoices = ( ! Array . isArray ( biomeLinks [ currentBiome ] )
? [ biomeLinks [ currentBiome ] as Biome ]
2024-04-16 20:58:02 +01:00
: biomeLinks [ currentBiome ] as ( Biome | [ Biome , integer ] ) [ ] )
. filter ( ( b , i ) = > ! Array . isArray ( b ) || ! Utils . randSeedInt ( b [ 1 ] ) )
. map ( b = > Array . isArray ( b ) ? b [ 0 ] : b ) ;
} , this . scene . currentBattle . waveIndex ) ;
const biomeSelectItems = biomeChoices . map ( b = > {
2024-04-19 23:56:45 +01:00
const ret : OptionSelectItem = {
2024-04-16 20:58:02 +01:00
label : getBiomeName ( b ) ,
handler : ( ) = > {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
setNextBiome ( b ) ;
2024-04-19 23:56:45 +01:00
return true ;
2024-04-16 20:58:02 +01:00
}
} ;
2024-04-19 23:56:45 +01:00
return ret ;
2024-04-16 20:58:02 +01:00
} ) ;
this . scene . ui . setMode ( Mode . OPTION_SELECT , {
options : biomeSelectItems ,
delay : 1000
2023-04-27 00:19:39 +01:00
} ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-10-18 23:01:15 +01:00
setNextBiome ( biomes [ Utils . randSeedInt ( biomes . length ) ] ) ;
2024-05-23 16:03:10 +01:00
}
} else if ( biomeLinks . hasOwnProperty ( currentBiome ) ) {
2024-06-14 18:16:32 +01:00
setNextBiome ( biomeLinks [ currentBiome ] as Biome ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-04-23 01:30:46 +01:00
setNextBiome ( this . generateNextBiome ( ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-12 05:37:56 +01:00
}
2023-12-07 18:41:47 +00:00
generateNextBiome ( ) : Biome {
2024-05-23 16:03:10 +01:00
if ( ! ( this . scene . currentBattle . waveIndex % 50 ) ) {
2023-12-07 18:41:47 +00:00
return Biome . END ;
2024-05-23 16:03:10 +01:00
}
2024-03-16 01:59:34 +00:00
return this . scene . generateRandomBiome ( this . scene . currentBattle . waveIndex ) ;
2023-12-07 18:41:47 +00:00
}
2023-04-12 05:37:56 +01:00
}
export class SwitchBiomePhase extends BattlePhase {
private nextBiome : Biome ;
constructor ( scene : BattleScene , nextBiome : Biome ) {
super ( scene ) ;
this . nextBiome = nextBiome ;
}
start() {
super . start ( ) ;
2024-05-23 16:03:10 +01:00
if ( this . nextBiome === undefined ) {
2023-10-18 23:01:15 +01:00
return this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-18 23:01:15 +01:00
2023-04-10 19:12:01 +01:00
this . scene . tweens . add ( {
2023-11-27 03:22:05 +00:00
targets : [ this . scene . arenaEnemy , this . scene . lastEnemyTrainer ] ,
2024-05-23 16:03:10 +01:00
x : "+=300" ,
2023-04-10 19:12:01 +01:00
duration : 2000 ,
onComplete : ( ) = > {
this . scene . arenaEnemy . setX ( this . scene . arenaEnemy . x - 600 ) ;
2023-04-18 03:44:41 +01:00
this . scene . newArena ( this . nextBiome ) ;
2023-04-10 19:12:01 +01:00
2023-05-09 17:43:28 +01:00
const biomeKey = getBiomeKey ( this . nextBiome ) ;
2023-04-10 19:12:01 +01:00
const bgTexture = ` ${ biomeKey } _bg ` ;
2024-05-23 16:03:10 +01:00
this . scene . arenaBgTransition . setTexture ( bgTexture ) ;
2023-04-10 19:12:01 +01:00
this . scene . arenaBgTransition . setAlpha ( 0 ) ;
this . scene . arenaBgTransition . setVisible ( true ) ;
2023-05-09 17:43:28 +01:00
this . scene . arenaPlayerTransition . setBiome ( this . nextBiome ) ;
2023-04-10 19:12:01 +01:00
this . scene . arenaPlayerTransition . setAlpha ( 0 ) ;
this . scene . arenaPlayerTransition . setVisible ( true ) ;
this . scene . tweens . add ( {
2023-11-27 03:22:05 +00:00
targets : [ this . scene . arenaPlayer , this . scene . arenaBgTransition , this . scene . arenaPlayerTransition ] ,
2023-04-10 19:12:01 +01:00
duration : 1000 ,
delay : 1000 ,
2024-05-23 16:03:10 +01:00
ease : "Sine.easeInOut" ,
2023-04-30 16:38:46 +01:00
alpha : ( target : any ) = > target === this . scene . arenaPlayer ? 0 : 1 ,
2023-04-10 19:12:01 +01:00
onComplete : ( ) = > {
this . scene . arenaBg . setTexture ( bgTexture ) ;
2023-05-09 17:43:28 +01:00
this . scene . arenaPlayer . setBiome ( this . nextBiome ) ;
2023-04-30 16:38:46 +01:00
this . scene . arenaPlayer . setAlpha ( 1 ) ;
2023-05-09 17:43:28 +01:00
this . scene . arenaEnemy . setBiome ( this . nextBiome ) ;
2023-05-07 22:05:19 +01:00
this . scene . arenaEnemy . setAlpha ( 1 ) ;
2023-05-09 17:43:28 +01:00
this . scene . arenaNextEnemy . setBiome ( this . nextBiome ) ;
2023-04-10 19:12:01 +01:00
this . scene . arenaBgTransition . setVisible ( false ) ;
this . scene . arenaPlayerTransition . setVisible ( false ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . lastEnemyTrainer ) {
2023-10-23 03:45:48 +01:00
this . scene . lastEnemyTrainer . destroy ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
this . end ( ) ;
}
2023-04-30 16:38:46 +01:00
} ) ;
2023-04-10 19:12:01 +01:00
}
} ) ;
}
}
2023-05-18 16:11:06 +01:00
export class SummonPhase extends PartyMemberPokemonPhase {
2024-04-11 01:57:22 +01:00
private loaded : boolean ;
constructor ( scene : BattleScene , fieldIndex : integer , player : boolean = true , loaded : boolean = false ) {
super ( scene , fieldIndex , player ) ;
this . loaded = loaded ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
this . preSummon ( ) ;
}
2024-05-05 04:16:59 +01:00
/ * *
* Sends out a Pokemon before the battle begins and shows the appropriate messages
* /
2023-04-10 19:12:01 +01:00
preSummon ( ) : void {
2023-05-31 17:38:55 +01:00
const partyMember = this . getPokemon ( ) ;
2024-06-08 06:07:23 +01:00
// If the Pokemon about to be sent out is fainted or illegal under a challenge, switch to the first non-fainted legal Pokemon
if ( ! partyMember . isAllowedInBattle ( ) ) {
console . warn ( "The Pokemon about to be sent out is fainted or illegal under a challenge. Attempting to resolve..." ) ;
// First check if they're somehow still in play, if so remove them.
if ( partyMember . isOnField ( ) ) {
partyMember . hideInfo ( ) ;
partyMember . setVisible ( false ) ;
this . scene . field . remove ( partyMember ) ;
this . scene . triggerPokemonFormChange ( partyMember , SpeciesFormChangeActiveTrigger , true ) ;
}
2023-10-07 21:08:33 +01:00
const party = this . getParty ( ) ;
2024-05-24 00:45:04 +01:00
2024-05-05 04:16:59 +01:00
// Find the first non-fainted Pokemon index above the current one
2024-06-08 06:07:23 +01:00
const legalIndex = party . findIndex ( ( p , i ) = > i > this . partyMemberIndex && p . isAllowedInBattle ( ) ) ;
if ( legalIndex === - 1 ) {
2024-05-05 04:16:59 +01:00
console . error ( "Party Details:\n" , party ) ;
2024-06-09 13:37:33 +01:00
console . error ( "All available Pokemon were fainted or illegal!" ) ;
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new GameOverPhase ( this . scene ) ) ;
this . end ( ) ;
return ;
2024-05-05 04:16:59 +01:00
}
2024-06-08 06:07:23 +01:00
// Swaps the fainted Pokemon and the first non-fainted legal Pokemon in the party
[ party [ this . partyMemberIndex ] , party [ legalIndex ] ] = [ party [ legalIndex ] , party [ this . partyMemberIndex ] ] ;
2024-05-05 04:16:59 +01:00
console . warn ( "Swapped %s %O with %s %O" , partyMember ? . name , partyMember , party [ 0 ] ? . name , party [ 0 ] ) ;
2023-05-31 17:38:55 +01:00
}
2023-10-07 21:08:33 +01:00
if ( this . player ) {
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:playerGo" , { pokemonName : this.getPokemon ( ) . name } ) ) ;
if ( this . player ) {
this . scene . pbTray . hide ( ) ;
}
this . scene . trainer . setTexture ( ` trainer_ ${ this . scene . gameData . gender === PlayerGender . FEMALE ? "f" : "m" } _back_pb ` ) ;
2024-02-06 04:05:56 +00:00
this . scene . time . delayedCall ( 562 , ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . trainer . setFrame ( "2" ) ;
2024-02-06 04:05:56 +00:00
this . scene . time . delayedCall ( 64 , ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . trainer . setFrame ( "3" ) ;
2024-02-06 04:05:56 +00:00
} ) ;
} ) ;
2023-10-07 21:08:33 +01:00
this . scene . tweens . add ( {
targets : this.scene.trainer ,
x : - 36 ,
2024-01-15 01:47:08 +00:00
duration : 1000 ,
onComplete : ( ) = > this . scene . trainer . setVisible ( false )
2023-10-07 21:08:33 +01:00
} ) ;
this . scene . time . delayedCall ( 750 , ( ) = > this . summon ( ) ) ;
2023-10-18 23:01:15 +01:00
} else {
2024-05-23 19:22:09 +01:00
const trainerName = this . scene . currentBattle . trainer . getName ( ! ( this . fieldIndex % 2 ) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER ) ;
const pokemonName = this . getPokemon ( ) . name ;
const message = i18next . t ( "battle:trainerSendOut" , { trainerName , pokemonName } ) ;
2023-10-18 23:01:15 +01:00
this . scene . pbTrayEnemy . hide ( ) ;
2024-05-23 19:22:09 +01:00
this . scene . ui . showText ( message , null , ( ) = > this . summon ( ) ) ;
2023-10-18 23:01:15 +01:00
}
2023-04-10 19:12:01 +01:00
}
summon ( ) : void {
2024-01-05 00:56:26 +00:00
const pokemon = this . getPokemon ( ) ;
2024-05-23 16:03:10 +01:00
const pokeball = this . scene . addFieldSprite ( this . player ? 36 : 248 , this . player ? 80 : 44 , "pb" , getPokeballAtlasKey ( pokemon . pokeball ) ) ;
2023-04-10 19:12:01 +01:00
pokeball . setVisible ( false ) ;
pokeball . setOrigin ( 0.5 , 0.625 ) ;
this . scene . field . add ( pokeball ) ;
2024-05-23 16:03:10 +01:00
if ( this . fieldIndex === 1 ) {
2023-10-07 21:08:33 +01:00
pokemon . setFieldPosition ( FieldPosition . RIGHT , 0 ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-06-08 06:07:23 +01:00
const availablePartyMembers = this . getParty ( ) . filter ( p = > p . isAllowedInBattle ( ) ) . length ;
2023-10-07 21:08:33 +01:00
pokemon . setFieldPosition ( ! this . scene . currentBattle . double || availablePartyMembers === 1 ? FieldPosition.CENTER : FieldPosition.LEFT ) ;
}
2023-05-18 16:11:06 +01:00
2023-10-07 21:08:33 +01:00
const fpOffset = pokemon . getFieldPositionOffset ( ) ;
2023-05-18 16:11:06 +01:00
2023-04-10 19:12:01 +01:00
pokeball . setVisible ( true ) ;
2023-05-18 16:11:06 +01:00
this . scene . tweens . add ( {
targets : pokeball ,
duration : 650 ,
2023-10-07 21:08:33 +01:00
x : ( this . player ? 100 : 236 ) + fpOffset [ 0 ]
2023-05-18 16:11:06 +01:00
} ) ;
2023-04-10 19:12:01 +01:00
this . scene . tweens . add ( {
targets : pokeball ,
duration : 150 ,
2024-05-23 16:03:10 +01:00
ease : "Cubic.easeOut" ,
2023-10-07 21:08:33 +01:00
y : ( this . player ? 70 : 34 ) + fpOffset [ 1 ] ,
2023-04-10 19:12:01 +01:00
onComplete : ( ) = > {
this . scene . tweens . add ( {
targets : pokeball ,
duration : 500 ,
2024-05-23 16:03:10 +01:00
ease : "Cubic.easeIn" ,
2023-05-18 16:11:06 +01:00
angle : 1440 ,
2023-10-07 21:08:33 +01:00
y : ( this . player ? 132 : 86 ) + fpOffset [ 1 ] ,
2023-04-10 19:12:01 +01:00
onComplete : ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "pb_rel" ) ;
2023-04-10 19:12:01 +01:00
pokeball . destroy ( ) ;
2023-10-07 21:08:33 +01:00
this . scene . add . existing ( pokemon ) ;
this . scene . field . add ( pokemon ) ;
if ( ! this . player ) {
const playerPokemon = this . scene . getPlayerPokemon ( ) as Pokemon ;
2024-05-23 16:03:10 +01:00
if ( playerPokemon ? . visible ) {
2023-10-07 21:08:33 +01:00
this . scene . field . moveBelow ( pokemon , playerPokemon ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-23 18:48:56 +01:00
this . scene . currentBattle . seenEnemyPartyMemberIds . add ( pokemon . id ) ;
2023-10-07 21:08:33 +01:00
}
2024-01-05 04:57:21 +00:00
addPokeballOpenParticles ( this . scene , pokemon . x , pokemon . y - 16 , pokemon . pokeball ) ;
2023-11-01 01:43:22 +00:00
this . scene . updateModifiers ( this . player ) ;
2024-02-15 04:25:12 +00:00
this . scene . updateFieldScale ( ) ;
2023-10-07 21:08:33 +01:00
pokemon . showInfo ( ) ;
pokemon . playAnim ( ) ;
pokemon . setVisible ( true ) ;
2023-10-31 18:09:33 +00:00
pokemon . getSprite ( ) . setVisible ( true ) ;
2023-10-07 21:08:33 +01:00
pokemon . setScale ( 0.5 ) ;
pokemon . tint ( getPokeballTintColor ( pokemon . pokeball ) ) ;
2024-05-23 16:03:10 +01:00
pokemon . untint ( 250 , "Sine.easeIn" ) ;
2024-02-15 04:25:12 +00:00
this . scene . updateFieldScale ( ) ;
2023-04-10 19:12:01 +01:00
this . scene . tweens . add ( {
2023-10-07 21:08:33 +01:00
targets : pokemon ,
2023-04-10 19:12:01 +01:00
duration : 250 ,
2024-05-23 16:03:10 +01:00
ease : "Sine.easeIn" ,
2024-02-15 04:25:12 +00:00
scale : pokemon.getSpriteScale ( ) ,
2023-04-10 19:12:01 +01:00
onComplete : ( ) = > {
2024-03-29 18:42:27 +00:00
pokemon . cry ( pokemon . getHpRatio ( ) > 0.25 ? undefined : { rate : 0.85 } ) ;
2023-10-07 21:08:33 +01:00
pokemon . getSprite ( ) . clearTint ( ) ;
pokemon . resetSummonData ( ) ;
2023-04-10 19:12:01 +01:00
this . scene . time . delayedCall ( 1000 , ( ) = > this . end ( ) ) ;
}
} ) ;
}
} ) ;
}
} ) ;
}
2023-07-05 21:13:00 +01:00
onEnd ( ) : void {
2023-06-02 16:41:08 +01:00
const pokemon = this . getPokemon ( ) ;
2023-05-06 05:42:01 +01:00
2024-05-23 16:03:10 +01:00
if ( pokemon . isShiny ( ) ) {
2023-06-02 16:41:08 +01:00
this . scene . unshiftPhase ( new ShinySparklePhase ( this . scene , pokemon . getBattlerIndex ( ) ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
2023-06-02 16:41:08 +01:00
pokemon . resetTurnData ( ) ;
2023-05-06 05:42:01 +01:00
2024-04-11 14:24:03 +01:00
if ( ! this . loaded || this . scene . currentBattle . battleType === BattleType . TRAINER || ( this . scene . currentBattle . waveIndex % 10 ) === 1 ) {
2024-04-11 01:57:22 +01:00
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeActiveTrigger , true ) ;
this . queuePostSummon ( ) ;
}
2023-07-05 21:13:00 +01:00
}
queuePostSummon ( ) : void {
this . scene . pushPhase ( new PostSummonPhase ( this . scene , this . getPokemon ( ) . getBattlerIndex ( ) ) ) ;
}
end() {
this . onEnd ( ) ;
2023-05-04 15:25:11 +01:00
2023-04-10 19:12:01 +01:00
super . end ( ) ;
}
}
export class SwitchSummonPhase extends SummonPhase {
private slotIndex : integer ;
private doReturn : boolean ;
2023-04-29 00:26:41 +01:00
private batonPass : boolean ;
2023-04-10 19:12:01 +01:00
2023-10-07 21:08:33 +01:00
private lastPokemon : Pokemon ;
2023-04-29 00:26:41 +01:00
2023-10-07 21:08:33 +01:00
constructor ( scene : BattleScene , fieldIndex : integer , slotIndex : integer , doReturn : boolean , batonPass : boolean , player? : boolean ) {
super ( scene , fieldIndex , player !== undefined ? player : true ) ;
2023-04-10 19:12:01 +01:00
this . slotIndex = slotIndex ;
this . doReturn = doReturn ;
2023-04-29 00:26:41 +01:00
this . batonPass = batonPass ;
2023-04-10 19:12:01 +01:00
}
2024-06-10 15:10:23 +01:00
start ( ) : void {
super . start ( ) ;
}
2023-04-10 19:12:01 +01:00
preSummon ( ) : void {
2024-02-22 04:57:49 +00:00
if ( ! this . player ) {
2024-05-23 16:03:10 +01:00
if ( this . slotIndex === - 1 ) {
2024-03-21 04:57:28 +00:00
this . slotIndex = this . scene . currentBattle . trainer . getNextSummonIndex ( ! this . fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-07 03:41:55 +00:00
if ( this . slotIndex > - 1 ) {
2024-03-21 04:57:28 +00:00
this . showEnemyTrainer ( ! ( this . fieldIndex % 2 ) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER ) ;
2024-03-07 03:41:55 +00:00
this . scene . pbTrayEnemy . showPbTray ( this . scene . getEnemyParty ( ) ) ;
}
2024-02-22 04:57:49 +00:00
}
2023-11-27 03:22:05 +00:00
2024-01-15 05:20:26 +00:00
if ( ! this . doReturn || ( this . slotIndex !== - 1 && ! ( this . player ? this . scene . getParty ( ) : this . scene . getEnemyParty ( ) ) [ this . slotIndex ] ) ) {
2024-05-23 16:03:10 +01:00
if ( this . player ) {
2024-02-22 04:57:49 +00:00
return this . switchAndSummon ( ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-02-22 04:57:49 +00:00
this . scene . time . delayedCall ( 750 , ( ) = > this . switchAndSummon ( ) ) ;
return ;
}
2023-04-10 19:12:01 +01:00
}
2023-10-07 21:08:33 +01:00
const pokemon = this . getPokemon ( ) ;
2023-04-10 19:12:01 +01:00
2024-05-23 16:03:10 +01:00
if ( ! this . batonPass ) {
2024-01-15 05:20:26 +00:00
( this . player ? this . scene . getEnemyField ( ) : this . scene . getPlayerField ( ) ) . forEach ( enemyPokemon = > enemyPokemon . removeTagsBySourceId ( pokemon . id ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-23 03:14:53 +01:00
2024-04-13 04:26:01 +01:00
this . scene . ui . showText ( this . player ?
2024-05-23 16:03:10 +01:00
i18next . t ( "battle:playerComeBack" , { pokemonName : pokemon.name } ) :
i18next . t ( "battle:trainerComeBack" , {
2024-04-13 04:26:01 +01:00
trainerName : this.scene.currentBattle.trainer.getName ( ! ( this . fieldIndex % 2 ) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER ) ,
pokemonName : pokemon.name
} )
) ;
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "pb_rel" ) ;
2023-10-07 21:08:33 +01:00
pokemon . hideInfo ( ) ;
2024-05-23 16:03:10 +01:00
pokemon . tint ( getPokeballTintColor ( pokemon . pokeball ) , 1 , 250 , "Sine.easeIn" ) ;
2023-04-10 19:12:01 +01:00
this . scene . tweens . add ( {
2023-10-07 21:08:33 +01:00
targets : pokemon ,
2023-04-10 19:12:01 +01:00
duration : 250 ,
2024-05-23 16:03:10 +01:00
ease : "Sine.easeIn" ,
2023-04-10 19:12:01 +01:00
scale : 0.5 ,
onComplete : ( ) = > {
2023-10-07 21:08:33 +01:00
pokemon . setVisible ( false ) ;
this . scene . field . remove ( pokemon ) ;
2024-01-10 04:34:43 +00:00
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeActiveTrigger , true ) ;
2023-04-10 19:12:01 +01:00
this . scene . time . delayedCall ( 750 , ( ) = > this . switchAndSummon ( ) ) ;
}
} ) ;
}
switchAndSummon() {
2024-01-15 05:20:26 +00:00
const party = this . player ? this . getParty ( ) : this . scene . getEnemyParty ( ) ;
2023-04-10 19:12:01 +01:00
const switchedPokemon = party [ this . slotIndex ] ;
2023-05-18 16:11:06 +01:00
this . lastPokemon = this . getPokemon ( ) ;
2024-05-28 12:11:04 +01:00
applyPreSwitchOutAbAttrs ( PreSwitchOutAbAttr , this . lastPokemon ) ;
2023-05-31 17:38:55 +01:00
if ( this . batonPass && switchedPokemon ) {
2024-01-15 05:20:26 +00:00
( this . player ? this . scene . getEnemyField ( ) : this . scene . getPlayerField ( ) ) . forEach ( enemyPokemon = > enemyPokemon . transferTagsBySourceId ( this . lastPokemon . id , switchedPokemon . id ) ) ;
2023-04-29 00:26:41 +01:00
if ( ! this . scene . findModifier ( m = > m instanceof SwitchEffectTransferModifier && ( m as SwitchEffectTransferModifier ) . pokemonId === switchedPokemon . id ) ) {
const batonPassModifier = this . scene . findModifier ( m = > m instanceof SwitchEffectTransferModifier
&& ( m as SwitchEffectTransferModifier ) . pokemonId === this . lastPokemon . id ) as SwitchEffectTransferModifier ;
2024-05-23 16:03:10 +01:00
if ( batonPassModifier && ! this . scene . findModifier ( m = > m instanceof SwitchEffectTransferModifier && ( m as SwitchEffectTransferModifier ) . pokemonId === switchedPokemon . id ) ) {
2024-05-30 22:58:10 +01:00
this . scene . tryTransferHeldItemModifier ( batonPassModifier , switchedPokemon , false ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-29 00:26:41 +01:00
}
}
2023-05-31 17:38:55 +01:00
if ( switchedPokemon ) {
party [ this . slotIndex ] = this . lastPokemon ;
party [ this . fieldIndex ] = switchedPokemon ;
2024-02-22 04:57:49 +00:00
const showTextAndSummon = ( ) = > {
2024-04-13 04:26:01 +01:00
this . scene . ui . showText ( this . player ?
2024-05-23 16:03:10 +01:00
i18next . t ( "battle:playerGo" , { pokemonName : switchedPokemon.name } ) :
i18next . t ( "battle:trainerGo" , {
2024-04-13 04:26:01 +01:00
trainerName : this.scene.currentBattle.trainer.getName ( ! ( this . fieldIndex % 2 ) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER ) ,
pokemonName : this.getPokemon ( ) . name
} )
) ;
2024-02-22 04:57:49 +00:00
this . summon ( ) ;
} ;
2024-05-23 16:03:10 +01:00
if ( this . player ) {
2024-02-22 04:57:49 +00:00
showTextAndSummon ( ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-02-22 04:57:49 +00:00
this . scene . time . delayedCall ( 1500 , ( ) = > {
this . hideEnemyTrainer ( ) ;
this . scene . pbTrayEnemy . hide ( ) ;
showTextAndSummon ( ) ;
} ) ;
}
2024-05-23 16:03:10 +01:00
} else {
2023-05-31 17:38:55 +01:00
this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
}
2023-04-29 00:26:41 +01:00
2023-07-05 21:13:00 +01:00
onEnd ( ) : void {
super . onEnd ( ) ;
2023-07-04 22:50:51 +01:00
2023-05-31 17:38:55 +01:00
const pokemon = this . getPokemon ( ) ;
2024-05-31 15:23:28 +01:00
const moveId = this . lastPokemon ? . scene . currentBattle . lastMove ;
2024-04-29 18:51:43 +01:00
const lastUsedMove = moveId ? allMoves [ moveId ] : undefined ;
2023-05-31 17:38:55 +01:00
2024-05-31 15:23:28 +01:00
const currentCommand = pokemon . scene . currentBattle . turnCommands [ this . fieldIndex ] ? . command ;
2024-06-01 01:50:30 +01:00
const lastPokemonIsForceSwitchedAndNotFainted = lastUsedMove ? . hasAttr ( ForceSwitchOutAttr ) && ! this . lastPokemon . isFainted ( ) ;
2024-05-31 15:23:28 +01:00
2024-03-08 18:14:08 +00:00
// Compensate for turn spent summoning
2024-05-31 15:23:28 +01:00
// Or compensate for force switch move if switched out pokemon is not fainted
if ( currentCommand === Command . POKEMON || lastPokemonIsForceSwitchedAndNotFainted ) {
2024-03-14 22:03:38 +00:00
pokemon . battleSummonData . turnCount -- ;
2024-05-23 16:03:10 +01:00
}
2024-03-08 18:14:08 +00:00
2024-05-23 16:03:10 +01:00
if ( this . batonPass && pokemon ) {
2023-05-31 17:38:55 +01:00
pokemon . transferSummon ( this . lastPokemon ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-29 00:26:41 +01:00
2023-05-18 16:11:06 +01:00
this . lastPokemon ? . resetSummonData ( ) ;
2024-01-10 04:34:43 +00:00
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeActiveTrigger , true ) ;
2023-07-05 21:13:00 +01:00
}
2023-04-29 00:26:41 +01:00
2023-07-05 21:13:00 +01:00
queuePostSummon ( ) : void {
this . scene . unshiftPhase ( new PostSummonPhase ( this . scene , this . getPokemon ( ) . getBattlerIndex ( ) ) ) ;
2023-04-29 00:26:41 +01:00
}
2023-04-10 19:12:01 +01:00
}
2023-05-18 16:11:06 +01:00
export class ReturnPhase extends SwitchSummonPhase {
constructor ( scene : BattleScene , fieldIndex : integer ) {
super ( scene , fieldIndex , - 1 , true , false ) ;
}
switchAndSummon ( ) : void {
this . end ( ) ;
}
summon ( ) : void { }
2023-07-04 23:06:12 +01:00
2023-07-05 21:13:00 +01:00
onEnd ( ) : void {
const pokemon = this . getPokemon ( ) ;
pokemon . resetTurnData ( ) ;
pokemon . resetSummonData ( ) ;
2024-01-10 04:34:43 +00:00
2024-02-15 04:25:12 +00:00
this . scene . updateFieldScale ( ) ;
2024-01-10 04:34:43 +00:00
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeActiveTrigger ) ;
2023-07-05 21:13:00 +01:00
}
2023-05-18 16:11:06 +01:00
}
2023-10-18 23:01:15 +01:00
export class ShowTrainerPhase extends BattlePhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
super . start ( ) ;
2024-05-23 16:03:10 +01:00
this . scene . trainer . setVisible ( true ) ;
2024-01-15 01:47:08 +00:00
2024-05-23 16:03:10 +01:00
this . scene . trainer . setTexture ( ` trainer_ ${ this . scene . gameData . gender === PlayerGender . FEMALE ? "f" : "m" } _back ` ) ;
2023-10-18 23:01:15 +01:00
this . scene . tweens . add ( {
targets : this.scene.trainer ,
x : 106 ,
duration : 1000 ,
onComplete : ( ) = > this . end ( )
} ) ;
}
}
2023-05-18 16:11:06 +01:00
export class ToggleDoublePositionPhase extends BattlePhase {
private double : boolean ;
constructor ( scene : BattleScene , double : boolean ) {
super ( scene ) ;
this . double = double ;
}
start() {
super . start ( ) ;
2023-07-06 03:33:27 +01:00
const playerPokemon = this . scene . getPlayerField ( ) . find ( p = > p . isActive ( true ) ) ;
2023-11-25 20:53:38 +00:00
if ( playerPokemon ) {
2024-06-08 06:07:23 +01:00
playerPokemon . setFieldPosition ( this . double && this . scene . getParty ( ) . filter ( p = > p . isAllowedInBattle ( ) ) . length > 1 ? FieldPosition.LEFT : FieldPosition.CENTER , 500 ) . then ( ( ) = > {
2023-11-25 20:53:38 +00:00
if ( playerPokemon . getFieldIndex ( ) === 1 ) {
const party = this . scene . getParty ( ) ;
party [ 1 ] = party [ 0 ] ;
party [ 0 ] = playerPokemon ;
}
this . end ( ) ;
} ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-05-18 16:11:06 +01:00
this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
}
}
2023-04-10 19:12:01 +01:00
export class CheckSwitchPhase extends BattlePhase {
2023-05-18 16:11:06 +01:00
protected fieldIndex : integer ;
protected useName : boolean ;
constructor ( scene : BattleScene , fieldIndex : integer , useName : boolean ) {
super ( scene ) ;
this . fieldIndex = fieldIndex ;
this . useName = useName ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
2023-05-18 16:11:06 +01:00
const pokemon = this . scene . getPlayerField ( ) [ this . fieldIndex ] ;
2024-06-13 15:10:31 +01:00
if ( this . scene . battleStyle === BattleStyle . SET ) {
2024-06-06 16:26:04 +01:00
super . end ( ) ;
return ;
}
2023-05-18 16:11:06 +01:00
if ( this . scene . field . getAll ( ) . indexOf ( pokemon ) === - 1 ) {
this . scene . unshiftPhase ( new SummonMissingPhase ( this . scene , this . fieldIndex ) ) ;
2023-04-14 04:50:48 +01:00
super . end ( ) ;
return ;
}
2023-05-18 16:11:06 +01:00
if ( ! this . scene . getParty ( ) . slice ( 1 ) . filter ( p = > p . isActive ( ) ) . length ) {
2023-04-20 16:29:26 +01:00
super . end ( ) ;
return ;
}
2023-05-18 16:11:06 +01:00
if ( pokemon . getTag ( BattlerTagType . FRENZY ) ) {
2023-04-15 06:32:16 +01:00
super . end ( ) ;
return ;
}
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:switchQuestion" , { pokemonName : this.useName ? pokemon.name : i18next.t ( "battle:pokemon" ) } ) , null , ( ) = > {
2023-04-10 19:12:01 +01:00
this . scene . ui . setMode ( Mode . CONFIRM , ( ) = > {
2023-04-19 19:07:38 +01:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-04-11 17:35:22 +01:00
this . scene . tryRemovePhase ( p = > p instanceof PostSummonPhase && p . player && p . fieldIndex === this . fieldIndex ) ;
2023-05-18 16:11:06 +01:00
this . scene . unshiftPhase ( new SwitchPhase ( this . scene , this . fieldIndex , false , true ) ) ;
2023-04-10 19:12:01 +01:00
this . end ( ) ;
2023-04-19 19:07:38 +01:00
} , ( ) = > {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . end ( ) ;
} ) ;
2023-04-10 19:12:01 +01:00
} ) ;
}
}
2023-04-14 04:50:48 +01:00
export class SummonMissingPhase extends SummonPhase {
2023-05-18 16:11:06 +01:00
constructor ( scene : BattleScene , fieldIndex : integer ) {
super ( scene , fieldIndex ) ;
2023-04-14 04:50:48 +01:00
}
preSummon ( ) : void {
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:sendOutPokemon" , { pokemonName : this.getPokemon ( ) . name } ) ) ;
2023-04-14 04:50:48 +01:00
this . scene . time . delayedCall ( 250 , ( ) = > this . summon ( ) ) ;
}
}
2023-10-03 17:50:31 +01:00
export class LevelCapPhase extends FieldPhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start ( ) : void {
super . start ( ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "level_up_fanfare" ) ;
this . scene . ui . showText ( i18next . t ( "battle:levelCapUp" , { levelCap : this.scene.getMaxExpLevel ( ) } ) , null , ( ) = > this . end ( ) , null , true ) ;
2023-10-03 17:50:31 +01:00
this . executeForAll ( pokemon = > pokemon . updateInfo ( true ) ) ;
} ) ;
}
}
2023-05-18 16:11:06 +01:00
export class TurnInitPhase extends FieldPhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
2023-04-27 19:30:03 +01:00
2023-05-18 16:11:06 +01:00
start() {
super . start ( ) ;
2023-04-27 19:30:03 +01:00
2024-06-08 06:07:23 +01:00
this . scene . getPlayerField ( ) . forEach ( p = > {
// If this pokemon is in play and evolved into something illegal under the current challenge, force a switch
if ( p . isOnField ( ) && ! p . isAllowedInBattle ( ) ) {
this . scene . queueMessage ( i18next . t ( "challenges:illegalEvolution" , { "pokemon" : p . name } ) , null , true ) ;
const allowedPokemon = this . scene . getParty ( ) . filter ( p = > p . isAllowedInBattle ( ) ) ;
if ( ! allowedPokemon . length ) {
// If there are no longer any legal pokemon in the party, game over.
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new GameOverPhase ( this . scene ) ) ;
} else if ( allowedPokemon . length >= this . scene . currentBattle . getBattlerCount ( ) || ( this . scene . currentBattle . double && ! allowedPokemon [ 0 ] . isActive ( true ) ) ) {
// If there is at least one pokemon in the back that is legal to switch in, force a switch.
p . switchOut ( false , true ) ;
} else {
// If there are no pokemon in the back but we're not game overing, just hide the pokemon.
// This should only happen in double battles.
p . hideInfo ( ) ;
p . setVisible ( false ) ;
this . scene . field . remove ( p ) ;
this . scene . triggerPokemonFormChange ( p , SpeciesFormChangeActiveTrigger , true ) ;
}
if ( allowedPokemon . length === 1 && this . scene . currentBattle . double ) {
this . scene . unshiftPhase ( new ToggleDoublePositionPhase ( this . scene , true ) ) ;
}
}
} ) ;
2023-12-22 01:58:00 +00:00
//this.scene.pushPhase(new MoveAnimTestPhase(this.scene));
2024-05-25 17:23:53 +01:00
this . scene . eventTarget . dispatchEvent ( new TurnInitEvent ( ) ) ;
2023-12-22 01:58:00 +00:00
2023-06-01 18:54:52 +01:00
this . scene . getField ( ) . forEach ( ( pokemon , i ) = > {
if ( pokemon ? . isActive ( ) ) {
2024-05-23 16:03:10 +01:00
if ( pokemon . isPlayer ( ) ) {
2023-06-01 18:54:52 +01:00
this . scene . currentBattle . addParticipant ( pokemon as PlayerPokemon ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-27 19:30:03 +01:00
2023-06-01 18:54:52 +01:00
pokemon . resetTurnData ( ) ;
2023-04-27 19:30:03 +01:00
2023-06-01 18:54:52 +01:00
this . scene . pushPhase ( pokemon . isPlayer ( ) ? new CommandPhase ( this . scene , i ) : new EnemyCommandPhase ( this . scene , i - BattlerIndex . ENEMY ) ) ;
}
2023-05-18 16:11:06 +01:00
} ) ;
this . scene . pushPhase ( new TurnStartPhase ( this . scene ) ) ;
this . end ( ) ;
2023-04-27 19:30:03 +01:00
}
}
export class CommandPhase extends FieldPhase {
2023-05-18 16:11:06 +01:00
protected fieldIndex : integer ;
constructor ( scene : BattleScene , fieldIndex : integer ) {
super ( scene ) ;
this . fieldIndex = fieldIndex ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
2023-10-27 22:43:53 +01:00
if ( this . fieldIndex ) {
2024-06-08 06:07:23 +01:00
// If we somehow are attempting to check the right pokemon but there's only one pokemon out
// Switch back to the center pokemon. This can happen rarely in double battles with mid turn switching
if ( this . scene . getPlayerField ( ) . filter ( p = > p . isActive ( ) ) . length === 1 ) {
this . fieldIndex = FieldPosition . CENTER ;
} else {
const allyCommand = this . scene . currentBattle . turnCommands [ this . fieldIndex - 1 ] ;
if ( allyCommand . command === Command . BALL || allyCommand . command === Command . RUN ) {
this . scene . currentBattle . turnCommands [ this . fieldIndex ] = { command : allyCommand.command , skip : true } ;
}
2024-05-23 16:03:10 +01:00
}
2023-10-27 22:43:53 +01:00
}
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . turnCommands [ this . fieldIndex ] ? . skip ) {
2023-10-27 22:43:53 +01:00
return this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-27 22:43:53 +01:00
2023-05-18 16:11:06 +01:00
const playerPokemon = this . scene . getPlayerField ( ) [ this . fieldIndex ] ;
2023-04-13 17:16:36 +01:00
2023-04-21 02:32:48 +01:00
const moveQueue = playerPokemon . getMoveQueue ( ) ;
while ( moveQueue . length && moveQueue [ 0 ]
2023-05-06 05:42:01 +01:00
&& moveQueue [ 0 ] . move && ( ! playerPokemon . getMoveset ( ) . find ( m = > m . moveId === moveQueue [ 0 ] . move )
2024-05-23 16:03:10 +01:00
|| ! playerPokemon . getMoveset ( ) [ playerPokemon . getMoveset ( ) . findIndex ( m = > m . moveId === moveQueue [ 0 ] . move ) ] . isUsable ( playerPokemon , moveQueue [ 0 ] . ignorePP ) ) ) {
moveQueue . shift ( ) ;
}
2023-04-21 02:32:48 +01:00
if ( moveQueue . length ) {
const queuedMove = moveQueue [ 0 ] ;
2024-05-23 16:03:10 +01:00
if ( ! queuedMove . move ) {
2023-05-01 04:58:16 +01:00
this . handleCommand ( Command . FIGHT , - 1 , false ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-05-06 05:42:01 +01:00
const moveIndex = playerPokemon . getMoveset ( ) . findIndex ( m = > m . moveId === queuedMove . move ) ;
2023-10-25 14:41:37 +01:00
if ( moveIndex > - 1 && playerPokemon . getMoveset ( ) [ moveIndex ] . isUsable ( playerPokemon , queuedMove . ignorePP ) ) {
2023-05-18 16:11:06 +01:00
this . handleCommand ( Command . FIGHT , moveIndex , queuedMove . ignorePP , { targets : queuedMove.targets , multiple : queuedMove.targets.length > 1 } ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-10-20 16:38:41 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-21 02:32:48 +01:00
}
2024-05-23 16:03:10 +01:00
} else {
2023-10-20 16:38:41 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
}
2023-04-29 00:26:41 +01:00
handleCommand ( command : Command , cursor : integer , . . . args : any [ ] ) : boolean {
2023-05-18 16:11:06 +01:00
const playerPokemon = this . scene . getPlayerField ( ) [ this . fieldIndex ] ;
const enemyField = this . scene . getEnemyField ( ) ;
2023-04-10 19:12:01 +01:00
let success : boolean ;
2023-04-12 00:08:03 +01:00
2023-04-10 19:12:01 +01:00
switch ( command ) {
2024-05-23 16:03:10 +01:00
case Command . FIGHT :
let useStruggle = false ;
2024-05-24 00:45:04 +01:00
if ( cursor === - 1 ||
playerPokemon . trySelectMove ( cursor , args [ 0 ] as boolean ) ||
( useStruggle = cursor > - 1 && ! playerPokemon . getMoveset ( ) . filter ( m = > m . isUsable ( playerPokemon ) ) . length ) ) {
2024-05-23 16:03:10 +01:00
const moveId = ! useStruggle ? cursor > - 1 ? playerPokemon . getMoveset ( ) [ cursor ] . moveId : Moves.NONE : Moves . STRUGGLE ;
const turnCommand : TurnCommand = { command : Command.FIGHT , cursor : cursor , move : { move : moveId , targets : [ ] , ignorePP : args [ 0 ] } , args : args } ;
const moveTargets : MoveTargetSet = args . length < 3 ? getMoveTargets ( playerPokemon , moveId ) : args [ 2 ] ;
if ( ! moveId ) {
turnCommand . targets = [ this . fieldIndex ] ;
2024-05-06 05:02:52 +01:00
}
2024-05-23 16:03:10 +01:00
console . log ( moveTargets , playerPokemon . name ) ;
if ( moveTargets . targets . length <= 1 || moveTargets . multiple ) {
turnCommand . move . targets = moveTargets . targets ;
2024-05-24 01:19:20 +01:00
} else if ( playerPokemon . getTag ( BattlerTagType . CHARGING ) && playerPokemon . getMoveQueue ( ) . length >= 1 ) {
2024-05-23 16:03:10 +01:00
turnCommand . move . targets = playerPokemon . getMoveQueue ( ) [ 0 ] . targets ;
} else {
this . scene . unshiftPhase ( new SelectTargetPhase ( this . scene , this . fieldIndex ) ) ;
}
this . scene . currentBattle . turnCommands [ this . fieldIndex ] = turnCommand ;
success = true ;
} else if ( cursor < playerPokemon . getMoveset ( ) . length ) {
const move = playerPokemon . getMoveset ( ) [ cursor ] ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-05-06 05:02:52 +01:00
2024-05-23 16:03:10 +01:00
// Decides between a Disabled, Not Implemented, or No PP translation message
2024-05-24 00:45:04 +01:00
const errorMessage =
playerPokemon . summonData . disabledMove === move . moveId ? "battle:moveDisabled" :
2024-05-23 16:03:10 +01:00
move . getName ( ) . endsWith ( " (N)" ) ? "battle:moveNotImplemented" : "battle:moveNoPP" ;
const moveName = move . getName ( ) . replace ( " (N)" , "" ) ; // Trims off the indicator
2024-05-06 05:02:52 +01:00
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( errorMessage , { moveName : moveName } ) , null , ( ) = > {
this . scene . ui . clearText ( ) ;
this . scene . ui . setMode ( Mode . FIGHT , this . fieldIndex ) ;
} , null , true ) ;
}
break ;
case Command . BALL :
if ( this . scene . arena . biomeType === Biome . END && ( ! this . scene . gameMode . isClassic || ( this . scene . getEnemyField ( ) . filter ( p = > p . isActive ( true ) ) . some ( p = > ! p . scene . gameData . dexData [ p . species . speciesId ] . caughtAttr ) && this . scene . gameData . getStarterCount ( d = > ! ! d . caughtAttr ) < Object . keys ( speciesStarters ) . length - 1 ) ) ) {
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . showText ( i18next . t ( "battle:noPokeballForce" ) , null , ( ) = > {
this . scene . ui . showText ( null , 0 ) ;
2023-10-20 16:38:41 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2024-05-23 16:03:10 +01:00
} , null , true ) ;
} else if ( this . scene . currentBattle . battleType === BattleType . TRAINER ) {
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . showText ( i18next . t ( "battle:noPokeballTrainer" ) , null , ( ) = > {
this . scene . ui . showText ( null , 0 ) ;
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
} , null , true ) ;
} else {
const targets = this . scene . getEnemyField ( ) . filter ( p = > p . isActive ( true ) ) . map ( p = > p . getBattlerIndex ( ) ) ;
if ( targets . length > 1 ) {
2023-10-20 16:38:41 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-10-18 23:01:15 +01:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:noPokeballMulti" ) , null , ( ) = > {
2023-04-30 05:51:33 +01:00
this . scene . ui . showText ( null , 0 ) ;
2023-10-20 16:38:41 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-04-29 21:26:09 +01:00
} , null , true ) ;
2024-05-23 16:03:10 +01:00
} else if ( cursor < 5 ) {
const targetPokemon = this . scene . getEnemyField ( ) . find ( p = > p . isActive ( true ) ) ;
if ( targetPokemon . isBoss ( ) && targetPokemon . bossSegmentIndex >= 1 && ! targetPokemon . hasAbility ( Abilities . WONDER_GUARD , false , true ) && cursor < PokeballType . MASTER_BALL ) {
2023-10-27 22:43:53 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:noPokeballStrong" ) , null , ( ) = > {
2023-10-27 22:43:53 +01:00
this . scene . ui . showText ( null , 0 ) ;
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
} , null , true ) ;
2024-05-23 16:03:10 +01:00
} else {
this . scene . currentBattle . turnCommands [ this . fieldIndex ] = { command : Command.BALL , cursor : cursor } ;
this . scene . currentBattle . turnCommands [ this . fieldIndex ] . targets = targets ;
if ( this . fieldIndex ) {
this . scene . currentBattle . turnCommands [ this . fieldIndex - 1 ] . skip = true ;
2023-10-27 22:43:53 +01:00
}
2024-05-23 16:03:10 +01:00
success = true ;
2023-10-27 22:43:53 +01:00
}
2023-04-10 19:12:01 +01:00
}
2024-05-23 16:03:10 +01:00
}
break ;
case Command . POKEMON :
case Command . RUN :
const isSwitch = command === Command . POKEMON ;
if ( ! isSwitch && this . scene . arena . biomeType === Biome . END ) {
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . showText ( i18next . t ( "battle:noEscapeForce" ) , null , ( ) = > {
this . scene . ui . showText ( null , 0 ) ;
2023-10-28 00:56:15 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2024-05-23 16:03:10 +01:00
} , null , true ) ;
} else if ( ! isSwitch && this . scene . currentBattle . battleType === BattleType . TRAINER ) {
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . showText ( i18next . t ( "battle:noEscapeTrainer" ) , null , ( ) = > {
this . scene . ui . showText ( null , 0 ) ;
2023-10-20 16:38:41 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2024-05-23 16:03:10 +01:00
} , null , true ) ;
} else {
const trapTag = playerPokemon . findTag ( t = > t instanceof TrappedTag ) as TrappedTag ;
const trapped = new Utils . BooleanHolder ( false ) ;
const batonPass = isSwitch && args [ 0 ] as boolean ;
if ( ! batonPass ) {
enemyField . forEach ( enemyPokemon = > applyCheckTrappedAbAttrs ( CheckTrappedAbAttr , enemyPokemon , trapped , playerPokemon ) ) ;
}
if ( batonPass || ( ! trapTag && ! trapped . value ) ) {
this . scene . currentBattle . turnCommands [ this . fieldIndex ] = isSwitch
? { command : Command.POKEMON , cursor : cursor , args : args }
: { command : Command.RUN } ;
success = true ;
if ( ! isSwitch && this . fieldIndex ) {
this . scene . currentBattle . turnCommands [ this . fieldIndex - 1 ] . skip = true ;
}
} else if ( trapTag ) {
2024-05-24 01:19:20 +01:00
if ( trapTag . sourceMove === Moves . INGRAIN && this . scene . getPokemonById ( trapTag . sourceId ) . isOfType ( Type . GHOST ) ) {
2023-10-18 23:01:15 +01:00
success = true ;
2024-05-24 00:45:04 +01:00
this . scene . currentBattle . turnCommands [ this . fieldIndex ] = isSwitch
2024-05-17 04:09:40 +01:00
? { command : Command.POKEMON , cursor : cursor , args : args }
: { command : Command.RUN } ;
2024-05-23 16:03:10 +01:00
break ;
2023-10-18 23:01:15 +01:00
}
2024-05-24 00:45:04 +01:00
if ( ! isSwitch ) {
2024-05-23 16:03:10 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
}
this . scene . ui . showText (
i18next . t ( "battle:noEscapePokemon" , {
pokemonName : this.scene.getPokemonById ( trapTag . sourceId ) . name ,
moveName : trapTag.getMoveName ( ) ,
escapeVerb : isSwitch ? i18next . t ( "battle:escapeVerbSwitch" ) : i18next . t ( "battle:escapeVerbFlee" )
} ) ,
null ,
( ) = > {
this . scene . ui . showText ( null , 0 ) ;
if ( ! isSwitch ) {
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
}
} , null , true ) ;
2023-10-18 23:01:15 +01:00
}
2024-05-23 16:03:10 +01:00
}
break ;
2023-04-10 19:12:01 +01:00
}
2023-04-12 05:37:56 +01:00
2024-05-23 16:03:10 +01:00
if ( success ) {
2023-04-10 19:12:01 +01:00
this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
return success ;
}
2023-10-19 04:16:38 +01:00
cancel() {
if ( this . fieldIndex ) {
this . scene . unshiftPhase ( new CommandPhase ( this . scene , 0 ) ) ;
this . scene . unshiftPhase ( new CommandPhase ( this . scene , 1 ) ) ;
this . end ( ) ;
}
}
2023-11-16 05:58:57 +00:00
checkFightOverride ( ) : boolean {
const pokemon = this . getPokemon ( ) ;
const encoreTag = pokemon . getTag ( EncoreTag ) as EncoreTag ;
2024-05-23 16:03:10 +01:00
if ( ! encoreTag ) {
2023-11-16 05:58:57 +00:00
return false ;
2024-05-23 16:03:10 +01:00
}
2023-11-16 05:58:57 +00:00
const moveIndex = pokemon . getMoveset ( ) . findIndex ( m = > m . moveId === encoreTag . moveId ) ;
2024-05-23 16:03:10 +01:00
if ( moveIndex === - 1 || ! pokemon . getMoveset ( ) [ moveIndex ] . isUsable ( pokemon ) ) {
2023-11-16 05:58:57 +00:00
return false ;
2024-05-23 16:03:10 +01:00
}
2023-11-16 05:58:57 +00:00
this . handleCommand ( Command . FIGHT , moveIndex , false ) ;
return true ;
}
2023-10-19 04:16:38 +01:00
getFieldIndex ( ) : integer {
return this . fieldIndex ;
}
2023-05-18 16:11:06 +01:00
getPokemon ( ) : PlayerPokemon {
return this . scene . getPlayerField ( ) [ this . fieldIndex ] ;
}
2023-04-10 19:12:01 +01:00
end() {
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > super . end ( ) ) ;
}
}
2023-05-18 16:11:06 +01:00
export class EnemyCommandPhase extends FieldPhase {
protected fieldIndex : integer ;
constructor ( scene : BattleScene , fieldIndex : integer ) {
super ( scene ) ;
this . fieldIndex = fieldIndex ;
}
start() {
super . start ( ) ;
const enemyPokemon = this . scene . getEnemyField ( ) [ this . fieldIndex ] ;
2024-04-05 15:59:03 +01:00
const battle = this . scene . currentBattle ;
const trainer = battle . trainer ;
2024-01-15 05:20:26 +00:00
2024-03-08 23:38:04 +00:00
if ( trainer && ! enemyPokemon . getMoveQueue ( ) . length ) {
2024-01-15 05:20:26 +00:00
const opponents = enemyPokemon . getOpponents ( ) ;
const trapTag = enemyPokemon . findTag ( t = > t instanceof TrappedTag ) as TrappedTag ;
const trapped = new Utils . BooleanHolder ( false ) ;
2024-05-19 18:05:46 +01:00
opponents . forEach ( playerPokemon = > applyCheckTrappedAbAttrs ( CheckTrappedAbAttr , playerPokemon , trapped , enemyPokemon ) ) ;
2024-04-03 17:14:26 +01:00
if ( ! trapTag && ! trapped . value ) {
2024-03-31 17:00:54 +01:00
const partyMemberScores = trainer . getPartyMemberMatchupScores ( enemyPokemon . trainerSlot , true ) ;
2024-01-15 05:20:26 +00:00
if ( partyMemberScores . length ) {
const matchupScores = opponents . map ( opp = > enemyPokemon . getMatchupScore ( opp ) ) ;
const matchupScore = matchupScores . reduce ( ( total , score ) = > total += score , 0 ) / matchupScores . length ;
2024-05-24 00:45:04 +01:00
2024-01-15 05:20:26 +00:00
const sortedPartyMemberScores = trainer . getSortedPartyMemberMatchupScores ( partyMemberScores ) ;
2024-04-05 15:59:03 +01:00
const switchMultiplier = 1 - ( battle . enemySwitchCounter ? Math . pow ( 0.1 , ( 1 / battle . enemySwitchCounter ) ) : 0 ) ;
if ( sortedPartyMemberScores [ 0 ] [ 1 ] * switchMultiplier >= matchupScore * ( trainer . config . isBoss ? 2 : 3 ) ) {
2024-03-25 02:24:26 +00:00
const index = trainer . getNextSummonIndex ( enemyPokemon . trainerSlot , partyMemberScores ) ;
2024-01-15 05:20:26 +00:00
2024-04-05 15:59:03 +01:00
battle . turnCommands [ this . fieldIndex + BattlerIndex . ENEMY ] =
2024-01-15 05:20:26 +00:00
{ command : Command.POKEMON , cursor : index , args : [ false ] } ;
2024-05-24 00:45:04 +01:00
2024-04-05 15:59:03 +01:00
battle . enemySwitchCounter ++ ;
2024-01-15 05:20:26 +00:00
return this . end ( ) ;
}
}
}
}
2023-05-18 16:11:06 +01:00
const nextMove = enemyPokemon . getNextMove ( ) ;
this . scene . currentBattle . turnCommands [ this . fieldIndex + BattlerIndex . ENEMY ] =
{ command : Command.FIGHT , move : nextMove } ;
2024-04-05 15:59:03 +01:00
this . scene . currentBattle . enemySwitchCounter = Math . max ( this . scene . currentBattle . enemySwitchCounter - 1 , 0 ) ;
2023-05-18 16:11:06 +01:00
this . end ( ) ;
}
}
export class SelectTargetPhase extends PokemonPhase {
constructor ( scene : BattleScene , fieldIndex : integer ) {
super ( scene , fieldIndex ) ;
}
start() {
super . start ( ) ;
const turnCommand = this . scene . currentBattle . turnCommands [ this . fieldIndex ] ;
const move = turnCommand . move ? . move ;
this . scene . ui . setMode ( Mode . TARGET_SELECT , this . fieldIndex , move , ( cursor : integer ) = > {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
if ( cursor === - 1 ) {
this . scene . currentBattle . turnCommands [ this . fieldIndex ] = null ;
this . scene . unshiftPhase ( new CommandPhase ( this . scene , this . fieldIndex ) ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-05-18 16:11:06 +01:00
turnCommand . targets = [ cursor ] ;
2024-05-23 16:03:10 +01:00
}
if ( turnCommand . command === Command . BALL && this . fieldIndex ) {
2023-10-27 22:43:53 +01:00
this . scene . currentBattle . turnCommands [ this . fieldIndex - 1 ] . skip = true ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
this . end ( ) ;
} ) ;
}
}
export class TurnStartPhase extends FieldPhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
super . start ( ) ;
const field = this . scene . getField ( ) ;
const order = this . getOrder ( ) ;
2024-05-15 14:42:45 +01:00
const battlerBypassSpeed = { } ;
this . scene . getField ( true ) . filter ( p = > p . summonData ) . map ( p = > {
const bypassSpeed = new Utils . BooleanHolder ( false ) ;
this . scene . applyModifiers ( BypassSpeedChanceModifier , p . isPlayer ( ) , p , bypassSpeed ) ;
battlerBypassSpeed [ p . getBattlerIndex ( ) ] = bypassSpeed ;
} ) ;
2023-05-18 16:11:06 +01:00
const moveOrder = order . slice ( 0 ) ;
moveOrder . sort ( ( a , b ) = > {
const aCommand = this . scene . currentBattle . turnCommands [ a ] ;
const bCommand = this . scene . currentBattle . turnCommands [ b ] ;
if ( aCommand . command !== bCommand . command ) {
2024-05-23 16:03:10 +01:00
if ( aCommand . command === Command . FIGHT ) {
2023-05-18 16:11:06 +01:00
return 1 ;
2024-05-23 16:03:10 +01:00
} else if ( bCommand . command === Command . FIGHT ) {
2023-05-18 16:11:06 +01:00
return - 1 ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
} else if ( aCommand . command === Command . FIGHT ) {
2024-03-28 20:24:11 +00:00
const aMove = allMoves [ aCommand . move . move ] ;
const bMove = allMoves [ bCommand . move . move ] ;
const aPriority = new Utils . IntegerHolder ( aMove . priority ) ;
const bPriority = new Utils . IntegerHolder ( bMove . priority ) ;
2024-05-19 23:52:51 +01:00
applyMoveAttrs ( IncrementMovePriorityAttr , this . scene . getField ( ) . find ( p = > p ? . isActive ( ) && p . getBattlerIndex ( ) === a ) , null , aMove , aPriority ) ;
applyMoveAttrs ( IncrementMovePriorityAttr , this . scene . getField ( ) . find ( p = > p ? . isActive ( ) && p . getBattlerIndex ( ) === b ) , null , bMove , bPriority ) ;
2024-03-28 20:24:11 +00:00
applyAbAttrs ( IncrementMovePriorityAbAttr , this . scene . getField ( ) . find ( p = > p ? . isActive ( ) && p . getBattlerIndex ( ) === a ) , null , aMove , aPriority ) ;
applyAbAttrs ( IncrementMovePriorityAbAttr , this . scene . getField ( ) . find ( p = > p ? . isActive ( ) && p . getBattlerIndex ( ) === b ) , null , bMove , bPriority ) ;
2024-05-24 00:45:04 +01:00
2024-05-23 16:03:10 +01:00
if ( aPriority . value !== bPriority . value ) {
2024-03-28 20:24:11 +00:00
return aPriority . value < bPriority . value ? 1 : - 1 ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
}
2024-05-15 14:42:45 +01:00
2024-05-23 16:03:10 +01:00
if ( battlerBypassSpeed [ a ] . value !== battlerBypassSpeed [ b ] . value ) {
2024-05-15 14:42:45 +01:00
return battlerBypassSpeed [ a ] . value ? - 1 : 1 ;
2024-05-23 16:03:10 +01:00
}
2024-05-24 00:45:04 +01:00
2023-05-18 16:11:06 +01:00
const aIndex = order . indexOf ( a ) ;
const bIndex = order . indexOf ( b ) ;
return aIndex < bIndex ? - 1 : aIndex > bIndex ? 1 : 0 ;
} ) ;
2024-05-23 16:03:10 +01:00
for ( const o of moveOrder ) {
2023-05-18 16:11:06 +01:00
const pokemon = field [ o ] ;
const turnCommand = this . scene . currentBattle . turnCommands [ o ] ;
2024-05-23 16:03:10 +01:00
if ( turnCommand . skip ) {
2023-10-27 22:43:53 +01:00
continue ;
2024-05-23 16:03:10 +01:00
}
2023-10-27 22:43:53 +01:00
2023-05-18 16:11:06 +01:00
switch ( turnCommand . command ) {
2024-05-23 16:03:10 +01:00
case Command . FIGHT :
const queuedMove = turnCommand . move ;
if ( ! queuedMove ) {
continue ;
}
const move = pokemon . getMoveset ( ) . find ( m = > m . moveId === queuedMove . move ) || new PokemonMove ( queuedMove . move ) ;
if ( pokemon . isPlayer ( ) ) {
if ( turnCommand . cursor === - 1 ) {
this . scene . pushPhase ( new MovePhase ( this . scene , pokemon , turnCommand . targets || turnCommand . move . targets , move ) ) ;
} else {
const playerPhase = new MovePhase ( this . scene , pokemon , turnCommand . targets || turnCommand . move . targets , move , false , queuedMove . ignorePP ) ;
this . scene . pushPhase ( playerPhase ) ;
}
} else {
this . scene . pushPhase ( new MovePhase ( this . scene , pokemon , turnCommand . targets || turnCommand . move . targets , move , false , queuedMove . ignorePP ) ) ;
}
break ;
case Command . BALL :
this . scene . unshiftPhase ( new AttemptCapturePhase ( this . scene , turnCommand . targets [ 0 ] % 2 , turnCommand . cursor ) ) ;
break ;
case Command . POKEMON :
2024-05-25 01:39:58 +01:00
this . scene . unshiftPhase ( new SwitchSummonPhase ( this . scene , pokemon . getFieldIndex ( ) , turnCommand . cursor , true , turnCommand . args [ 0 ] as boolean , pokemon . isPlayer ( ) ) ) ;
break ;
2024-05-23 16:03:10 +01:00
case Command . RUN :
2024-05-25 01:39:58 +01:00
let runningPokemon = pokemon ;
if ( this . scene . currentBattle . double ) {
2024-05-25 02:16:27 +01:00
const playerActivePokemon = field . filter ( pokemon = > {
if ( ! ! pokemon ) {
return pokemon . isPlayer ( ) && pokemon . isActive ( ) ;
} else {
return ;
}
} ) ;
2024-05-25 01:39:58 +01:00
// if only one pokemon is alive, use that one
if ( playerActivePokemon . length > 1 ) {
// find which active pokemon has faster speed
const fasterPokemon = playerActivePokemon [ 0 ] . getStat ( Stat . SPD ) > playerActivePokemon [ 1 ] . getStat ( Stat . SPD ) ? playerActivePokemon [ 0 ] : playerActivePokemon [ 1 ] ;
// check if either active pokemon has the ability "Run Away"
const hasRunAway = playerActivePokemon . find ( p = > p . hasAbility ( Abilities . RUN_AWAY ) ) ;
runningPokemon = hasRunAway !== undefined ? hasRunAway : fasterPokemon ;
}
2024-05-23 16:03:10 +01:00
}
2024-05-25 01:39:58 +01:00
this . scene . unshiftPhase ( new AttemptRunPhase ( this . scene , runningPokemon . getFieldIndex ( ) ) ) ;
2024-05-23 16:03:10 +01:00
break ;
2023-05-18 16:11:06 +01:00
}
}
2024-05-22 13:52:45 +01:00
2024-05-30 18:07:28 +01:00
this . scene . pushPhase ( new WeatherEffectPhase ( this . scene ) ) ;
2023-07-04 21:16:50 +01:00
2024-05-23 16:03:10 +01:00
for ( const o of order ) {
if ( field [ o ] . status && field [ o ] . status . isPostTurn ( ) ) {
2023-05-18 16:11:06 +01:00
this . scene . pushPhase ( new PostTurnStatusEffectPhase ( this . scene , o ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
}
2024-05-23 00:34:49 +01:00
this . scene . pushPhase ( new BerryPhase ( this . scene ) ) ;
2023-05-18 16:11:06 +01:00
this . scene . pushPhase ( new TurnEndPhase ( this . scene ) ) ;
this . end ( ) ;
}
}
2024-05-22 13:52:45 +01:00
/** The phase after attacks where the pokemon eat berries */
export class BerryPhase extends FieldPhase {
start() {
super . start ( ) ;
this . executeForAll ( ( pokemon ) = > {
2024-05-23 00:34:49 +01:00
const hasUsableBerry = ! ! this . scene . findModifier ( ( m ) = > {
return m instanceof BerryModifier && m . shouldApply ( [ pokemon ] ) ;
} , pokemon . isPlayer ( ) ) ;
2024-05-22 13:52:45 +01:00
if ( hasUsableBerry ) {
const cancelled = new Utils . BooleanHolder ( false ) ;
pokemon . getOpponents ( ) . map ( ( opp ) = > applyAbAttrs ( PreventBerryUseAbAttr , opp , cancelled ) ) ;
if ( cancelled . value ) {
pokemon . scene . queueMessage ( getPokemonMessage ( pokemon , " is too\nnervous to eat berries!" ) ) ;
} else {
2024-05-28 18:19:03 +01:00
this . scene . unshiftPhase (
new CommonAnimPhase ( this . scene , pokemon . getBattlerIndex ( ) , pokemon . getBattlerIndex ( ) , CommonAnim . USE_ITEM )
) ;
2024-05-22 13:52:45 +01:00
for ( const berryModifier of this . scene . applyModifiers ( BerryModifier , pokemon . isPlayer ( ) , pokemon ) as BerryModifier [ ] ) {
if ( berryModifier . consumed ) {
if ( ! -- berryModifier . stackCount ) {
this . scene . removeModifier ( berryModifier ) ;
} else {
berryModifier . consumed = false ;
}
}
2024-06-04 22:59:39 +01:00
this . scene . eventTarget . dispatchEvent ( new BerryUsedEvent ( berryModifier ) ) ; // Announce a berry was used
2024-05-22 13:52:45 +01:00
}
this . scene . updateModifiers ( pokemon . isPlayer ( ) ) ;
2024-05-28 18:19:03 +01:00
applyAbAttrs ( HealFromBerryUseAbAttr , pokemon , new Utils . BooleanHolder ( false ) ) ;
2024-05-22 13:52:45 +01:00
}
}
} ) ;
this . end ( ) ;
}
}
2023-04-27 19:30:03 +01:00
export class TurnEndPhase extends FieldPhase {
2023-04-13 17:16:36 +01:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
2023-04-19 03:09:37 +01:00
super . start ( ) ;
2023-05-18 16:11:06 +01:00
this . scene . currentBattle . incrementTurn ( this . scene ) ;
2024-05-25 17:23:53 +01:00
this . scene . eventTarget . dispatchEvent ( new TurnEndEvent ( this . scene . currentBattle . turn ) ) ;
2024-05-24 00:45:04 +01:00
2023-04-19 23:19:55 +01:00
const handlePokemon = ( pokemon : Pokemon ) = > {
2023-04-22 00:30:04 +01:00
pokemon . lapseTags ( BattlerTagLapseType . TURN_END ) ;
2024-05-24 00:45:04 +01:00
2023-10-25 14:41:37 +01:00
if ( pokemon . summonData . disabledMove && ! -- pokemon . summonData . disabledTurns ) {
2024-06-06 14:36:12 +01:00
this . scene . pushPhase ( new MessagePhase ( this . scene , i18next . t ( "battle:notDisabled" , { pokemonName : getPokemonNameWithAffix ( pokemon ) , moveName : allMoves [ pokemon . summonData . disabledMove ] . name } ) ) ) ;
2024-02-25 17:45:41 +00:00
pokemon . summonData . disabledMove = Moves . NONE ;
2023-04-19 23:19:55 +01:00
}
2023-04-15 22:40:18 +01:00
2023-04-21 01:15:16 +01:00
this . scene . applyModifiers ( TurnHealModifier , pokemon . isPlayer ( ) , pokemon ) ;
2024-03-10 05:14:09 +00:00
if ( this . scene . arena . terrain ? . terrainType === TerrainType . GRASSY && pokemon . isGrounded ( ) ) {
2024-03-10 02:57:33 +00:00
this . scene . unshiftPhase ( new PokemonHealPhase ( this . scene , pokemon . getBattlerIndex ( ) ,
2024-05-23 16:03:10 +01:00
Math . max ( pokemon . getMaxHp ( ) >> 4 , 1 ) , getPokemonMessage ( pokemon , "'s HP was restored." ) , true ) ) ;
2024-03-10 02:57:33 +00:00
}
2024-04-02 06:44:38 +01:00
if ( ! pokemon . isPlayer ( ) ) {
this . scene . applyModifiers ( EnemyTurnHealModifier , false , pokemon ) ;
this . scene . applyModifier ( EnemyStatusEffectHealChanceModifier , false , pokemon ) ;
}
2023-05-31 16:20:06 +01:00
applyPostTurnAbAttrs ( PostTurnAbAttr , pokemon ) ;
2023-05-02 20:56:41 +01:00
2024-05-31 20:31:11 +01:00
this . scene . applyModifiers ( TurnStatusEffectModifier , pokemon . isPlayer ( ) , pokemon ) ;
2023-05-05 21:55:46 +01:00
this . scene . applyModifiers ( TurnHeldItemTransferModifier , pokemon . isPlayer ( ) , pokemon ) ;
2023-04-23 15:24:22 +01:00
2023-04-19 23:19:55 +01:00
pokemon . battleSummonData . turnCount ++ ;
} ;
2023-05-18 16:11:06 +01:00
this . executeForAll ( handlePokemon ) ;
2024-05-24 00:45:04 +01:00
2023-04-22 00:30:04 +01:00
this . scene . arena . lapseTags ( ) ;
2023-04-13 17:16:36 +01:00
2024-05-23 16:03:10 +01:00
if ( this . scene . arena . weather && ! this . scene . arena . weather . lapse ( ) ) {
2023-04-19 19:07:38 +01:00
this . scene . arena . trySetWeather ( WeatherType . NONE , false ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-19 19:07:38 +01:00
2024-05-23 16:03:10 +01:00
if ( this . scene . arena . terrain && ! this . scene . arena . terrain . lapse ( ) ) {
2024-03-26 14:08:17 +00:00
this . scene . arena . trySetTerrain ( TerrainType . NONE , false ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-26 14:08:17 +00:00
2023-04-13 17:16:36 +01:00
this . end ( ) ;
}
}
2023-04-19 03:09:37 +01:00
export class BattleEndPhase extends BattlePhase {
start() {
super . start ( ) ;
2024-03-17 15:36:19 +00:00
this . scene . currentBattle . addBattleScore ( this . scene ) ;
2024-01-11 17:26:32 +00:00
this . scene . gameData . gameStats . battles ++ ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . trainer ) {
2024-01-11 17:26:32 +00:00
this . scene . gameData . gameStats . trainersDefeated ++ ;
2024-05-23 16:03:10 +01:00
}
if ( this . scene . gameMode . isEndless && this . scene . currentBattle . waveIndex + 1 > this . scene . gameData . gameStats . highestEndlessWave ) {
this . scene . gameData . gameStats . highestEndlessWave = this . scene . currentBattle . waveIndex + 1 ;
}
2024-01-11 17:26:32 +00:00
2024-05-28 02:01:13 +01:00
// Endless graceful end
if ( this . scene . gameMode . isEndless && this . scene . currentBattle . waveIndex >= 5850 ) {
this . scene . clearPhaseQueue ( ) ;
this . scene . unshiftPhase ( new GameOverPhase ( this . scene , true ) ) ;
}
2024-05-23 16:03:10 +01:00
for ( const pokemon of this . scene . getField ( ) ) {
if ( pokemon ) {
2023-05-18 16:11:06 +01:00
pokemon . resetBattleSummonData ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
}
2024-06-08 06:07:23 +01:00
for ( const pokemon of this . scene . getParty ( ) . filter ( p = > p . isAllowedInBattle ( ) ) ) {
2024-03-07 02:05:23 +00:00
applyPostBattleAbAttrs ( PostBattleAbAttr , pokemon ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-07 02:05:23 +00:00
2024-05-25 14:01:08 +01:00
if ( this . scene . currentBattle . moneyScattered ) {
this . scene . currentBattle . pickUpScatteredMoney ( this . scene ) ;
}
2023-10-28 18:24:57 +01:00
this . scene . clearEnemyHeldItemModifiers ( ) ;
2023-04-21 00:44:56 +01:00
2024-02-17 05:40:03 +00:00
const lapsingModifiers = this . scene . findModifiers ( m = > m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier ) as ( LapsingPersistentModifier | LapsingPokemonHeldItemModifier ) [ ] ;
2024-05-23 16:03:10 +01:00
for ( const m of lapsingModifiers ) {
2024-02-17 05:40:03 +00:00
const args : any [ ] = [ ] ;
2024-05-23 16:03:10 +01:00
if ( m instanceof LapsingPokemonHeldItemModifier ) {
2024-02-17 05:40:03 +00:00
args . push ( this . scene . getPokemonById ( m . pokemonId ) ) ;
2024-05-23 16:03:10 +01:00
}
if ( ! m . lapse ( args ) ) {
2023-04-19 03:09:37 +01:00
this . scene . removeModifier ( m ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-19 03:09:37 +01:00
}
this . scene . updateModifiers ( ) . then ( ( ) = > this . end ( ) ) ;
}
}
2023-05-18 16:11:06 +01:00
export class NewBattlePhase extends BattlePhase {
start() {
super . start ( ) ;
2023-04-10 19:12:01 +01:00
2023-05-18 16:11:06 +01:00
this . scene . newBattle ( ) ;
2023-04-10 19:12:01 +01:00
2023-05-18 16:11:06 +01:00
this . end ( ) ;
2023-04-10 19:12:01 +01:00
}
}
2023-04-14 06:08:44 +01:00
export class CommonAnimPhase extends PokemonPhase {
private anim : CommonAnim ;
2023-05-18 16:11:06 +01:00
private targetIndex : integer ;
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , targetIndex : BattlerIndex , anim : CommonAnim ) {
super ( scene , battlerIndex ) ;
2023-04-14 06:08:44 +01:00
this . anim = anim ;
2023-05-18 16:11:06 +01:00
this . targetIndex = targetIndex ;
2023-04-14 06:08:44 +01:00
}
2024-05-30 18:07:28 +01:00
setAnimation ( anim : CommonAnim ) {
this . anim = anim ;
}
2023-04-14 06:08:44 +01:00
start() {
2023-05-19 18:02:58 +01:00
new CommonBattleAnim ( this . anim , this . getPokemon ( ) , this . targetIndex !== undefined ? ( this . player ? this . scene . getEnemyField ( ) : this . scene . getPlayerField ( ) ) [ this . targetIndex ] : this . getPokemon ( ) ) . play ( this . scene , ( ) = > {
2023-04-14 06:08:44 +01:00
this . end ( ) ;
} ) ;
}
}
2023-05-18 16:11:06 +01:00
export class MovePhase extends BattlePhase {
2024-01-13 17:24:24 +00:00
public pokemon : Pokemon ;
2024-02-20 18:15:53 +00:00
public move : PokemonMove ;
2024-03-28 04:24:24 +00:00
public targets : BattlerIndex [ ] ;
2023-04-16 23:40:32 +01:00
protected followUp : boolean ;
2023-05-01 04:58:16 +01:00
protected ignorePp : boolean ;
2024-05-08 21:51:56 +01:00
protected failed : boolean ;
2023-04-12 00:08:03 +01:00
protected cancelled : boolean ;
2023-04-10 19:12:01 +01:00
2023-05-18 16:11:06 +01:00
constructor ( scene : BattleScene , pokemon : Pokemon , targets : BattlerIndex [ ] , move : PokemonMove , followUp? : boolean , ignorePp? : boolean ) {
2023-04-10 19:12:01 +01:00
super ( scene ) ;
this . pokemon = pokemon ;
2023-05-18 16:11:06 +01:00
this . targets = targets ;
2023-04-10 19:12:01 +01:00
this . move = move ;
2023-04-16 23:40:32 +01:00
this . followUp = ! ! followUp ;
2023-05-01 04:58:16 +01:00
this . ignorePp = ! ! ignorePp ;
2024-05-08 21:51:56 +01:00
this . failed = false ;
2023-04-12 00:08:03 +01:00
this . cancelled = false ;
2023-04-10 19:12:01 +01:00
}
canMove ( ) : boolean {
2023-10-25 14:41:37 +01:00
return this . pokemon . isActive ( true ) && this . move . isUsable ( this . pokemon , this . ignorePp ) && ! ! this . targets . length ;
2023-04-10 19:12:01 +01:00
}
2024-05-08 21:51:56 +01:00
/**Signifies the current move should fail but still use PP */
fail ( ) : void {
this . failed = true ;
}
/**Signifies the current move should cancel and retain PP */
2023-04-15 06:32:16 +01:00
cancel ( ) : void {
this . cancelled = true ;
}
2023-04-10 19:12:01 +01:00
start() {
super . start ( ) ;
2023-04-19 23:19:55 +01:00
console . log ( Moves [ this . move . moveId ] ) ;
2023-05-04 21:38:56 +01:00
if ( ! this . canMove ( ) ) {
2024-05-25 05:00:58 +01:00
if ( this . move . moveId && this . pokemon . summonData ? . disabledMove === this . move . moveId ) {
2023-05-04 21:38:56 +01:00
this . scene . queueMessage ( ` ${ this . move . getName ( ) } is disabled! ` ) ;
2024-05-23 16:03:10 +01:00
}
2023-12-07 22:43:56 +00:00
return this . end ( ) ;
2023-05-04 21:38:56 +01:00
}
2024-03-14 04:40:57 +00:00
if ( ! this . followUp ) {
2024-05-23 16:03:10 +01:00
if ( this . move . getMove ( ) . checkFlag ( MoveFlags . IGNORE_ABILITIES , this . pokemon , null ) ) {
2024-04-18 06:44:03 +01:00
this . scene . arena . setIgnoreAbilities ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-03 16:42:09 +01:00
} else {
this . pokemon . turnData . hitsLeft = undefined ;
this . pokemon . turnData . hitCount = undefined ;
2024-03-14 04:40:57 +00:00
}
2024-03-12 00:55:41 +00:00
// Move redirection abilities (ie. Storm Drain) only support single target moves
2024-05-11 14:22:26 +01:00
const moveTarget = this . targets . length === 1
? new Utils . IntegerHolder ( this . targets [ 0 ] )
: null ;
if ( moveTarget ) {
2024-05-23 16:03:10 +01:00
const oldTarget = moveTarget . value ;
2024-05-11 14:22:26 +01:00
this . scene . getField ( true ) . filter ( p = > p !== this . pokemon ) . forEach ( p = > applyAbAttrs ( RedirectMoveAbAttr , p , null , this . move . moveId , moveTarget ) ) ;
//Check if this move is immune to being redirected, and restore its target to the intended target if it is.
2024-06-01 01:50:30 +01:00
if ( ( this . pokemon . hasAbilityWithAttr ( BlockRedirectAbAttr ) || this . move . getMove ( ) . hasAttr ( BypassRedirectAttr ) ) ) {
2024-05-11 14:22:26 +01:00
//If an ability prevented this move from being redirected, display its ability pop up.
2024-06-01 01:50:30 +01:00
if ( ( this . pokemon . hasAbilityWithAttr ( BlockRedirectAbAttr ) && ! this . move . getMove ( ) . hasAttr ( BypassRedirectAttr ) ) && oldTarget !== moveTarget . value ) {
2024-05-11 14:22:26 +01:00
this . scene . unshiftPhase ( new ShowAbilityPhase ( this . scene , this . pokemon . getBattlerIndex ( ) , this . pokemon . getPassiveAbility ( ) . hasAttr ( BlockRedirectAbAttr ) ) ) ;
}
2024-05-10 16:40:21 +01:00
moveTarget . value = oldTarget ;
2024-05-11 14:22:26 +01:00
}
this . targets [ 0 ] = moveTarget . value ;
}
2024-03-12 00:55:41 +00:00
2023-11-27 16:42:03 +00:00
if ( this . targets . length === 1 && this . targets [ 0 ] === BattlerIndex . ATTACKER ) {
if ( this . pokemon . turnData . attacksReceived . length ) {
const attacker = this . pokemon . turnData . attacksReceived . length ? this . pokemon . scene . getPokemonById ( this . pokemon . turnData . attacksReceived [ 0 ] . sourceId ) : null ;
2024-05-23 16:03:10 +01:00
if ( attacker ? . isActive ( true ) ) {
2023-11-27 16:42:03 +00:00
this . targets [ 0 ] = attacker . getBattlerIndex ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-27 16:42:03 +00:00
}
if ( this . targets [ 0 ] === BattlerIndex . ATTACKER ) {
2024-05-08 21:51:56 +01:00
this . fail ( ) ; // Marks the move as failed for later in doMove
2023-11-27 16:42:03 +00:00
this . showMoveText ( ) ;
this . showFailedText ( ) ;
}
}
2023-05-18 16:11:06 +01:00
2024-03-12 00:55:41 +00:00
const targets = this . scene . getField ( true ) . filter ( p = > {
2024-05-23 16:03:10 +01:00
if ( this . targets . indexOf ( p . getBattlerIndex ( ) ) > - 1 ) {
2023-05-18 16:11:06 +01:00
return true ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
return false ;
} ) ;
2023-04-16 23:40:32 +01:00
2023-04-12 00:08:03 +01:00
const doMove = ( ) = > {
2024-04-14 16:54:46 +01:00
this . pokemon . turnData . acted = true ; // Record that the move was attempted, even if it fails
2024-05-24 00:45:04 +01:00
2024-04-06 01:42:30 +01:00
this . pokemon . lapseTags ( BattlerTagLapseType . PRE_MOVE ) ;
2024-05-24 00:45:04 +01:00
2024-05-08 21:51:56 +01:00
let ppUsed = 1 ;
// Filter all opponents to include only those this move is targeting
const targetedOpponents = this . pokemon . getOpponents ( ) . filter ( o = > this . targets . includes ( o . getBattlerIndex ( ) ) ) ;
2024-05-23 16:03:10 +01:00
for ( const opponent of targetedOpponents ) {
if ( this . move . ppUsed + ppUsed >= this . move . getMovePp ( ) ) { // If we're already at max PP usage, stop checking
2024-05-08 21:51:56 +01:00
break ;
2024-05-23 16:03:10 +01:00
}
if ( opponent . hasAbilityWithAttr ( IncreasePpAbAttr ) ) { // Accounting for abilities like Pressure
2024-05-08 21:51:56 +01:00
ppUsed ++ ;
2024-05-23 16:03:10 +01:00
}
2024-05-08 21:51:56 +01:00
}
2024-05-24 00:45:04 +01:00
2024-04-06 01:42:30 +01:00
if ( ! this . followUp && this . canMove ( ) && ! this . cancelled ) {
2023-11-15 23:45:10 +00:00
this . pokemon . lapseTags ( BattlerTagLapseType . MOVE ) ;
2024-04-06 01:42:30 +01:00
}
2024-05-11 14:22:26 +01:00
const moveQueue = this . pokemon . getMoveQueue ( ) ;
2024-05-08 21:51:56 +01:00
if ( this . cancelled || this . failed ) {
2024-05-23 16:03:10 +01:00
if ( this . failed ) {
2024-05-24 23:01:47 +01:00
this . move . usePp ( ppUsed ) ; // Only use PP if the move failed
this . scene . eventTarget . dispatchEvent ( new MoveUsedEvent ( this . pokemon ? . id , this . move . getMove ( ) , ppUsed ) ) ;
}
2024-05-08 21:51:56 +01:00
2024-05-11 14:22:26 +01:00
// Record a failed move so Abilities like Truant don't trigger next turn and soft-lock
2024-04-06 01:42:30 +01:00
this . pokemon . pushMoveHistory ( { move : Moves.NONE , result : MoveResult.FAIL } ) ;
2024-05-11 14:22:26 +01:00
this . pokemon . lapseTags ( BattlerTagLapseType . MOVE_EFFECT ) ; // Remove any tags from moves like Fly/Dive/etc.
moveQueue . shift ( ) ; // Remove the second turn of charge moves
2024-04-06 01:42:30 +01:00
return this . end ( ) ;
2023-11-15 23:45:10 +00:00
}
2024-03-30 22:46:33 +00:00
this . scene . triggerPokemonFormChange ( this . pokemon , SpeciesFormChangePreMoveTrigger ) ;
2024-05-23 16:03:10 +01:00
if ( this . move . moveId ) {
2023-11-27 18:47:32 +00:00
this . showMoveText ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-15 10:10:20 +01:00
2024-05-16 08:05:36 +01:00
// This should only happen when there are no valid targets left on the field
2024-05-24 00:45:04 +01:00
if ( ( moveQueue . length && moveQueue [ 0 ] . move === Moves . NONE ) || ! targets . length ) {
2024-05-16 08:05:36 +01:00
this . showFailedText ( ) ;
2023-04-21 02:32:48 +01:00
this . cancel ( ) ;
2024-05-24 00:45:04 +01:00
2024-05-16 08:05:36 +01:00
// Record a failed move so Abilities like Truant don't trigger next turn and soft-lock
2023-05-18 16:11:06 +01:00
this . pokemon . pushMoveHistory ( { move : Moves.NONE , result : MoveResult.FAIL } ) ;
2024-05-16 08:05:36 +01:00
this . pokemon . lapseTags ( BattlerTagLapseType . MOVE_EFFECT ) ; // Remove any tags from moves like Fly/Dive/etc.
moveQueue . shift ( ) ;
2023-12-07 22:43:56 +00:00
return this . end ( ) ;
2023-04-12 00:08:03 +01:00
}
2023-04-16 23:40:32 +01:00
2024-05-24 23:01:47 +01:00
if ( ! moveQueue . length || ! moveQueue . shift ( ) . ignorePP ) { // using .shift here clears out two turn moves once they've been used
2024-05-08 21:51:56 +01:00
this . move . usePp ( ppUsed ) ;
2024-05-24 23:01:47 +01:00
this . scene . eventTarget . dispatchEvent ( new MoveUsedEvent ( this . pokemon ? . id , this . move . getMove ( ) , ppUsed ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-16 23:40:32 +01:00
2024-06-01 01:50:30 +01:00
if ( ! allMoves [ this . move . moveId ] . hasAttr ( CopyMoveAttr ) ) {
2023-10-10 01:20:02 +01:00
this . scene . currentBattle . lastMove = this . move . moveId ;
2024-05-23 16:03:10 +01:00
}
2023-10-10 01:20:02 +01:00
2023-05-18 16:11:06 +01:00
// Assume conditions affecting targets only apply to moves with a single target
let success = this . move . getMove ( ) . applyConditions ( this . pokemon , targets [ 0 ] , this . move . getMove ( ) ) ;
2024-05-23 16:03:10 +01:00
const cancelled = new Utils . BooleanHolder ( false ) ;
2024-05-05 15:52:27 +01:00
let failedText = this . move . getMove ( ) . getFailedText ( this . pokemon , targets [ 0 ] , this . move . getMove ( ) , cancelled ) ;
2024-05-23 16:03:10 +01:00
if ( success && this . scene . arena . isMoveWeatherCancelled ( this . move . getMove ( ) ) ) {
2023-04-24 19:30:21 +01:00
success = false ;
2024-05-23 16:03:10 +01:00
} else if ( success && this . scene . arena . isMoveTerrainCancelled ( this . pokemon , this . targets , this . move . getMove ( ) ) ) {
2024-03-18 22:03:13 +00:00
success = false ;
2024-05-23 16:03:10 +01:00
if ( failedText === null ) {
2024-05-05 15:52:27 +01:00
failedText = getTerrainBlockMessage ( targets [ 0 ] , this . scene . arena . terrain . terrainType ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-18 22:03:13 +00:00
}
2024-06-13 15:54:23 +01:00
/ * *
* Trigger pokemon type change before playing the move animation
* Will still change the user 's type when using Roar, Whirlwind, Trick-or-Treat, and Forest' s Curse ,
* regardless of whether the move successfully executes or not .
* /
if ( success || [ Moves . ROAR , Moves . WHIRLWIND , Moves . TRICK_OR_TREAT , Moves . FORESTS_CURSE ] . includes ( this . move . moveId ) ) {
applyPreAttackAbAttrs ( PokemonTypeChangeAbAttr , this . pokemon , null , this . move . getMove ( ) ) ;
}
2024-05-23 16:03:10 +01:00
if ( success ) {
2023-04-24 19:30:21 +01:00
this . scene . unshiftPhase ( this . getEffectPhase ( ) ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-05-18 16:11:06 +01:00
this . pokemon . pushMoveHistory ( { move : this.move.moveId , targets : this.targets , result : MoveResult.FAIL , virtual : this.move.virtual } ) ;
2024-05-23 16:03:10 +01:00
if ( ! cancelled . value ) {
2024-05-05 15:52:27 +01:00
this . showFailedText ( failedText ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-24 19:30:21 +01:00
}
2024-05-25 05:00:58 +01:00
// Checks if Dancer ability is triggered
if ( this . move . getMove ( ) . hasFlag ( MoveFlags . DANCE_MOVE ) && ! this . followUp ) {
// Pokemon with Dancer can be on either side of the battle so we check in both cases
this . scene . getPlayerField ( ) . forEach ( pokemon = > {
applyPostMoveUsedAbAttrs ( PostMoveUsedAbAttr , pokemon , this . move , this . pokemon , this . targets ) ;
} ) ;
2024-06-05 18:10:24 +01:00
this . scene . getEnemyField ( ) . forEach ( pokemon = > {
2024-05-25 05:00:58 +01:00
applyPostMoveUsedAbAttrs ( PostMoveUsedAbAttr , pokemon , this . move , this . pokemon , this . targets ) ;
} ) ;
}
2023-04-12 00:08:03 +01:00
this . end ( ) ;
} ;
2023-04-16 23:40:32 +01:00
if ( ! this . followUp && this . pokemon . status && ! this . pokemon . status . isPostTurn ( ) ) {
2023-04-12 00:08:03 +01:00
this . pokemon . status . incrementTurn ( ) ;
let activated = false ;
let healed = false ;
2024-05-24 00:45:04 +01:00
2023-04-12 00:08:03 +01:00
switch ( this . pokemon . status . effect ) {
2024-05-23 16:03:10 +01:00
case StatusEffect . PARALYSIS :
if ( ! this . pokemon . randSeedInt ( 4 ) ) {
activated = true ;
this . cancelled = true ;
}
break ;
case StatusEffect . SLEEP :
applyMoveAttrs ( BypassSleepAttr , this . pokemon , null , this . move . getMove ( ) ) ;
healed = this . pokemon . status . turnCount === this . pokemon . status . cureTurn ;
activated = ! healed && ! this . pokemon . getTag ( BattlerTagType . BYPASS_SLEEP ) ;
this . cancelled = activated ;
break ;
case StatusEffect . FREEZE :
healed = ! ! this . move . getMove ( ) . findAttr ( attr = > attr instanceof HealStatusEffectAttr && attr . selfTarget && attr . isOfEffect ( StatusEffect . FREEZE ) ) || ! this . pokemon . randSeedInt ( 5 ) ;
activated = ! healed ;
this . cancelled = activated ;
break ;
2023-04-12 00:08:03 +01:00
}
2024-05-24 00:45:04 +01:00
2023-04-12 00:08:03 +01:00
if ( activated ) {
2023-04-22 00:30:04 +01:00
this . scene . queueMessage ( getPokemonMessage ( this . pokemon , getStatusEffectActivationText ( this . pokemon . status . effect ) ) ) ;
2023-05-18 16:11:06 +01:00
this . scene . unshiftPhase ( new CommonAnimPhase ( this . scene , this . pokemon . getBattlerIndex ( ) , undefined , CommonAnim . POISON + ( this . pokemon . status . effect - 1 ) ) ) ;
2023-04-15 06:32:16 +01:00
doMove ( ) ;
2023-04-12 00:08:03 +01:00
} else {
if ( healed ) {
2023-04-22 00:30:04 +01:00
this . scene . queueMessage ( getPokemonMessage ( this . pokemon , getStatusEffectHealText ( this . pokemon . status . effect ) ) ) ;
2023-04-12 00:08:03 +01:00
this . pokemon . resetStatus ( ) ;
2023-04-20 20:46:05 +01:00
this . pokemon . updateInfo ( ) ;
2023-04-12 00:08:03 +01:00
}
doMove ( ) ;
}
2024-05-23 16:03:10 +01:00
} else {
2023-04-12 00:08:03 +01:00
doMove ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
}
2023-04-16 23:40:32 +01:00
2023-04-10 19:12:01 +01:00
getEffectPhase ( ) : MoveEffectPhase {
2023-05-18 16:11:06 +01:00
return new MoveEffectPhase ( this . scene , this . pokemon . getBattlerIndex ( ) , this . targets , this . move ) ;
2023-04-10 19:12:01 +01:00
}
2023-11-27 16:42:03 +00:00
showMoveText ( ) : void {
2024-06-01 01:50:30 +01:00
if ( this . move . getMove ( ) . hasAttr ( ChargeAttr ) ) {
2023-12-12 02:46:49 +00:00
const lastMove = this . pokemon . getLastXMoves ( ) as TurnMove [ ] ;
2024-05-24 01:19:20 +01:00
if ( ! lastMove . length || lastMove [ 0 ] . move !== this . move . getMove ( ) . id || lastMove [ 0 ] . result !== MoveResult . OTHER ) {
2024-06-06 14:36:12 +01:00
this . scene . queueMessage ( i18next . t ( "battle:useMove" , {
pokemonNameWithAffix : getPokemonNameWithAffix ( this . pokemon ) ,
moveName : this.move.getName ( )
} ) , 500 ) ;
2023-12-12 02:46:49 +00:00
return ;
2024-05-02 23:18:05 +01:00
}
2023-12-12 02:46:49 +00:00
}
2024-01-08 04:17:24 +00:00
2024-05-23 16:03:10 +01:00
if ( this . pokemon . getTag ( BattlerTagType . RECHARGING || BattlerTagType . INTERRUPTED ) ) {
2024-01-08 04:17:24 +00:00
return ;
2024-05-23 16:03:10 +01:00
}
2024-05-24 00:45:04 +01:00
2024-06-06 14:36:12 +01:00
this . scene . queueMessage ( i18next . t ( "battle:useMove" , {
pokemonNameWithAffix : getPokemonNameWithAffix ( this . pokemon ) ,
moveName : this.move.getName ( )
} ) , 500 ) ;
2024-03-19 02:05:21 +00:00
applyMoveAttrs ( PreMoveMessageAttr , this . pokemon , this . pokemon . getOpponents ( ) . find ( ( ) = > true ) , this . move . getMove ( ) ) ;
2023-11-27 16:42:03 +00:00
}
2024-03-18 22:03:13 +00:00
showFailedText ( failedText : string = null ) : void {
2024-05-23 16:03:10 +01:00
this . scene . queueMessage ( failedText || i18next . t ( "battle:attackFailed" ) ) ;
2023-11-27 16:42:03 +00:00
}
2023-05-18 16:11:06 +01:00
end() {
2024-05-23 16:03:10 +01:00
if ( ! this . followUp && this . canMove ( ) ) {
2023-05-18 16:11:06 +01:00
this . scene . unshiftPhase ( new MoveEndPhase ( this . scene , this . pokemon . getBattlerIndex ( ) ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
2023-05-18 16:11:06 +01:00
super . end ( ) ;
2023-04-10 19:12:01 +01:00
}
}
2023-12-04 05:09:38 +00:00
export class MoveEffectPhase extends PokemonPhase {
2024-03-13 21:09:23 +00:00
public move : PokemonMove ;
2023-05-18 16:11:06 +01:00
protected targets : BattlerIndex [ ] ;
2024-05-24 00:45:04 +01:00
2023-05-18 16:11:06 +01:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , targets : BattlerIndex [ ] , move : PokemonMove ) {
super ( scene , battlerIndex ) ;
2023-04-10 19:12:01 +01:00
this . move = move ;
2024-05-25 20:06:50 +01:00
// 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
// of the left Pokemon and gets hit unless this is checked.
if ( targets . includes ( battlerIndex ) && this . move . getMove ( ) . moveTarget === MoveTarget . ALL_NEAR_OTHERS ) {
const i = targets . indexOf ( battlerIndex ) ;
targets . splice ( i , i + 1 ) ;
}
2023-05-18 16:11:06 +01:00
this . targets = targets ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
const user = this . getUserPokemon ( ) ;
2023-05-18 16:11:06 +01:00
const targets = this . getTargets ( ) ;
2023-04-10 19:12:01 +01:00
2024-05-23 16:03:10 +01:00
if ( ! user ? . isOnField ( ) ) {
2024-04-08 01:44:57 +01:00
return super . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-12-07 22:43:56 +00:00
2023-04-13 17:16:36 +01:00
const overridden = new Utils . BooleanHolder ( false ) ;
2024-06-07 21:57:57 +01:00
const move = this . move . getMove ( ) ;
2023-04-10 21:17:25 +01:00
2023-05-18 16:11:06 +01:00
// Assume single target for override
2024-06-07 21:57:57 +01:00
applyMoveAttrs ( OverrideMoveEffectAttr , user , this . getTarget ( ) , move , overridden , this . move . virtual ) . then ( ( ) = > {
2023-04-10 21:17:25 +01:00
2024-05-23 16:03:10 +01:00
if ( overridden . value ) {
2023-12-07 22:43:56 +00:00
return this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-24 00:45:04 +01:00
2023-04-22 00:30:04 +01:00
user . lapseTags ( BattlerTagLapseType . MOVE_EFFECT ) ;
2023-04-13 17:16:36 +01:00
if ( user . turnData . hitsLeft === undefined ) {
const hitCount = new Utils . IntegerHolder ( 1 ) ;
2023-05-18 16:11:06 +01:00
// Assume single target for multi hit
2024-06-07 21:57:57 +01:00
applyMoveAttrs ( MultiHitAttr , user , this . getTarget ( ) , move , hitCount ) ;
if ( move instanceof AttackMove && ! move . hasAttr ( FixedDamageAttr ) ) {
2024-04-03 16:42:09 +01:00
this . scene . applyModifiers ( PokemonMultiHitModifier , user . isPlayer ( ) , user , hitCount , new Utils . IntegerHolder ( 0 ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-05-03 05:00:21 +01:00
user . turnData . hitsLeft = user . turnData . hitCount = hitCount . value ;
2023-04-13 17:16:36 +01:00
}
2023-05-18 16:11:06 +01:00
const moveHistoryEntry = { move : this.move.moveId , targets : this.targets , result : MoveResult.PENDING , virtual : this.move.virtual } ;
user . pushMoveHistory ( moveHistoryEntry ) ;
const targetHitChecks = Object . fromEntries ( targets . map ( p = > [ p . getBattlerIndex ( ) , this . hitCheck ( p ) ] ) ) ;
2023-12-05 22:12:39 +00:00
const activeTargets = targets . map ( t = > t . isActive ( true ) ) ;
2024-06-07 21:57:57 +01:00
if ( ! activeTargets . length || ( ! move . hasAttr ( VariableTargetAttr ) && ! move . isMultiTarget ( ) && ! targetHitChecks [ this . targets [ 0 ] ] ) ) {
2023-10-30 16:33:20 +00:00
user . turnData . hitCount = 1 ;
user . turnData . hitsLeft = 1 ;
2023-12-05 22:12:39 +00:00
if ( activeTargets . length ) {
2024-05-23 16:03:10 +01:00
this . scene . queueMessage ( getPokemonMessage ( user , "'s\nattack missed!" ) ) ;
2023-12-05 22:12:39 +00:00
moveHistoryEntry . result = MoveResult . MISS ;
2024-06-07 21:57:57 +01:00
applyMoveAttrs ( MissEffectAttr , user , null , move ) ;
2023-12-05 22:12:39 +00:00
} else {
2024-05-23 16:03:10 +01:00
this . scene . queueMessage ( i18next . t ( "battle:attackFailed" ) ) ;
2023-12-05 22:12:39 +00:00
moveHistoryEntry . result = MoveResult . FAIL ;
}
2023-12-07 22:43:56 +00:00
return this . end ( ) ;
2023-04-10 19:12:01 +01:00
}
2023-04-18 17:30:47 +01:00
2023-10-31 18:09:33 +00:00
const applyAttrs : Promise < void > [ ] = [ ] ;
2023-05-18 16:11:06 +01:00
// Move animation only needs one target
2024-06-07 21:57:57 +01:00
new MoveAnim ( move . id as Moves , user , this . getTarget ( ) ? . getBattlerIndex ( ) ) . play ( this . scene , ( ) = > {
2024-05-23 16:03:10 +01:00
for ( const target of targets ) {
2023-05-18 16:11:06 +01:00
if ( ! targetHitChecks [ target . getBattlerIndex ( ) ] ) {
2023-10-30 16:33:20 +00:00
user . turnData . hitCount = 1 ;
user . turnData . hitsLeft = 1 ;
2024-05-23 16:03:10 +01:00
this . scene . queueMessage ( getPokemonMessage ( user , "'s\nattack missed!" ) ) ;
if ( moveHistoryEntry . result === MoveResult . PENDING ) {
2023-05-18 16:11:06 +01:00
moveHistoryEntry . result = MoveResult . MISS ;
2024-05-23 16:03:10 +01:00
}
2024-06-07 21:57:57 +01:00
applyMoveAttrs ( MissEffectAttr , user , null , move ) ;
2023-05-18 16:11:06 +01:00
continue ;
2023-04-25 06:32:48 +01:00
}
2023-05-18 16:11:06 +01:00
2024-06-07 21:57:57 +01:00
const isProtected = ! move . hasFlag ( MoveFlags . IGNORE_PROTECT ) && target . findTags ( t = > t instanceof ProtectedTag ) . find ( t = > target . lapseTag ( t . tagType ) ) ;
2023-05-18 16:11:06 +01:00
2024-03-18 20:36:00 +00:00
const firstHit = moveHistoryEntry . result !== MoveResult . SUCCESS ;
2023-05-18 16:11:06 +01:00
moveHistoryEntry . result = MoveResult . SUCCESS ;
2024-05-24 00:45:04 +01:00
2024-06-07 21:57:57 +01:00
const hitResult = ! isProtected ? target . apply ( user , move ) : HitResult . NO_EFFECT ;
2023-07-10 15:54:22 +01:00
2024-03-30 22:46:33 +00:00
this . scene . triggerPokemonFormChange ( user , SpeciesFormChangePostMoveTrigger ) ;
2024-01-10 04:34:43 +00:00
2023-10-31 18:09:33 +00:00
applyAttrs . push ( new Promise ( resolve = > {
2024-06-07 21:57:57 +01:00
applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && attr . trigger === MoveEffectTrigger . PRE_APPLY && ( ! attr . firstHitOnly || firstHit ) ,
user , target , move ) . then ( ( ) = > {
2023-10-31 18:09:33 +00:00
if ( hitResult !== HitResult . FAIL ) {
2024-06-07 21:57:57 +01:00
const chargeEffect = ! ! move . getAttrs ( ChargeAttr ) . find ( ca = > ca . usedChargeEffect ( user , this . getTarget ( ) , move ) ) ;
2023-10-31 18:09:33 +00:00
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present
2024-06-07 21:57:57 +01:00
Utils . executeIf ( ! chargeEffect , ( ) = > applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && attr . trigger === MoveEffectTrigger . POST_APPLY
&& attr . selfTarget && ( ! attr . firstHitOnly || firstHit ) , user , target , move ) ) . then ( ( ) = > {
2023-10-31 18:09:33 +00:00
if ( hitResult !== HitResult . NO_EFFECT ) {
2024-06-07 21:57:57 +01:00
applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && attr . trigger === MoveEffectTrigger . POST_APPLY
&& ! attr . selfTarget && ( ! attr . firstHitOnly || firstHit ) , user , target , move ) . then ( ( ) = > {
2023-10-31 18:09:33 +00:00
if ( hitResult < HitResult . NO_EFFECT ) {
const flinched = new Utils . BooleanHolder ( false ) ;
user . scene . applyModifiers ( FlinchChanceModifier , user . isPlayer ( ) , user , flinched ) ;
2024-05-23 16:03:10 +01:00
if ( flinched . value ) {
2023-10-31 18:09:33 +00:00
target . addTag ( BattlerTagType . FLINCHED , undefined , this . move . moveId , user . id ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-31 18:09:33 +00:00
}
2024-06-07 21:57:57 +01:00
Utils . executeIf ( ! isProtected && ! chargeEffect , ( ) = > applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && attr . trigger === MoveEffectTrigger . HIT && ( ! attr . firstHitOnly || firstHit ) ,
user , target , move ) . then ( ( ) = > {
return Utils . executeIf ( ! target . isFainted ( ) || target . canApplyAbility ( ) , ( ) = > applyPostDefendAbAttrs ( PostDefendAbAttr , target , user , move , hitResult ) . then ( ( ) = > {
if ( ! user . isPlayer ( ) && move instanceof AttackMove ) {
2024-05-23 16:03:10 +01:00
user . scene . applyShuffledModifiers ( this . scene , EnemyAttackStatusEffectChanceModifier , false , target ) ;
}
} ) ) . then ( ( ) = > {
2024-06-07 21:57:57 +01:00
applyPostAttackAbAttrs ( PostAttackAbAttr , user , target , move , hitResult ) . then ( ( ) = > {
if ( move instanceof AttackMove ) {
2024-05-23 16:03:10 +01:00
this . scene . applyModifiers ( ContactHeldItemTransferChanceModifier , this . player , user , target . getFieldIndex ( ) ) ;
}
resolve ( ) ;
2023-12-23 02:42:47 +00:00
} ) ;
2024-05-23 16:03:10 +01:00
} ) ;
} )
2023-10-31 18:09:33 +00:00
) . then ( ( ) = > resolve ( ) ) ;
} ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-06-07 21:57:57 +01:00
applyMoveAttrs ( NoEffectAttr , user , null , move ) . then ( ( ) = > resolve ( ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-31 18:09:33 +00:00
} ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-10-31 21:38:44 +00:00
resolve ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-31 18:09:33 +00:00
} ) ;
} ) ) ;
2023-04-23 15:24:22 +01:00
}
2024-05-05 22:37:43 +01:00
// Trigger effect which should only apply one time after all targeted effects have already applied
2024-06-07 21:57:57 +01:00
const postTarget = applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && attr . trigger === MoveEffectTrigger . POST_TARGET ,
user , null , move ) ;
2024-05-24 00:45:04 +01:00
2024-05-23 16:03:10 +01:00
if ( applyAttrs . length ) { // If there is a pending asynchronous move effect, do this after
2024-05-15 10:10:20 +01:00
applyAttrs [ applyAttrs . length - 1 ] ? . then ( ( ) = > postTarget ) ;
2024-05-23 16:03:10 +01:00
} else { // Otherwise, push a new asynchronous move effect
2024-05-15 10:10:20 +01:00
applyAttrs . push ( postTarget ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-15 10:10:20 +01:00
2023-10-31 18:09:33 +00:00
Promise . allSettled ( applyAttrs ) . then ( ( ) = > this . end ( ) ) ;
2023-04-13 17:16:36 +01:00
} ) ;
2023-04-10 19:12:01 +01:00
} ) ;
}
2023-04-10 21:17:25 +01:00
end() {
2024-06-07 21:57:57 +01:00
const move = this . move . getMove ( ) ;
move . type = move . defaultType ;
2023-04-10 21:17:25 +01:00
const user = this . getUserPokemon ( ) ;
2023-12-08 14:51:45 +00:00
if ( user ) {
2024-05-23 16:03:10 +01:00
if ( -- user . turnData . hitsLeft >= 1 && this . getTarget ( ) ? . isActive ( ) ) {
2023-12-08 14:51:45 +00:00
this . scene . unshiftPhase ( this . getNewHitPhase ( ) ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-12-22 22:08:37 +00:00
const hitsTotal = user . turnData . hitCount - Math . max ( user . turnData . hitsLeft , 0 ) ;
2024-05-23 16:03:10 +01:00
if ( hitsTotal > 1 ) {
this . scene . queueMessage ( i18next . t ( "battle:attackHitsCount" , { count : hitsTotal } ) ) ;
}
2023-12-08 14:51:45 +00:00
this . scene . applyModifiers ( HitHealModifier , this . player , user ) ;
}
2023-04-13 17:16:36 +01:00
}
2024-05-24 00:45:04 +01:00
2023-04-10 21:17:25 +01:00
super . end ( ) ;
}
2023-05-18 16:11:06 +01:00
hitCheck ( target : Pokemon ) : boolean {
2024-05-18 02:20:02 +01:00
// Moves targeting the user and entry hazards can't miss
2024-05-23 16:03:10 +01:00
if ( [ MoveTarget . USER , MoveTarget . ENEMY_SIDE ] . includes ( this . move . getMove ( ) . moveTarget ) ) {
2023-04-18 17:30:47 +01:00
return true ;
2024-05-23 16:03:10 +01:00
}
2023-04-13 17:16:36 +01:00
2024-04-08 01:44:57 +01:00
const user = this . getUserPokemon ( ) ;
2024-05-29 00:07:35 +01:00
// Hit check only calculated on first hit for multi-hit moves unless flag is set to check all hits.
// However, if an ability with the MaxMultiHitAbAttr, namely Skill Link, is present, act as a normal
// multi-hit move and proceed with all hits
2024-05-23 16:03:10 +01:00
if ( user . turnData . hitsLeft < user . turnData . hitCount ) {
2024-05-29 00:07:35 +01:00
if ( ! this . move . getMove ( ) . hasFlag ( MoveFlags . CHECK_ALL_HITS ) || user . hasAbilityWithAttr ( MaxMultiHitAbAttr ) ) {
return true ;
}
2024-05-23 16:03:10 +01:00
}
2023-10-30 16:33:20 +00:00
2024-05-23 16:03:10 +01:00
if ( user . hasAbilityWithAttr ( AlwaysHitAbAttr ) || target . hasAbilityWithAttr ( AlwaysHitAbAttr ) ) {
2024-04-21 06:26:30 +01:00
return true ;
2024-05-23 16:03:10 +01:00
}
2024-04-21 06:26:30 +01:00
2024-05-16 08:05:36 +01:00
// If the user should ignore accuracy on a target, check who the user targeted last turn and see if they match
2024-05-23 16:03:10 +01:00
if ( user . getTag ( BattlerTagType . IGNORE_ACCURACY ) && ( user . getLastXMoves ( ) . slice ( 1 ) . find ( ( ) = > true ) ? . targets || [ ] ) . indexOf ( target . getBattlerIndex ( ) ) !== - 1 ) {
2024-05-16 08:05:36 +01:00
return true ;
2024-05-23 16:03:10 +01:00
}
2024-05-24 00:45:04 +01:00
2023-05-18 16:11:06 +01:00
const hiddenTag = target . getTag ( HiddenTag ) ;
2024-06-01 01:50:30 +01:00
if ( hiddenTag && ! this . move . getMove ( ) . getAttrs ( HitsTagAttr ) . some ( hta = > hta . tagType === hiddenTag . tagType ) ) {
2023-05-18 16:11:06 +01:00
return false ;
2024-05-23 16:03:10 +01:00
}
2023-04-19 19:07:38 +01:00
const moveAccuracy = new Utils . NumberHolder ( this . move . getMove ( ) . accuracy ) ;
2024-04-08 01:44:57 +01:00
applyMoveAttrs ( VariableAccuracyAttr , user , target , this . move . getMove ( ) , moveAccuracy ) ;
2024-06-15 17:06:32 +01:00
applyPreDefendAbAttrs ( WonderSkinAbAttr , target , user , this . move . getMove ( ) , { value : false } , moveAccuracy ) ;
2023-07-05 17:10:23 +01:00
2024-05-23 16:03:10 +01:00
if ( moveAccuracy . value === - 1 ) {
2023-04-19 19:07:38 +01:00
return true ;
2024-05-23 16:03:10 +01:00
}
2023-05-08 23:48:35 +01:00
2024-06-01 01:50:30 +01:00
const isOhko = this . move . getMove ( ) . hasAttr ( OneHitKOAccuracyAttr ) ;
2024-04-22 06:05:48 +01:00
2024-05-23 16:03:10 +01:00
if ( ! isOhko ) {
2024-04-22 06:05:48 +01:00
user . scene . applyModifiers ( PokemonMoveAccuracyBoosterModifier , user . isPlayer ( ) , user , moveAccuracy ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-20 12:52:47 +01:00
2024-05-23 16:03:10 +01:00
if ( this . scene . arena . weather ? . weatherType === WeatherType . FOG ) {
2024-04-07 22:12:53 +01:00
moveAccuracy . value = Math . floor ( moveAccuracy . value * 0.9 ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-07 03:10:38 +01:00
2024-05-23 16:03:10 +01:00
if ( ! isOhko && this . scene . arena . getTag ( ArenaTagType . GRAVITY ) ) {
2023-05-08 23:48:35 +01:00
moveAccuracy . value = Math . floor ( moveAccuracy . value * 1.67 ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-24 00:45:04 +01:00
2024-04-08 01:44:57 +01:00
const userAccuracyLevel = new Utils . IntegerHolder ( user . summonData . battleStats [ BattleStat . ACC ] ) ;
2023-07-28 15:23:37 +01:00
const targetEvasionLevel = new Utils . IntegerHolder ( target . summonData . battleStats [ BattleStat . EVA ] ) ;
2023-11-06 04:27:40 +00:00
applyAbAttrs ( IgnoreOpponentStatChangesAbAttr , target , null , userAccuracyLevel ) ;
2024-04-08 01:44:57 +01:00
applyAbAttrs ( IgnoreOpponentStatChangesAbAttr , user , null , targetEvasionLevel ) ;
2024-05-22 04:11:42 +01:00
applyAbAttrs ( IgnoreOpponentEvasionAbAttr , user , null , targetEvasionLevel ) ;
2024-04-08 01:44:57 +01:00
applyMoveAttrs ( IgnoreOpponentStatChangesAttr , user , target , this . move . getMove ( ) , targetEvasionLevel ) ;
2023-07-28 15:23:37 +01:00
this . scene . applyModifiers ( TempBattleStatBoosterModifier , this . player , TempBattleStat . ACC , userAccuracyLevel ) ;
2024-03-26 19:47:19 +00:00
2024-04-08 01:44:57 +01:00
const rand = user . randSeedInt ( 100 , 1 ) ;
2024-03-26 19:47:19 +00:00
const accuracyMultiplier = new Utils . NumberHolder ( 1 ) ;
2023-07-28 15:23:37 +01:00
if ( userAccuracyLevel . value !== targetEvasionLevel . value ) {
2024-03-26 19:47:19 +00:00
accuracyMultiplier . value = userAccuracyLevel . value > targetEvasionLevel . value
2023-07-28 15:23:37 +01:00
? ( 3 + Math . min ( userAccuracyLevel . value - targetEvasionLevel . value , 6 ) ) / 3
: 3 / ( 3 + Math . min ( targetEvasionLevel . value - userAccuracyLevel . value , 6 ) ) ;
2023-04-10 21:17:25 +01:00
}
2024-03-26 19:47:19 +00:00
2024-04-30 16:47:10 +01:00
applyBattleStatMultiplierAbAttrs ( BattleStatMultiplierAbAttr , user , BattleStat . ACC , accuracyMultiplier , this . move . getMove ( ) ) ;
2024-03-26 19:47:19 +00:00
const evasionMultiplier = new Utils . NumberHolder ( 1 ) ;
applyBattleStatMultiplierAbAttrs ( BattleStatMultiplierAbAttr , this . getTarget ( ) , BattleStat . EVA , evasionMultiplier ) ;
accuracyMultiplier . value /= evasionMultiplier . value ;
return rand <= moveAccuracy . value * accuracyMultiplier . value ;
2023-04-10 21:17:25 +01:00
}
2023-04-10 19:12:01 +01:00
getUserPokemon ( ) : Pokemon {
2024-05-23 16:03:10 +01:00
if ( this . battlerIndex > BattlerIndex . ENEMY_2 ) {
2023-12-04 05:09:38 +00:00
return this . scene . getPokemonById ( this . battlerIndex ) ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
return ( this . player ? this . scene . getPlayerField ( ) : this . scene . getEnemyField ( ) ) [ this . fieldIndex ] ;
2023-04-10 19:12:01 +01:00
}
2023-05-18 16:11:06 +01:00
getTargets ( ) : Pokemon [ ] {
2024-03-12 00:55:41 +00:00
return this . scene . getField ( true ) . filter ( p = > this . targets . indexOf ( p . getBattlerIndex ( ) ) > - 1 ) ;
2023-04-10 19:12:01 +01:00
}
2023-04-10 21:17:25 +01:00
2023-05-18 16:11:06 +01:00
getTarget ( ) : Pokemon {
return this . getTargets ( ) . find ( ( ) = > true ) ;
2023-04-10 19:12:01 +01:00
}
2023-04-10 21:17:25 +01:00
getNewHitPhase() {
2023-05-18 16:11:06 +01:00
return new MoveEffectPhase ( this . scene , this . battlerIndex , this . targets , this . move ) ;
2023-04-10 21:17:25 +01:00
}
2023-04-10 19:12:01 +01:00
}
2023-04-16 23:40:32 +01:00
export class MoveEndPhase extends PokemonPhase {
2023-05-18 16:11:06 +01:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex ) ;
2023-04-16 23:40:32 +01:00
}
start() {
super . start ( ) ;
2024-01-16 04:29:22 +00:00
const pokemon = this . getPokemon ( ) ;
2024-05-23 16:03:10 +01:00
if ( pokemon . isActive ( true ) ) {
2024-01-16 04:29:22 +00:00
pokemon . lapseTags ( BattlerTagLapseType . AFTER_MOVE ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-16 23:40:32 +01:00
2024-03-14 04:40:57 +00:00
this . scene . arena . setIgnoreAbilities ( false ) ;
2023-04-16 23:40:32 +01:00
this . end ( ) ;
}
}
2023-04-14 04:04:51 +01:00
export class MoveAnimTestPhase extends BattlePhase {
private moveQueue : Moves [ ] ;
constructor ( scene : BattleScene , moveQueue? : Moves [ ] ) {
super ( scene ) ;
2023-04-21 02:32:48 +01:00
this . moveQueue = moveQueue || Utils . getEnumValues ( Moves ) . slice ( 1 ) ;
2023-04-14 04:04:51 +01:00
}
start() {
const moveQueue = this . moveQueue . slice ( 0 ) ;
this . playMoveAnim ( moveQueue , true ) ;
}
playMoveAnim ( moveQueue : Moves [ ] , player : boolean ) {
const moveId = player ? moveQueue [ 0 ] : moveQueue . shift ( ) ;
if ( moveId === undefined ) {
this . playMoveAnim ( this . moveQueue . slice ( 0 ) , true ) ;
return ;
2024-05-23 16:03:10 +01:00
} else if ( player ) {
2023-04-25 03:32:12 +01:00
console . log ( Moves [ moveId ] ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-14 04:04:51 +01:00
2024-04-24 04:28:55 +01:00
initMoveAnim ( this . scene , moveId ) . then ( ( ) = > {
2023-04-14 04:04:51 +01:00
loadMoveAnimAssets ( this . scene , [ moveId ] , true )
. then ( ( ) = > {
2023-11-30 21:37:16 +00:00
new MoveAnim ( moveId , player ? this . scene . getPlayerPokemon ( ) : this . scene . getEnemyPokemon ( ) , ( player !== ( allMoves [ moveId ] instanceof SelfStatusMove ) ? this . scene . getEnemyPokemon ( ) : this . scene . getPlayerPokemon ( ) ) . getBattlerIndex ( ) ) . play ( this . scene , ( ) = > {
2024-05-23 16:03:10 +01:00
if ( player ) {
2023-04-14 04:04:51 +01:00
this . playMoveAnim ( moveQueue , false ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-04-14 04:04:51 +01:00
this . playMoveAnim ( moveQueue , true ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-14 04:04:51 +01:00
} ) ;
2024-05-23 16:03:10 +01:00
} ) ;
2023-04-14 04:04:51 +01:00
} ) ;
}
}
2023-04-27 19:30:03 +01:00
export class ShowAbilityPhase extends PokemonPhase {
2024-04-11 14:24:03 +01:00
private passive : boolean ;
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , passive : boolean = false ) {
2023-05-18 16:11:06 +01:00
super ( scene , battlerIndex ) ;
2024-04-11 14:24:03 +01:00
this . passive = passive ;
2023-04-27 19:30:03 +01:00
}
start() {
2023-12-23 02:42:47 +00:00
super . start ( ) ;
2024-06-12 18:30:10 +01:00
const pokemon = this . getPokemon ( ) ;
this . scene . abilityBar . showAbility ( pokemon , this . passive ) ;
2024-06-12 19:39:37 +01:00
if ( pokemon . battleData ) {
pokemon . battleData . abilityRevealed = true ;
}
2023-04-27 19:30:03 +01:00
this . end ( ) ;
}
}
2023-04-11 04:15:06 +01:00
export class StatChangePhase extends PokemonPhase {
private stats : BattleStat [ ] ;
2023-04-27 04:33:13 +01:00
private selfTarget : boolean ;
2023-04-11 04:15:06 +01:00
private levels : integer ;
2024-01-16 04:29:22 +00:00
private showMessage : boolean ;
2024-04-11 17:11:55 +01:00
private ignoreAbilities : boolean ;
2024-05-05 00:38:53 +01:00
private canBeCopied : boolean ;
2023-04-11 04:15:06 +01:00
2024-05-05 00:38:53 +01:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , selfTarget : boolean , stats : BattleStat [ ] , levels : integer , showMessage : boolean = true , ignoreAbilities : boolean = false , canBeCopied : boolean = true ) {
2023-05-18 16:11:06 +01:00
super ( scene , battlerIndex ) ;
2023-04-11 04:15:06 +01:00
2023-04-27 19:30:03 +01:00
this . selfTarget = selfTarget ;
2024-01-03 02:31:59 +00:00
this . stats = stats ;
2023-04-11 04:15:06 +01:00
this . levels = levels ;
2024-01-16 04:29:22 +00:00
this . showMessage = showMessage ;
2024-04-11 17:11:55 +01:00
this . ignoreAbilities = ignoreAbilities ;
2024-05-05 00:38:53 +01:00
this . canBeCopied = canBeCopied ;
2023-04-11 04:15:06 +01:00
}
start() {
const pokemon = this . getPokemon ( ) ;
2023-04-27 04:33:13 +01:00
2024-04-24 05:25:01 +01:00
let random = false ;
2023-11-27 03:22:05 +00:00
2024-04-24 05:25:01 +01:00
if ( this . stats . length === 1 && this . stats [ 0 ] === BattleStat . RAND ) {
this . stats [ 0 ] = this . getRandomStat ( ) ;
random = true ;
}
this . aggregateStatChanges ( random ) ;
2024-05-23 16:03:10 +01:00
if ( ! pokemon . isActive ( true ) ) {
2024-04-24 05:25:01 +01:00
return this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-24 00:19:36 +01:00
2024-04-24 05:25:01 +01:00
const filteredStats = this . stats . map ( s = > s !== BattleStat . RAND ? s : this.getRandomStat ( ) ) . filter ( stat = > {
2023-04-27 04:33:13 +01:00
const cancelled = new Utils . BooleanHolder ( false ) ;
2024-05-23 16:03:10 +01:00
if ( ! this . selfTarget && this . levels < 0 ) {
2024-01-16 04:29:22 +00:00
this . scene . arena . applyTagsForSide ( MistTag , pokemon . isPlayer ( ) ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY , cancelled ) ;
2024-05-23 16:03:10 +01:00
}
2024-01-16 04:29:22 +00:00
2024-05-23 16:03:10 +01:00
if ( ! cancelled . value && ! this . selfTarget && this . levels < 0 ) {
2023-05-06 17:13:35 +01:00
applyPreStatChangeAbAttrs ( ProtectStatAbAttr , this . getPokemon ( ) , stat , cancelled ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-24 00:45:04 +01:00
2023-04-27 04:33:13 +01:00
return ! cancelled . value ;
} ) ;
2023-11-06 03:11:38 +00:00
const levels = new Utils . IntegerHolder ( this . levels ) ;
2024-04-11 17:11:55 +01:00
2024-05-23 16:03:10 +01:00
if ( ! this . ignoreAbilities ) {
2024-04-11 17:11:55 +01:00
applyAbAttrs ( StatChangeMultiplierAbAttr , pokemon , null , levels ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-06 03:11:38 +00:00
2023-04-11 04:15:06 +01:00
const battleStats = this . getPokemon ( ) . summonData . battleStats ;
2023-11-06 03:11:38 +00:00
const relLevels = filteredStats . map ( stat = > ( levels . value >= 1 ? Math . min ( battleStats [ stat ] + levels . value , 6 ) : Math . max ( battleStats [ stat ] + levels . value , - 6 ) ) - battleStats [ stat ] ) ;
2023-04-11 04:15:06 +01:00
const end = ( ) = > {
2024-01-16 04:29:22 +00:00
if ( this . showMessage ) {
const messages = this . getStatChangeMessages ( filteredStats , levels . value , relLevels ) ;
2024-05-23 16:03:10 +01:00
for ( const message of messages ) {
2024-01-16 04:29:22 +00:00
this . scene . queueMessage ( message ) ;
2024-05-23 16:03:10 +01:00
}
2024-01-16 04:29:22 +00:00
}
2023-04-11 04:15:06 +01:00
2024-05-23 16:03:10 +01:00
for ( const stat of filteredStats ) {
2023-11-06 03:11:38 +00:00
pokemon . summonData . battleStats [ stat ] = Math . max ( Math . min ( pokemon . summonData . battleStats [ stat ] + levels . value , 6 ) , - 6 ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-24 00:45:04 +01:00
2024-05-23 16:03:10 +01:00
if ( levels . value > 0 && this . canBeCopied ) {
for ( const opponent of pokemon . getOpponents ( ) ) {
2024-05-05 00:38:53 +01:00
applyAbAttrs ( StatChangeCopyAbAttr , opponent , null , this . stats , levels . value ) ;
2024-05-23 16:03:10 +01:00
}
}
2024-05-24 00:45:04 +01:00
2024-05-01 04:02:16 +01:00
applyPostStatChangeAbAttrs ( PostStatChangeAbAttr , pokemon , filteredStats , this . levels , this . selfTarget ) ;
2024-05-24 00:45:04 +01:00
2024-05-01 04:02:16 +01:00
pokemon . updateInfo ( ) ;
handleTutorial ( this . scene , Tutorial . Stat_Change ) . then ( ( ) = > super . end ( ) ) ;
2023-04-11 04:15:06 +01:00
} ;
2024-04-09 20:17:42 +01:00
if ( relLevels . filter ( l = > l ) . length && this . scene . moveAnimations ) {
2023-04-11 04:15:06 +01:00
pokemon . enableMask ( ) ;
const pokemonMaskSprite = pokemon . maskSprite ;
2024-01-15 01:47:08 +00:00
const tileX = ( this . player ? 106 : 236 ) * pokemon . getSpriteScale ( ) * this . scene . field . scale ;
const tileY = ( ( this . player ? 148 : 84 ) + ( levels . value >= 1 ? 160 : 0 ) ) * pokemon . getSpriteScale ( ) * this . scene . field . scale ;
const tileWidth = 156 * this . scene . field . scale * pokemon . getSpriteScale ( ) ;
const tileHeight = 316 * this . scene . field . scale * pokemon . getSpriteScale ( ) ;
2024-05-30 01:26:24 +01:00
// On increase, show the red sprite located at ATK
// On decrease, show the blue sprite located at SPD
const spriteColor = levels . value >= 1 ? BattleStat [ BattleStat . ATK ] . toLowerCase ( ) : BattleStat [ BattleStat . SPD ] . toLowerCase ( ) ;
const statSprite = this . scene . add . tileSprite ( tileX , tileY , tileWidth , tileHeight , "battle_stats" , spriteColor ) ;
2023-12-30 02:04:40 +00:00
statSprite . setPipeline ( this . scene . fieldSpritePipeline ) ;
2023-04-11 04:15:06 +01:00
statSprite . setAlpha ( 0 ) ;
statSprite . setScale ( 6 ) ;
statSprite . setOrigin ( 0.5 , 1 ) ;
2024-05-23 16:03:10 +01:00
this . scene . playSound ( ` stat_ ${ levels . value >= 1 ? "up" : "down" } ` ) ;
2023-04-11 04:15:06 +01:00
statSprite . setMask ( new Phaser . Display . Masks . BitmapMask ( this . scene , pokemonMaskSprite ) ) ;
this . scene . tweens . add ( {
targets : statSprite ,
duration : 250 ,
alpha : 0.8375 ,
onComplete : ( ) = > {
this . scene . tweens . add ( {
targets : statSprite ,
delay : 1000 ,
duration : 250 ,
alpha : 0
} ) ;
}
} ) ;
2023-04-11 14:41:11 +01:00
2023-04-11 04:15:06 +01:00
this . scene . tweens . add ( {
targets : statSprite ,
duration : 1500 ,
2024-05-23 16:03:10 +01:00
y : ` ${ levels . value >= 1 ? "-" : "+" } = ${ 160 * 6 } `
2023-04-11 04:15:06 +01:00
} ) ;
2024-05-24 00:45:04 +01:00
2023-04-11 04:15:06 +01:00
this . scene . time . delayedCall ( 1750 , ( ) = > {
pokemon . disableMask ( ) ;
end ( ) ;
} ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-04-11 04:15:06 +01:00
end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-11 04:15:06 +01:00
}
2024-04-24 05:25:01 +01:00
getRandomStat ( ) : BattleStat {
const allStats = Utils . getEnumValues ( BattleStat ) ;
return allStats [ this . getPokemon ( ) . randSeedInt ( BattleStat . SPD + 1 ) ] ;
}
aggregateStatChanges ( random : boolean = false ) : void {
const isAccEva = [ BattleStat . ACC , BattleStat . EVA ] . some ( s = > this . stats . includes ( s ) ) ;
let existingPhase : StatChangePhase ;
if ( this . stats . length === 1 ) {
while ( ( existingPhase = ( this . scene . findPhase ( p = > p instanceof StatChangePhase && p . battlerIndex === this . battlerIndex && p . stats . length === 1
&& ( p . stats [ 0 ] === this . stats [ 0 ] || ( random && p . stats [ 0 ] === BattleStat . RAND ) )
&& p . selfTarget === this . selfTarget && p . showMessage === this . showMessage && p . ignoreAbilities === this . ignoreAbilities ) as StatChangePhase ) ) ) {
if ( existingPhase . stats [ 0 ] === BattleStat . RAND ) {
existingPhase . stats [ 0 ] = this . getRandomStat ( ) ;
2024-05-23 16:03:10 +01:00
if ( existingPhase . stats [ 0 ] !== this . stats [ 0 ] ) {
2024-04-24 05:25:01 +01:00
continue ;
2024-05-23 16:03:10 +01:00
}
2024-04-24 05:25:01 +01:00
}
this . levels += existingPhase . levels ;
2024-04-24 00:19:36 +01:00
2024-05-23 16:03:10 +01:00
if ( ! this . scene . tryRemovePhase ( p = > p === existingPhase ) ) {
2024-04-24 05:25:01 +01:00
break ;
2024-05-23 16:03:10 +01:00
}
2024-04-24 05:25:01 +01:00
}
}
while ( ( existingPhase = ( this . scene . findPhase ( p = > p instanceof StatChangePhase && p . battlerIndex === this . battlerIndex && p . selfTarget === this . selfTarget
&& ( [ BattleStat . ACC , BattleStat . EVA ] . some ( s = > p . stats . includes ( s ) ) === isAccEva )
&& p . levels === this . levels && p . showMessage === this . showMessage && p . ignoreAbilities === this . ignoreAbilities ) as StatChangePhase ) ) ) {
this . stats . push ( . . . existingPhase . stats ) ;
2024-05-23 16:03:10 +01:00
if ( ! this . scene . tryRemovePhase ( p = > p === existingPhase ) ) {
2024-04-24 05:25:01 +01:00
break ;
2024-05-23 16:03:10 +01:00
}
2024-04-24 05:25:01 +01:00
}
}
2023-11-06 03:11:38 +00:00
getStatChangeMessages ( stats : BattleStat [ ] , levels : integer , relLevels : integer [ ] ) : string [ ] {
2023-04-11 04:15:06 +01:00
const messages : string [ ] = [ ] ;
2024-04-24 05:25:01 +01:00
const relLevelStatIndexes = { } ;
for ( let rl = 0 ; rl < relLevels . length ; rl ++ ) {
const relLevel = relLevels [ rl ] ;
2024-05-23 16:03:10 +01:00
if ( ! relLevelStatIndexes [ relLevel ] ) {
2024-04-24 05:25:01 +01:00
relLevelStatIndexes [ relLevel ] = [ ] ;
2024-05-23 16:03:10 +01:00
}
2024-04-24 05:25:01 +01:00
relLevelStatIndexes [ relLevel ] . push ( rl ) ;
}
Object . keys ( relLevelStatIndexes ) . forEach ( rl = > {
const relLevelStats = stats . filter ( ( _ , i ) = > relLevelStatIndexes [ rl ] . includes ( i ) ) ;
2024-05-23 16:03:10 +01:00
let statsFragment = "" ;
2024-04-24 05:25:01 +01:00
if ( relLevelStats . length > 1 ) {
statsFragment = relLevelStats . length >= 5
2024-05-23 16:03:10 +01:00
? "stats"
: ` ${ relLevelStats . slice ( 0 , - 1 ) . map ( s = > getBattleStatName ( s ) ) . join ( ", " ) } ${ relLevelStats . length > 2 ? "," : "" } and ${ getBattleStatName ( relLevelStats [ relLevelStats . length - 1 ] ) } ` ;
} else {
2024-04-24 05:25:01 +01:00
statsFragment = getBattleStatName ( relLevelStats [ 0 ] ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-24 05:25:01 +01:00
messages . push ( getPokemonMessage ( this . getPokemon ( ) , ` 's ${ statsFragment } ${ getBattleStatLevelChangeDescription ( Math . abs ( parseInt ( rl ) ) , levels >= 1 ) } ! ` ) ) ;
} ) ;
2023-04-11 04:15:06 +01:00
return messages ;
}
}
2023-04-19 19:07:38 +01:00
export class WeatherEffectPhase extends CommonAnimPhase {
2024-03-19 01:22:27 +00:00
public weather : Weather ;
2023-04-19 19:07:38 +01:00
2024-05-30 18:07:28 +01:00
constructor ( scene : BattleScene ) {
super ( scene , undefined , undefined , CommonAnim . SUNNY + ( ( scene ? . arena ? . weather ? . weatherType || WeatherType . NONE ) - 1 ) ) ;
this . weather = scene ? . arena ? . weather ;
2023-04-19 19:07:38 +01:00
}
start() {
2024-05-30 18:07:28 +01:00
// Update weather state with any changes that occurred during the turn
this . weather = this . scene ? . arena ? . weather ;
if ( ! this . weather ) {
this . end ( ) ;
return ;
}
this . setAnimation ( CommonAnim . SUNNY + ( this . weather . weatherType - 1 ) ) ;
2023-04-19 19:07:38 +01:00
if ( this . weather . isDamaging ( ) ) {
2024-05-24 00:45:04 +01:00
2023-04-27 19:30:03 +01:00
const cancelled = new Utils . BooleanHolder ( false ) ;
2023-05-18 16:11:06 +01:00
this . executeForAll ( ( pokemon : Pokemon ) = > applyPreWeatherEffectAbAttrs ( SuppressWeatherEffectAbAttr , pokemon , this . weather , cancelled ) ) ;
2023-04-27 19:30:03 +01:00
if ( ! cancelled . value ) {
const inflictDamage = ( pokemon : Pokemon ) = > {
const cancelled = new Utils . BooleanHolder ( false ) ;
applyPreWeatherEffectAbAttrs ( PreWeatherDamageAbAttr , pokemon , this . weather , cancelled ) ;
2024-04-02 20:14:07 +01:00
applyAbAttrs ( BlockNonDirectDamageAbAttr , pokemon , cancelled ) ;
2023-04-27 19:30:03 +01:00
2024-05-23 16:03:10 +01:00
if ( cancelled . value ) {
2023-04-27 19:30:03 +01:00
return ;
2024-05-23 16:03:10 +01:00
}
2023-04-27 19:30:03 +01:00
2024-03-01 14:35:36 +00:00
const damage = Math . ceil ( pokemon . getMaxHp ( ) / 16 ) ;
2023-04-27 19:30:03 +01:00
this . scene . queueMessage ( getWeatherDamageMessage ( this . weather . weatherType , pokemon ) ) ;
2024-04-11 15:33:08 +01:00
pokemon . damageAndUpdate ( damage , HitResult . EFFECTIVE , false , false , true ) ;
2023-04-27 19:30:03 +01:00
} ;
2023-05-18 16:11:06 +01:00
this . executeForAll ( ( pokemon : Pokemon ) = > {
2024-03-28 14:28:05 +00:00
const immune = ! pokemon || ! ! pokemon . getTypes ( true , true ) . filter ( t = > this . weather . isTypeDamageImmune ( t ) ) . length ;
2024-05-23 16:03:10 +01:00
if ( ! immune ) {
2023-04-27 19:30:03 +01:00
inflictDamage ( pokemon ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-27 19:30:03 +01:00
} ) ;
}
2023-04-19 19:07:38 +01:00
}
2023-04-27 19:30:03 +01:00
this . scene . ui . showText ( getWeatherLapseMessage ( this . weather . weatherType ) , null , ( ) = > {
2023-05-18 16:11:06 +01:00
this . executeForAll ( ( pokemon : Pokemon ) = > applyPostWeatherLapseAbAttrs ( PostWeatherLapseAbAttr , pokemon , this . weather ) ) ;
2023-04-27 19:30:03 +01:00
super . start ( ) ;
} ) ;
2023-04-19 19:07:38 +01:00
}
}
2023-04-10 19:12:01 +01:00
export class ObtainStatusEffectPhase extends PokemonPhase {
private statusEffect : StatusEffect ;
2023-04-16 23:40:32 +01:00
private cureTurn : integer ;
2023-04-24 19:30:21 +01:00
private sourceText : string ;
2024-05-14 19:00:37 +01:00
private sourcePokemon : Pokemon ;
2023-04-10 19:12:01 +01:00
2024-05-14 19:00:37 +01:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , statusEffect : StatusEffect , cureTurn? : integer , sourceText? : string , sourcePokemon? : Pokemon ) {
2023-05-18 16:11:06 +01:00
super ( scene , battlerIndex ) ;
2023-04-12 00:08:03 +01:00
this . statusEffect = statusEffect ;
2023-04-16 23:40:32 +01:00
this . cureTurn = cureTurn ;
2023-04-24 19:30:21 +01:00
this . sourceText = sourceText ;
2024-05-14 19:00:37 +01:00
this . sourcePokemon = sourcePokemon ; // For tracking which Pokemon caused the status effect
2023-04-10 19:12:01 +01:00
}
start() {
const pokemon = this . getPokemon ( ) ;
2023-04-12 00:08:03 +01:00
if ( ! pokemon . status ) {
2024-05-14 19:00:37 +01:00
if ( pokemon . trySetStatus ( this . statusEffect , false , this . sourcePokemon ) ) {
2024-05-23 16:03:10 +01:00
if ( this . cureTurn ) {
2023-04-16 23:40:32 +01:00
pokemon . status . cureTurn = this . cureTurn ;
2024-05-23 16:03:10 +01:00
}
2023-04-12 00:08:03 +01:00
pokemon . updateInfo ( true ) ;
new CommonBattleAnim ( CommonAnim . POISON + ( this . statusEffect - 1 ) , pokemon ) . play ( this . scene , ( ) = > {
2023-04-24 19:30:21 +01:00
this . scene . queueMessage ( getPokemonMessage ( pokemon , getStatusEffectObtainText ( this . statusEffect , this . sourceText ) ) ) ;
2024-05-23 16:03:10 +01:00
if ( pokemon . status . isPostTurn ( ) ) {
2023-05-18 16:11:06 +01:00
this . scene . pushPhase ( new PostTurnStatusEffectPhase ( this . scene , this . battlerIndex ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-12 00:08:03 +01:00
this . end ( ) ;
} ) ;
return ;
}
2024-05-23 16:03:10 +01:00
} else if ( pokemon . status . effect === this . statusEffect ) {
2023-04-22 00:30:04 +01:00
this . scene . queueMessage ( getPokemonMessage ( pokemon , getStatusEffectOverlapText ( this . statusEffect ) ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-12 00:08:03 +01:00
this . end ( ) ;
}
}
export class PostTurnStatusEffectPhase extends PokemonPhase {
2023-05-18 16:11:06 +01:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex ) ;
2023-04-12 00:08:03 +01:00
}
start() {
const pokemon = this . getPokemon ( ) ;
2023-06-02 16:41:08 +01:00
if ( pokemon ? . isActive ( true ) && pokemon . status && pokemon . status . isPostTurn ( ) ) {
2023-04-12 00:08:03 +01:00
pokemon . status . incrementTurn ( ) ;
2024-04-02 20:14:07 +01:00
const cancelled = new Utils . BooleanHolder ( false ) ;
applyAbAttrs ( BlockNonDirectDamageAbAttr , pokemon , cancelled ) ;
if ( ! cancelled . value ) {
this . scene . queueMessage ( getPokemonMessage ( pokemon , getStatusEffectActivationText ( pokemon . status . effect ) ) ) ;
let damage : integer = 0 ;
switch ( pokemon . status . effect ) {
2024-05-23 16:03:10 +01:00
case StatusEffect . POISON :
damage = Math . max ( pokemon . getMaxHp ( ) >> 3 , 1 ) ;
break ;
case StatusEffect . TOXIC :
damage = Math . max ( Math . floor ( ( pokemon . getMaxHp ( ) / 16 ) * pokemon . status . turnCount ) , 1 ) ;
break ;
case StatusEffect . BURN :
damage = Math . max ( pokemon . getMaxHp ( ) >> 4 , 1 ) ;
break ;
2024-04-02 20:14:07 +01:00
}
if ( damage ) {
2024-05-19 16:10:57 +01:00
// Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ...
this . scene . damageNumberHandler . add ( this . getPokemon ( ) , pokemon . damage ( damage , false , true ) ) ;
2024-04-02 20:14:07 +01:00
pokemon . updateInfo ( ) ;
}
new CommonBattleAnim ( CommonAnim . POISON + ( pokemon . status . effect - 1 ) , pokemon ) . play ( this . scene , ( ) = > this . end ( ) ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-04-02 20:14:07 +01:00
this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
} else {
2023-04-10 19:12:01 +01:00
this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
}
}
2024-02-22 04:57:49 +00:00
export class MessagePhase extends Phase {
2023-04-10 19:12:01 +01:00
private text : string ;
2023-04-12 00:08:03 +01:00
private callbackDelay : integer ;
2023-04-10 19:12:01 +01:00
private prompt : boolean ;
2023-05-07 22:05:19 +01:00
private promptDelay : integer ;
2023-04-10 19:12:01 +01:00
2023-05-07 22:05:19 +01:00
constructor ( scene : BattleScene , text : string , callbackDelay? : integer , prompt? : boolean , promptDelay? : integer ) {
2023-04-10 19:12:01 +01:00
super ( scene ) ;
this . text = text ;
2023-04-12 00:08:03 +01:00
this . callbackDelay = callbackDelay ;
2023-04-10 19:12:01 +01:00
this . prompt = prompt ;
2023-05-07 22:05:19 +01:00
this . promptDelay = promptDelay ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
2024-05-23 16:03:10 +01:00
if ( this . text . indexOf ( "$" ) > - 1 ) {
const pageIndex = this . text . indexOf ( "$" ) ;
2023-10-18 23:01:15 +01:00
this . scene . unshiftPhase ( new MessagePhase ( this . scene , this . text . slice ( pageIndex + 1 ) , this . callbackDelay , this . prompt , this . promptDelay ) ) ;
this . text = this . text . slice ( 0 , pageIndex ) . trim ( ) ;
}
2023-05-07 22:05:19 +01:00
this . scene . ui . showText ( this . text , null , ( ) = > this . end ( ) , this . callbackDelay || ( this . prompt ? 0 : 1500 ) , this . prompt , this . promptDelay ) ;
2023-04-10 19:12:01 +01:00
}
2023-04-27 06:14:15 +01:00
end() {
2024-05-23 16:03:10 +01:00
if ( this . scene . abilityBar . shown ) {
2023-04-27 06:14:15 +01:00
this . scene . abilityBar . hide ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-27 06:14:15 +01:00
super . end ( ) ;
}
2023-04-10 19:12:01 +01:00
}
2023-04-15 06:32:16 +01:00
export class DamagePhase extends PokemonPhase {
2024-03-01 14:35:36 +00:00
private amount : integer ;
2023-04-15 06:32:16 +01:00
private damageResult : DamageResult ;
2024-03-01 14:35:36 +00:00
private critical : boolean ;
2023-04-15 06:32:16 +01:00
2024-03-01 14:35:36 +00:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , amount : integer , damageResult? : DamageResult , critical : boolean = false ) {
2023-05-18 16:11:06 +01:00
super ( scene , battlerIndex ) ;
2023-04-15 06:32:16 +01:00
2024-03-01 14:35:36 +00:00
this . amount = amount ;
2023-05-18 16:11:06 +01:00
this . damageResult = damageResult || HitResult . EFFECTIVE ;
2024-03-01 14:35:36 +00:00
this . critical = critical ;
2023-04-15 06:32:16 +01:00
}
start() {
super . start ( ) ;
2023-11-08 03:23:42 +00:00
if ( this . damageResult === HitResult . ONE_HIT_KO ) {
this . scene . toggleInvert ( true ) ;
this . scene . time . delayedCall ( Utils . fixedInt ( 1000 ) , ( ) = > {
this . scene . toggleInvert ( false ) ;
this . applyDamage ( ) ;
} ) ;
return ;
}
this . applyDamage ( ) ;
}
2024-03-01 15:15:43 +00:00
updateAmount ( amount : integer ) : void {
this . amount = amount ;
}
2023-11-08 03:23:42 +00:00
applyDamage() {
2023-04-15 06:32:16 +01:00
switch ( this . damageResult ) {
2024-05-23 16:03:10 +01:00
case HitResult . EFFECTIVE :
this . scene . playSound ( "hit" ) ;
break ;
case HitResult . SUPER_EFFECTIVE :
case HitResult . ONE_HIT_KO :
this . scene . playSound ( "hit_strong" ) ;
break ;
case HitResult . NOT_VERY_EFFECTIVE :
this . scene . playSound ( "hit_weak" ) ;
break ;
2023-04-15 06:32:16 +01:00
}
2024-05-23 16:03:10 +01:00
if ( this . amount ) {
2024-03-01 14:35:36 +00:00
this . scene . damageNumberHandler . add ( this . getPokemon ( ) , this . amount , this . damageResult , this . critical ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-01 14:35:36 +00:00
2023-05-18 16:11:06 +01:00
if ( this . damageResult !== HitResult . OTHER ) {
2023-04-18 17:30:47 +01:00
const flashTimer = this . scene . time . addEvent ( {
delay : 100 ,
repeat : 5 ,
startAt : 200 ,
callback : ( ) = > {
this . getPokemon ( ) . getSprite ( ) . setVisible ( flashTimer . repeatCount % 2 === 0 ) ;
2024-05-23 16:03:10 +01:00
if ( ! flashTimer . repeatCount ) {
2023-04-18 17:30:47 +01:00
this . getPokemon ( ) . updateInfo ( ) . then ( ( ) = > this . end ( ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-18 17:30:47 +01:00
}
} ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-04-18 17:30:47 +01:00
this . getPokemon ( ) . updateInfo ( ) . then ( ( ) = > this . end ( ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-15 06:32:16 +01:00
}
2024-01-13 17:24:24 +00:00
end() {
switch ( this . scene . currentBattle . battleSpec ) {
2024-05-23 16:03:10 +01:00
case BattleSpec . FINAL_BOSS :
const pokemon = this . getPokemon ( ) ;
if ( pokemon instanceof EnemyPokemon && pokemon . isBoss ( ) && ! pokemon . formIndex && pokemon . bossSegmentIndex < 1 ) {
this . scene . fadeOutBgm ( Utils . fixedInt ( 2000 ) , false ) ;
this . scene . ui . showDialogue ( battleSpecDialogue [ BattleSpec . FINAL_BOSS ] . firstStageWin , pokemon . species . name , null , ( ) = > {
this . scene . addEnemyModifier ( getModifierType ( modifierTypes . MINI_BLACK_HOLE ) . newModifier ( pokemon ) as PersistentModifier , false , true ) ;
pokemon . generateAndPopulateMoveset ( 1 ) ;
this . scene . setFieldScale ( 0.75 ) ;
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeManualTrigger , false ) ;
this . scene . currentBattle . double = true ;
2024-06-08 06:07:23 +01:00
const availablePartyMembers = this . scene . getParty ( ) . filter ( p = > p . isAllowedInBattle ( ) ) ;
2024-05-23 16:03:10 +01:00
if ( availablePartyMembers . length > 1 ) {
this . scene . pushPhase ( new ToggleDoublePositionPhase ( this . scene , true ) ) ;
if ( ! availablePartyMembers [ 1 ] . isOnField ( ) ) {
this . scene . pushPhase ( new SummonPhase ( this . scene , 1 ) ) ;
2024-01-14 01:15:55 +00:00
}
2024-05-23 16:03:10 +01:00
}
2024-01-14 01:15:55 +00:00
2024-05-23 16:03:10 +01:00
super . end ( ) ;
} ) ;
return ;
}
break ;
2024-01-13 17:24:24 +00:00
}
super . end ( ) ;
}
2023-04-15 06:32:16 +01:00
}
2023-04-10 19:12:01 +01:00
export class FaintPhase extends PokemonPhase {
2023-11-08 01:02:42 +00:00
private preventEndure : boolean ;
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , preventEndure? : boolean ) {
2023-05-18 16:11:06 +01:00
super ( scene , battlerIndex ) ;
2023-11-08 01:02:42 +00:00
this . preventEndure = preventEndure ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
2023-11-08 01:02:42 +00:00
if ( ! this . preventEndure ) {
const instantReviveModifier = this . scene . applyModifier ( PokemonInstantReviveModifier , this . player , this . getPokemon ( ) ) as PokemonInstantReviveModifier ;
2023-06-06 13:16:07 +01:00
2023-11-08 01:02:42 +00:00
if ( instantReviveModifier ) {
2024-05-23 16:03:10 +01:00
if ( ! -- instantReviveModifier . stackCount ) {
2023-11-08 01:02:42 +00:00
this . scene . removeModifier ( instantReviveModifier ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-08 01:02:42 +00:00
this . scene . updateModifiers ( this . player ) ;
return this . end ( ) ;
}
2023-06-06 13:16:07 +01:00
}
2024-05-23 16:03:10 +01:00
if ( ! this . tryOverrideForBattleSpec ( ) ) {
2024-01-13 17:24:24 +00:00
this . doFaint ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-01-13 17:24:24 +00:00
}
doFaint ( ) : void {
const pokemon = this . getPokemon ( ) ;
2024-05-29 16:07:59 +01:00
// Track total times pokemon have been KO'd for supreme overlord/last respects
if ( pokemon . isPlayer ( ) ) {
this . scene . currentBattle . playerFaints += 1 ;
} else {
this . scene . currentBattle . enemyFaints += 1 ;
}
2024-06-12 02:41:24 +01:00
this . scene . queueMessage ( i18next . t ( "battle:fainted" , { pokemonNameWithAffix : getPokemonNameWithAffix ( pokemon ) } ) , null , true ) ;
2023-04-15 06:32:16 +01:00
2024-04-11 14:24:03 +01:00
if ( pokemon . turnData ? . attacksReceived ? . length ) {
const lastAttack = pokemon . turnData . attacksReceived [ 0 ] ;
2024-06-07 21:57:57 +01:00
applyPostFaintAbAttrs ( PostFaintAbAttr , pokemon , this . scene . getPokemonById ( lastAttack . sourceId ) , new PokemonMove ( lastAttack . move ) . getMove ( ) , lastAttack . result ) ;
2024-04-09 23:05:15 +01:00
}
2024-04-07 22:20:24 +01:00
const alivePlayField = this . scene . getField ( true ) ;
2024-04-14 04:21:34 +01:00
alivePlayField . forEach ( p = > applyPostKnockOutAbAttrs ( PostKnockOutAbAttr , p , pokemon ) ) ;
2024-04-07 22:20:24 +01:00
if ( pokemon . turnData ? . attacksReceived ? . length ) {
const defeatSource = this . scene . getPokemonById ( pokemon . turnData . attacksReceived [ 0 ] . sourceId ) ;
2024-05-08 05:44:55 +01:00
if ( defeatSource ? . isOnField ( ) ) {
2024-04-07 22:20:24 +01:00
applyPostVictoryAbAttrs ( PostVictoryAbAttr , defeatSource ) ;
2024-05-08 05:44:55 +01:00
const pvmove = allMoves [ pokemon . turnData . attacksReceived [ 0 ] . move ] ;
2024-06-01 01:50:30 +01:00
const pvattrs = pvmove . getAttrs ( PostVictoryStatChangeAttr ) ;
2024-05-08 05:44:55 +01:00
if ( pvattrs . length ) {
2024-05-23 16:03:10 +01:00
for ( const pvattr of pvattrs ) {
2024-05-08 05:44:55 +01:00
pvattr . applyPostVictory ( defeatSource , defeatSource , pvmove ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-08 05:44:55 +01:00
}
}
2024-04-07 22:20:24 +01:00
}
2023-04-22 03:59:09 +01:00
if ( this . player ) {
2024-06-08 06:07:23 +01:00
const nonFaintedLegalPartyMembers = this . scene . getParty ( ) . filter ( p = > p . isAllowedInBattle ( ) ) ;
const nonFaintedPartyMemberCount = nonFaintedLegalPartyMembers . length ;
2024-05-23 16:03:10 +01:00
if ( ! nonFaintedPartyMemberCount ) {
2023-05-18 16:11:06 +01:00
this . scene . unshiftPhase ( new GameOverPhase ( this . scene ) ) ;
2024-06-08 06:07:23 +01:00
} else if ( nonFaintedPartyMemberCount >= this . scene . currentBattle . getBattlerCount ( ) || ( this . scene . currentBattle . double && ! nonFaintedLegalPartyMembers [ 0 ] . isActive ( true ) ) ) {
2023-11-15 23:45:10 +00:00
this . scene . pushPhase ( new SwitchPhase ( this . scene , this . fieldIndex , true , false ) ) ;
2024-05-23 16:03:10 +01:00
}
if ( nonFaintedPartyMemberCount === 1 && this . scene . currentBattle . double ) {
2023-07-06 03:23:50 +01:00
this . scene . unshiftPhase ( new ToggleDoublePositionPhase ( this . scene , true ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-07 21:08:33 +01:00
} else {
2023-05-29 17:24:38 +01:00
this . scene . unshiftPhase ( new VictoryPhase ( this . scene , this . battlerIndex ) ) ;
2023-10-07 21:08:33 +01:00
if ( this . scene . currentBattle . battleType === BattleType . TRAINER ) {
2024-03-21 04:57:28 +00:00
const hasReservePartyMember = ! ! this . scene . getEnemyParty ( ) . filter ( p = > p . isActive ( ) && ! p . isOnField ( ) && p . trainerSlot === ( pokemon as EnemyPokemon ) . trainerSlot ) . length ;
2024-05-23 16:03:10 +01:00
if ( hasReservePartyMember ) {
2023-11-27 03:22:05 +00:00
this . scene . pushPhase ( new SwitchSummonPhase ( this . scene , this . fieldIndex , - 1 , false , false , false ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-07 21:08:33 +01:00
}
}
2023-04-14 04:04:51 +01:00
2024-03-28 04:24:24 +00:00
if ( this . scene . currentBattle . double ) {
const allyPokemon = pokemon . getAlly ( ) ;
if ( allyPokemon ? . isActive ( true ) ) {
let targetingMovePhase : MovePhase ;
do {
2024-04-04 19:42:20 +01:00
targetingMovePhase = this . scene . findPhase ( mp = > mp instanceof MovePhase && mp . targets . length === 1 && mp . targets [ 0 ] === pokemon . getBattlerIndex ( ) && mp . pokemon . isPlayer ( ) !== allyPokemon . isPlayer ( ) ) as MovePhase ;
2024-05-23 16:03:10 +01:00
if ( targetingMovePhase && targetingMovePhase . targets [ 0 ] !== allyPokemon . getBattlerIndex ( ) ) {
2024-03-28 04:24:24 +00:00
targetingMovePhase . targets [ 0 ] = allyPokemon . getBattlerIndex ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-28 04:24:24 +00:00
} while ( targetingMovePhase ) ;
}
}
2023-04-22 00:30:04 +01:00
pokemon . lapseTags ( BattlerTagLapseType . FAINT ) ;
2024-03-12 00:55:41 +00:00
this . scene . getField ( true ) . filter ( p = > p !== pokemon ) . forEach ( p = > p . removeTagsBySourceId ( pokemon . id ) ) ;
2023-04-13 17:16:36 +01:00
2023-04-10 19:12:01 +01:00
pokemon . faintCry ( ( ) = > {
2024-05-23 16:03:10 +01:00
if ( pokemon instanceof PlayerPokemon ) {
2024-05-02 22:48:08 +01:00
pokemon . addFriendship ( - 10 ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
pokemon . hideInfo ( ) ;
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "faint" ) ;
2023-04-10 19:12:01 +01:00
this . scene . tweens . add ( {
targets : pokemon ,
duration : 500 ,
y : pokemon.y + 150 ,
2024-05-23 16:03:10 +01:00
ease : "Sine.easeIn" ,
2023-04-10 19:12:01 +01:00
onComplete : ( ) = > {
pokemon . setVisible ( false ) ;
pokemon . y -= 150 ;
2023-04-16 23:40:32 +01:00
pokemon . trySetStatus ( StatusEffect . FAINT ) ;
2024-05-23 16:03:10 +01:00
if ( pokemon . isPlayer ( ) ) {
2023-04-10 19:12:01 +01:00
this . scene . currentBattle . removeFaintedParticipant ( pokemon as PlayerPokemon ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-03-17 15:36:19 +00:00
this . scene . addFaintedEnemyScore ( pokemon as EnemyPokemon ) ;
2024-03-07 02:05:23 +00:00
this . scene . currentBattle . addPostBattleLoot ( pokemon as EnemyPokemon ) ;
2024-03-17 15:36:19 +00:00
}
2023-04-10 19:12:01 +01:00
this . scene . field . remove ( pokemon ) ;
this . end ( ) ;
}
} ) ;
} ) ;
}
2024-01-13 17:24:24 +00:00
tryOverrideForBattleSpec ( ) : boolean {
switch ( this . scene . currentBattle . battleSpec ) {
2024-05-23 16:03:10 +01:00
case BattleSpec . FINAL_BOSS :
if ( ! this . player ) {
const enemy = this . getPokemon ( ) ;
if ( enemy . formIndex ) {
this . scene . ui . showDialogue ( battleSpecDialogue [ BattleSpec . FINAL_BOSS ] . secondStageWin , enemy . species . name , null , ( ) = > this . doFaint ( ) ) ;
} else {
// Final boss' HP threshold has been bypassed; cancel faint and force check for 2nd phase
enemy . hp ++ ;
this . scene . unshiftPhase ( new DamagePhase ( this . scene , enemy . getBattlerIndex ( ) , 0 , HitResult . OTHER ) ) ;
this . end ( ) ;
2024-01-13 17:24:24 +00:00
}
2024-05-23 16:03:10 +01:00
return true ;
}
2024-01-13 17:24:24 +00:00
}
return false ;
}
2023-04-10 19:12:01 +01:00
}
export class VictoryPhase extends PokemonPhase {
2023-05-29 17:24:38 +01:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex ) ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
2024-01-11 17:26:32 +00:00
this . scene . gameData . gameStats . pokemonDefeated ++ ;
2023-04-10 19:12:01 +01:00
const participantIds = this . scene . currentBattle . playerParticipantIds ;
const party = this . scene . getParty ( ) ;
2023-04-19 03:09:37 +01:00
const expShareModifier = this . scene . findModifier ( m = > m instanceof ExpShareModifier ) as ExpShareModifier ;
2023-04-20 03:51:46 +01:00
const expBalanceModifier = this . scene . findModifier ( m = > m instanceof ExpBalanceModifier ) as ExpBalanceModifier ;
2023-04-28 21:35:03 +01:00
const multipleParticipantExpBonusModifier = this . scene . findModifier ( m = > m instanceof MultipleParticipantExpBonusModifier ) as MultipleParticipantExpBonusModifier ;
2024-05-04 05:19:50 +01:00
const nonFaintedPartyMembers = party . filter ( p = > p . hp ) ;
const expPartyMembers = nonFaintedPartyMembers . filter ( p = > p . level < this . scene . getMaxExpLevel ( ) ) ;
2023-04-20 03:51:46 +01:00
const partyMemberExp = [ ] ;
2023-11-24 21:27:26 +00:00
if ( participantIds . size ) {
let expValue = this . getPokemon ( ) . getExpValue ( ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . battleType === BattleType . TRAINER ) {
2023-11-24 21:27:26 +00:00
expValue = Math . floor ( expValue * 1.5 ) ;
2024-05-23 16:03:10 +01:00
}
for ( const partyMember of nonFaintedPartyMembers ) {
2023-11-24 21:27:26 +00:00
const pId = partyMember . id ;
const participated = participantIds . has ( pId ) ;
2024-05-23 16:03:10 +01:00
if ( participated ) {
2024-05-02 22:48:08 +01:00
partyMember . addFriendship ( 2 ) ;
2024-05-23 16:03:10 +01:00
}
if ( ! expPartyMembers . includes ( partyMember ) ) {
2024-05-04 05:19:50 +01:00
continue ;
2024-05-23 16:03:10 +01:00
}
2024-05-04 05:19:50 +01:00
if ( ! participated && ! expShareModifier ) {
2023-11-24 21:27:26 +00:00
partyMemberExp . push ( 0 ) ;
continue ;
}
let expMultiplier = 0 ;
if ( participated ) {
expMultiplier += ( 1 / participantIds . size ) ;
2024-05-23 16:03:10 +01:00
if ( participantIds . size > 1 && multipleParticipantExpBonusModifier ) {
2023-11-24 21:27:26 +00:00
expMultiplier += multipleParticipantExpBonusModifier . getStackCount ( ) * 0.2 ;
2024-05-23 16:03:10 +01:00
}
} else if ( expShareModifier ) {
2023-11-24 21:27:26 +00:00
expMultiplier += ( expShareModifier . getStackCount ( ) * 0.2 ) / participantIds . size ;
2024-05-23 16:03:10 +01:00
}
if ( partyMember . pokerus ) {
2023-11-24 21:27:26 +00:00
expMultiplier *= 1.5 ;
2024-05-23 16:03:10 +01:00
}
2024-05-26 17:17:41 +01:00
if ( Overrides . XP_MULTIPLIER_OVERRIDE !== null ) {
expMultiplier = Overrides . XP_MULTIPLIER_OVERRIDE ;
}
2023-11-24 21:27:26 +00:00
const pokemonExp = new Utils . NumberHolder ( expValue * expMultiplier ) ;
this . scene . applyModifiers ( PokemonExpBoosterModifier , true , partyMember , pokemonExp ) ;
partyMemberExp . push ( Math . floor ( pokemonExp . value ) ) ;
2023-04-20 03:51:46 +01:00
}
2023-11-24 21:27:26 +00:00
if ( expBalanceModifier ) {
let totalLevel = 0 ;
let totalExp = 0 ;
expPartyMembers . forEach ( ( expPartyMember , epm ) = > {
totalExp += partyMemberExp [ epm ] ;
totalLevel += expPartyMember . level ;
} ) ;
2023-04-20 03:51:46 +01:00
2023-11-24 21:27:26 +00:00
const medianLevel = Math . floor ( totalLevel / expPartyMembers . length ) ;
2023-04-20 03:51:46 +01:00
2023-11-24 21:27:26 +00:00
const recipientExpPartyMemberIndexes = [ ] ;
expPartyMembers . forEach ( ( expPartyMember , epm ) = > {
2024-05-23 16:03:10 +01:00
if ( expPartyMember . level <= medianLevel ) {
2023-11-24 21:27:26 +00:00
recipientExpPartyMemberIndexes . push ( epm ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-24 21:27:26 +00:00
} ) ;
2023-04-20 03:51:46 +01:00
2023-11-24 21:27:26 +00:00
const splitExp = Math . floor ( totalExp / recipientExpPartyMemberIndexes . length ) ;
2023-04-20 03:51:46 +01:00
2023-11-24 21:27:26 +00:00
expPartyMembers . forEach ( ( _partyMember , pm ) = > {
2024-03-27 17:26:13 +00:00
partyMemberExp [ pm ] = Phaser . Math . Linear ( partyMemberExp [ pm ] , recipientExpPartyMemberIndexes . indexOf ( pm ) > - 1 ? splitExp : 0 , 0.2 * expBalanceModifier . getStackCount ( ) ) ;
2023-11-24 21:27:26 +00:00
} ) ;
}
2023-04-20 03:51:46 +01:00
2023-11-24 21:27:26 +00:00
for ( let pm = 0 ; pm < expPartyMembers . length ; pm ++ ) {
const exp = partyMemberExp [ pm ] ;
2023-04-20 03:51:46 +01:00
2023-11-24 21:27:26 +00:00
if ( exp ) {
const partyMemberIndex = party . indexOf ( expPartyMembers [ pm ] ) ;
this . scene . unshiftPhase ( expPartyMembers [ pm ] . isOnField ( ) ? new ExpPhase ( this . scene , partyMemberIndex , exp ) : new ShowPartyExpBarPhase ( this . scene , partyMemberIndex , exp ) ) ;
}
2023-04-10 19:12:01 +01:00
}
}
2024-05-24 00:45:04 +01:00
2024-03-07 13:42:04 +00:00
if ( ! this . scene . getEnemyParty ( ) . find ( p = > this . scene . currentBattle . battleType ? ! p ? . isFainted ( true ) : p . isOnField ( ) ) ) {
2023-05-18 16:11:06 +01:00
this . scene . pushPhase ( new BattleEndPhase ( this . scene ) ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . battleType === BattleType . TRAINER ) {
2023-10-18 23:01:15 +01:00
this . scene . pushPhase ( new TrainerVictoryPhase ( this . scene ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-14 20:26:57 +00:00
if ( this . scene . gameMode . isEndless || ! this . scene . gameMode . isWaveFinal ( this . scene . currentBattle . waveIndex ) ) {
2024-04-23 18:12:57 +01:00
this . scene . pushPhase ( new EggLapsePhase ( this . scene ) ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . waveIndex % 10 ) {
2023-10-25 19:15:44 +01:00
this . scene . pushPhase ( new SelectModifierPhase ( this . scene ) ) ;
2024-05-23 16:03:10 +01:00
} else if ( this . scene . gameMode . isDaily ) {
2024-03-17 02:06:56 +00:00
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , modifierTypes . EXP_CHARM ) ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . waveIndex > 10 && ! this . scene . gameMode . isWaveFinal ( this . scene . currentBattle . waveIndex ) ) {
2024-03-17 02:06:56 +00:00
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , modifierTypes . GOLDEN_POKEBALL ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-17 02:06:56 +00:00
} else {
2024-03-25 15:00:42 +00:00
const superExpWave = ! this . scene . gameMode . isEndless ? ( this . scene . offsetGym ? 0 : 20 ) : 10 ;
2024-05-23 16:03:10 +01:00
if ( this . scene . gameMode . isEndless && this . scene . currentBattle . waveIndex === 10 ) {
2024-03-24 15:17:12 +00:00
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , modifierTypes . EXP_SHARE ) ) ;
2024-05-23 16:03:10 +01:00
}
if ( this . scene . currentBattle . waveIndex <= 750 && ( this . scene . currentBattle . waveIndex <= 500 || ( this . scene . currentBattle . waveIndex % 30 ) === superExpWave ) ) {
2024-01-05 00:37:07 +00:00
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , ( this . scene . currentBattle . waveIndex % 30 ) !== superExpWave || this . scene . currentBattle . waveIndex > 250 ? modifierTypes.EXP_CHARM : modifierTypes.SUPER_EXP_CHARM ) ) ;
2024-05-23 16:03:10 +01:00
}
if ( this . scene . currentBattle . waveIndex <= 150 && ! ( this . scene . currentBattle . waveIndex % 50 ) ) {
2024-01-05 00:37:07 +00:00
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , modifierTypes . GOLDEN_POKEBALL ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-14 20:26:57 +00:00
if ( this . scene . gameMode . isEndless && ! ( this . scene . currentBattle . waveIndex % 50 ) ) {
2024-01-01 04:19:06 +00:00
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , ! ( this . scene . currentBattle . waveIndex % 250 ) ? modifierTypes.VOUCHER_PREMIUM : modifierTypes.VOUCHER_PLUS ) ) ;
2023-11-05 15:56:09 +00:00
this . scene . pushPhase ( new AddEnemyBuffModifierPhase ( this . scene ) ) ;
2023-12-20 04:51:48 +00:00
}
2023-11-28 21:59:40 +00:00
}
2023-05-18 16:11:06 +01:00
this . scene . pushPhase ( new NewBattlePhase ( this . scene ) ) ;
2024-03-18 00:31:09 +00:00
} else {
this . scene . currentBattle . battleType = BattleType . CLEAR ;
2024-03-18 00:58:12 +00:00
this . scene . score += this . scene . gameMode . getClearScoreBonus ( ) ;
2024-03-19 04:03:41 +00:00
this . scene . updateScoreText ( ) ;
2023-05-18 16:11:06 +01:00
this . scene . pushPhase ( new GameOverPhase ( this . scene , true ) ) ;
2024-03-18 00:31:09 +00:00
}
2023-05-18 16:11:06 +01:00
}
2023-04-10 19:12:01 +01:00
this . end ( ) ;
}
}
2023-10-18 23:01:15 +01:00
export class TrainerVictoryPhase extends BattlePhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
2024-04-18 05:24:57 +01:00
this . scene . disableMenu = true ;
2023-10-20 16:38:41 +01:00
this . scene . playBgm ( this . scene . currentBattle . trainer . config . victoryBgm ) ;
2023-11-10 20:51:34 +00:00
this . scene . unshiftPhase ( new MoneyRewardPhase ( this . scene , this . scene . currentBattle . trainer . config . moneyMultiplier ) ) ;
2023-10-20 16:38:41 +01:00
const modifierRewardFuncs = this . scene . currentBattle . trainer . config . modifierRewardFuncs ;
2024-05-23 16:03:10 +01:00
for ( const modifierRewardFunc of modifierRewardFuncs ) {
2023-10-25 19:15:44 +01:00
this . scene . unshiftPhase ( new ModifierRewardPhase ( this . scene , modifierRewardFunc ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-18 23:01:15 +01:00
2023-12-20 22:30:06 +00:00
const trainerType = this . scene . currentBattle . trainer . config . trainerType ;
if ( vouchers . hasOwnProperty ( TrainerType [ trainerType ] ) ) {
2024-05-23 16:03:10 +01:00
if ( ! this . scene . validateVoucher ( vouchers [ TrainerType [ trainerType ] ] ) && this . scene . currentBattle . trainer . config . isBoss ) {
2024-05-01 04:02:16 +01:00
this . scene . unshiftPhase ( new ModifierRewardPhase ( this . scene , [ modifierTypes . VOUCHER , modifierTypes . VOUCHER , modifierTypes . VOUCHER_PLUS , modifierTypes . VOUCHER_PREMIUM ] [ vouchers [ TrainerType [ trainerType ] ] . voucherType ] ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-12-20 22:30:06 +00:00
}
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:trainerDefeated" , { trainerName : this.scene.currentBattle.trainer.getName ( TrainerSlot . NONE , true ) } ) , null , ( ) = > {
2024-02-14 19:41:39 +00:00
const victoryMessages = this . scene . currentBattle . trainer . getVictoryMessages ( ) ;
2024-06-03 11:49:13 +01:00
let message : string ;
this . scene . executeWithSeedOffset ( ( ) = > message = Utils . randSeedItem ( victoryMessages ) , this . scene . currentBattle . waveIndex ) ;
2024-05-24 00:45:04 +01:00
2024-06-03 11:49:13 +01:00
const showMessage = ( ) = > {
const originalFunc = showMessageOrEnd ;
showMessageOrEnd = ( ) = > this . scene . ui . showDialogue ( message , this . scene . currentBattle . trainer . getName ( ) , null , originalFunc ) ;
2024-02-23 00:24:00 +00:00
showMessageOrEnd ( ) ;
} ;
let showMessageOrEnd = ( ) = > this . end ( ) ;
if ( victoryMessages ? . length ) {
2024-06-03 11:49:13 +01:00
if ( this . scene . currentBattle . trainer . config . hasCharSprite && ! this . scene . ui . shouldSkipDialogue ( message ) ) {
2024-02-23 00:24:00 +00:00
const originalFunc = showMessageOrEnd ;
showMessageOrEnd = ( ) = > this . scene . charSprite . hide ( ) . then ( ( ) = > this . scene . hideFieldOverlay ( 250 ) . then ( ( ) = > originalFunc ( ) ) ) ;
2024-02-26 00:09:24 +00:00
this . scene . showFieldOverlay ( 500 ) . then ( ( ) = > this . scene . charSprite . showCharacter ( this . scene . currentBattle . trainer . getKey ( ) , getCharVariantFromDialogue ( victoryMessages [ 0 ] ) ) . then ( ( ) = > showMessage ( ) ) ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-02-23 00:24:00 +00:00
showMessage ( ) ;
2024-05-23 16:03:10 +01:00
}
} else {
2024-02-23 00:24:00 +00:00
showMessageOrEnd ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-18 23:01:15 +01:00
} , null , true ) ;
2024-02-22 04:57:49 +00:00
this . showEnemyTrainer ( ) ;
2023-10-18 23:01:15 +01:00
}
}
2023-11-10 20:51:34 +00:00
export class MoneyRewardPhase extends BattlePhase {
private moneyMultiplier : number ;
constructor ( scene : BattleScene , moneyMultiplier : number ) {
super ( scene ) ;
this . moneyMultiplier = moneyMultiplier ;
}
start() {
2024-02-04 05:30:19 +00:00
const moneyAmount = new Utils . IntegerHolder ( this . scene . getWaveMoneyAmount ( this . moneyMultiplier ) ) ;
2024-01-18 22:22:18 +00:00
2023-11-11 02:11:36 +00:00
this . scene . applyModifiers ( MoneyMultiplierModifier , true , moneyAmount ) ;
2023-11-10 20:51:34 +00:00
2024-04-22 02:17:07 +01:00
this . scene . addMoney ( moneyAmount . value ) ;
2023-11-12 05:31:40 +00:00
2024-05-23 19:22:09 +01:00
const userLocale = navigator . language || "en-US" ;
const formattedMoneyAmount = moneyAmount . value . toLocaleString ( userLocale ) ;
const message = i18next . t ( "battle:moneyWon" , { moneyAmount : formattedMoneyAmount } ) ;
this . scene . ui . showText ( message , null , ( ) = > this . end ( ) , null , true ) ;
2023-11-10 20:51:34 +00:00
}
}
2023-10-20 16:38:41 +01:00
export class ModifierRewardPhase extends BattlePhase {
2024-02-22 21:21:34 +00:00
protected modifierType : ModifierType ;
2023-10-20 16:38:41 +01:00
2023-10-25 19:15:44 +01:00
constructor ( scene : BattleScene , modifierTypeFunc : ModifierTypeFunc ) {
2023-10-20 16:38:41 +01:00
super ( scene ) ;
2023-10-28 18:24:57 +01:00
this . modifierType = getModifierType ( modifierTypeFunc ) ;
2023-10-20 16:38:41 +01:00
}
start() {
super . start ( ) ;
2024-02-22 21:21:34 +00:00
this . doReward ( ) . then ( ( ) = > this . end ( ) ) ;
}
2023-10-20 16:38:41 +01:00
2024-02-22 21:21:34 +00:00
doReward ( ) : Promise < void > {
return new Promise < void > ( resolve = > {
const newModifier = this . modifierType . newModifier ( ) ;
this . scene . addModifier ( newModifier ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "item_fanfare" ) ;
2024-02-22 21:21:34 +00:00
this . scene . ui . showText ( ` You received \ n ${ newModifier . type . name } ! ` , null , ( ) = > resolve ( ) , null , true ) ;
} ) ;
2024-05-23 16:03:10 +01:00
} ) ;
2024-02-22 21:21:34 +00:00
}
}
export class GameOverModifierRewardPhase extends ModifierRewardPhase {
constructor ( scene : BattleScene , modifierTypeFunc : ModifierTypeFunc ) {
super ( scene , modifierTypeFunc ) ;
}
doReward ( ) : Promise < void > {
return new Promise < void > ( resolve = > {
const newModifier = this . modifierType . newModifier ( ) ;
this . scene . addModifier ( newModifier ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "level_up_fanfare" ) ;
2024-05-10 23:12:33 +01:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-05-17 11:51:48 +01:00
this . scene . ui . fadeIn ( 250 ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( ` You received \ n ${ newModifier . type . name } ! ` , null , ( ) = > {
this . scene . time . delayedCall ( 1500 , ( ) = > this . scene . arenaBg . setVisible ( true ) ) ;
resolve ( ) ;
} , null , true , 1500 ) ;
2024-05-17 11:51:48 +01:00
} ) ;
2024-02-22 21:21:34 +00:00
} ) ;
2024-05-23 16:03:10 +01:00
} ) ;
2023-10-20 16:38:41 +01:00
}
}
2024-05-07 05:44:41 +01:00
export class RibbonModifierRewardPhase extends ModifierRewardPhase {
private species : PokemonSpecies ;
constructor ( scene : BattleScene , modifierTypeFunc : ModifierTypeFunc , species : PokemonSpecies ) {
super ( scene , modifierTypeFunc ) ;
this . species = species ;
}
doReward ( ) : Promise < void > {
return new Promise < void > ( resolve = > {
const newModifier = this . modifierType . newModifier ( ) ;
this . scene . addModifier ( newModifier ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "level_up_fanfare" ) ;
2024-05-10 23:12:33 +01:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-05-17 02:41:38 +01:00
this . scene . ui . showText ( ` ${ this . species . name } beat ${ this . scene . gameMode . getName ( ) } Mode for the first time! \ nYou received ${ newModifier . type . name } ! ` , null , ( ) = > {
resolve ( ) ;
} , null , true , 1500 ) ;
2024-05-07 05:44:41 +01:00
} ) ;
2024-05-23 16:03:10 +01:00
} ) ;
2024-05-07 05:44:41 +01:00
}
}
2023-04-22 03:59:09 +01:00
export class GameOverPhase extends BattlePhase {
2023-04-26 22:40:08 +01:00
private victory : boolean ;
2024-05-07 05:44:41 +01:00
private firstRibbons : PokemonSpecies [ ] = [ ] ;
2023-04-26 22:40:08 +01:00
constructor ( scene : BattleScene , victory? : boolean ) {
2023-04-22 03:59:09 +01:00
super ( scene ) ;
2023-04-26 22:40:08 +01:00
this . victory = ! ! victory ;
2023-04-22 03:59:09 +01:00
}
start() {
super . start ( ) ;
2024-05-25 10:36:30 +01:00
// Failsafe if players somehow skip floor 200 in classic mode
if ( this . scene . gameMode . isClassic && this . scene . currentBattle . waveIndex > 200 ) {
this . victory = true ;
}
2024-05-28 02:36:32 +01:00
if ( this . victory && this . scene . gameMode . isEndless ) {
2024-05-28 02:01:13 +01:00
this . scene . ui . showDialogue ( i18next . t ( "PGMmiscDialogue:ending_endless" ) , i18next . t ( "PGMmiscDialogue:ending_name" ) , 0 , ( ) = > this . handleGameOver ( ) ) ;
} else if ( this . victory || ! this . scene . enableRetries ) {
2024-05-10 23:12:33 +01:00
this . handleGameOver ( ) ;
2024-05-23 16:03:10 +01:00
} else {
this . scene . ui . showText ( "Would you like to retry from the start of the battle?" , null , ( ) = > {
2024-04-04 20:22:05 +01:00
this . scene . ui . setMode ( Mode . CONFIRM , ( ) = > {
this . scene . ui . fadeOut ( 1250 ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . reset ( ) ;
2024-04-04 20:22:05 +01:00
this . scene . clearPhaseQueue ( ) ;
this . scene . gameData . loadSession ( this . scene , this . scene . sessionSlotId ) . then ( ( ) = > {
this . scene . pushPhase ( new EncounterPhase ( this . scene , true ) ) ;
2024-06-08 06:07:23 +01:00
const availablePartyMembers = this . scene . getParty ( ) . filter ( p = > p . isAllowedInBattle ( ) ) . length ;
2024-05-24 00:45:04 +01:00
2024-04-04 20:22:05 +01:00
this . scene . pushPhase ( new SummonPhase ( this . scene , 0 ) ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . double && availablePartyMembers > 1 ) {
2024-04-04 20:22:05 +01:00
this . scene . pushPhase ( new SummonPhase ( this . scene , 1 ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-04 20:22:05 +01:00
if ( this . scene . currentBattle . waveIndex > 1 && this . scene . currentBattle . battleType !== BattleType . TRAINER ) {
this . scene . pushPhase ( new CheckSwitchPhase ( this . scene , 0 , this . scene . currentBattle . double ) ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . currentBattle . double && availablePartyMembers > 1 ) {
2024-04-04 20:22:05 +01:00
this . scene . pushPhase ( new CheckSwitchPhase ( this . scene , 1 , this . scene . currentBattle . double ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-04 20:22:05 +01:00
}
this . scene . ui . fadeIn ( 1250 ) ;
this . end ( ) ;
} ) ;
} ) ;
2024-05-10 23:12:33 +01:00
} , ( ) = > this . handleGameOver ( ) , false , 0 , 0 , 1000 ) ;
2024-04-04 20:22:05 +01:00
} ) ;
}
}
2024-05-10 23:12:33 +01:00
handleGameOver ( ) : void {
const doGameOver = ( newClear : boolean ) = > {
2024-05-17 02:41:38 +01:00
this . scene . disableMenu = true ;
2024-01-01 03:49:50 +00:00
this . scene . time . delayedCall ( 1000 , ( ) = > {
2024-02-22 05:34:54 +00:00
let firstClear = false ;
2024-05-10 23:12:33 +01:00
if ( this . victory && newClear ) {
2024-03-17 02:06:56 +00:00
if ( this . scene . gameMode . isClassic ) {
firstClear = this . scene . validateAchv ( achvs . CLASSIC_VICTORY ) ;
this . scene . gameData . gameStats . sessionsWon ++ ;
2024-05-23 16:03:10 +01:00
for ( const pokemon of this . scene . getParty ( ) ) {
2024-05-07 05:44:41 +01:00
this . awardRibbon ( pokemon ) ;
2024-05-23 16:03:10 +01:00
if ( pokemon . species . getRootSpeciesId ( ) !== pokemon . species . getRootSpeciesId ( true ) ) {
2024-05-07 05:44:41 +01:00
this . awardRibbon ( pokemon , true ) ;
}
}
2024-05-23 16:03:10 +01:00
} else if ( this . scene . gameMode . isDaily && newClear ) {
2024-03-17 02:06:56 +00:00
this . scene . gameData . gameStats . dailyRunSessionsWon ++ ;
2024-05-23 16:03:10 +01:00
}
2024-01-11 17:26:32 +00:00
}
2024-01-01 03:49:50 +00:00
const fadeDuration = this . victory ? 10000 : 5000 ;
this . scene . fadeOutBgm ( fadeDuration , true ) ;
2024-05-10 23:12:33 +01:00
const activeBattlers = this . scene . getField ( ) . filter ( p = > p ? . isActive ( true ) ) ;
activeBattlers . map ( p = > p . hideInfo ( ) ) ;
2024-01-01 03:49:50 +00:00
this . scene . ui . fadeOut ( fadeDuration ) . then ( ( ) = > {
2024-05-17 02:41:38 +01:00
activeBattlers . map ( a = > a . setVisible ( false ) ) ;
2024-03-17 04:36:33 +00:00
this . scene . setFieldScale ( 1 , true ) ;
2024-01-01 03:49:50 +00:00
this . scene . clearPhaseQueue ( ) ;
this . scene . ui . clearText ( ) ;
2024-05-17 02:41:38 +01:00
2024-06-08 06:07:23 +01:00
if ( this . victory && this . scene . gameMode . isChallenge ) {
this . scene . gameMode . challenges . forEach ( c = > this . scene . validateAchvs ( ChallengeAchv , c ) ) ;
}
2024-05-17 02:41:38 +01:00
const clear = ( endCardPhase? : EndCardPhase ) = > {
2024-05-23 16:03:10 +01:00
if ( newClear ) {
2024-05-17 02:41:38 +01:00
this . handleUnlocks ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-17 02:41:38 +01:00
if ( this . victory && newClear ) {
2024-05-23 16:03:10 +01:00
for ( const species of this . firstRibbons ) {
2024-05-17 02:41:38 +01:00
this . scene . unshiftPhase ( new RibbonModifierRewardPhase ( this . scene , modifierTypes . VOUCHER_PLUS , species ) ) ;
2024-05-23 16:03:10 +01:00
}
if ( ! firstClear ) {
2024-05-17 02:41:38 +01:00
this . scene . unshiftPhase ( new GameOverModifierRewardPhase ( this . scene , modifierTypes . VOUCHER_PREMIUM ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-17 02:41:38 +01:00
}
this . scene . pushPhase ( new PostGameOverPhase ( this . scene , endCardPhase ) ) ;
this . end ( ) ;
2024-05-23 16:03:10 +01:00
} ;
2024-05-17 02:41:38 +01:00
2024-05-17 05:20:18 +01:00
if ( this . victory && this . scene . gameMode . isClassic ) {
2024-06-03 11:49:13 +01:00
const message = miscDialogue . ending [ this . scene . gameData . gender === PlayerGender . FEMALE ? 0 : 1 ] ;
if ( ! this . scene . ui . shouldSkipDialogue ( message ) ) {
this . scene . ui . fadeIn ( 500 ) . then ( ( ) = > {
this . scene . charSprite . showCharacter ( ` rival_ ${ this . scene . gameData . gender === PlayerGender . FEMALE ? "m" : "f" } ` , getCharVariantFromDialogue ( miscDialogue . ending [ this . scene . gameData . gender === PlayerGender . FEMALE ? 0 : 1 ] ) ) . then ( ( ) = > {
this . scene . ui . showDialogue ( message , this . scene . gameData . gender === PlayerGender . FEMALE ? trainerConfigs [ TrainerType . RIVAL ] . name : trainerConfigs [ TrainerType . RIVAL ] . nameFemale , null , ( ) = > {
this . scene . ui . fadeOut ( 500 ) . then ( ( ) = > {
this . scene . charSprite . hide ( ) . then ( ( ) = > {
const endCardPhase = new EndCardPhase ( this . scene ) ;
this . scene . unshiftPhase ( endCardPhase ) ;
clear ( endCardPhase ) ;
} ) ;
2024-05-17 02:41:38 +01:00
} ) ;
} ) ;
} ) ;
} ) ;
2024-06-03 11:49:13 +01:00
} else {
const endCardPhase = new EndCardPhase ( this . scene ) ;
this . scene . unshiftPhase ( endCardPhase ) ;
clear ( endCardPhase ) ;
}
2024-05-23 16:03:10 +01:00
} else {
2024-05-17 02:41:38 +01:00
clear ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-01-01 03:49:50 +00:00
} ) ;
2023-04-22 03:59:09 +01:00
} ) ;
2024-05-10 23:12:33 +01:00
} ;
2024-05-14 20:42:30 +01:00
/ * A d d e d a l o c a l c h e c k t o s e e i f t h e g a m e i s r u n n i n g o f f l i n e o n v i c t o r y
If Online , execute apiFetch as intended
If Offline , execute offlineNewClear ( ) , a localStorage implementation of newClear daily run checks * /
2024-05-10 23:12:33 +01:00
if ( this . victory ) {
2024-05-14 20:42:30 +01:00
if ( ! Utils . isLocal ) {
2024-06-16 03:06:40 +01:00
Utils . apiFetch ( ` savedata/session/newclear?slot= ${ this . scene . sessionSlotId } &clientSessionId= ${ clientSessionId } ` , true ) . then ( response = > response . json ( ) )
2024-05-23 16:03:10 +01:00
. then ( newClear = > doGameOver ( newClear ) ) ;
2024-05-14 20:42:30 +01:00
} else {
this . scene . gameData . offlineNewClear ( this . scene ) . then ( result = > {
doGameOver ( result ) ;
} ) ;
}
2024-05-23 16:03:10 +01:00
} else {
2024-05-10 23:12:33 +01:00
doGameOver ( false ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-22 03:59:09 +01:00
}
2023-04-29 06:40:24 +01:00
2024-02-22 21:21:34 +00:00
handleUnlocks ( ) : void {
2024-03-26 01:09:32 +00:00
if ( this . victory && this . scene . gameMode . isClassic ) {
2024-05-23 16:03:10 +01:00
if ( ! this . scene . gameData . unlocks [ Unlockables . ENDLESS_MODE ] ) {
2023-06-05 16:39:49 +01:00
this . scene . unshiftPhase ( new UnlockPhase ( this . scene , Unlockables . ENDLESS_MODE ) ) ;
2024-05-23 16:03:10 +01:00
}
if ( this . scene . getParty ( ) . filter ( p = > p . fusionSpecies ) . length && ! this . scene . gameData . unlocks [ Unlockables . SPLICED_ENDLESS_MODE ] ) {
2023-11-04 23:46:48 +00:00
this . scene . unshiftPhase ( new UnlockPhase ( this . scene , Unlockables . SPLICED_ENDLESS_MODE ) ) ;
2024-05-23 16:03:10 +01:00
}
if ( ! this . scene . gameData . unlocks [ Unlockables . MINI_BLACK_HOLE ] ) {
2023-06-05 16:39:49 +01:00
this . scene . unshiftPhase ( new UnlockPhase ( this . scene , Unlockables . MINI_BLACK_HOLE ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-06-05 16:39:49 +01:00
}
2023-04-29 06:40:24 +01:00
}
2024-05-07 05:44:41 +01:00
awardRibbon ( pokemon : Pokemon , forStarter : boolean = false ) : void {
2024-05-23 16:03:10 +01:00
const speciesId = getPokemonSpecies ( pokemon . species . speciesId ) ;
2024-05-07 05:44:41 +01:00
const speciesRibbonCount = this . scene . gameData . incrementRibbonCount ( speciesId , forStarter ) ;
// first time classic win, award voucher
if ( speciesRibbonCount === 1 ) {
this . firstRibbons . push ( getPokemonSpecies ( pokemon . species . getRootSpeciesId ( forStarter ) ) ) ;
}
}
2023-04-29 06:40:24 +01:00
}
2024-05-17 02:41:38 +01:00
export class EndCardPhase extends Phase {
public endCard : Phaser.GameObjects.Image ;
public text : Phaser.GameObjects.Text ;
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start ( ) : void {
super . start ( ) ;
this . scene . ui . getMessageHandler ( ) . bg . setVisible ( false ) ;
this . scene . ui . getMessageHandler ( ) . nameBoxContainer . setVisible ( false ) ;
2024-05-23 16:03:10 +01:00
this . endCard = this . scene . add . image ( 0 , 0 , ` end_ ${ this . scene . gameData . gender === PlayerGender . FEMALE ? "f" : "m" } ` ) ;
2024-05-17 02:41:38 +01:00
this . endCard . setOrigin ( 0 ) ;
this . endCard . setScale ( 0.5 ) ;
this . scene . field . add ( this . endCard ) ;
2024-05-23 16:03:10 +01:00
this . text = addTextObject ( this . scene , this . scene . game . canvas . width / 12 , ( this . scene . game . canvas . height / 6 ) - 16 , "Congratulations!" , TextStyle . SUMMARY , { fontSize : "128px" } ) ;
2024-05-17 02:41:38 +01:00
this . text . setOrigin ( 0.5 ) ;
this . scene . field . add ( this . text ) ;
this . scene . ui . clearText ( ) ;
2024-05-24 00:45:04 +01:00
2024-05-17 02:41:38 +01:00
this . scene . ui . fadeIn ( 1000 ) . then ( ( ) = > {
2024-05-24 00:45:04 +01:00
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( "" , null , ( ) = > {
2024-05-17 02:41:38 +01:00
this . scene . ui . getMessageHandler ( ) . bg . setVisible ( true ) ;
this . end ( ) ;
} , null , true ) ;
} ) ;
}
}
2024-02-22 04:57:49 +00:00
export class UnlockPhase extends Phase {
2023-04-29 06:40:24 +01:00
private unlockable : Unlockables ;
constructor ( scene : BattleScene , unlockable : Unlockables ) {
super ( scene ) ;
this . unlockable = unlockable ;
}
start ( ) : void {
this . scene . time . delayedCall ( 2000 , ( ) = > {
this . scene . gameData . unlocks [ this . unlockable ] = true ;
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "level_up_fanfare" ) ;
2024-05-10 23:12:33 +01:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-05-17 02:41:38 +01:00
this . scene . ui . showText ( ` ${ getUnlockableName ( this . unlockable ) } \ nhas been unlocked. ` , null , ( ) = > {
this . scene . time . delayedCall ( 1500 , ( ) = > this . scene . arenaBg . setVisible ( true ) ) ;
this . end ( ) ;
} , null , true , 1500 ) ;
2024-05-10 23:12:33 +01:00
} ) ;
}
}
export class PostGameOverPhase extends Phase {
2024-05-17 02:41:38 +01:00
private endCardPhase : EndCardPhase ;
constructor ( scene : BattleScene , endCardPhase : EndCardPhase ) {
2024-05-10 23:12:33 +01:00
super ( scene ) ;
2024-05-17 02:41:38 +01:00
this . endCardPhase = endCardPhase ;
2024-05-10 23:12:33 +01:00
}
start() {
super . start ( ) ;
2024-05-17 02:41:38 +01:00
const saveAndReset = ( ) = > {
this . scene . gameData . saveAll ( this . scene , true , true , true ) . then ( success = > {
2024-05-23 16:03:10 +01:00
if ( ! success ) {
2024-05-10 23:12:33 +01:00
return this . scene . reset ( true ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-17 02:41:38 +01:00
this . scene . gameData . tryClearSession ( this . scene , this . scene . sessionSlotId ) . then ( ( success : boolean | [ boolean , boolean ] ) = > {
2024-05-23 16:03:10 +01:00
if ( ! success [ 0 ] ) {
2024-05-17 02:41:38 +01:00
return this . scene . reset ( true ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-17 02:41:38 +01:00
this . scene . reset ( ) ;
this . scene . unshiftPhase ( new TitlePhase ( this . scene ) ) ;
this . end ( ) ;
} ) ;
2023-04-29 06:40:24 +01:00
} ) ;
2024-05-17 02:41:38 +01:00
} ;
if ( this . endCardPhase ) {
this . scene . ui . fadeOut ( 500 ) . then ( ( ) = > {
this . scene . ui . getMessageHandler ( ) . bg . setVisible ( true ) ;
this . endCardPhase . endCard . destroy ( ) ;
this . endCardPhase . text . destroy ( ) ;
saveAndReset ( ) ;
} ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-05-17 02:41:38 +01:00
saveAndReset ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-29 06:40:24 +01:00
}
2023-04-22 03:59:09 +01:00
}
2023-04-10 19:12:01 +01:00
export class SwitchPhase extends BattlePhase {
2023-05-18 16:11:06 +01:00
protected fieldIndex : integer ;
2023-04-10 19:12:01 +01:00
private isModal : boolean ;
private doReturn : boolean ;
2023-05-18 16:11:06 +01:00
constructor ( scene : BattleScene , fieldIndex : integer , isModal : boolean , doReturn : boolean ) {
2023-04-10 19:12:01 +01:00
super ( scene ) ;
2023-05-18 16:11:06 +01:00
this . fieldIndex = fieldIndex ;
2023-04-10 19:12:01 +01:00
this . isModal = isModal ;
this . doReturn = doReturn ;
}
start() {
super . start ( ) ;
2024-02-20 19:39:25 +00:00
// Skip modal switch if impossible
2024-06-09 13:37:33 +01:00
if ( this . isModal && ! this . scene . getParty ( ) . filter ( p = > p . isAllowedInBattle ( ) && ! p . isActive ( true ) ) . length ) {
2024-02-20 19:39:25 +00:00
return super . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-02-20 19:39:25 +00:00
2024-05-20 00:02:17 +01:00
// Check if there is any space still in field
2024-06-09 13:37:33 +01:00
if ( this . isModal && this . scene . getPlayerField ( ) . filter ( p = > p . isAllowedInBattle ( ) && p . isActive ( true ) ) . length >= this . scene . currentBattle . getBattlerCount ( ) ) {
2024-05-20 00:02:17 +01:00
return super . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-20 00:02:17 +01:00
2024-06-08 06:07:23 +01:00
// Override field index to 0 in case of double battle where 2/3 remaining legal party members fainted at once
const fieldIndex = this . scene . currentBattle . getBattlerCount ( ) === 1 || this . scene . getParty ( ) . filter ( p = > p . isAllowedInBattle ( ) ) . length > 1 ? this . fieldIndex : 0 ;
2023-11-25 20:53:38 +00:00
this . scene . ui . setMode ( Mode . PARTY , this . isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH , fieldIndex , ( slotIndex : integer , option : PartyOption ) = > {
2024-05-23 16:03:10 +01:00
if ( slotIndex >= this . scene . currentBattle . getBattlerCount ( ) && slotIndex < 6 ) {
2023-11-25 20:53:38 +00:00
this . scene . unshiftPhase ( new SwitchSummonPhase ( this . scene , fieldIndex , slotIndex , this . doReturn , option === PartyOption . PASS_BATON ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > super . end ( ) ) ;
} , PartyUiHandler . FilterNonFainted ) ;
}
}
2023-10-07 21:08:33 +01:00
export class ExpPhase extends PlayerPartyMemberPokemonPhase {
2023-04-10 19:12:01 +01:00
private expValue : number ;
constructor ( scene : BattleScene , partyMemberIndex : integer , expValue : number ) {
super ( scene , partyMemberIndex ) ;
this . expValue = expValue ;
}
start() {
super . start ( ) ;
const pokemon = this . getPokemon ( ) ;
2024-05-23 16:03:10 +01:00
const exp = new Utils . NumberHolder ( this . expValue ) ;
2023-04-21 00:44:56 +01:00
this . scene . applyModifiers ( ExpBoosterModifier , true , exp ) ;
2023-04-10 19:12:01 +01:00
exp . value = Math . floor ( exp . value ) ;
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:expGain" , { pokemonName : pokemon.name , exp : exp.value } ) , null , ( ) = > {
2023-04-10 19:12:01 +01:00
const lastLevel = pokemon . level ;
pokemon . addExp ( exp . value ) ;
2024-05-23 16:03:10 +01:00
const newLevel = pokemon . level ;
if ( newLevel > lastLevel ) {
2023-04-10 19:12:01 +01:00
this . scene . unshiftPhase ( new LevelUpPhase ( this . scene , this . partyMemberIndex , lastLevel , newLevel ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
pokemon . updateInfo ( ) . then ( ( ) = > this . end ( ) ) ;
} , null , true ) ;
}
2023-10-04 22:24:28 +01:00
}
2023-10-07 21:08:33 +01:00
export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase {
2023-10-04 22:24:28 +01:00
private expValue : number ;
constructor ( scene : BattleScene , partyMemberIndex : integer , expValue : number ) {
super ( scene , partyMemberIndex ) ;
this . expValue = expValue ;
}
start() {
super . start ( ) ;
const pokemon = this . getPokemon ( ) ;
2024-05-23 16:03:10 +01:00
const exp = new Utils . NumberHolder ( this . expValue ) ;
2023-10-04 22:24:28 +01:00
this . scene . applyModifiers ( ExpBoosterModifier , true , exp ) ;
exp . value = Math . floor ( exp . value ) ;
const lastLevel = pokemon . level ;
pokemon . addExp ( exp . value ) ;
2024-05-23 16:03:10 +01:00
const newLevel = pokemon . level ;
if ( newLevel > lastLevel ) {
2023-10-04 22:24:28 +01:00
this . scene . unshiftPhase ( new LevelUpPhase ( this . scene , this . partyMemberIndex , lastLevel , newLevel ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-04 22:24:28 +01:00
this . scene . unshiftPhase ( new HidePartyExpBarPhase ( this . scene ) ) ;
pokemon . updateInfo ( ) ;
2024-06-07 18:14:52 +01:00
if ( this . scene . expParty === ExpNotification . SKIP ) {
2024-05-23 16:03:10 +01:00
this . end ( ) ;
2024-06-07 18:14:52 +01:00
} else if ( this . scene . expParty === ExpNotification . ONLY_LEVEL_UP ) {
2024-05-10 14:54:14 +01:00
if ( newLevel > lastLevel ) { // this means if we level up
// instead of displaying the exp gain in the small frame, we display the new level
// we use the same method for mode 0 & 1, by giving a parameter saying to display the exp or the level
2024-06-07 18:14:52 +01:00
this . scene . partyExpBar . showPokemonExp ( pokemon , exp . value , this . scene . expParty === ExpNotification . ONLY_LEVEL_UP , newLevel ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
setTimeout ( ( ) = > this . end ( ) , 800 / Math . pow ( 2 , this . scene . expGainsSpeed ) ) ;
2024-05-10 14:54:14 +01:00
} ) ;
} else {
this . end ( ) ;
}
} else if ( this . scene . expGainsSpeed < 3 ) {
2024-06-07 18:14:52 +01:00
this . scene . partyExpBar . showPokemonExp ( pokemon , exp . value , false , newLevel ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
setTimeout ( ( ) = > this . end ( ) , 500 / Math . pow ( 2 , this . scene . expGainsSpeed ) ) ;
2024-04-18 05:24:47 +01:00
} ) ;
} else {
this . end ( ) ;
}
2023-10-04 22:24:28 +01:00
}
}
export class HidePartyExpBarPhase extends BattlePhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
2023-04-10 19:12:01 +01:00
2023-10-04 22:24:28 +01:00
start() {
super . start ( ) ;
2023-04-10 19:12:01 +01:00
2023-10-04 22:24:28 +01:00
this . scene . partyExpBar . hide ( ) . then ( ( ) = > this . end ( ) ) ;
}
2023-04-10 19:12:01 +01:00
}
2023-10-07 21:08:33 +01:00
export class LevelUpPhase extends PlayerPartyMemberPokemonPhase {
2023-04-10 19:12:01 +01:00
private lastLevel : integer ;
private level : integer ;
constructor ( scene : BattleScene , partyMemberIndex : integer , lastLevel : integer , level : integer ) {
super ( scene , partyMemberIndex ) ;
this . lastLevel = lastLevel ;
this . level = level ;
2024-05-10 14:54:14 +01:00
this . scene = scene ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
2024-05-23 16:03:10 +01:00
if ( this . level > this . scene . gameData . gameStats . highestLevel ) {
2024-01-11 17:26:32 +00:00
this . scene . gameData . gameStats . highestLevel = this . level ;
2024-05-23 16:03:10 +01:00
}
2024-01-11 17:26:32 +00:00
2023-11-12 05:31:40 +00:00
this . scene . validateAchvs ( LevelAchv , new Utils . IntegerHolder ( this . level ) ) ;
2023-04-10 19:12:01 +01:00
const pokemon = this . getPokemon ( ) ;
const prevStats = pokemon . stats . slice ( 0 ) ;
pokemon . calculateStats ( ) ;
pokemon . updateInfo ( ) ;
2024-06-07 18:14:52 +01:00
if ( this . scene . expParty === ExpNotification . DEFAULT ) {
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "level_up_fanfare" ) ;
this . scene . ui . showText ( i18next . t ( "battle:levelUp" , { pokemonName : this.getPokemon ( ) . name , level : this.level } ) , null , ( ) = > this . scene . ui . getMessageHandler ( ) . promptLevelUpStats ( this . partyMemberIndex , prevStats , false ) . then ( ( ) = > this . end ( ) ) , null , true ) ;
2024-06-07 18:14:52 +01:00
} else if ( this . scene . expParty === ExpNotification . SKIP ) {
2024-05-10 14:54:14 +01:00
this . end ( ) ;
2024-06-07 18:14:52 +01:00
} else {
2024-05-10 14:54:14 +01:00
// we still want to display the stats if activated
this . scene . ui . getMessageHandler ( ) . promptLevelUpStats ( this . partyMemberIndex , prevStats , false ) . then ( ( ) = > this . end ( ) ) ;
}
2023-04-19 19:07:38 +01:00
if ( this . level <= 100 ) {
const levelMoves = this . getPokemon ( ) . getLevelMoves ( this . lastLevel + 1 ) ;
2024-05-23 16:03:10 +01:00
for ( const lm of levelMoves ) {
2024-03-01 21:21:28 +00:00
this . scene . unshiftPhase ( new LearnMovePhase ( this . scene , this . partyMemberIndex , lm [ 1 ] ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-19 19:07:38 +01:00
}
2023-12-14 17:55:11 +00:00
if ( ! pokemon . pauseEvolutions ) {
const evolution = pokemon . getEvolution ( ) ;
2024-05-23 16:03:10 +01:00
if ( evolution ) {
2024-01-10 04:34:43 +00:00
this . scene . unshiftPhase ( new EvolutionPhase ( this . scene , pokemon as PlayerPokemon , evolution , this . lastLevel ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-12-14 17:55:11 +00:00
}
2023-04-10 19:12:01 +01:00
}
}
2023-10-07 21:08:33 +01:00
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
2023-04-10 19:12:01 +01:00
private moveId : Moves ;
constructor ( scene : BattleScene , partyMemberIndex : integer , moveId : Moves ) {
super ( scene , partyMemberIndex ) ;
this . moveId = moveId ;
}
start() {
super . start ( ) ;
const pokemon = this . getPokemon ( ) ;
2023-04-21 02:32:48 +01:00
const move = allMoves [ this . moveId ] ;
2023-04-10 19:12:01 +01:00
2023-05-06 05:42:01 +01:00
const existingMoveIndex = pokemon . getMoveset ( ) . findIndex ( m = > m ? . moveId === move . id ) ;
2023-04-10 19:12:01 +01:00
2024-05-23 16:03:10 +01:00
if ( existingMoveIndex > - 1 ) {
2023-12-07 22:43:56 +00:00
return this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
2023-05-06 05:42:01 +01:00
const emptyMoveIndex = pokemon . getMoveset ( ) . length < 4
? pokemon . getMoveset ( ) . length
: pokemon . getMoveset ( ) . findIndex ( m = > m === null ) ;
2023-04-10 19:12:01 +01:00
const messageMode = this . scene . ui . getHandler ( ) instanceof EvolutionSceneHandler
? Mode . EVOLUTION_SCENE
: Mode . MESSAGE ;
if ( emptyMoveIndex > - 1 ) {
2023-05-06 05:42:01 +01:00
pokemon . setMove ( emptyMoveIndex , this . moveId ) ;
2024-04-24 04:28:55 +01:00
initMoveAnim ( this . scene , this . moveId ) . then ( ( ) = > {
2023-04-10 19:12:01 +01:00
loadMoveAnimAssets ( this . scene , [ this . moveId ] , true )
. then ( ( ) = > {
this . scene . ui . setMode ( messageMode ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "level_up_fanfare" ) ;
this . scene . ui . showText ( i18next . t ( "battle:learnMove" , { pokemonName : pokemon.name , moveName : move.name } ) , null , ( ) = > {
2024-01-10 04:34:43 +00:00
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeMoveLearnedTrigger , true ) ;
this . end ( ) ;
} , messageMode === Mode . EVOLUTION_SCENE ? 1000 : null , true ) ;
2023-04-10 19:12:01 +01:00
} ) ;
} ) ;
2024-05-23 16:03:10 +01:00
} ) ;
2023-04-10 19:12:01 +01:00
} else {
this . scene . ui . setMode ( messageMode ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:learnMovePrompt" , { pokemonName : pokemon.name , moveName : move.name } ) , null , ( ) = > {
this . scene . ui . showText ( i18next . t ( "battle:learnMoveLimitReached" , { pokemonName : pokemon.name } ) , null , ( ) = > {
this . scene . ui . showText ( i18next . t ( "battle:learnMoveReplaceQuestion" , { moveName : move.name } ) , null , ( ) = > {
2023-04-10 19:12:01 +01:00
const noHandler = ( ) = > {
this . scene . ui . setMode ( messageMode ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:learnMoveStopTeaching" , { moveName : move.name } ) , null , ( ) = > {
2023-04-10 19:12:01 +01:00
this . scene . ui . setModeWithoutClear ( Mode . CONFIRM , ( ) = > {
this . scene . ui . setMode ( messageMode ) ;
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:learnMoveNotLearned" , { pokemonName : pokemon.name , moveName : move.name } ) , null , ( ) = > this . end ( ) , null , true ) ;
2023-04-10 19:12:01 +01:00
} , ( ) = > {
this . scene . ui . setMode ( messageMode ) ;
this . scene . unshiftPhase ( new LearnMovePhase ( this . scene , this . partyMemberIndex , this . moveId ) ) ;
this . end ( ) ;
} ) ;
} ) ;
} ) ;
} ;
this . scene . ui . setModeWithoutClear ( Mode . CONFIRM , ( ) = > {
this . scene . ui . setMode ( messageMode ) ;
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:learnMoveForgetQuestion" ) , null , ( ) = > {
2023-04-10 19:12:01 +01:00
this . scene . ui . setModeWithoutClear ( Mode . SUMMARY , this . getPokemon ( ) , SummaryUiMode . LEARN_MOVE , move , ( moveIndex : integer ) = > {
if ( moveIndex === 4 ) {
noHandler ( ) ;
return ;
}
this . scene . ui . setMode ( messageMode ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:countdownPoof" ) , null , ( ) = > {
this . scene . ui . showText ( i18next . t ( "battle:learnMoveForgetSuccess" , { pokemonName : pokemon.name , moveName : pokemon.moveset [ moveIndex ] . getName ( ) } ) , null , ( ) = > {
this . scene . ui . showText ( i18next . t ( "battle:learnMoveAnd" ) , null , ( ) = > {
2023-05-06 05:42:01 +01:00
pokemon . setMove ( moveIndex , Moves . NONE ) ;
2023-04-10 19:12:01 +01:00
this . scene . unshiftPhase ( new LearnMovePhase ( this . scene , this . partyMemberIndex , this . moveId ) ) ;
this . end ( ) ;
} , null , true ) ;
} , null , true ) ;
} , null , true ) ;
} ) ;
} ) ;
} , null , true ) ;
} , noHandler ) ;
} ) ;
} , null , true ) ;
} , null , true ) ;
} ) ;
}
}
}
2023-04-14 06:08:44 +01:00
export class PokemonHealPhase extends CommonAnimPhase {
private hpHealed : integer ;
2023-04-16 05:29:55 +01:00
private message : string ;
private showFullHpMessage : boolean ;
private skipAnim : boolean ;
2023-06-06 15:14:53 +01:00
private revive : boolean ;
2024-03-11 17:18:49 +00:00
private healStatus : boolean ;
2024-04-02 06:44:38 +01:00
private preventFullHeal : boolean ;
2023-04-14 06:08:44 +01:00
2024-04-02 06:44:38 +01:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , hpHealed : integer , message : string , showFullHpMessage : boolean , skipAnim : boolean = false , revive : boolean = false , healStatus : boolean = false , preventFullHeal : boolean = false ) {
2023-05-18 16:11:06 +01:00
super ( scene , battlerIndex , undefined , CommonAnim . HEALTH_UP ) ;
2023-04-14 06:08:44 +01:00
this . hpHealed = hpHealed ;
2023-04-16 05:29:55 +01:00
this . message = message ;
this . showFullHpMessage = showFullHpMessage ;
2024-03-11 17:18:49 +00:00
this . skipAnim = skipAnim ;
this . revive = revive ;
this . healStatus = healStatus ;
2024-04-02 06:44:38 +01:00
this . preventFullHeal = preventFullHeal ;
2023-04-16 05:29:55 +01:00
}
start() {
2024-05-23 16:03:10 +01:00
if ( ! this . skipAnim && ( this . revive || this . getPokemon ( ) . hp ) && this . getPokemon ( ) . getHpRatio ( ) < 1 ) {
2023-04-16 05:29:55 +01:00
super . start ( ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-04-16 05:29:55 +01:00
this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-14 06:08:44 +01:00
}
end() {
const pokemon = this . getPokemon ( ) ;
2024-05-24 00:45:04 +01:00
2023-06-06 15:14:53 +01:00
if ( ! pokemon . isOnField ( ) || ( ! this . revive && ! pokemon . isActive ( ) ) ) {
2023-04-23 06:03:09 +01:00
super . end ( ) ;
return ;
}
2023-04-14 06:08:44 +01:00
2023-04-16 05:29:55 +01:00
const fullHp = pokemon . getHpRatio ( ) >= 1 ;
2024-03-11 17:18:49 +00:00
const hasMessage = ! ! this . message ;
2024-06-01 20:53:32 +01:00
const healOrDamage = ( ! fullHp || this . hpHealed < 0 ) ;
2024-03-11 17:18:49 +00:00
let lastStatusEffect = StatusEffect . NONE ;
2024-06-01 20:53:32 +01:00
if ( healOrDamage ) {
2023-04-20 20:46:05 +01:00
const hpRestoreMultiplier = new Utils . IntegerHolder ( 1 ) ;
2024-05-23 16:03:10 +01:00
if ( ! this . revive ) {
2023-06-06 15:14:53 +01:00
this . scene . applyModifiers ( HealingBoosterModifier , this . player , hpRestoreMultiplier ) ;
2024-05-23 16:03:10 +01:00
}
2024-02-20 16:42:05 +00:00
const healAmount = new Utils . NumberHolder ( Math . floor ( this . hpHealed * hpRestoreMultiplier . value ) ) ;
2024-04-22 04:02:10 +01:00
if ( healAmount . value < 0 ) {
2024-05-25 03:57:28 +01:00
pokemon . damageAndUpdate ( healAmount . value * - 1 , HitResult . HEAL as DamageResult ) ;
2024-04-22 04:02:10 +01:00
healAmount . value = 0 ;
}
2024-04-02 06:44:38 +01:00
// Prevent healing to full if specified (in case of healing tokens so Sturdy doesn't cause a softlock)
2024-05-23 16:03:10 +01:00
if ( this . preventFullHeal && pokemon . hp + healAmount . value >= pokemon . getMaxHp ( ) ) {
2024-04-02 06:44:38 +01:00
healAmount . value = ( pokemon . getMaxHp ( ) - pokemon . hp ) - 1 ;
2024-05-23 16:03:10 +01:00
}
2024-01-08 04:17:24 +00:00
healAmount . value = pokemon . heal ( healAmount . value ) ;
2024-05-23 16:03:10 +01:00
if ( healAmount . value ) {
2024-03-01 14:35:36 +00:00
this . scene . damageNumberHandler . add ( pokemon , healAmount . value , HitResult . HEAL ) ;
2024-05-23 16:03:10 +01:00
}
2024-02-19 03:21:57 +00:00
if ( pokemon . isPlayer ( ) ) {
this . scene . validateAchvs ( HealAchv , healAmount ) ;
2024-05-23 16:03:10 +01:00
if ( healAmount . value > this . scene . gameData . gameStats . highestHeal ) {
2024-02-19 03:21:57 +00:00
this . scene . gameData . gameStats . highestHeal = healAmount . value ;
2024-05-23 16:03:10 +01:00
}
2024-02-19 03:21:57 +00:00
}
2024-03-11 17:18:49 +00:00
if ( this . healStatus && ! this . revive && pokemon . status ) {
lastStatusEffect = pokemon . status . effect ;
pokemon . resetStatus ( ) ;
}
2023-04-16 05:29:55 +01:00
pokemon . updateInfo ( ) . then ( ( ) = > super . end ( ) ) ;
2024-03-11 22:13:07 +00:00
} else if ( this . healStatus && ! this . revive && pokemon . status ) {
2024-05-23 16:03:10 +01:00
lastStatusEffect = pokemon . status . effect ;
pokemon . resetStatus ( ) ;
pokemon . updateInfo ( ) . then ( ( ) = > super . end ( ) ) ;
} else if ( this . showFullHpMessage ) {
this . message = getPokemonMessage ( pokemon , "'s\nHP is full!" ) ;
}
2023-04-16 05:29:55 +01:00
2024-05-23 16:03:10 +01:00
if ( this . message ) {
2023-04-22 00:30:04 +01:00
this . scene . queueMessage ( this . message ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-16 05:29:55 +01:00
2024-05-23 16:03:10 +01:00
if ( this . healStatus && lastStatusEffect && ! hasMessage ) {
2024-03-11 17:18:49 +00:00
this . scene . queueMessage ( getPokemonMessage ( pokemon , getStatusEffectHealText ( lastStatusEffect ) ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-11 17:18:49 +00:00
2024-06-01 20:53:32 +01:00
if ( ! healOrDamage && ! lastStatusEffect ) {
2023-04-16 05:29:55 +01:00
super . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-14 06:08:44 +01:00
}
}
2023-05-18 16:11:06 +01:00
export class AttemptCapturePhase extends PokemonPhase {
2023-04-10 19:12:01 +01:00
private pokeballType : PokeballType ;
private pokeball : Phaser.GameObjects.Sprite ;
private originalY : number ;
2023-05-18 16:11:06 +01:00
constructor ( scene : BattleScene , targetIndex : integer , pokeballType : PokeballType ) {
super ( scene , BattlerIndex . ENEMY + targetIndex ) ;
2023-04-10 19:12:01 +01:00
this . pokeballType = pokeballType ;
}
start() {
super . start ( ) ;
2024-01-08 04:17:24 +00:00
const pokemon = this . getPokemon ( ) as EnemyPokemon ;
2023-05-18 16:11:06 +01:00
2024-05-23 16:03:10 +01:00
if ( ! pokemon ? . hp ) {
2023-12-07 22:43:56 +00:00
return this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-05-18 16:11:06 +01:00
2023-04-10 19:12:01 +01:00
this . scene . pokeballCounts [ this . pokeballType ] -- ;
this . originalY = pokemon . y ;
2024-02-20 15:16:09 +00:00
const _3m = 3 * pokemon . getMaxHp ( ) ;
2023-04-10 19:12:01 +01:00
const _2h = 2 * pokemon . hp ;
const catchRate = pokemon . species . catchRate ;
const pokeballMultiplier = getPokeballCatchMultiplier ( this . pokeballType ) ;
2023-04-19 03:09:37 +01:00
const statusMultiplier = pokemon . status ? getStatusEffectCatchRateMultiplier ( pokemon . status . effect ) : 1 ;
2023-04-10 19:12:01 +01:00
const x = Math . round ( ( ( ( _3m - _2h ) * catchRate * pokeballMultiplier ) / _3m ) * statusMultiplier ) ;
const y = Math . round ( 65536 / Math . sqrt ( Math . sqrt ( 255 / x ) ) ) ;
2023-05-18 16:11:06 +01:00
const fpOffset = pokemon . getFieldPositionOffset ( ) ;
2023-04-10 19:12:01 +01:00
const pokeballAtlasKey = getPokeballAtlasKey ( this . pokeballType ) ;
2024-05-23 16:03:10 +01:00
this . pokeball = this . scene . addFieldSprite ( 16 , 80 , "pb" , pokeballAtlasKey ) ;
2023-04-10 19:12:01 +01:00
this . pokeball . setOrigin ( 0.5 , 0.625 ) ;
this . scene . field . add ( this . pokeball ) ;
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "pb_throw" ) ;
2023-04-10 19:12:01 +01:00
this . scene . time . delayedCall ( 300 , ( ) = > {
2023-06-06 15:14:53 +01:00
this . scene . field . moveBelow ( this . pokeball as Phaser . GameObjects . GameObject , pokemon ) ;
2023-04-10 19:12:01 +01:00
} ) ;
2024-01-05 04:57:21 +00:00
2023-04-10 19:12:01 +01:00
this . scene . tweens . add ( {
targets : this.pokeball ,
2024-05-23 16:03:10 +01:00
x : { value : 236 + fpOffset [ 0 ] , ease : "Linear" } ,
y : { value : 16 + fpOffset [ 1 ] , ease : "Cubic.easeOut" } ,
2023-04-10 19:12:01 +01:00
duration : 500 ,
onComplete : ( ) = > {
2024-05-23 16:03:10 +01:00
this . pokeball . setTexture ( "pb" , ` ${ pokeballAtlasKey } _opening ` ) ;
this . scene . time . delayedCall ( 17 , ( ) = > this . pokeball . setTexture ( "pb" , ` ${ pokeballAtlasKey } _open ` ) ) ;
this . scene . playSound ( "pb_rel" ) ;
2023-04-10 19:12:01 +01:00
pokemon . tint ( getPokeballTintColor ( this . pokeballType ) ) ;
2024-01-05 04:57:21 +00:00
addPokeballOpenParticles ( this . scene , this . pokeball . x , this . pokeball . y , this . pokeballType ) ;
2023-04-10 19:12:01 +01:00
this . scene . tweens . add ( {
targets : pokemon ,
2024-01-05 04:57:21 +00:00
duration : 500 ,
2024-05-23 16:03:10 +01:00
ease : "Sine.easeIn" ,
2023-04-10 19:12:01 +01:00
scale : 0.25 ,
y : 20 ,
onComplete : ( ) = > {
2024-05-23 16:03:10 +01:00
this . pokeball . setTexture ( "pb" , ` ${ pokeballAtlasKey } _opening ` ) ;
2023-04-10 19:12:01 +01:00
pokemon . setVisible ( false ) ;
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "pb_catch" ) ;
this . scene . time . delayedCall ( 17 , ( ) = > this . pokeball . setTexture ( "pb" , ` ${ pokeballAtlasKey } ` ) ) ;
2023-04-10 19:12:01 +01:00
2024-01-05 00:56:26 +00:00
const doShake = ( ) = > {
2023-04-10 19:12:01 +01:00
let shakeCount = 0 ;
const pbX = this . pokeball . x ;
const shakeCounter = this . scene . tweens . addCounter ( {
from : 0 ,
to : 1 ,
repeat : 4 ,
yoyo : true ,
2024-05-23 16:03:10 +01:00
ease : "Cubic.easeOut" ,
2023-04-10 19:12:01 +01:00
duration : 250 ,
repeatDelay : 500 ,
onUpdate : t = > {
if ( shakeCount && shakeCount < 4 ) {
const value = t . getValue ( ) ;
const directionMultiplier = shakeCount % 2 === 1 ? 1 : - 1 ;
this . pokeball . setX ( pbX + value * 4 * directionMultiplier ) ;
this . pokeball . setAngle ( value * 27.5 * directionMultiplier ) ;
}
} ,
onRepeat : ( ) = > {
2023-04-26 21:07:29 +01:00
if ( ! pokemon . species . isObtainable ( ) ) {
shakeCounter . stop ( ) ;
this . failCatch ( shakeCount ) ;
} else if ( shakeCount ++ < 3 ) {
2024-05-23 16:03:10 +01:00
if ( pokeballMultiplier === - 1 || pokemon . randSeedInt ( 65536 ) < y ) {
this . scene . playSound ( "pb_move" ) ;
} else {
2023-04-10 19:12:01 +01:00
shakeCounter . stop ( ) ;
2023-04-11 06:31:18 +01:00
this . failCatch ( shakeCount ) ;
2023-04-10 19:12:01 +01:00
}
2024-01-05 04:57:21 +00:00
} else {
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "pb_lock" ) ;
2024-01-05 04:57:21 +00:00
addPokeballCaptureStars ( this . scene , this . pokeball ) ;
2024-05-24 00:45:04 +01:00
2024-05-23 16:03:10 +01:00
const pbTint = this . scene . add . sprite ( this . pokeball . x , this . pokeball . y , "pb" , "pb" ) ;
2024-01-05 04:57:21 +00:00
pbTint . setOrigin ( this . pokeball . originX , this . pokeball . originY ) ;
pbTint . setTintFill ( 0 ) ;
pbTint . setAlpha ( 0 ) ;
this . scene . field . add ( pbTint ) ;
this . scene . tweens . add ( {
targets : pbTint ,
alpha : 0.375 ,
duration : 200 ,
2024-05-23 16:03:10 +01:00
easing : "Sine.easeOut" ,
2024-01-05 04:57:21 +00:00
onComplete : ( ) = > {
this . scene . tweens . add ( {
targets : pbTint ,
alpha : 0 ,
duration : 200 ,
2024-05-23 16:03:10 +01:00
easing : "Sine.easeIn" ,
2024-01-05 04:57:21 +00:00
onComplete : ( ) = > pbTint . destroy ( )
} ) ;
}
} ) ;
}
2023-04-10 19:12:01 +01:00
} ,
2023-04-21 00:44:56 +01:00
onComplete : ( ) = > this . catch ( )
2023-04-10 19:12:01 +01:00
} ) ;
2024-01-05 00:56:26 +00:00
} ;
2023-04-10 19:12:01 +01:00
this . scene . time . delayedCall ( 250 , ( ) = > doPokeballBounceAnim ( this . scene , this . pokeball , 16 , 72 , 350 , doShake ) ) ;
}
} ) ;
}
} ) ;
}
2023-04-11 06:31:18 +01:00
failCatch ( shakeCount : integer ) {
2023-05-18 16:11:06 +01:00
const pokemon = this . getPokemon ( ) ;
2023-04-10 19:12:01 +01:00
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "pb_rel" ) ;
2023-04-10 19:12:01 +01:00
pokemon . setY ( this . originalY ) ;
2024-05-23 16:03:10 +01:00
if ( pokemon . status ? . effect !== StatusEffect . SLEEP ) {
2024-04-06 02:12:29 +01:00
pokemon . cry ( pokemon . getHpRatio ( ) > 0.25 ? undefined : { rate : 0.85 } ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-10 19:12:01 +01:00
pokemon . tint ( getPokeballTintColor ( this . pokeballType ) ) ;
pokemon . setVisible ( true ) ;
2024-05-23 16:03:10 +01:00
pokemon . untint ( 250 , "Sine.easeOut" ) ;
2023-04-10 19:12:01 +01:00
const pokeballAtlasKey = getPokeballAtlasKey ( this . pokeballType ) ;
2024-05-23 16:03:10 +01:00
this . pokeball . setTexture ( "pb" , ` ${ pokeballAtlasKey } _opening ` ) ;
this . scene . time . delayedCall ( 17 , ( ) = > this . pokeball . setTexture ( "pb" , ` ${ pokeballAtlasKey } _open ` ) ) ;
2023-04-10 19:12:01 +01:00
this . scene . tweens . add ( {
targets : pokemon ,
duration : 250 ,
2024-05-23 16:03:10 +01:00
ease : "Sine.easeOut" ,
2023-04-10 19:12:01 +01:00
scale : 1
} ) ;
2024-05-24 00:45:04 +01:00
2024-05-19 17:13:33 +01:00
this . scene . currentBattle . lastUsedPokeball = this . pokeballType ;
2023-04-10 19:12:01 +01:00
this . removePb ( ) ;
this . end ( ) ;
}
catch ( ) {
2023-05-18 16:11:06 +01:00
const pokemon = this . getPokemon ( ) as EnemyPokemon ;
2023-05-29 17:24:38 +01:00
this . scene . unshiftPhase ( new VictoryPhase ( this . scene , this . battlerIndex ) ) ;
2023-11-12 05:31:40 +00:00
2023-12-06 04:31:34 +00:00
const speciesForm = ! pokemon . fusionSpecies ? pokemon . getSpeciesForm ( ) : pokemon . getFusionSpeciesForm ( ) ;
2023-12-05 22:12:39 +00:00
2024-05-23 16:03:10 +01:00
if ( speciesForm . abilityHidden && ( pokemon . fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex ) === speciesForm . getAbilityCount ( ) - 1 ) {
2023-11-12 05:31:40 +00:00
this . scene . validateAchv ( achvs . HIDDEN_ABILITY ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-12 05:31:40 +00:00
2024-05-23 16:03:10 +01:00
if ( pokemon . species . subLegendary ) {
2024-05-05 16:05:22 +01:00
this . scene . validateAchv ( achvs . CATCH_SUB_LEGENDARY ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-05 16:05:22 +01:00
2024-05-23 16:03:10 +01:00
if ( pokemon . species . legendary ) {
2023-11-12 05:31:40 +00:00
this . scene . validateAchv ( achvs . CATCH_LEGENDARY ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-12 05:31:40 +00:00
2024-05-23 16:03:10 +01:00
if ( pokemon . species . mythical ) {
2023-11-12 05:31:40 +00:00
this . scene . validateAchv ( achvs . CATCH_MYTHICAL ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-12 14:27:50 +00:00
2024-03-08 03:43:15 +00:00
this . scene . pokemonInfoContainer . show ( pokemon , true ) ;
2024-05-01 22:47:32 +01:00
this . scene . gameData . updateSpeciesDexIvs ( pokemon . species . getRootSpeciesId ( true ) , pokemon . ivs ) ;
2024-05-24 00:45:04 +01:00
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:pokemonCaught" , { pokemonName : pokemon.name } ) , null , ( ) = > {
2023-04-10 21:52:27 +01:00
const end = ( ) = > {
2024-03-08 03:43:15 +00:00
this . scene . pokemonInfoContainer . hide ( ) ;
2023-04-10 19:12:01 +01:00
this . removePb ( ) ;
this . end ( ) ;
2023-04-10 21:52:27 +01:00
} ;
2023-07-03 15:59:56 +01:00
const removePokemon = ( ) = > {
2024-03-17 15:36:19 +00:00
this . scene . addFaintedEnemyScore ( pokemon ) ;
2024-02-18 15:53:32 +00:00
this . scene . getPlayerField ( ) . filter ( p = > p . isActive ( true ) ) . forEach ( playerPokemon = > playerPokemon . removeTagsBySourceId ( pokemon . id ) ) ;
2023-07-03 15:59:56 +01:00
pokemon . hp = 0 ;
pokemon . trySetStatus ( StatusEffect . FAINT ) ;
2023-10-28 18:24:57 +01:00
this . scene . clearEnemyHeldItemModifiers ( ) ;
2023-07-03 15:59:56 +01:00
this . scene . field . remove ( pokemon , true ) ;
} ;
2023-04-11 06:31:18 +01:00
const addToParty = ( ) = > {
2024-01-05 00:56:26 +00:00
const newPokemon = pokemon . addToParty ( this . pokeballType ) ;
2023-04-21 00:44:56 +01:00
const modifiers = this . scene . findModifiers ( m = > m instanceof PokemonHeldItemModifier , false ) ;
2024-05-23 16:03:10 +01:00
if ( this . scene . getParty ( ) . filter ( p = > p . isShiny ( ) ) . length === 6 ) {
2023-11-12 14:27:50 +00:00
this . scene . validateAchv ( achvs . SHINY_PARTY ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-01 01:43:22 +00:00
Promise . all ( modifiers . map ( m = > this . scene . addModifier ( m , true ) ) ) . then ( ( ) = > {
this . scene . updateModifiers ( true ) ;
2023-07-03 15:59:56 +01:00
removePokemon ( ) ;
2024-05-23 16:03:10 +01:00
if ( newPokemon ) {
2023-04-21 00:44:56 +01:00
newPokemon . loadAssets ( ) . then ( end ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-04-21 00:44:56 +01:00
end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-21 00:44:56 +01:00
} ) ;
2023-04-11 06:31:18 +01:00
} ;
2023-04-18 18:36:11 +01:00
Promise . all ( [ pokemon . hideInfo ( ) , this . scene . gameData . setPokemonCaught ( pokemon ) ] ) . then ( ( ) = > {
2023-04-18 06:32:26 +01:00
if ( this . scene . getParty ( ) . length === 6 ) {
const promptRelease = ( ) = > {
2024-05-23 19:22:09 +01:00
this . scene . ui . showText ( i18next . t ( "battle:partyFull" , { pokemonName : pokemon.name } ) , null , ( ) = > {
2024-05-21 08:24:03 +01:00
this . scene . pokemonInfoContainer . makeRoomForConfirmUi ( ) ;
2023-04-18 06:32:26 +01:00
this . scene . ui . setMode ( Mode . CONFIRM , ( ) = > {
2023-05-18 16:11:06 +01:00
this . scene . ui . setMode ( Mode . PARTY , PartyUiMode . RELEASE , this . fieldIndex , ( slotIndex : integer , _option : PartyOption ) = > {
2023-04-18 06:32:26 +01:00
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
if ( slotIndex < 6 ) {
2023-04-18 06:32:26 +01:00
addToParty ( ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-04-18 06:32:26 +01:00
promptRelease ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-18 06:32:26 +01:00
} ) ;
2023-04-11 06:31:18 +01:00
} ) ;
2023-04-18 06:32:26 +01:00
} , ( ) = > {
2023-07-03 15:59:56 +01:00
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > {
removePokemon ( ) ;
end ( ) ;
} ) ;
2023-04-11 06:31:18 +01:00
} ) ;
} ) ;
2023-04-18 06:32:26 +01:00
} ;
promptRelease ( ) ;
2024-05-23 16:03:10 +01:00
} else {
2023-04-18 06:32:26 +01:00
addToParty ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-04-18 06:32:26 +01:00
} ) ;
2023-04-10 19:12:01 +01:00
} , 0 , true ) ;
}
removePb() {
this . scene . tweens . add ( {
targets : this.pokeball ,
duration : 250 ,
delay : 250 ,
2024-05-23 16:03:10 +01:00
ease : "Sine.easeIn" ,
2023-04-10 19:12:01 +01:00
alpha : 0 ,
onComplete : ( ) = > this . pokeball . destroy ( )
} ) ;
}
}
2023-05-18 16:11:06 +01:00
export class AttemptRunPhase extends PokemonPhase {
constructor ( scene : BattleScene , fieldIndex : integer ) {
super ( scene , fieldIndex ) ;
2023-05-07 22:05:19 +01:00
}
start() {
super . start ( ) ;
2023-05-18 16:11:06 +01:00
const playerPokemon = this . getPokemon ( ) ;
const enemyField = this . scene . getEnemyField ( ) ;
2023-10-29 05:28:56 +00:00
const enemySpeed = enemyField . reduce ( ( total : integer , enemyPokemon : Pokemon ) = > total + enemyPokemon . getStat ( Stat . SPD ) , 0 ) / enemyField . length ;
2023-05-07 22:05:19 +01:00
2023-12-22 22:08:37 +00:00
const escapeChance = new Utils . IntegerHolder ( ( ( ( playerPokemon . getStat ( Stat . SPD ) * 128 ) / enemySpeed ) + ( 30 * this . scene . currentBattle . escapeAttempts ++ ) ) % 256 ) ;
applyAbAttrs ( RunSuccessAbAttr , playerPokemon , null , escapeChance ) ;
2023-05-07 22:05:19 +01:00
2024-01-03 02:31:59 +00:00
if ( playerPokemon . randSeedInt ( 256 ) < escapeChance . value ) {
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "flee" ) ;
this . scene . queueMessage ( i18next . t ( "battle:runAwaySuccess" ) , null , true , 500 ) ;
2024-05-24 00:45:04 +01:00
2023-05-07 22:05:19 +01:00
this . scene . tweens . add ( {
2023-05-18 16:11:06 +01:00
targets : [ this . scene . arenaEnemy , enemyField ] . flat ( ) ,
2023-05-07 22:05:19 +01:00
alpha : 0 ,
duration : 250 ,
2024-05-23 16:03:10 +01:00
ease : "Sine.easeIn" ,
2023-10-31 18:09:33 +00:00
onComplete : ( ) = > enemyField . forEach ( enemyPokemon = > enemyPokemon . destroy ( ) )
2023-05-07 22:05:19 +01:00
} ) ;
2023-12-26 17:01:56 +00:00
this . scene . clearEnemyHeldItemModifiers ( ) ;
2023-06-01 18:54:52 +01:00
enemyField . forEach ( enemyPokemon = > {
2023-10-31 18:09:33 +00:00
enemyPokemon . hideInfo ( ) . then ( ( ) = > enemyPokemon . destroy ( ) ) ;
2023-06-01 18:54:52 +01:00
enemyPokemon . hp = 0 ;
2023-11-04 04:32:12 +00:00
enemyPokemon . trySetStatus ( StatusEffect . FAINT ) ;
2023-06-01 18:54:52 +01:00
} ) ;
2023-05-07 22:05:19 +01:00
this . scene . pushPhase ( new BattleEndPhase ( this . scene ) ) ;
2023-05-18 16:11:06 +01:00
this . scene . pushPhase ( new NewBattlePhase ( this . scene ) ) ;
2024-05-23 16:03:10 +01:00
} else {
this . scene . queueMessage ( i18next . t ( "battle:runAwayCannotEscape" ) , null , true , 500 ) ;
}
2023-05-07 22:05:19 +01:00
this . end ( ) ;
}
}
2023-04-10 19:12:01 +01:00
export class SelectModifierPhase extends BattlePhase {
2023-11-10 20:51:34 +00:00
private rerollCount : integer ;
2024-04-04 05:38:02 +01:00
private modifierTiers : ModifierTier [ ] ;
2023-11-10 20:51:34 +00:00
2024-04-04 05:38:02 +01:00
constructor ( scene : BattleScene , rerollCount : integer = 0 , modifierTiers? : ModifierTier [ ] ) {
2023-04-10 19:12:01 +01:00
super ( scene ) ;
2023-11-10 20:51:34 +00:00
this . rerollCount = rerollCount ;
2024-04-04 05:38:02 +01:00
this . modifierTiers = modifierTiers ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
2024-05-23 16:03:10 +01:00
if ( ! this . rerollCount ) {
2023-11-10 20:51:34 +00:00
this . updateSeed ( ) ;
2024-05-30 18:45:30 +01:00
} else {
this . scene . reroll = false ;
2024-05-23 16:03:10 +01:00
}
2023-10-18 23:01:15 +01:00
2023-04-10 19:12:01 +01:00
const party = this . scene . getParty ( ) ;
2024-04-04 05:38:02 +01:00
regenerateModifierPoolThresholds ( party , this . getPoolType ( ) , this . rerollCount ) ;
2023-04-10 19:12:01 +01:00
const modifierCount = new Utils . IntegerHolder ( 3 ) ;
2024-05-23 16:03:10 +01:00
if ( this . isPlayer ( ) ) {
2023-10-29 20:15:18 +00:00
this . scene . applyModifiers ( ExtraModifierModifier , true , modifierCount ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-29 20:05:17 +00:00
const typeOptions : ModifierTypeOption [ ] = this . getModifierTypeOptions ( modifierCount . value ) ;
2023-04-10 19:12:01 +01:00
2024-02-04 05:30:19 +00:00
const modifierSelectCallback = ( rowCursor : integer , cursor : integer ) = > {
if ( rowCursor < 0 || cursor < 0 ) {
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:skipItemQuestion" ) , null , ( ) = > {
2024-03-24 01:03:00 +00:00
this . scene . ui . setOverlayMode ( Mode . CONFIRM , ( ) = > {
this . scene . ui . revertMode ( ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
super . end ( ) ;
2024-04-05 15:14:49 +01:00
} , ( ) = > this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) , typeOptions , modifierSelectCallback , this . getRerollCost ( typeOptions , this . scene . lockModifierTiers ) ) ) ;
2024-03-24 01:03:00 +00:00
} ) ;
return false ;
2023-04-10 19:12:01 +01:00
}
2024-02-04 05:30:19 +00:00
let modifierType : ModifierType ;
let cost : integer ;
switch ( rowCursor ) {
2024-05-23 16:03:10 +01:00
case 0 :
2024-06-08 06:07:23 +01:00
switch ( cursor ) {
case 0 :
2024-05-23 16:03:10 +01:00
const rerollCost = this . getRerollCost ( typeOptions , this . scene . lockModifierTiers ) ;
if ( this . scene . money < rerollCost ) {
this . scene . ui . playError ( ) ;
2024-04-05 15:14:49 +01:00
return false ;
2024-05-23 16:03:10 +01:00
} else {
2024-05-30 18:45:30 +01:00
this . scene . reroll = true ;
2024-05-23 16:03:10 +01:00
this . scene . unshiftPhase ( new SelectModifierPhase ( this . scene , this . rerollCount + 1 , typeOptions . map ( o = > o . type . tier ) ) ) ;
this . scene . ui . clearText ( ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > super . end ( ) ) ;
this . scene . money -= rerollCost ;
this . scene . updateMoneyText ( ) ;
2024-06-06 17:50:02 +01:00
this . scene . animateMoneyChanged ( false ) ;
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "buy" ) ;
2024-02-04 05:30:19 +00:00
}
2024-06-08 06:07:23 +01:00
break ;
case 1 :
2024-05-30 22:58:10 +01:00
this . scene . ui . setModeWithoutClear ( Mode . PARTY , PartyUiMode . MODIFIER_TRANSFER , - 1 , ( fromSlotIndex : integer , itemIndex : integer , itemQuantity : integer , toSlotIndex : integer ) = > {
2024-05-23 16:03:10 +01:00
if ( toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > - 1 ) {
2024-05-30 22:58:10 +01:00
const itemModifiers = this . scene . findModifiers ( m = > m instanceof PokemonHeldItemModifier
2024-06-08 06:07:23 +01:00
&& ( m as PokemonHeldItemModifier ) . getTransferrable ( true ) && ( m as PokemonHeldItemModifier ) . pokemonId === party [ fromSlotIndex ] . id ) as PokemonHeldItemModifier [ ] ;
2024-05-30 22:58:10 +01:00
const itemModifier = itemModifiers [ itemIndex ] ;
this . scene . tryTransferHeldItemModifier ( itemModifier , party [ toSlotIndex ] , true , itemQuantity ) ;
2024-05-23 16:03:10 +01:00
} else {
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) , typeOptions , modifierSelectCallback , this . getRerollCost ( typeOptions , this . scene . lockModifierTiers ) ) ;
}
} , PartyUiHandler . FilterItemMaxStacks ) ;
2024-06-08 06:07:23 +01:00
break ;
case 2 :
this . scene . ui . setModeWithoutClear ( Mode . PARTY , PartyUiMode . CHECK , - 1 , ( ) = > {
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) , typeOptions , modifierSelectCallback , this . getRerollCost ( typeOptions , this . scene . lockModifierTiers ) ) ;
} ) ;
break ;
case 3 :
2024-05-23 16:03:10 +01:00
this . scene . lockModifierTiers = ! this . scene . lockModifierTiers ;
const uiHandler = this . scene . ui . getHandler ( ) as ModifierSelectUiHandler ;
uiHandler . setRerollCost ( this . getRerollCost ( typeOptions , this . scene . lockModifierTiers ) ) ;
uiHandler . updateLockRaritiesText ( ) ;
uiHandler . updateRerollCostText ( ) ;
return false ;
}
return true ;
case 1 :
modifierType = typeOptions [ cursor ] . type ;
break ;
default :
const shopOptions = getPlayerShopModifierTypeOptionsForWave ( this . scene . currentBattle . waveIndex , this . scene . getWaveMoneyAmount ( 1 ) ) ;
const shopOption = shopOptions [ rowCursor > 2 || shopOptions . length <= SHOP_OPTIONS_ROW_LIMIT ? cursor : cursor + SHOP_OPTIONS_ROW_LIMIT ] ;
modifierType = shopOption . type ;
cost = shopOption . cost ;
break ;
2024-02-04 05:30:19 +00:00
}
if ( cost && this . scene . money < cost ) {
this . scene . ui . playError ( ) ;
return false ;
}
const applyModifier = ( modifier : Modifier , playSound : boolean = false ) = > {
2024-04-12 01:40:16 +01:00
const result = this . scene . addModifier ( modifier , false , playSound ) ;
2024-03-14 21:15:01 +00:00
if ( cost ) {
2024-04-16 17:04:52 +01:00
result . then ( success = > {
if ( success ) {
this . scene . money -= cost ;
this . scene . updateMoneyText ( ) ;
2024-06-06 17:50:02 +01:00
this . scene . animateMoneyChanged ( false ) ;
2024-05-23 16:03:10 +01:00
this . scene . playSound ( "buy" ) ;
2024-04-16 17:04:52 +01:00
( this . scene . ui . getHandler ( ) as ModifierSelectUiHandler ) . updateCostText ( ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-04-16 17:04:52 +01:00
this . scene . ui . playError ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-04-16 17:04:52 +01:00
} ) ;
2024-03-14 21:15:01 +00:00
} else {
2024-04-12 01:40:16 +01:00
const doEnd = ( ) = > {
this . scene . ui . clearText ( ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
super . end ( ) ;
} ;
2024-05-23 16:03:10 +01:00
if ( result instanceof Promise ) {
2024-04-12 01:40:16 +01:00
result . then ( ( ) = > doEnd ( ) ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-04-12 01:40:16 +01:00
doEnd ( ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-14 21:15:01 +00:00
}
2024-02-04 05:30:19 +00:00
} ;
2023-04-10 19:12:01 +01:00
if ( modifierType instanceof PokemonModifierType ) {
2023-11-04 04:32:12 +00:00
if ( modifierType instanceof FusePokemonModifierType ) {
this . scene . ui . setModeWithoutClear ( Mode . PARTY , PartyUiMode . SPLICE , - 1 , ( fromSlotIndex : integer , spliceSlotIndex : integer ) = > {
if ( spliceSlotIndex !== undefined && fromSlotIndex < 6 && spliceSlotIndex < 6 && fromSlotIndex !== spliceSlotIndex ) {
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) ) . then ( ( ) = > {
const modifier = modifierType . newModifier ( party [ fromSlotIndex ] , party [ spliceSlotIndex ] ) ;
2024-02-04 05:30:19 +00:00
applyModifier ( modifier , true ) ;
2023-11-04 04:32:12 +00:00
} ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-04-05 15:14:49 +01:00
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) , typeOptions , modifierSelectCallback , this . getRerollCost ( typeOptions , this . scene . lockModifierTiers ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-04 04:32:12 +00:00
} , modifierType . selectFilter ) ;
} else {
const pokemonModifierType = modifierType as PokemonModifierType ;
const isMoveModifier = modifierType instanceof PokemonMoveModifierType ;
const isTmModifier = modifierType instanceof TmModifierType ;
2023-11-08 22:30:07 +00:00
const isRememberMoveModifier = modifierType instanceof RememberMoveModifierType ;
2024-05-05 01:31:25 +01:00
const isPpRestoreModifier = ( modifierType instanceof PokemonPpRestoreModifierType || modifierType instanceof PokemonPpUpModifierType ) ;
2023-11-04 04:32:12 +00:00
const partyUiMode = isMoveModifier ? PartyUiMode . MOVE_MODIFIER
2023-11-08 22:30:07 +00:00
: isTmModifier ? PartyUiMode . TM_MODIFIER
2024-05-23 16:03:10 +01:00
: isRememberMoveModifier ? PartyUiMode . REMEMBER_MOVE_MODIFIER
: PartyUiMode . MODIFIER ;
2023-11-04 04:32:12 +00:00
const tmMoveId = isTmModifier
? ( modifierType as TmModifierType ) . moveId
: undefined ;
this . scene . ui . setModeWithoutClear ( Mode . PARTY , partyUiMode , - 1 , ( slotIndex : integer , option : PartyOption ) = > {
if ( slotIndex < 6 ) {
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) ) . then ( ( ) = > {
const modifier = ! isMoveModifier
2023-11-08 22:30:07 +00:00
? ! isRememberMoveModifier
? modifierType . newModifier ( party [ slotIndex ] )
2024-05-23 16:03:10 +01:00
: modifierType . newModifier ( party [ slotIndex ] , option as integer )
2023-11-04 04:32:12 +00:00
: modifierType . newModifier ( party [ slotIndex ] , option - PartyOption . MOVE_1 ) ;
2024-02-04 05:30:19 +00:00
applyModifier ( modifier , true ) ;
2023-11-04 04:32:12 +00:00
} ) ;
2024-05-23 16:03:10 +01:00
} else {
2024-04-05 15:14:49 +01:00
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) , typeOptions , modifierSelectCallback , this . getRerollCost ( typeOptions , this . scene . lockModifierTiers ) ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-01 15:16:58 +01:00
} , pokemonModifierType . selectFilter , modifierType instanceof PokemonMoveModifierType ? ( modifierType as PokemonMoveModifierType ) . moveSelectFilter : undefined , tmMoveId , isPpRestoreModifier ) ;
2023-11-04 04:32:12 +00:00
}
2024-05-23 16:03:10 +01:00
} else {
2024-02-15 00:20:12 +00:00
applyModifier ( modifierType . newModifier ( ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-10 20:51:34 +00:00
2024-02-04 05:30:19 +00:00
return ! cost ;
2023-04-10 19:12:01 +01:00
} ;
2024-04-05 15:14:49 +01:00
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) , typeOptions , modifierSelectCallback , this . getRerollCost ( typeOptions , this . scene . lockModifierTiers ) ) ;
2023-10-29 20:05:17 +00:00
}
updateSeed ( ) : void {
this . scene . resetSeed ( ) ;
}
isPlayer ( ) : boolean {
return true ;
}
2023-11-10 20:51:34 +00:00
2024-04-05 15:14:49 +01:00
getRerollCost ( typeOptions : ModifierTypeOption [ ] , lockRarities : boolean ) : integer {
2024-04-04 05:38:02 +01:00
let baseValue = 0 ;
2024-04-05 15:14:49 +01:00
if ( lockRarities ) {
const tierValues = [ 50 , 125 , 300 , 750 , 2000 ] ;
2024-05-23 16:03:10 +01:00
for ( const opt of typeOptions ) {
2024-04-05 15:14:49 +01:00
baseValue += tierValues [ opt . type . tier ] ;
2024-05-23 16:03:10 +01:00
}
} else {
2024-04-05 15:14:49 +01:00
baseValue = 250 ;
2024-05-23 16:03:10 +01:00
}
2024-04-26 18:05:48 +01:00
return Math . min ( Math . ceil ( this . scene . currentBattle . waveIndex / 10 ) * baseValue * Math . pow ( 2 , this . rerollCount ) , Number . MAX_SAFE_INTEGER ) ;
2023-11-10 20:51:34 +00:00
}
2024-05-24 00:45:04 +01:00
2023-10-29 20:05:17 +00:00
getPoolType ( ) : ModifierPoolType {
return ModifierPoolType . PLAYER ;
}
getModifierTypeOptions ( modifierCount : integer ) : ModifierTypeOption [ ] {
2024-04-05 15:14:49 +01:00
return getPlayerModifierTypeOptions ( modifierCount , this . scene . getParty ( ) , this . scene . lockModifierTiers ? this . modifierTiers : undefined ) ;
2023-10-29 20:05:17 +00:00
}
2024-04-16 20:22:19 +01:00
addModifier ( modifier : Modifier ) : Promise < boolean > {
2023-11-01 01:43:22 +00:00
return this . scene . addModifier ( modifier , false , true ) ;
2023-10-29 20:05:17 +00:00
}
}
2024-02-22 04:57:49 +00:00
export class EggLapsePhase extends Phase {
2023-12-20 04:51:48 +00:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
super . start ( ) ;
2024-04-05 22:50:01 +01:00
const eggsToHatch : Egg [ ] = this . scene . gameData . eggs . filter ( ( egg : Egg ) = > {
2024-05-27 11:49:49 +01:00
return Overrides . IMMEDIATE_HATCH_EGGS_OVERRIDE ? true : -- egg . hatchWaves < 1 ;
2024-04-21 21:19:11 +01:00
} ) ;
2023-12-20 04:51:48 +00:00
2024-06-11 02:20:00 +01:00
let eggCount : integer = eggsToHatch . length ;
2024-05-29 23:17:41 +01:00
2024-06-11 02:20:00 +01:00
if ( eggCount ) {
2024-05-23 16:03:10 +01:00
this . scene . queueMessage ( i18next . t ( "battle:eggHatching" ) ) ;
2024-05-24 00:45:04 +01:00
2024-05-23 16:03:10 +01:00
for ( const egg of eggsToHatch ) {
2024-06-11 02:20:00 +01:00
this . scene . unshiftPhase ( new EggHatchPhase ( this . scene , egg , eggCount ) ) ;
if ( eggCount > 0 ) {
eggCount -- ;
2024-05-29 23:17:41 +01:00
}
2024-05-23 16:03:10 +01:00
}
2024-05-24 00:45:04 +01:00
2024-04-05 22:50:01 +01:00
}
2023-12-20 04:51:48 +00:00
this . end ( ) ;
}
}
2024-02-22 04:57:49 +00:00
export class AddEnemyBuffModifierPhase extends Phase {
2023-10-29 20:05:17 +00:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
2023-11-05 15:56:09 +00:00
super . start ( ) ;
2023-10-29 20:05:17 +00:00
2023-11-05 15:56:09 +00:00
const waveIndex = this . scene . currentBattle . waveIndex ;
const tier = ! ( waveIndex % 1000 ) ? ModifierTier . ULTRA : ! ( waveIndex % 250 ) ? ModifierTier.GREAT : ModifierTier.COMMON ;
2023-10-29 20:05:17 +00:00
2023-11-05 15:56:09 +00:00
regenerateModifierPoolThresholds ( this . scene . getEnemyParty ( ) , ModifierPoolType . ENEMY_BUFF ) ;
2024-05-24 00:45:04 +01:00
2024-03-11 04:16:24 +00:00
const count = Math . ceil ( waveIndex / 250 ) ;
2024-05-23 16:03:10 +01:00
for ( let i = 0 ; i < count ; i ++ ) {
2024-03-11 04:16:24 +00:00
this . scene . addEnemyModifier ( getEnemyBuffModifierForWave ( tier , this . scene . findModifiers ( m = > m instanceof EnemyPersistentModifier , false ) , this . scene ) , true , true ) ;
2024-05-23 16:03:10 +01:00
}
2024-03-11 04:16:24 +00:00
this . scene . updateModifiers ( false , true ) . then ( ( ) = > this . end ( ) ) ;
2023-04-10 19:12:01 +01:00
}
}
2024-05-09 20:15:56 +01:00
/ * *
* Cures the party of all non - volatile status conditions , shows a message
* @param { BattleScene } scene The current scene
* @param { Pokemon } user The user of the move that cures the party
* @param { string } message The message that should be displayed
* @param { Abilities } abilityCondition Pokemon with this ability will not be affected ie . Soundproof
* /
export class PartyStatusCurePhase extends BattlePhase {
private user : Pokemon ;
private message : string ;
private abilityCondition : Abilities ;
constructor ( scene : BattleScene , user : Pokemon , message : string , abilityCondition : Abilities ) {
super ( scene ) ;
this . user = user ;
this . message = message ;
this . abilityCondition = abilityCondition ;
}
start() {
super . start ( ) ;
2024-05-23 16:03:10 +01:00
for ( const pokemon of this . scene . getParty ( ) ) {
2024-05-09 20:15:56 +01:00
if ( ! pokemon . isOnField ( ) || pokemon === this . user ) {
pokemon . resetStatus ( false ) ;
pokemon . updateInfo ( true ) ;
} else {
if ( ! pokemon . hasAbility ( this . abilityCondition ) ) {
pokemon . resetStatus ( ) ;
pokemon . updateInfo ( true ) ;
} else {
// Manually show ability bar, since we're not hooked into the targeting system
pokemon . scene . unshiftPhase ( new ShowAbilityPhase ( pokemon . scene , pokemon . id , pokemon . getPassiveAbility ( ) ? . id === this . abilityCondition ) ) ;
}
}
}
2024-05-23 16:03:10 +01:00
if ( this . message ) {
2024-05-09 20:15:56 +01:00
this . scene . queueMessage ( this . message ) ;
2024-05-23 16:03:10 +01:00
}
2024-05-09 20:15:56 +01:00
this . end ( ) ;
}
}
2023-10-18 23:01:15 +01:00
export class PartyHealPhase extends BattlePhase {
private resumeBgm : boolean ;
constructor ( scene : BattleScene , resumeBgm : boolean ) {
super ( scene ) ;
this . resumeBgm = resumeBgm ;
}
start() {
super . start ( ) ;
const bgmPlaying = this . scene . isBgmPlaying ( ) ;
2024-05-23 16:03:10 +01:00
if ( bgmPlaying ) {
2023-10-18 23:01:15 +01:00
this . scene . fadeOutBgm ( 1000 , false ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-18 23:01:15 +01:00
this . scene . ui . fadeOut ( 1000 ) . then ( ( ) = > {
2024-05-23 16:03:10 +01:00
for ( const pokemon of this . scene . getParty ( ) ) {
2023-10-18 23:01:15 +01:00
pokemon . hp = pokemon . getMaxHp ( ) ;
pokemon . resetStatus ( ) ;
2024-05-23 16:03:10 +01:00
for ( const move of pokemon . moveset ) {
2023-10-18 23:01:15 +01:00
move . ppUsed = 0 ;
2024-05-23 16:03:10 +01:00
}
2023-10-20 16:38:41 +01:00
pokemon . updateInfo ( true ) ;
2023-10-18 23:01:15 +01:00
}
2024-05-23 16:03:10 +01:00
const healSong = this . scene . playSoundWithoutBgm ( "heal" ) ;
2024-04-04 22:43:37 +01:00
this . scene . time . delayedCall ( Utils . fixedInt ( healSong . totalDuration * 1000 ) , ( ) = > {
2023-10-18 23:01:15 +01:00
healSong . destroy ( ) ;
2024-05-23 16:03:10 +01:00
if ( this . resumeBgm && bgmPlaying ) {
2023-10-18 23:01:15 +01:00
this . scene . playBgm ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-10-18 23:01:15 +01:00
this . scene . ui . fadeIn ( 500 ) . then ( ( ) = > this . end ( ) ) ;
} ) ;
} ) ;
}
}
2023-04-10 19:12:01 +01:00
export class ShinySparklePhase extends PokemonPhase {
2023-05-18 16:11:06 +01:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex ) ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
this . getPokemon ( ) . sparkle ( ) ;
this . scene . time . delayedCall ( 1000 , ( ) = > this . end ( ) ) ;
}
2023-10-18 23:01:15 +01:00
}
2023-11-12 17:49:06 +00:00
export class ScanIvsPhase extends PokemonPhase {
private shownIvs : integer ;
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , shownIvs : integer ) {
super ( scene , battlerIndex ) ;
this . shownIvs = shownIvs ;
}
start() {
super . start ( ) ;
2024-05-23 16:03:10 +01:00
if ( ! this . shownIvs ) {
2023-11-12 17:49:06 +00:00
return this . end ( ) ;
2024-05-23 16:03:10 +01:00
}
2023-11-12 17:49:06 +00:00
const pokemon = this . getPokemon ( ) ;
2024-05-23 16:03:10 +01:00
this . scene . ui . showText ( i18next . t ( "battle:ivScannerUseQuestion" , { pokemonName : pokemon.name } ) , null , ( ) = > {
2023-11-12 17:49:06 +00:00
this . scene . ui . setMode ( Mode . CONFIRM , ( ) = > {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . clearText ( ) ;
new CommonBattleAnim ( CommonAnim . LOCK_ON , pokemon , pokemon ) . play ( this . scene , ( ) = > {
this . scene . ui . getMessageHandler ( ) . promptIvs ( pokemon . id , pokemon . ivs , this . shownIvs ) . then ( ( ) = > this . end ( ) ) ;
} ) ;
} , ( ) = > {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . clearText ( ) ;
this . end ( ) ;
} ) ;
} ) ;
}
}
2023-12-12 02:46:49 +00:00
export class TrainerMessageTestPhase extends BattlePhase {
2024-02-06 21:15:35 +00:00
private trainerTypes : TrainerType [ ] ;
constructor ( scene : BattleScene , . . . trainerTypes : TrainerType [ ] ) {
2023-12-12 02:46:49 +00:00
super ( scene ) ;
2024-05-24 00:45:04 +01:00
2024-02-06 21:15:35 +00:00
this . trainerTypes = trainerTypes ;
2023-12-12 02:46:49 +00:00
}
start() {
super . start ( ) ;
2024-05-23 16:03:10 +01:00
const testMessages : string [ ] = [ ] ;
2024-05-24 00:45:04 +01:00
2024-05-23 16:03:10 +01:00
for ( const t of Object . keys ( trainerConfigs ) ) {
2024-02-06 21:15:35 +00:00
const type = parseInt ( t ) ;
2024-05-23 16:03:10 +01:00
if ( this . trainerTypes . length && ! this . trainerTypes . find ( tt = > tt === type as TrainerType ) ) {
2024-02-06 21:15:35 +00:00
continue ;
2024-05-23 16:03:10 +01:00
}
2024-02-06 21:15:35 +00:00
const config = trainerConfigs [ type ] ;
2023-12-12 02:46:49 +00:00
[ config . encounterMessages , config . femaleEncounterMessages , config . victoryMessages , config . femaleVictoryMessages , config . defeatMessages , config . femaleDefeatMessages ]
. map ( messages = > {
2024-05-23 16:03:10 +01:00
if ( messages ? . length ) {
2023-12-12 02:46:49 +00:00
testMessages . push ( . . . messages ) ;
2024-05-23 16:03:10 +01:00
}
2023-12-12 02:46:49 +00:00
} ) ;
}
2024-05-23 16:03:10 +01:00
for ( const message of testMessages ) {
2023-12-12 02:46:49 +00:00
this . scene . pushPhase ( new TestMessagePhase ( this . scene , message ) ) ;
2024-05-23 16:03:10 +01:00
}
2023-12-12 02:46:49 +00:00
this . end ( ) ;
}
}
2023-10-18 23:01:15 +01:00
export class TestMessagePhase extends MessagePhase {
constructor ( scene : BattleScene , message : string ) {
super ( scene , message , null , true ) ;
}
2024-04-06 01:42:30 +01:00
}