2024-04-30 11:47:53 +01:00
import BattleScene , { bypassLogin , startingWave } 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" ;
2023-04-18 03:44:41 +01:00
import * as Utils from './utils' ;
2024-01-13 17:24:24 +00:00
import { Moves } from "./data/enums/moves" ;
2024-04-29 18:51:43 +01:00
import { allMoves , applyMoveAttrs , BypassSleepAttr , ChargeAttr , applyFilteredMoveAttrs , HitsTagAttr , MissEffectAttr , MoveAttr , MoveEffectAttr , MoveFlags , MultiHitAttr , OverrideMoveEffectAttr , VariableAccuracyAttr , MoveTarget , OneHitKOAttr , getMoveTargets , MoveTargetSet , MoveEffectTrigger , CopyMoveAttr , AttackMove , SelfStatusMove , DelayedAttackAttr , RechargeAttr , PreMoveMessageAttr , HealStatusEffectAttr , IgnoreOpponentStatChangesAttr , NoEffectAttr , FixedDamageAttr , OneHitKOAccuracyAttr , ForceSwitchOutAttr } from "./data/move" ;
2023-04-10 19:12:01 +01:00
import { Mode } from './ui/ui' ;
import { Command } from "./ui/command-ui-handler" ;
2023-04-20 20:46:05 +01:00
import { Stat } from "./data/pokemon-stat" ;
2024-04-20 06:59:07 +01:00
import { BerryModifier , ContactHeldItemTransferChanceModifier , EnemyAttackStatusEffectChanceModifier , EnemyPersistentModifier , EnemyStatusEffectHealChanceModifier , EnemyTurnHealModifier , ExpBalanceModifier , ExpBoosterModifier , ExpShareModifier , ExtraModifierModifier , FlinchChanceModifier , FusePokemonModifier , HealingBoosterModifier , HitHealModifier , LapsingPersistentModifier , MapModifier , Modifier , MultipleParticipantExpBonusModifier , PersistentModifier , PokemonExpBoosterModifier , PokemonHeldItemModifier , PokemonInstantReviveModifier , SwitchEffectTransferModifier , TempBattleStatBoosterModifier , TurnHealModifier , TurnHeldItemTransferModifier , MoneyMultiplierModifier , MoneyInterestModifier , IvScannerModifier , PokemonFriendshipBoosterModifier , LapsingPokemonHeldItemModifier , PokemonMultiHitModifier , PokemonMoveAccuracyBoosterModifier } 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 { Biome } from "./data/enums/biome" ;
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-01-13 17:24:24 +00:00
import { BattlerTagType } from "./data/enums/battler-tag-type" ;
2023-04-15 06:32:16 +01:00
import { getPokemonMessage } 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" ;
import { ArenaTagType } from "./data/enums/arena-tag-type" ;
2024-04-26 02:40:00 +01:00
import { CheckTrappedAbAttr , IgnoreOpponentStatChangesAbAttr , PostAttackAbAttr , PostBattleAbAttr , PostDefendAbAttr , PostSummonAbAttr , PostTurnAbAttr , PostWeatherLapseAbAttr , PreSwitchOutAbAttr , PreWeatherDamageAbAttr , ProtectStatAbAttr , RedirectMoveAbAttr , RunSuccessAbAttr , StatChangeMultiplierAbAttr , SuppressWeatherEffectAbAttr , SyncEncounterNatureAbAttr , applyAbAttrs , applyCheckTrappedAbAttrs , applyPostAttackAbAttrs , applyPostBattleAbAttrs , applyPostDefendAbAttrs , applyPostSummonAbAttrs , applyPostTurnAbAttrs , applyPostWeatherLapseAbAttrs , applyPreStatChangeAbAttrs , applyPreSwitchOutAbAttrs , applyPreWeatherEffectAbAttrs , BattleStatMultiplierAbAttr , applyBattleStatMultiplierAbAttrs , IncrementMovePriorityAbAttr , applyPostVictoryAbAttrs , PostVictoryAbAttr , applyPostBattleInitAbAttrs , PostBattleInitAbAttr , BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr , applyPostKnockOutAbAttrs , PostKnockOutAbAttr , PostBiomeChangeAbAttr , applyPostFaintAbAttrs , PostFaintAbAttr , IncreasePpAbAttr , PostStatChangeAbAttr , applyPostStatChangeAbAttrs , AlwaysHitAbAttr , PreventBerryUseAbAttr } 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-01-13 17:24:24 +00:00
import { BattleSpec } from "./enums/battle-spec" ;
import { Species } from "./data/enums/species" ;
2024-04-22 06:05:48 +01:00
import { HealAchv , LevelAchv , achvs } from "./system/achv" ;
2024-03-21 04:57:28 +00:00
import { TrainerSlot , trainerConfigs } from "./data/trainer-config" ;
2024-01-13 17:24:24 +00:00
import { TrainerType } from "./data/enums/trainer-type" ;
2023-12-20 04:51:48 +00:00
import { EggHatchPhase } from "./egg-hatch-phase" ;
import { Egg } from "./data/egg" ;
import { vouchers } from "./system/voucher" ;
2023-12-31 23:30:37 +00:00
import { loggedInUser , updateUserInfo } from "./account" ;
2024-04-22 06:05:48 +01:00
import { PlayerGender , 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-02-26 00:09:24 +00:00
import { battleSpecDialogue , getCharVariantFromDialogue } from "./data/dialogue" ;
2024-02-04 05:30:19 +00:00
import ModifierSelectUiHandler , { SHOP_OPTIONS_ROW_LIMIT } from "./ui/modifier-select-ui-handler" ;
2024-02-06 21:15:35 +00:00
import { Setting } from "./system/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-03-16 01:59:34 +00:00
import { GameModes , gameModes } from "./game-mode" ;
2024-04-06 03:58:40 +01:00
import { getPokemonSpecies , speciesStarters } from "./data/pokemon-species" ;
2024-04-12 04:47:03 +01:00
import i18next from './plugins/i18n' ;
2024-04-30 11:47:53 +01:00
import { STARTER_FORM_OVERRIDE , STARTER_SPECIES_OVERRIDE } from './overrides' ;
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 ) {
if ( this . showText )
2024-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:logInOrCreateAccount' ) ) ;
2023-12-30 23:41:25 +00:00
2024-04-10 06:32:49 +01:00
this . scene . playSound ( 'menu_open' ) ;
const loadData = ( ) = > {
updateUserInfo ( ) . then ( ( ) = > this . scene . gameData . loadSystem ( ) . then ( ( ) = > this . end ( ) ) ) ;
} ;
this . scene . ui . setMode ( Mode . LOGIN_FORM , {
buttonActions : [
( ) = > {
this . scene . ui . playSelect ( ) ;
loadData ( ) ;
} , ( ) = > {
this . scene . playSound ( 'menu_open' ) ;
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-03-09 21:09:06 +00:00
if ( success || bypassLogin )
2024-02-20 01:36:10 +00:00
this . end ( ) ;
else {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-04-13 04:26:01 +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-02-20 01:36:10 +00:00
if ( ! this . scene . gameData . gender )
this . scene . unshiftPhase ( new SelectGenderPhase ( this . scene ) ) ;
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-04-07 04:50:26 +01:00
private 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-04-03 19:33:23 +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 ;
2024-04-19 03:52:26 +01:00
console . log ( sessionData ) ;
2024-03-21 17:17:43 +00:00
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-04-12 04:47:03 +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-04-12 04:47:03 +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
{
label : gameModes [ GameModes . CLASSIC ] . getName ( ) ,
2024-04-13 23:59:58 +01:00
handler : ( ) = > {
setModeAndEnd ( GameModes . CLASSIC ) ;
return true ;
}
2024-04-07 04:50:26 +01:00
} ,
{
label : gameModes [ GameModes . ENDLESS ] . getName ( ) ,
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 ( {
label : gameModes [ GameModes . SPLICED_ENDLESS ] . getName ( ) ,
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-04-12 04:47:03 +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-04-12 04:47:03 +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 ) = > {
if ( slotId === - 1 )
return this . showOptions ( ) ;
this . loadSaveSlot ( slotId ) ;
} ) ;
return true ;
}
2024-03-17 02:06:56 +00:00
} ,
2024-03-16 01:59:34 +00:00
{
2024-04-12 04:47:03 +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-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-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:sessionSuccess' ) , null , ( ) = > this . end ( ) ) ;
2024-03-15 19:13:32 +00:00
} else
this . end ( ) ;
} ) . catch ( err = > {
console . error ( err ) ;
2024-04-13 04:26:01 +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
fetchDailyRunSeed ( ) . then ( seed = > {
2024-03-25 15:00:42 +00:00
this . scene . gameMode = gameModes [ GameModes . DAILY ] ;
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 > [ ] = [ ] ;
for ( let starter of starters ) {
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 ( ) ) ;
}
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 ) ) ;
for ( let m of modifiers )
this . scene . addModifier ( m , true , false , false , true ) ;
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 ;
this . end ( ) ;
} ) ;
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-04-07 04:50:26 +01:00
this . scene . pushPhase ( new SelectStarterPhase ( this . scene , this . gameMode ) ) ;
2024-04-01 17:48:35 +01:00
this . scene . newArena ( this . scene . gameMode . getStartingBiome ( this . scene ) ) ;
2023-04-28 20:03:42 +01:00
} else
2023-10-10 01:20:02 +01:00
this . scene . playBgm ( ) ;
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 ) {
const availablePartyMembers = this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) . length ;
2024-04-11 01:57:22 +01:00
this . scene . pushPhase ( new SummonPhase ( this . scene , 0 , true , true ) ) ;
2023-10-07 21:08:33 +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 ) ) ;
2023-12-02 15:30:23 +00: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 ) ) ;
if ( this . scene . currentBattle . double && availablePartyMembers > 1 )
this . scene . pushPhase ( new CheckSwitchPhase ( this . scene , 1 , this . scene . currentBattle . double ) ) ;
}
2023-10-03 17:50:31 +01:00
}
2023-04-28 20:03:42 +01:00
2023-12-20 04:51:48 +00:00
for ( let achv of Object . keys ( this . scene . gameData . achvUnlocks ) ) {
if ( vouchers . hasOwnProperty ( achv ) )
this . scene . validateVoucher ( vouchers [ achv ] ) ;
}
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 {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start ( ) : void {
this . scene . ui . setMode ( Mode . SESSION_RELOAD ) ;
let delayElapsed = false ;
let loaded = false ;
this . scene . time . delayedCall ( Utils . fixedInt ( 1500 ) , ( ) = > {
if ( loaded )
this . end ( ) ;
else
delayElapsed = true ;
} ) ;
this . scene . gameData . loadSystem ( ) . then ( ( ) = > {
if ( delayElapsed )
this . end ( ) ;
else
loaded = true ;
} ) ;
}
}
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 ) ;
}
start ( ) : void {
super . start ( ) ;
2024-04-13 04:26:01 +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-04-13 04:26:01 +01:00
label : i18next.t ( 'menu:boy' ) ,
2024-02-06 21:15:35 +00:00
handler : ( ) = > {
this . scene . gameData . gender = PlayerGender . MALE ;
this . scene . gameData . saveSetting ( Setting . Player_Gender , 0 ) ;
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-04-13 04:26:01 +01:00
label : i18next.t ( 'menu:girl' ) ,
2024-02-06 21:15:35 +00:00
handler : ( ) = > {
this . scene . gameData . gender = PlayerGender . FEMALE ;
this . scene . gameData . saveSetting ( Setting . Player_Gender , 1 ) ;
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-02-22 04:57:49 +00:00
export class SelectStarterPhase extends Phase {
2024-04-07 04:50:26 +01:00
private gameMode : GameModes ;
constructor ( scene : BattleScene , gameMode : GameModes ) {
2023-04-10 19:12:01 +01:00
super ( scene ) ;
2024-04-07 04:50:26 +01:00
this . gameMode = gameMode ;
2023-04-10 19:12:01 +01:00
}
start() {
super . start ( ) ;
2023-10-26 21:33:59 +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 ;
const party = this . scene . getParty ( ) ;
const loadPokemonAssets : Promise < void > [ ] = [ ] ;
2024-03-30 22:46:33 +00:00
starters . forEach ( ( starter : Starter , i : integer ) = > {
if ( ! i && STARTER_SPECIES_OVERRIDE )
starter . species = getPokemonSpecies ( STARTER_SPECIES_OVERRIDE as Species ) ;
2024-03-15 19:13:32 +00:00
const starterProps = this . scene . gameData . getSpeciesDexAttrProps ( starter . species , starter . dexAttr ) ;
2024-03-30 22:46:33 +00:00
let starterFormIndex = Math . min ( starterProps . formIndex , Math . max ( starter . species . forms . length - 1 , 0 ) ) ;
if ( ! i && STARTER_SPECIES_OVERRIDE )
starterFormIndex = STARTER_FORM_OVERRIDE ;
2024-03-15 19:13:32 +00:00
const starterGender = starter . species . malePercent !== null
? ! starterProps . female ? Gender.MALE : Gender.FEMALE
: Gender . GENDERLESS ;
const starterIvs = this . scene . gameData . dexData [ starter . species . speciesId ] . ivs . slice ( 0 ) ;
2024-04-19 03:52:26 +01:00
const starterPokemon = this . scene . addPlayerPokemon ( starter . species , this . scene . gameMode . getStartingLevel ( ) , starter . abilityIndex , starterFormIndex , starterGender , starterProps . shiny , starterProps . variant , starterIvs , starter . nature ) ;
2024-03-15 19:13:32 +00:00
starterPokemon . tryPopulateMoveset ( starter . moveset ) ;
2024-04-13 23:59:58 +01:00
if ( starter . passive )
starterPokemon . passive = true ;
2024-04-26 22:32:28 +01:00
starterPokemon . luck = this . scene . gameData . getDexAttrLuck ( this . scene . gameData . dexData [ starter . species . speciesId ] . caughtAttr ) ;
2024-03-15 19:13:32 +00:00
if ( starter . pokerus )
starterPokemon . pokerus = true ;
if ( this . scene . gameMode . isSplicedOnly )
starterPokemon . generateFusionSpecies ( true ) ;
starterPokemon . setVisible ( false ) ;
party . push ( starterPokemon ) ;
loadPokemonAssets . push ( starterPokemon . loadAssets ( ) ) ;
2024-03-30 22:46:33 +00:00
} ) ;
2024-03-15 19:13:32 +00:00
Promise . all ( loadPokemonAssets ) . then ( ( ) = > {
2023-11-04 04:32:12 +00:00
SoundFade . fadeOut ( this . scene , this . scene . sound . get ( 'menu' ) , 500 , true ) ;
2023-10-10 01:20:02 +01:00
this . scene . time . delayedCall ( 500 , ( ) = > this . scene . playBgm ( ) ) ;
2024-03-14 20:26:57 +00:00
if ( this . scene . gameMode . isClassic )
2024-01-11 17:26:32 +00:00
this . scene . gameData . gameStats . classicSessionsPlayed ++ ;
else
this . scene . gameData . gameStats . endlessSessionsPlayed ++ ;
2023-12-30 02:04:40 +00:00
this . scene . newBattle ( ) ;
2024-04-01 17:48:35 +01:00
this . scene . arena . init ( ) ;
2024-03-15 19:13:32 +00:00
this . scene . sessionPlayTime = 0 ;
2023-04-13 00:09:15 +01:00
this . end ( ) ;
} ) ;
} ) ;
2024-04-07 04:50:26 +01:00
} , this . gameMode ) ;
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 = > {
if ( visible )
2024-03-27 14:20:08 +00:00
sprite . x = trainerSlot || sprites . length < 2 ? 0 : i ? 16 : - 16 ;
2024-03-21 04:57:28 +00:00
sprite . setVisible ( visible ) ;
sprite . clearTint ( ) ;
} )
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 ,
x : '-=16' ,
y : '+=16' ,
alpha : 1 ,
ease : 'Sine.easeInOut' ,
duration : 750
} ) ;
}
hideEnemyTrainer ( ) : void {
this . scene . tweens . add ( {
targets : this.scene.currentBattle.trainer ,
x : '+=16' ,
y : '-=16' ,
alpha : 0 ,
ease : 'Sine.easeInOut' ,
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 [ ] ;
let orderedTargets : Pokemon [ ] = playerField . concat ( enemyField ) . sort ( ( a : Pokemon , b : Pokemon ) = > {
const aSpeed = a ? . getBattleStat ( Stat . SPD ) || 0 ;
const bSpeed = b ? . getBattleStat ( Stat . SPD ) || 0 ;
2024-04-01 17:48:35 +01:00
return aSpeed < bSpeed ? 1 : aSpeed > bSpeed ? - 1 : ! this . scene . randBattleSeedInt ( 2 ) ? - 1 : 1 ;
2023-05-18 16:11:06 +01:00
} ) ;
const speedReversed = new Utils . BooleanHolder ( false ) ;
this . scene . arena . applyTags ( TrickRoomTag , speedReversed ) ;
if ( speedReversed . value )
orderedTargets = orderedTargets . reverse ( ) ;
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 ) ;
if ( battlerIndex === undefined )
battlerIndex = scene . getField ( ) . find ( p = > p ? . isActive ( ) ) . getBattlerIndex ( ) ;
this . battlerIndex = battlerIndex ;
this . player = battlerIndex < 2 ;
this . fieldIndex = battlerIndex % 2 ;
}
getPokemon() {
2023-12-04 05:09:38 +00:00
if ( this . battlerIndex > BattlerIndex . ENEMY_2 )
return this . scene . getPokemonById ( this . battlerIndex ) ;
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-01-12 01:27:50 +00:00
this . scene . initSession ( ) ;
2024-01-13 17:24:24 +00:00
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 ) {
if ( battle . battleType === BattleType . TRAINER )
battle . enemyParty [ e ] = battle . trainer . genPartyMember ( e ) ;
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-02-20 03:58:25 +00:00
if ( this . scene . currentBattle . battleSpec === BattleSpec . FINAL_BOSS )
battle . enemyParty [ e ] . ivs = new Array ( 6 ) . fill ( 31 ) ;
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-04-08 01:16:03 +01:00
if ( ! this . loaded )
2024-04-22 16:28:13 +01:00
this . scene . gameData . setPokemonSeen ( enemyPokemon , true , battle . battleType === BattleType . TRAINER ) ;
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-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 ( ) ) ;
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-04-07 04:14:13 +01:00
if ( this . scene . getParty ( ) . filter ( p = > p . isShiny ( ) ) . length === 6 )
this . scene . validateAchv ( achvs . SHINY_PARTY ) ;
2023-10-07 21:08:33 +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-03-29 14:29:19 +00:00
else {
2024-03-29 14:37:28 +00:00
if ( battle . enemyParty . filter ( p = > p . isBoss ( ) ) . length > 1 ) {
2024-03-29 14:29:19 +00:00
for ( let enemyPokemon of battle . enemyParty ) {
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-03-29 04:03:54 +00:00
if ( playerPokemon ? . visible )
2023-10-07 21:08:33 +01:00
this . scene . field . moveBelow ( enemyPokemon as Pokemon , playerPokemon ) ;
enemyPokemon . tint ( 0 , 0.5 ) ;
} else if ( battle . battleType === BattleType . TRAINER ) {
enemyPokemon . setVisible ( false ) ;
this . scene . currentBattle . trainer . tint ( 0 , 0.5 ) ;
}
if ( battle . double )
enemyPokemon . setFieldPosition ( e ? FieldPosition.RIGHT : FieldPosition.LEFT ) ;
}
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 ) {
2023-12-30 23:41:25 +00:00
this . scene . gameData . saveSystem ( ) . then ( success = > {
2024-04-17 20:56:29 +01:00
this . scene . disableMenu = false ;
2023-12-30 23:41:25 +00:00
if ( ! success )
return this . scene . reset ( true ) ;
this . scene . gameData . saveSession ( this . scene , true ) . then ( ( ) = > this . doEncounter ( ) ) ;
} ) ;
} else
this . doEncounter ( ) ;
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
} * /
for ( let pokemon of this . scene . getParty ( ) ) {
if ( pokemon )
pokemon . resetBattleData ( ) ;
2023-04-19 19:07:38 +01:00
}
2024-04-21 17:43:50 +01:00
if ( ! this . loaded )
this . scene . arena . trySetWeather ( getRandomWeatherType ( this . scene . arena ) , false ) ;
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 : ( ) = > {
if ( ! this . tryOverrideForBattleSpec ( ) )
this . doEncounterCommon ( ) ;
}
2023-04-10 19:12:01 +01:00
} ) ;
}
2024-01-13 17:24:24 +00:00
getEncounterMessage ( ) : string {
const enemyField = this . scene . getEnemyField ( ) ;
if ( this . scene . currentBattle . battleSpec === BattleSpec . FINAL_BOSS )
2024-04-13 04:26:01 +01:00
return i18next . t ( 'menu:bossAppeared' , { bossName : enemyField [ 0 ] . name } ) ;
2024-01-13 17:24:24 +00:00
if ( this . scene . currentBattle . battleType === BattleType . TRAINER )
2024-04-13 04:26:01 +01:00
return i18next . t ( 'menu:trainerAppeared' , { trainerName : this.scene.currentBattle.trainer.getName ( TrainerSlot . NONE , true ) } ) ;
2024-01-13 17:24:24 +00:00
return enemyField . length === 1
2024-04-13 04:26:01 +01:00
? i18next . t ( 'menu:singleWildAppeared' , { pokemonName : enemyField [ 0 ] . name } )
: i18next . t ( 'menu: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 = > {
enemyPokemon . untint ( 100 , 'Sine.easeOut' ) ;
enemyPokemon . cry ( ) ;
enemyPokemon . showInfo ( ) ;
2023-11-12 05:31:40 +00:00
if ( enemyPokemon . isShiny ( ) )
this . scene . validateAchv ( achvs . SEE_SHINY ) ;
2023-10-07 21:08:33 +01:00
} ) ;
2024-02-15 04:25:12 +00:00
this . scene . updateFieldScale ( ) ;
2024-01-13 17:24:24 +00:00
if ( showEncounterMessage )
this . scene . ui . showText ( this . getEncounterMessage ( ) , null , ( ) = > this . end ( ) , 1500 ) ;
else
this . end ( ) ;
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 ;
trainer . untint ( 100 , 'Sine.easeOut' ) ;
trainer . playAnim ( ) ;
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 ) ) ;
2023-12-02 15:30:23 +00: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 ) ) ;
this . end ( ) ;
2024-01-13 17:24:24 +00:00
} ;
if ( showEncounterMessage )
this . scene . ui . showText ( this . getEncounterMessage ( ) , null , doTrainerSummon , 1500 , true ) ;
else
doTrainerSummon ( ) ;
2023-10-18 23:01:15 +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-02-14 20:33:02 +00:00
if ( ! encounterMessages ? . length )
2023-10-18 23:01:15 +01:00
doSummon ( ) ;
else {
2024-02-22 23:03:36 +00:00
const showDialogueAndSummon = ( ) = > {
let message : string ;
this . scene . executeWithSeedOffset ( ( ) = > message = Utils . randSeedItem ( encounterMessages ) , this . scene . currentBattle . waveIndex ) ;
this . scene . ui . showDialogue ( message , trainer . getName ( ) , null , ( ) = > {
this . scene . charSprite . hide ( ) . then ( ( ) = > this . scene . hideFieldOverlay ( 250 ) . then ( ( ) = > doSummon ( ) ) ) ;
} ) ;
} ;
if ( this . scene . currentBattle . trainer . config . hasCharSprite )
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-02-22 23:03:36 +00:00
else
showDialogueAndSummon ( ) ;
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 ) = > {
2023-11-06 04:48:04 +00:00
if ( enemyPokemon . isShiny ( ) )
2023-05-18 16:11:06 +01:00
this . scene . unshiftPhase ( new ShinySparklePhase ( this . scene , BattlerIndex . ENEMY + e ) ) ;
} ) ;
2023-04-24 19:30:21 +01:00
2023-11-12 17:49:06 +00:00
if ( this . scene . currentBattle . battleType !== BattleType . TRAINER ) {
2023-11-06 03:44:49 +00:00
enemyField . map ( p = > this . scene . pushPhase ( new PostSummonPhase ( this . scene , p . getBattlerIndex ( ) ) ) ) ;
2023-11-12 17:49:06 +00:00
const ivScannerModifier = this . scene . findModifier ( m = > m instanceof IvScannerModifier ) ;
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 ) ) ) ) ;
2023-11-12 17:49:06 +00:00
}
2023-12-02 15:30:23 +00:00
if ( ! this . loaded ) {
const availablePartyMembers = this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) ;
2024-03-30 04:53:35 +00:00
if ( availablePartyMembers [ 0 ] . isOnField ( ) )
applyPostBattleInitAbAttrs ( PostBattleInitAbAttr , availablePartyMembers [ 0 ] ) ;
else
2023-12-02 15:30:23 +00:00
this . scene . pushPhase ( new SummonPhase ( this . scene , 0 ) ) ;
if ( this . scene . currentBattle . double ) {
if ( availablePartyMembers . length > 1 ) {
this . scene . pushPhase ( new ToggleDoublePositionPhase ( this . scene , true ) ) ;
2024-03-30 04:53:35 +00:00
if ( availablePartyMembers [ 1 ] . isOnField ( ) )
applyPostBattleInitAbAttrs ( PostBattleInitAbAttr , availablePartyMembers [ 1 ] ) ;
else
2023-12-02 15:30:23 +00:00
this . scene . pushPhase ( new SummonPhase ( this . scene , 1 ) ) ;
}
} else {
if ( availablePartyMembers . length > 1 && availablePartyMembers [ 1 ] . isOnField ( ) )
this . scene . pushPhase ( new ReturnPhase ( this . scene , 1 ) ) ;
this . scene . pushPhase ( new ToggleDoublePositionPhase ( this . scene , false ) ) ;
}
if ( this . scene . currentBattle . waveIndex > startingWave && this . scene . currentBattle . battleType !== BattleType . TRAINER ) {
this . scene . pushPhase ( new CheckSwitchPhase ( this . scene , 0 , this . scene . currentBattle . double ) ) ;
if ( this . scene . currentBattle . double && availablePartyMembers . length > 1 )
this . scene . pushPhase ( new CheckSwitchPhase ( this . scene , 1 , this . scene . currentBattle . double ) ) ;
}
}
2023-04-10 19:12:01 +01: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 ) {
case BattleSpec . FINAL_BOSS :
const enemy = this . scene . getEnemyPokemon ( ) ;
this . scene . ui . showText ( this . getEncounterMessage ( ) , null , ( ) = > {
2024-02-15 04:38:37 +00:00
this . scene . ui . showDialogue ( battleSpecDialogue [ BattleSpec . FINAL_BOSS ] . encounter , enemy . species . name , null , ( ) = > {
2024-01-13 17:24:24 +00:00
this . doEncounterCommon ( false ) ;
2024-02-13 23:42:11 +00:00
} ) ;
2024-01-13 17:24:24 +00:00
} , 1500 , true ) ;
return true ;
}
return false ;
}
2023-04-10 19:12:01 +01:00
}
export class NextEncounterPhase extends EncounterPhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
doEncounter ( ) : void {
2023-10-10 01:20:02 +01:00
this . scene . playBgm ( undefined , true ) ;
2024-02-26 20:14:47 +00:00
for ( let pokemon of this . scene . getParty ( ) ) {
if ( pokemon )
pokemon . resetBattleData ( ) ;
}
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 ( ) ,
2023-04-10 19:12:01 +01:00
x : '+=300' ,
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 ) ;
2023-10-23 03:45:48 +01:00
if ( this . scene . lastEnemyTrainer )
this . scene . lastEnemyTrainer . destroy ( ) ;
2023-10-07 21:08:33 +01:00
2024-01-13 17:24:24 +00:00
if ( ! this . tryOverrideForBattleSpec ( ) )
this . doEncounterCommon ( ) ;
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 ) ;
2023-11-29 02:35:52 +00:00
for ( let pokemon of this . scene . getParty ( ) ) {
if ( pokemon )
pokemon . resetBattleData ( ) ;
}
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-04-08 14:31:30 +01:00
for ( let pokemon of this . scene . getParty ( ) . filter ( p = > p . isOnField ( ) ) )
applyAbAttrs ( PostBiomeChangeAbAttr , pokemon , null ) ;
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 ( ) ,
2023-05-30 17:15:59 +01:00
x : '+=300' ,
2023-04-10 19:12:01 +01:00
duration : 2000 ,
2024-01-13 17:24:24 +00:00
onComplete : ( ) = > {
if ( ! this . tryOverrideForBattleSpec ( ) )
this . doEncounterCommon ( ) ;
}
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 ( ) ;
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 ) )
|| ( this . scene . gameMode . hasShortBiomes && ! ( this . scene . currentBattle . waveIndex % 50 ) ) )
2023-04-26 22:40:08 +01:00
setNextBiome ( Biome . END ) ;
2024-03-14 20:26:57 +00:00
else if ( this . scene . gameMode . hasRandomBiomes )
2023-12-07 18:41:47 +00:00
setNextBiome ( this . generateNextBiome ( ) ) ;
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 ( ( ) = > {
biomeChoices = ( ! Array . isArray ( biomeLinks [ currentBiome ] )
? [ biomeLinks [ currentBiome ] as Biome ]
: 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
} ) ;
} else
2023-10-18 23:01:15 +01:00
setNextBiome ( biomes [ Utils . randSeedInt ( biomes . length ) ] ) ;
2024-04-23 01:30:46 +01:00
} else if ( biomeLinks . hasOwnProperty ( currentBiome ) )
2023-04-27 00:19:39 +01:00
setNextBiome ( biomeLinks [ currentBiome ] as Biome ) ;
2024-04-23 01:30:46 +01:00
else
setNextBiome ( this . generateNextBiome ( ) ) ;
2023-04-12 05:37:56 +01:00
}
2023-12-07 18:41:47 +00:00
generateNextBiome ( ) : Biome {
if ( ! ( this . scene . currentBattle . waveIndex % 50 ) )
return Biome . END ;
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 ( ) ;
2023-10-18 23:01:15 +01:00
if ( this . nextBiome === undefined )
return this . end ( ) ;
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 ] ,
2023-04-10 19:12:01 +01:00
x : '+=300' ,
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 ` ;
this . scene . arenaBgTransition . setTexture ( bgTexture )
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 ,
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 ) ;
2023-10-23 03:45:48 +01:00
if ( this . scene . lastEnemyTrainer )
this . scene . lastEnemyTrainer . destroy ( ) ;
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 ( ) ;
}
preSummon ( ) : void {
2023-05-31 17:38:55 +01:00
const partyMember = this . getPokemon ( ) ;
if ( partyMember . isFainted ( ) ) {
2023-10-07 21:08:33 +01:00
const party = this . getParty ( ) ;
2023-05-31 17:38:55 +01:00
const nonFaintedIndex = party . slice ( this . partyMemberIndex ) . findIndex ( p = > ! p . isFainted ( ) ) + this . partyMemberIndex ;
const nonFaintedPartyMember = party [ nonFaintedIndex ] ;
party [ nonFaintedIndex ] = partyMember ;
party [ this . partyMemberIndex ] = nonFaintedPartyMember ;
}
2023-10-07 21:08:33 +01:00
if ( this . player ) {
2024-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:playerGo' , { pokemonName : this.getPokemon ( ) . name } ) ) ;
2023-10-18 23:01:15 +01:00
if ( this . player )
this . scene . pbTray . hide ( ) ;
2024-02-06 21:15:35 +00:00
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 , ( ) = > {
this . scene . trainer . setFrame ( '2' ) ;
this . scene . time . delayedCall ( 64 , ( ) = > {
this . scene . trainer . setFrame ( '3' ) ;
} ) ;
} ) ;
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 {
this . scene . pbTrayEnemy . hide ( ) ;
2024-03-21 04:57:28 +00:00
this . scene . ui . showText ( ` ${ this . scene . currentBattle . trainer . getName ( ! ( this . fieldIndex % 2 ) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER ) } sent out \ n ${ this . getPokemon ( ) . name } ! ` , 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 ( ) ;
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 ) ;
2023-05-18 16:11:06 +01:00
if ( this . fieldIndex === 1 )
2023-10-07 21:08:33 +01:00
pokemon . setFieldPosition ( FieldPosition . RIGHT , 0 ) ;
else {
const availablePartyMembers = this . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) . length ;
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 ,
2023-05-18 16:11:06 +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 ,
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 : ( ) = > {
2023-10-21 13:58:39 +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 ;
2023-10-27 21:20:51 +01:00
if ( playerPokemon ? . visible )
2023-10-07 21:08:33 +01:00
this . scene . field . moveBelow ( pokemon , playerPokemon ) ;
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 ) ) ;
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 ,
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
2023-11-06 04:48:04 +00:00
if ( pokemon . isShiny ( ) )
2023-06-02 16:41:08 +01:00
this . scene . unshiftPhase ( new ShinySparklePhase ( this . scene , pokemon . getBattlerIndex ( ) ) ) ;
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 ) ;
2024-01-10 04:34:43 +00:00
2024-04-11 01:57:22 +01:00
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
}
preSummon ( ) : void {
2024-02-22 04:57:49 +00:00
if ( ! this . player ) {
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-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-02-22 04:57:49 +00:00
if ( this . player )
return this . switchAndSummon ( ) ;
else {
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
2023-04-29 00:26:41 +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 ) ) ;
2023-04-23 03:14:53 +01:00
2024-01-16 05:28:03 +00:00
applyPreSwitchOutAbAttrs ( PreSwitchOutAbAttr , pokemon ) ;
2024-04-13 04:26:01 +01:00
this . scene . ui . showText ( this . player ?
i18next . t ( 'menu:playerComeBack' , { pokemonName : pokemon.name } ) :
i18next . t ( 'menu:trainerComeBack' , {
trainerName : this.scene.currentBattle.trainer.getName ( ! ( this . fieldIndex % 2 ) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER ) ,
pokemonName : pokemon.name
} )
) ;
2023-10-21 13:58:39 +01:00
this . scene . playSound ( 'pb_rel' ) ;
2023-10-07 21:08:33 +01:00
pokemon . hideInfo ( ) ;
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 ,
ease : 'Sine.easeIn' ,
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 ( ) ;
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-04-12 01:58:24 +01:00
if ( batonPassModifier && ! this . scene . findModifier ( m = > m instanceof SwitchEffectTransferModifier && ( m as SwitchEffectTransferModifier ) . pokemonId === switchedPokemon . id ) )
2023-10-31 18:09:33 +00:00
this . scene . tryTransferHeldItemModifier ( batonPassModifier , switchedPokemon , false , false ) ;
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 ?
i18next . t ( 'menu:playerGo' , { pokemonName : switchedPokemon.name } ) :
i18next . t ( 'menu:trainerGo' , {
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 ( ) ;
} ;
if ( this . player )
showTextAndSummon ( ) ;
else {
this . scene . time . delayedCall ( 1500 , ( ) = > {
this . hideEnemyTrainer ( ) ;
this . scene . pbTrayEnemy . hide ( ) ;
showTextAndSummon ( ) ;
} ) ;
}
2023-05-31 17:38:55 +01:00
} else
this . end ( ) ;
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-04-29 18:51:43 +01:00
const moveId = pokemon . scene . currentBattle . turnCommands [ this . fieldIndex ] ? . move ? . move ;
const lastUsedMove = moveId ? allMoves [ moveId ] : undefined ;
2023-05-31 17:38:55 +01:00
2024-03-08 18:14:08 +00:00
// Compensate for turn spent summoning
2024-04-29 18:51:43 +01:00
if ( pokemon . scene . currentBattle . turnCommands [ this . fieldIndex ] ? . command === Command . POKEMON || ! ! lastUsedMove ? . findAttr ( attr = > attr instanceof ForceSwitchOutAttr ) ) //check if hard switch OR pivot move was used
2024-03-14 22:03:38 +00:00
pokemon . battleSummonData . turnCount -- ;
2024-03-08 18:14:08 +00:00
2023-05-31 17:38:55 +01:00
if ( this . batonPass && pokemon )
pokemon . transferSummon ( this . lastPokemon ) ;
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-01-15 01:47:08 +00:00
this . scene . trainer . setVisible ( true )
2024-02-06 21:15:35 +00: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 ) {
2023-11-25 21:02:47 +00:00
playerPokemon . setFieldPosition ( this . double && this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) . 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 ( ) ;
} ) ;
} else
2023-05-18 16:11:06 +01:00
this . end ( ) ;
}
}
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 ] ;
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-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:switchQuestion' , { pokemonName : this.useName ? pokemon.name : i18next.t ( 'menu: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-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu: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-04-04 22:43:37 +01:00
this . scene . playSound ( 'level_up_fanfare' ) ;
2024-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu: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
2023-12-22 01:58:00 +00:00
//this.scene.pushPhase(new MoveAnimTestPhase(this.scene));
2023-06-01 18:54:52 +01:00
this . scene . getField ( ) . forEach ( ( pokemon , i ) = > {
if ( pokemon ? . isActive ( ) ) {
if ( pokemon . isPlayer ( ) )
this . scene . currentBattle . addParticipant ( pokemon as PlayerPokemon ) ;
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 ) {
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 } ;
}
if ( this . scene . currentBattle . turnCommands [ this . fieldIndex ] ? . skip )
return this . end ( ) ;
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 )
2023-10-25 14:41:37 +01:00
|| ! playerPokemon . getMoveset ( ) [ playerPokemon . getMoveset ( ) . findIndex ( m = > m . moveId === moveQueue [ 0 ] . move ) ] . isUsable ( playerPokemon , moveQueue [ 0 ] . ignorePP ) ) )
2023-04-21 02:32:48 +01:00
moveQueue . shift ( ) ;
if ( moveQueue . length ) {
const queuedMove = moveQueue [ 0 ] ;
if ( ! queuedMove . move )
2023-05-01 04:58:16 +01:00
this . handleCommand ( Command . FIGHT , - 1 , false ) ;
2023-04-21 02:32:48 +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 } ) ;
} else
2023-10-20 16:38:41 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-04-21 02:32:48 +01:00
}
2023-04-19 23:19:55 +01:00
} else
2023-10-20 16:38:41 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
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 ) {
case Command . FIGHT :
2023-10-27 13:45:47 +01:00
let useStruggle = false ;
if ( cursor === - 1 || playerPokemon . trySelectMove ( cursor , args [ 0 ] as boolean ) || ( useStruggle = cursor > - 1 && ! playerPokemon . getMoveset ( ) . filter ( m = > m . isUsable ( playerPokemon ) ) . length ) ) {
2023-11-27 18:47:32 +00:00
const moveId = ! useStruggle ? cursor > - 1 ? playerPokemon . getMoveset ( ) [ cursor ] . moveId : Moves.NONE : Moves . STRUGGLE ;
2023-12-22 04:57:11 +00:00
const turnCommand : TurnCommand = { command : Command.FIGHT , cursor : cursor , move : { move : moveId , targets : [ ] , ignorePP : args [ 0 ] } , args : args } ;
2023-11-27 18:47:32 +00:00
const moveTargets : MoveTargetSet = args . length < 3 ? getMoveTargets ( playerPokemon , moveId ) : args [ 2 ] ;
2024-04-11 23:53:03 +01:00
if ( moveId ) {
const move = playerPokemon . getMoveset ( ) [ cursor ] ;
if ( move . getName ( ) . endsWith ( ' (N)' ) ) {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:moveNotImplemented' , { moveName : move.getName ( ) . slice ( 0 , - 4 ) } ) , null , ( ) = > {
2024-04-11 23:53:03 +01:00
this . scene . ui . clearText ( ) ;
this . scene . ui . setMode ( Mode . FIGHT , this . fieldIndex ) ;
} , null , true ) ;
return ;
}
} else
2023-11-27 18:47:32 +00:00
turnCommand . targets = [ this . fieldIndex ] ;
2023-05-18 16:11:06 +01:00
console . log ( moveTargets , playerPokemon . name ) ;
if ( moveTargets . targets . length <= 1 || moveTargets . multiple )
turnCommand . move . targets = moveTargets . targets ;
else
this . scene . unshiftPhase ( new SelectTargetPhase ( this . scene , this . fieldIndex ) ) ;
this . scene . currentBattle . turnCommands [ this . fieldIndex ] = turnCommand ;
2023-04-10 19:12:01 +01:00
success = true ;
2023-05-06 05:42:01 +01:00
} else if ( cursor < playerPokemon . getMoveset ( ) . length ) {
const move = playerPokemon . getMoveset ( ) [ cursor ] ;
2023-10-25 14:41:37 +01:00
if ( playerPokemon . summonData . disabledMove === move . moveId ) {
2023-04-20 03:51:46 +01:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:moveDisabled' , { moveName : move.getName ( ) } ) , null , ( ) = > {
2023-04-20 03:51:46 +01:00
this . scene . ui . clearText ( ) ;
2023-10-19 04:16:38 +01:00
this . scene . ui . setMode ( Mode . FIGHT , this . fieldIndex ) ;
2023-04-20 03:51:46 +01:00
} , null , true ) ;
}
2023-04-10 19:12:01 +01:00
}
break ;
case Command . BALL :
2024-04-28 15:52:43 +01:00
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 ) ) ) {
2023-10-20 16:38:41 +01:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-04-29 21:26:09 +01:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:noPokeballForce' ) , null , ( ) = > {
2023-10-18 23:01:15 +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-10-18 23:01:15 +01:00
} , null , true ) ;
} else if ( this . scene . currentBattle . battleType === BattleType . TRAINER ) {
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-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:noPokeballTrainer' ) , 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 ) ;
2023-10-27 22:43:53 +01:00
} else {
2023-06-02 16:41:08 +01:00
const targets = this . scene . getEnemyField ( ) . filter ( p = > p . isActive ( true ) ) . map ( p = > p . getBattlerIndex ( ) ) ;
2023-10-27 22:43:53 +01:00
if ( targets . length > 1 ) {
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:noPokeballMulti' ) , 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-02-29 14:58:07 +00:00
} else if ( cursor < 5 ) {
2024-01-08 04:17:24 +00:00
const targetPokemon = this . scene . getEnemyField ( ) . find ( p = > p . isActive ( true ) ) ;
2024-04-05 17:30:48 +01:00
if ( targetPokemon . isBoss ( ) && targetPokemon . bossSegmentIndex >= 1 && cursor < PokeballType . MASTER_BALL ) {
2024-01-08 04:17:24 +00:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:noPokeballStrong' ) , null , ( ) = > {
2024-01-08 04:17:24 +00:00
this . scene . ui . showText ( null , 0 ) ;
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
} , null , true ) ;
} else {
this . scene . currentBattle . turnCommands [ this . fieldIndex ] = { command : Command.BALL , cursor : cursor } ;
2023-10-27 22:43:53 +01:00
this . scene . currentBattle . turnCommands [ this . fieldIndex ] . targets = targets ;
if ( this . fieldIndex )
this . scene . currentBattle . turnCommands [ this . fieldIndex - 1 ] . skip = true ;
2024-01-08 04:17:24 +00:00
success = true ;
2023-10-27 22:43:53 +01:00
}
}
2023-04-10 19:12:01 +01:00
}
break ;
case Command . POKEMON :
2023-05-08 15:03:57 +01:00
case Command . RUN :
const isSwitch = command === Command . POKEMON ;
2023-10-28 00:56:15 +01:00
if ( ! isSwitch && this . scene . arena . biomeType === Biome . END ) {
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:noEscapeForce' ) , null , ( ) = > {
2023-10-28 00:56:15 +01:00
this . scene . ui . showText ( null , 0 ) ;
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
} , null , true ) ;
} else if ( ! isSwitch && this . scene . currentBattle . battleType === BattleType . TRAINER ) {
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-04-13 04:26:01 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:noEscapeTrainer' ) , null , ( ) = > {
2023-04-23 03:14:53 +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-23 03:14:53 +01:00
} , null , true ) ;
2023-10-18 23:01:15 +01:00
} 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 ) ) ;
if ( batonPass || ( ! trapTag && ! trapped . value ) ) {
this . scene . currentBattle . turnCommands [ this . fieldIndex ] = isSwitch
? { command : Command.POKEMON , cursor : cursor , args : args }
: { command : Command.RUN } ;
success = true ;
2023-10-29 05:35:12 +00:00
if ( ! isSwitch && this . fieldIndex )
2023-10-27 22:43:53 +01:00
this . scene . currentBattle . turnCommands [ this . fieldIndex - 1 ] . skip = true ;
2023-10-18 23:01:15 +01:00
} else if ( trapTag ) {
2023-12-14 16:54:56 +00:00
if ( ! isSwitch ) {
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
}
2024-04-13 04:26:01 +01:00
this . scene . ui . showText (
i18next . t ( 'menu:noEscapePokemon' , {
pokemonName : this.scene.getPokemonById ( trapTag . sourceId ) . name ,
moveName : trapTag.getMoveName ( ) ,
escapeVerb : isSwitch ? i18next . t ( 'menu:escapeVerbSwitch' ) : i18next . t ( 'menu: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
}
}
2023-04-10 19:12:01 +01:00
break ;
}
2023-04-12 05:37:56 +01:00
2023-05-18 16:11:06 +01:00
if ( success )
2023-04-10 19:12:01 +01:00
this . end ( ) ;
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 ;
if ( ! encoreTag )
return false ;
const moveIndex = pokemon . getMoveset ( ) . findIndex ( m = > m . moveId === encoreTag . moveId ) ;
if ( moveIndex === - 1 || ! pokemon . getMoveset ( ) [ moveIndex ] . isUsable ( pokemon ) )
return false ;
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 ) ;
opponents . forEach ( playerPokemon = > applyCheckTrappedAbAttrs ( CheckTrappedAbAttr , playerPokemon , trapped ) ) ;
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 ;
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-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 ) ) ;
} else
turnCommand . targets = [ cursor ] ;
2023-10-28 00:34:55 +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 ;
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 ( ) ;
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 ) {
if ( aCommand . command === Command . FIGHT )
return 1 ;
else if ( bCommand . command === Command . FIGHT )
return - 1 ;
} 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 ) ;
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 ) ;
2023-05-18 16:11:06 +01:00
2024-03-28 20:24:11 +00:00
if ( aPriority . value !== bPriority . value )
return aPriority . value < bPriority . value ? 1 : - 1 ;
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 ;
} ) ;
for ( let o of moveOrder ) {
const pokemon = field [ o ] ;
const turnCommand = this . scene . currentBattle . turnCommands [ o ] ;
2023-10-27 22:43:53 +01:00
if ( turnCommand . skip )
continue ;
2023-05-18 16:11:06 +01:00
switch ( turnCommand . command ) {
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 :
case Command . RUN :
const isSwitch = turnCommand . command === Command . POKEMON ;
if ( isSwitch )
2024-01-15 05:20:26 +00:00
this . scene . unshiftPhase ( new SwitchSummonPhase ( this . scene , pokemon . getFieldIndex ( ) , turnCommand . cursor , true , turnCommand . args [ 0 ] as boolean , pokemon . isPlayer ( ) ) ) ;
2023-05-18 16:11:06 +01:00
else
this . scene . unshiftPhase ( new AttemptRunPhase ( this . scene , pokemon . getFieldIndex ( ) ) ) ;
break ;
}
}
2023-07-04 21:16:50 +01:00
if ( this . scene . arena . weather )
this . scene . pushPhase ( new WeatherEffectPhase ( this . scene , this . scene . arena . weather ) ) ;
2023-05-18 16:11:06 +01:00
for ( let o of order ) {
if ( field [ o ] . status && field [ o ] . status . isPostTurn ( ) )
this . scene . pushPhase ( new PostTurnStatusEffectPhase ( this . scene , o ) ) ;
}
this . scene . pushPhase ( new TurnEndPhase ( this . scene ) ) ;
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 ) ;
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 ) ;
2023-04-19 23:19:55 +01:00
2023-10-25 14:41:37 +01:00
if ( pokemon . summonData . disabledMove && ! -- pokemon . summonData . disabledTurns ) {
2024-04-13 04:26:01 +01:00
this . scene . pushPhase ( new MessagePhase ( this . scene , i18next . t ( 'menu:notDisabled' , { 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 00:44:56 +01:00
const hasUsableBerry = ! ! this . scene . findModifier ( m = > m instanceof BerryModifier && m . shouldApply ( [ pokemon ] ) , pokemon . isPlayer ( ) ) ;
2023-04-20 20:46:05 +01:00
if ( hasUsableBerry )
2024-03-20 03:14:06 +00:00
this . scene . unshiftPhase ( new BerryPhase ( this . scene , pokemon . getBattlerIndex ( ) ) ) ;
2023-04-20 20:46:05 +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-03-19 01:22:27 +00: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
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 ) ;
2023-04-22 00:30:04 +01:00
this . scene . arena . lapseTags ( ) ;
2023-04-13 17:16:36 +01:00
2023-04-19 19:07:38 +01:00
if ( this . scene . arena . weather && ! this . scene . arena . weather . lapse ( ) )
this . scene . arena . trySetWeather ( WeatherType . NONE , false ) ;
2024-03-26 14:08:17 +00:00
if ( this . scene . arena . terrain && ! this . scene . arena . terrain . lapse ( ) )
this . scene . arena . trySetTerrain ( TerrainType . NONE , false ) ;
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-04-04 03:52:39 +01:00
if ( this . scene . currentBattle . moneyScattered )
this . scene . currentBattle . pickUpScatteredMoney ( this . scene ) ;
2024-03-17 15:36:19 +00:00
2024-01-11 17:26:32 +00:00
this . scene . gameData . gameStats . battles ++ ;
if ( this . scene . currentBattle . trainer )
this . scene . gameData . gameStats . trainersDefeated ++ ;
2024-03-14 20:26:57 +00:00
if ( this . scene . gameMode . isEndless && this . scene . currentBattle . waveIndex + 1 > this . scene . gameData . gameStats . highestEndlessWave )
2024-01-11 17:26:32 +00:00
this . scene . gameData . gameStats . highestEndlessWave = this . scene . currentBattle . waveIndex + 1 ;
2023-05-18 16:11:06 +01:00
for ( let pokemon of this . scene . getField ( ) ) {
if ( pokemon )
pokemon . resetBattleSummonData ( ) ;
}
2024-03-07 13:42:04 +00:00
for ( let pokemon of this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) )
2024-03-07 02:05:23 +00:00
applyPostBattleAbAttrs ( PostBattleAbAttr , pokemon ) ;
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 ) [ ] ;
2023-06-01 16:22:34 +01:00
for ( let m of lapsingModifiers ) {
2024-02-17 05:40:03 +00:00
const args : any [ ] = [ ] ;
if ( m instanceof LapsingPokemonHeldItemModifier )
args . push ( this . scene . getPokemonById ( m . pokemonId ) ) ;
if ( ! m . lapse ( args ) )
2023-04-19 03:09:37 +01:00
this . scene . removeModifier ( m ) ;
}
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
}
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 ;
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 ;
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
}
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 ( ) ) {
2023-11-27 18:47:32 +00: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! ` ) ;
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-04-18 06:44:03 +01:00
if ( this . move . getMove ( ) . checkFlag ( MoveFlags . IGNORE_ABILITIES , this . pokemon , null ) )
this . scene . arena . setIgnoreAbilities ( ) ;
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
const moveTarget = this . targets . length === 1
? new Utils . IntegerHolder ( this . targets [ 0 ] )
: null ;
if ( moveTarget ) {
2024-03-12 12:44:05 +00:00
this . scene . getField ( true ) . filter ( p = > p !== this . pokemon ) . forEach ( p = > applyAbAttrs ( RedirectMoveAbAttr , p , null , this . move . moveId , moveTarget ) ) ;
2024-03-12 00:55:41 +00:00
this . targets [ 0 ] = moveTarget . value ;
}
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 ;
if ( attacker ? . isActive ( true ) )
this . targets [ 0 ] = attacker . getBattlerIndex ( ) ;
}
if ( this . targets [ 0 ] === BattlerIndex . ATTACKER ) {
this . cancel ( ) ;
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 = > {
if ( this . targets . indexOf ( p . getBattlerIndex ( ) ) > - 1 ) {
2023-05-18 16:11:06 +01:00
const hiddenTag = p . getTag ( HiddenTag ) ;
2024-04-21 06:26:30 +01:00
if ( hiddenTag && ! this . move . getMove ( ) . getAttrs ( HitsTagAttr ) . filter ( hta = > ( hta as HitsTagAttr ) . tagType === hiddenTag . tagType ) . length && ! p . hasAbilityWithAttr ( AlwaysHitAbAttr ) && ! this . pokemon . hasAbilityWithAttr ( AlwaysHitAbAttr ) )
2023-05-18 16:11:06 +01:00
return false ;
return true ;
}
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-04-06 01:42:30 +01:00
this . pokemon . lapseTags ( BattlerTagLapseType . PRE_MOVE ) ;
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
}
if ( this . cancelled ) {
this . pokemon . pushMoveHistory ( { move : Moves.NONE , result : MoveResult.FAIL } ) ;
return this . end ( ) ;
2023-11-15 23:45:10 +00:00
}
2023-04-21 02:32:48 +01:00
const moveQueue = this . pokemon . getMoveQueue ( ) ;
2024-03-30 22:46:33 +00:00
this . scene . triggerPokemonFormChange ( this . pokemon , SpeciesFormChangePreMoveTrigger ) ;
2023-11-27 18:47:32 +00:00
if ( this . move . moveId )
this . showMoveText ( ) ;
2023-05-18 16:11:06 +01:00
if ( ( moveQueue . length && moveQueue [ 0 ] . move === Moves . NONE ) || ! targets . length ) {
2023-04-21 02:32:48 +01:00
moveQueue . shift ( ) ;
this . cancel ( ) ;
}
2023-04-12 00:08:03 +01:00
if ( this . cancelled ) {
2023-05-18 16:11:06 +01:00
this . pokemon . pushMoveHistory ( { move : Moves.NONE , result : MoveResult.FAIL } ) ;
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
2023-10-30 04:16:23 +00:00
if ( ! moveQueue . length || ! moveQueue . shift ( ) . ignorePP ) {
2023-04-13 17:16:36 +01:00
this . move . ppUsed ++ ;
2024-04-08 16:06:06 +01:00
const targetedOpponents = this . pokemon . getOpponents ( ) . filter ( o = > this . targets . includes ( o . getBattlerIndex ( ) ) ) ;
for ( let opponent of targetedOpponents ) {
2023-10-30 04:16:23 +00:00
if ( this . move . ppUsed === this . move . getMove ( ) . pp )
break ;
2024-04-15 06:03:50 +01:00
if ( opponent . hasAbilityWithAttr ( IncreasePpAbAttr ) )
2024-03-27 01:09:20 +00:00
this . move . ppUsed = Math . min ( this . move . ppUsed + 1 , this . move . getMovePp ( ) ) ;
2023-10-30 04:16:23 +00:00
}
}
2023-04-16 23:40:32 +01:00
2023-10-10 01:20:02 +01:00
if ( ! allMoves [ this . move . moveId ] . getAttrs ( CopyMoveAttr ) . length )
this . scene . currentBattle . lastMove = this . move . moveId ;
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-03-18 22:03:13 +00:00
let failedText = null ;
2023-04-24 19:30:21 +01:00
if ( success && this . scene . arena . isMoveWeatherCancelled ( this . move . getMove ( ) ) )
success = false ;
2024-03-28 20:24:11 +00:00
else if ( success && this . scene . arena . isMoveTerrainCancelled ( this . pokemon , this . move . getMove ( ) ) ) {
2024-03-18 22:03:13 +00:00
success = false ;
failedText = getTerrainBlockMessage ( targets [ 0 ] , this . scene . arena . terrain . terrainType ) ;
}
2023-04-24 19:30:21 +01:00
if ( success )
this . scene . unshiftPhase ( this . getEffectPhase ( ) ) ;
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-03-18 22:03:13 +00:00
this . showFailedText ( failedText ) ;
2023-04-24 19:30:21 +01:00
}
2023-04-15 22:40:18 +01:00
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 ;
2023-04-16 23:40:32 +01:00
2023-04-12 00:08:03 +01:00
switch ( this . pokemon . status . effect ) {
case StatusEffect . PARALYSIS :
2024-01-03 02:31:59 +00:00
if ( ! this . pokemon . randSeedInt ( 4 ) ) {
2023-04-12 00:08:03 +01:00
activated = true ;
this . cancelled = true ;
}
break ;
case StatusEffect . SLEEP :
2023-05-18 16:11:06 +01:00
applyMoveAttrs ( BypassSleepAttr , this . pokemon , null , this . move . getMove ( ) ) ;
2023-04-12 00:08:03 +01:00
healed = this . pokemon . status . turnCount === this . pokemon . status . cureTurn ;
2023-04-22 00:30:04 +01:00
activated = ! healed && ! this . pokemon . getTag ( BattlerTagType . BYPASS_SLEEP ) ;
2023-04-12 00:08:03 +01:00
this . cancelled = activated ;
break ;
case StatusEffect . FREEZE :
2024-03-30 22:46:33 +00:00
healed = ! ! this . move . getMove ( ) . findAttr ( attr = > attr instanceof HealStatusEffectAttr && attr . selfTarget && attr . isOfEffect ( StatusEffect . FREEZE ) ) || ! this . pokemon . randSeedInt ( 5 ) ;
2023-04-12 00:08:03 +01:00
activated = ! healed ;
this . cancelled = activated ;
break ;
}
2024-04-22 00:10:21 +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 ( ) ;
}
} else
doMove ( ) ;
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 {
2023-12-12 02:46:49 +00:00
if ( this . move . getMove ( ) . getAttrs ( ChargeAttr ) . length ) {
2024-04-22 00:10:21 +01:00
this . scene . queueMessage ( getPokemonMessage ( this . pokemon , ` used \ n ${ this . move . getName ( ) } ! ` ) , 500 ) ;
2023-12-12 02:46:49 +00:00
const lastMove = this . pokemon . getLastXMoves ( ) as TurnMove [ ] ;
if ( ! lastMove . length || lastMove [ 0 ] . move !== this . move . getMove ( ) . id || lastMove [ 0 ] . result !== MoveResult . OTHER )
return ;
}
2024-01-08 04:17:24 +00:00
if ( this . pokemon . getTag ( BattlerTagType . RECHARGING ) )
return ;
2023-12-12 02:46:49 +00:00
2023-11-27 16:42:03 +00:00
this . scene . queueMessage ( getPokemonMessage ( this . pokemon , ` used \ n ${ 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-04-24 00:19:36 +01:00
this . scene . queueMessage ( failedText || i18next . t ( 'menu:attackFailed' ) ) ;
2023-11-27 16:42:03 +00:00
}
2023-05-18 16:11:06 +01:00
end() {
if ( ! this . followUp && this . canMove ( ) )
this . scene . unshiftPhase ( new MoveEndPhase ( this . scene , this . pokemon . getBattlerIndex ( ) ) ) ;
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 [ ] ;
2023-04-10 19:12:01 +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 ;
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-04-08 01:44:57 +01:00
if ( ! user ? . isOnField ( ) )
return super . end ( ) ;
2023-12-07 22:43:56 +00:00
2023-04-13 17:16:36 +01:00
const overridden = new Utils . BooleanHolder ( false ) ;
2023-04-10 21:17:25 +01:00
2023-05-18 16:11:06 +01:00
// Assume single target for override
2023-12-04 05:09:38 +00:00
applyMoveAttrs ( OverrideMoveEffectAttr , user , this . getTarget ( ) , this . move . getMove ( ) , overridden , this . move . virtual ) . then ( ( ) = > {
2023-04-10 21:17:25 +01:00
2023-12-07 22:43:56 +00:00
if ( overridden . value )
return this . end ( ) ;
2023-12-04 05:09:38 +00: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
applyMoveAttrs ( MultiHitAttr , user , this . getTarget ( ) , this . move . getMove ( ) , hitCount ) ;
2024-04-14 17:02:44 +01:00
if ( this . move . getMove ( ) instanceof AttackMove && ! this . move . getMove ( ) . getAttrs ( FixedDamageAttr ) . length )
2024-04-03 16:42:09 +01:00
this . scene . applyModifiers ( PokemonMultiHitModifier , user . isPlayer ( ) , user , hitCount , new Utils . IntegerHolder ( 0 ) ) ;
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-03-29 01:18:07 +00:00
if ( ! activeTargets . length || ( ! this . move . getMove ( ) . 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 ) {
this . scene . queueMessage ( getPokemonMessage ( user , '\'s\nattack missed!' ) ) ;
moveHistoryEntry . result = MoveResult . MISS ;
applyMoveAttrs ( MissEffectAttr , user , null , this . move . getMove ( ) ) ;
} else {
2024-04-24 00:19:36 +01:00
this . scene . queueMessage ( i18next . t ( 'menu: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
new MoveAnim ( this . move . getMove ( ) . id as Moves , user , this . getTarget ( ) ? . getBattlerIndex ( ) ) . play ( this . scene , ( ) = > {
for ( let target of targets ) {
if ( ! targetHitChecks [ target . getBattlerIndex ( ) ] ) {
2023-10-30 16:33:20 +00:00
user . turnData . hitCount = 1 ;
user . turnData . hitsLeft = 1 ;
2023-05-18 16:11:06 +01:00
this . scene . queueMessage ( getPokemonMessage ( user , '\'s\nattack missed!' ) ) ;
if ( moveHistoryEntry . result === MoveResult . PENDING )
moveHistoryEntry . result = MoveResult . MISS ;
applyMoveAttrs ( MissEffectAttr , user , null , this . move . getMove ( ) ) ;
continue ;
2023-04-25 06:32:48 +01:00
}
2023-05-18 16:11:06 +01:00
2024-03-13 21:09:23 +00:00
const isProtected = ! this . move . getMove ( ) . 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 ;
2023-10-10 01:20:02 +01:00
const hitResult = ! isProtected ? target . apply ( user , this . 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-03-18 21:15:20 +00:00
applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && ( attr as MoveEffectAttr ) . trigger === MoveEffectTrigger . PRE_APPLY && ( ! attr . firstHitOnly || firstHit ) ,
2023-10-31 18:09:33 +00:00
user , target , this . move . getMove ( ) ) . then ( ( ) = > {
if ( hitResult !== HitResult . FAIL ) {
2024-02-27 19:21:17 +00:00
const chargeEffect = ! ! this . move . getMove ( ) . getAttrs ( ChargeAttr ) . find ( ca = > ( ca as ChargeAttr ) . usedChargeEffect ( user , this . getTarget ( ) , this . move . getMove ( ) ) ) ;
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
Utils . executeIf ( ! chargeEffect , ( ) = > applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && ( attr as MoveEffectAttr ) . trigger === MoveEffectTrigger . POST_APPLY
2024-03-18 21:15:20 +00:00
&& ( attr as MoveEffectAttr ) . selfTarget && ( ! attr . firstHitOnly || firstHit ) , user , target , this . move . getMove ( ) ) ) . then ( ( ) = > {
2023-10-31 18:09:33 +00:00
if ( hitResult !== HitResult . NO_EFFECT ) {
applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && ( attr as MoveEffectAttr ) . trigger === MoveEffectTrigger . POST_APPLY
2024-03-18 21:15:20 +00:00
&& ! ( attr as MoveEffectAttr ) . selfTarget && ( ! attr . firstHitOnly || firstHit ) , user , target , this . move . getMove ( ) ) . 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 ) ;
if ( flinched . value )
target . addTag ( BattlerTagType . FLINCHED , undefined , this . move . moveId , user . id ) ;
}
2024-03-18 21:15:20 +00:00
Utils . executeIf ( ! isProtected && ! chargeEffect , ( ) = > applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && ( attr as MoveEffectAttr ) . trigger === MoveEffectTrigger . HIT && ( ! attr . firstHitOnly || firstHit ) ,
2023-10-31 18:09:33 +00:00
user , target , this . move . getMove ( ) ) . then ( ( ) = > {
2024-04-15 03:26:35 +01:00
return Utils . executeIf ( ! target . isFainted ( ) || target . canApplyAbility ( ) , ( ) = > applyPostDefendAbAttrs ( PostDefendAbAttr , target , user , this . move , hitResult ) . then ( ( ) = > {
2024-02-28 02:34:21 +00:00
if ( ! user . isPlayer ( ) && this . move . getMove ( ) instanceof AttackMove )
2024-04-23 04:58:26 +01:00
user . scene . applyShuffledModifiers ( this . scene , EnemyAttackStatusEffectChanceModifier , false , target ) ;
2023-12-23 02:42:47 +00:00
} ) ) . then ( ( ) = > {
applyPostAttackAbAttrs ( PostAttackAbAttr , user , target , this . move , hitResult ) . then ( ( ) = > {
2024-02-28 02:34:21 +00:00
if ( this . move . getMove ( ) instanceof AttackMove )
2023-12-23 02:42:47 +00:00
this . scene . applyModifiers ( ContactHeldItemTransferChanceModifier , this . player , user , target . getFieldIndex ( ) ) ;
resolve ( ) ;
} ) ;
} ) ;
2023-10-31 18:09:33 +00:00
} )
) . then ( ( ) = > resolve ( ) ) ;
} ) ;
2024-04-13 02:49:48 +01:00
} else
applyMoveAttrs ( NoEffectAttr , user , null , this . move . getMove ( ) ) . then ( ( ) = > resolve ( ) ) ;
2023-10-31 18:09:33 +00:00
} ) ;
2023-10-31 21:38:44 +00:00
} else
resolve ( ) ;
2023-10-31 18:09:33 +00:00
} ) ;
} ) ) ;
2023-04-23 15:24:22 +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() {
const user = this . getUserPokemon ( ) ;
2023-12-08 14:51:45 +00:00
if ( user ) {
if ( -- user . turnData . hitsLeft >= 1 && this . getTarget ( ) ? . isActive ( ) )
this . scene . unshiftPhase ( this . getNewHitPhase ( ) ) ;
else {
2023-12-22 22:08:37 +00:00
const hitsTotal = user . turnData . hitCount - Math . max ( user . turnData . hitsLeft , 0 ) ;
if ( hitsTotal > 1 )
2024-04-24 00:19:36 +01:00
this . scene . queueMessage ( i18next . t ( 'menu: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
}
2023-04-10 21:17:25 +01:00
super . end ( ) ;
}
2023-05-18 16:11:06 +01:00
hitCheck ( target : Pokemon ) : boolean {
2023-05-01 19:41:44 +01:00
if ( this . move . getMove ( ) . moveTarget === MoveTarget . USER )
2023-04-18 17:30:47 +01:00
return true ;
2023-04-13 17:16:36 +01:00
2024-04-08 01:44:57 +01:00
const user = this . getUserPokemon ( ) ;
2023-10-30 16:33:20 +00:00
// Hit check only calculated on first hit for multi-hit moves
2024-04-08 01:44:57 +01:00
if ( user . turnData . hitsLeft < user . turnData . hitCount )
2023-10-30 16:33:20 +00:00
return true ;
2024-04-21 06:26:30 +01:00
if ( user . hasAbilityWithAttr ( AlwaysHitAbAttr ) || target . hasAbilityWithAttr ( AlwaysHitAbAttr ) )
return true ;
2023-05-18 16:11:06 +01:00
const hiddenTag = target . getTag ( HiddenTag ) ;
if ( hiddenTag && ! this . move . getMove ( ) . getAttrs ( HitsTagAttr ) . filter ( hta = > ( hta as HitsTagAttr ) . tagType === hiddenTag . tagType ) . length )
return false ;
2023-04-19 19:07:38 +01:00
2024-04-08 01:44:57 +01:00
if ( user . getTag ( BattlerTagType . IGNORE_ACCURACY ) && ( user . getLastXMoves ( ) . find ( ( ) = > true ) ? . targets || [ ] ) . indexOf ( target . getBattlerIndex ( ) ) > - 1 )
2023-04-28 01:12:25 +01:00
return true ;
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 ) ;
2023-07-05 17:10:23 +01:00
2023-04-19 19:07:38 +01:00
if ( moveAccuracy . value === - 1 )
return true ;
2023-05-08 23:48:35 +01:00
2024-04-22 06:05:48 +01:00
const isOhko = ! ! this . move . getMove ( ) . getAttrs ( OneHitKOAccuracyAttr ) . length ;
if ( ! isOhko )
user . scene . applyModifiers ( PokemonMoveAccuracyBoosterModifier , user . isPlayer ( ) , user , moveAccuracy ) ;
2024-04-20 12:52:47 +01:00
2024-04-07 03:10:38 +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-04-07 03:10:38 +01:00
2024-04-22 06:05:48 +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 ) ;
2023-04-13 17:16:36 +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 ) ;
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 {
2023-12-04 05:09:38 +00:00
if ( this . battlerIndex > BattlerIndex . ENEMY_2 )
return this . scene . getPokemonById ( this . battlerIndex ) ;
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 ( ) ;
if ( pokemon . isActive ( true ) )
pokemon . lapseTags ( BattlerTagLapseType . AFTER_MOVE ) ;
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 ;
2023-04-25 03:32:12 +01:00
} else if ( player )
console . log ( Moves [ moveId ] ) ;
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 , ( ) = > {
2023-04-14 04:04:51 +01:00
if ( player )
this . playMoveAnim ( moveQueue , false ) ;
else
this . playMoveAnim ( moveQueue , true ) ;
} ) ;
} ) ;
} ) ;
}
}
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-04-11 14:24:03 +01:00
this . scene . abilityBar . showAbility ( this . getPokemon ( ) , this . passive ) ;
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 ;
2023-04-11 04:15:06 +01:00
2024-04-11 17:11:55 +01:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , selfTarget : boolean , stats : BattleStat [ ] , levels : integer , showMessage : boolean = true , ignoreAbilities : boolean = false ) {
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 ;
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 ) ;
if ( ! pokemon . isActive ( true ) )
return this . end ( ) ;
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 ) ;
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 ) ;
if ( ! cancelled . value && ! this . selfTarget && this . levels < 0 )
2023-05-06 17:13:35 +01:00
applyPreStatChangeAbAttrs ( ProtectStatAbAttr , this . getPokemon ( ) , stat , cancelled ) ;
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
if ( ! this . ignoreAbilities )
applyAbAttrs ( StatChangeMultiplierAbAttr , pokemon , null , levels ) ;
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 ) ;
for ( let message of messages )
this . scene . queueMessage ( message ) ;
}
2023-04-11 04:15:06 +01:00
2023-04-27 04:33:13 +01:00
for ( let 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 ) ;
2023-04-11 04:15:06 +01:00
2024-05-01 04:02:16 +01:00
applyPostStatChangeAbAttrs ( PostStatChangeAbAttr , pokemon , filteredStats , this . levels , this . selfTarget ) ;
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 ( ) ;
const statSprite = this . scene . add . tileSprite ( tileX , tileY , tileWidth , tileHeight , 'battle_stats' , filteredStats . length > 1 ? 'mix' : BattleStat [ filteredStats [ 0 ] ] . toLowerCase ( ) ) ;
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 ) ;
2023-11-06 03:11:38 +00: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 ,
2023-11-06 03:11:38 +00:00
y : ` ${ levels . value >= 1 ? '-' : '+' } = ${ 160 * 6 } `
2023-04-11 04:15:06 +01:00
} ) ;
this . scene . time . delayedCall ( 1750 , ( ) = > {
pokemon . disableMask ( ) ;
end ( ) ;
} ) ;
} else
end ( ) ;
}
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 ( ) ;
if ( existingPhase . stats [ 0 ] !== this . stats [ 0 ] )
continue ;
}
this . levels += existingPhase . levels ;
2024-04-24 00:19:36 +01:00
2024-04-24 05:25:01 +01:00
if ( ! this . scene . tryRemovePhase ( p = > p === existingPhase ) )
break ;
}
}
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 ) ;
if ( ! this . scene . tryRemovePhase ( p = > p === existingPhase ) )
break ;
}
}
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 ] ;
if ( ! relLevelStatIndexes [ relLevel ] )
relLevelStatIndexes [ relLevel ] = [ ] ;
relLevelStatIndexes [ relLevel ] . push ( rl ) ;
}
Object . keys ( relLevelStatIndexes ) . forEach ( rl = > {
const relLevelStats = stats . filter ( ( _ , i ) = > relLevelStatIndexes [ rl ] . includes ( i ) ) ;
let statsFragment = '' ;
if ( relLevelStats . length > 1 ) {
statsFragment = relLevelStats . length >= 5
? 'stats'
: ` ${ relLevelStats . slice ( 0 , - 1 ) . map ( s = > getBattleStatName ( s ) ) . join ( ', ' ) } , and ${ getBattleStatName ( relLevelStats [ relLevelStats . length - 1 ] ) } ` ;
} else
statsFragment = getBattleStatName ( relLevelStats [ 0 ] ) ;
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
2023-04-27 19:30:03 +01:00
constructor ( scene : BattleScene , weather : Weather ) {
2023-05-18 16:11:06 +01:00
super ( scene , undefined , undefined , CommonAnim . SUNNY + ( weather . weatherType - 1 ) ) ;
2023-04-19 19:07:38 +01:00
this . weather = weather ;
}
start() {
if ( this . weather . isDamaging ( ) ) {
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
if ( cancelled . value )
return ;
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 ;
2023-04-27 19:30:03 +01:00
if ( ! immune )
inflictDamage ( pokemon ) ;
} ) ;
}
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 ;
2023-04-10 19:12:01 +01:00
2023-05-18 16:11:06 +01:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , statusEffect : StatusEffect , cureTurn? : integer , sourceText? : string ) {
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 ;
2023-04-10 19:12:01 +01:00
}
start() {
const pokemon = this . getPokemon ( ) ;
2023-04-12 00:08:03 +01:00
if ( ! pokemon . status ) {
if ( pokemon . trySetStatus ( this . statusEffect ) ) {
2023-04-16 23:40:32 +01:00
if ( this . cureTurn )
pokemon . status . cureTurn = this . cureTurn ;
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 ) ) ) ;
2023-04-12 00:08:03 +01:00
if ( pokemon . status . isPostTurn ( ) )
2023-05-18 16:11:06 +01:00
this . scene . pushPhase ( new PostTurnStatusEffectPhase ( this . scene , this . battlerIndex ) ) ;
2023-04-12 00:08:03 +01:00
this . end ( ) ;
} ) ;
return ;
}
} else if ( pokemon . status . effect === this . statusEffect )
2023-04-22 00:30:04 +01:00
this . scene . queueMessage ( getPokemonMessage ( pokemon , getStatusEffectOverlapText ( this . statusEffect ) ) ) ;
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 ) {
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 ;
}
if ( damage ) {
this . scene . damageNumberHandler . add ( this . getPokemon ( ) , pokemon . damage ( damage ) ) ;
pokemon . updateInfo ( ) ;
}
new CommonBattleAnim ( CommonAnim . POISON + ( pokemon . status . effect - 1 ) , pokemon ) . play ( this . scene , ( ) = > this . end ( ) ) ;
} else
this . end ( ) ;
2023-04-10 19:12:01 +01:00
} else
this . end ( ) ;
}
}
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 ( ) ;
2023-10-18 23:01:15 +01:00
if ( this . text . indexOf ( '$' ) > - 1 ) {
const pageIndex = this . text . indexOf ( '$' ) ;
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() {
if ( this . scene . abilityBar . shown )
this . scene . abilityBar . hide ( ) ;
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 ) {
2023-05-18 16:11:06 +01:00
case HitResult . EFFECTIVE :
2023-10-21 13:58:39 +01:00
this . scene . playSound ( 'hit' ) ;
2023-04-15 06:32:16 +01:00
break ;
2023-05-18 16:11:06 +01:00
case HitResult . SUPER_EFFECTIVE :
2023-11-08 03:23:42 +00:00
case HitResult . ONE_HIT_KO :
2023-10-21 13:58:39 +01:00
this . scene . playSound ( 'hit_strong' ) ;
2023-04-15 06:32:16 +01:00
break ;
2023-05-18 16:11:06 +01:00
case HitResult . NOT_VERY_EFFECTIVE :
2023-10-21 13:58:39 +01:00
this . scene . playSound ( 'hit_weak' ) ;
2023-04-15 06:32:16 +01:00
break ;
}
2024-03-01 14:35:36 +00:00
if ( this . amount )
this . scene . damageNumberHandler . add ( this . getPokemon ( ) , this . amount , this . damageResult , this . critical ) ;
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 ) ;
if ( ! flashTimer . repeatCount )
this . getPokemon ( ) . updateInfo ( ) . then ( ( ) = > this . end ( ) ) ;
}
} ) ;
} else
this . getPokemon ( ) . updateInfo ( ) . then ( ( ) = > this . end ( ) ) ;
2023-04-15 06:32:16 +01:00
}
2024-01-13 17:24:24 +00:00
end() {
switch ( this . scene . currentBattle . battleSpec ) {
case BattleSpec . FINAL_BOSS :
const pokemon = this . getPokemon ( ) ;
2024-03-02 04:10:46 +00:00
if ( pokemon instanceof EnemyPokemon && pokemon . isBoss ( ) && ! pokemon . formIndex && pokemon . bossSegmentIndex < 1 ) {
2024-01-13 17:24:24 +00:00
this . scene . fadeOutBgm ( Utils . fixedInt ( 2000 ) , false ) ;
2024-02-15 04:38:37 +00:00
this . scene . ui . showDialogue ( battleSpecDialogue [ BattleSpec . FINAL_BOSS ] . firstStageWin , pokemon . species . name , null , ( ) = > {
2024-01-13 17:24:24 +00:00
this . scene . addEnemyModifier ( getModifierType ( modifierTypes . MINI_BLACK_HOLE ) . newModifier ( pokemon ) as PersistentModifier , false , true ) ;
pokemon . generateAndPopulateMoveset ( 1 ) ;
2024-01-15 01:47:08 +00:00
this . scene . setFieldScale ( 0.75 ) ;
2024-01-13 17:24:24 +00:00
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeManualTrigger , false ) ;
2024-01-14 01:15:55 +00:00
this . scene . currentBattle . double = true ;
const availablePartyMembers = this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) ;
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-13 17:24:24 +00:00
super . end ( ) ;
2024-02-13 23:42:11 +00:00
} ) ;
2024-01-13 17:24:24 +00:00
return ;
}
break ;
}
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 ) {
if ( ! -- instantReviveModifier . stackCount )
this . scene . removeModifier ( instantReviveModifier ) ;
this . scene . updateModifiers ( this . player ) ;
return this . end ( ) ;
}
2023-06-06 13:16:07 +01:00
}
2024-01-13 17:24:24 +00:00
if ( ! this . tryOverrideForBattleSpec ( ) )
this . doFaint ( ) ;
}
doFaint ( ) : void {
const pokemon = this . getPokemon ( ) ;
2023-06-06 13:16:07 +01:00
this . scene . queueMessage ( getPokemonMessage ( pokemon , ' fainted!' ) , 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 ] ;
applyPostFaintAbAttrs ( PostFaintAbAttr , pokemon , this . scene . getPokemonById ( lastAttack . sourceId ) , new PokemonMove ( lastAttack . move ) , 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 ) ;
if ( defeatSource ? . isOnField ( ) )
applyPostVictoryAbAttrs ( PostVictoryAbAttr , defeatSource ) ;
}
2023-04-22 03:59:09 +01:00
if ( this . player ) {
2024-02-20 19:39:25 +00:00
const nonFaintedPartyMembers = this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) ;
const nonFaintedPartyMemberCount = nonFaintedPartyMembers . length ;
2023-05-18 16:11:06 +01:00
if ( ! nonFaintedPartyMemberCount )
this . scene . unshiftPhase ( new GameOverPhase ( this . scene ) ) ;
2024-02-20 19:39:25 +00:00
else if ( nonFaintedPartyMemberCount >= this . scene . currentBattle . getBattlerCount ( ) || ( this . scene . currentBattle . double && ! nonFaintedPartyMembers [ 0 ] . isActive ( true ) ) )
2023-11-15 23:45:10 +00:00
this . scene . pushPhase ( new SwitchPhase ( this . scene , this . fieldIndex , true , false ) ) ;
2024-02-20 19:39:25 +00:00
if ( nonFaintedPartyMemberCount === 1 && this . scene . currentBattle . double )
2023-07-06 03:23:50 +01:00
this . scene . unshiftPhase ( new ToggleDoublePositionPhase ( this . scene , true ) ) ;
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 ;
2023-11-15 23:45:10 +00:00
if ( hasReservePartyMember )
2023-11-27 03:22:05 +00:00
this . scene . pushPhase ( new SwitchSummonPhase ( this . scene , this . fieldIndex , - 1 , false , false , false ) ) ;
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-03-28 04:24:24 +00:00
if ( targetingMovePhase && targetingMovePhase . targets [ 0 ] !== allyPokemon . getBattlerIndex ( ) )
targetingMovePhase . targets [ 0 ] = allyPokemon . getBattlerIndex ( ) ;
} 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 ( ( ) = > {
2023-12-22 04:00:45 +00:00
const friendshipDecrease = new Utils . IntegerHolder ( 10 ) ;
pokemon . friendship = Math . max ( pokemon . friendship - friendshipDecrease . value , 0 ) ;
2023-04-10 19:12:01 +01:00
pokemon . hideInfo ( ) ;
2023-10-21 13:58:39 +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 ,
ease : 'Sine.easeIn' ,
onComplete : ( ) = > {
pokemon . setVisible ( false ) ;
pokemon . y -= 150 ;
2023-04-16 23:40:32 +01:00
pokemon . trySetStatus ( StatusEffect . FAINT ) ;
2023-04-15 06:32:16 +01:00
if ( pokemon . isPlayer ( ) )
2023-04-10 19:12:01 +01:00
this . scene . currentBattle . removeFaintedParticipant ( pokemon as PlayerPokemon ) ;
2024-03-17 15:36:19 +00:00
else {
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 ) {
case BattleSpec . FINAL_BOSS :
2024-01-30 03:07:34 +00:00
if ( ! this . player ) {
const enemy = this . getPokemon ( ) ;
2024-03-01 14:35:36 +00:00
if ( enemy . formIndex )
2024-02-15 04:38:37 +00:00
this . scene . ui . showDialogue ( battleSpecDialogue [ BattleSpec . FINAL_BOSS ] . secondStageWin , enemy . species . name , null , ( ) = > this . doFaint ( ) ) ;
2024-03-01 14:35:36 +00:00
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-30 03:07:34 +00:00
}
2024-03-01 14:35:36 +00: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 ;
2023-10-03 17:50:31 +01:00
const expPartyMembers = party . filter ( p = > p . hp && 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 ( ) ;
if ( this . scene . currentBattle . battleType === BattleType . TRAINER )
expValue = Math . floor ( expValue * 1.5 ) ;
for ( let partyMember of expPartyMembers ) {
const pId = partyMember . id ;
const participated = participantIds . has ( pId ) ;
2023-12-22 04:00:45 +00:00
if ( participated ) {
const friendshipIncrease = new Utils . IntegerHolder ( 2 ) ;
this . scene . applyModifier ( PokemonFriendshipBoosterModifier , true , partyMember , friendshipIncrease ) ;
partyMember . friendship = Math . min ( partyMember . friendship + friendshipIncrease . value , 255 ) ;
if ( partyMember . friendship === 255 )
this . scene . validateAchv ( achvs . MAX_FRIENDSHIP ) ;
}
2023-11-24 21:27:26 +00:00
else if ( ! expShareModifier ) {
partyMemberExp . push ( 0 ) ;
continue ;
}
let expMultiplier = 0 ;
if ( participated ) {
expMultiplier += ( 1 / participantIds . size ) ;
if ( participantIds . size > 1 && multipleParticipantExpBonusModifier )
expMultiplier += multipleParticipantExpBonusModifier . getStackCount ( ) * 0.2 ;
} else if ( expShareModifier )
expMultiplier += ( expShareModifier . getStackCount ( ) * 0.2 ) / participantIds . size ;
if ( partyMember . pokerus )
expMultiplier *= 1.5 ;
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 ) = > {
if ( expPartyMember . level <= medianLevel )
recipientExpPartyMemberIndexes . push ( epm ) ;
} ) ;
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-04-07 22:20:24 +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 ) ) ;
2023-10-18 23:01:15 +01:00
if ( this . scene . currentBattle . battleType === BattleType . TRAINER )
this . scene . pushPhase ( new TrainerVictoryPhase ( this . scene ) ) ;
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 ) ) ;
2023-11-28 21:59:40 +00:00
if ( this . scene . currentBattle . waveIndex % 10 )
2023-10-25 19:15:44 +01:00
this . scene . pushPhase ( new SelectModifierPhase ( this . scene ) ) ;
2024-03-17 02:06:56 +00:00
else if ( this . scene . gameMode . isDaily ) {
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , modifierTypes . EXP_CHARM ) ) ;
if ( this . scene . currentBattle . waveIndex > 10 && ! this . scene . gameMode . isWaveFinal ( this . scene . currentBattle . waveIndex ) )
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , modifierTypes . GOLDEN_POKEBALL ) ) ;
} else {
2024-03-25 15:00:42 +00:00
const superExpWave = ! this . scene . gameMode . isEndless ? ( this . scene . offsetGym ? 0 : 20 ) : 10 ;
2024-03-24 15:17:12 +00:00
if ( this . scene . gameMode . isEndless && this . scene . currentBattle . waveIndex === 10 )
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , modifierTypes . EXP_SHARE ) ) ;
2024-01-05 00:37:07 +00:00
if ( this . scene . currentBattle . waveIndex <= 750 && ( this . scene . currentBattle . waveIndex <= 500 || ( this . scene . currentBattle . waveIndex % 30 ) === superExpWave ) )
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 ) ) ;
if ( this . scene . currentBattle . waveIndex <= 150 && ! ( this . scene . currentBattle . waveIndex % 50 ) )
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , modifierTypes . GOLDEN_POKEBALL ) ) ;
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 ;
for ( let modifierRewardFunc of modifierRewardFuncs )
2023-10-25 19:15:44 +01:00
this . scene . unshiftPhase ( new ModifierRewardPhase ( this . scene , modifierRewardFunc ) ) ;
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 ] ) ) {
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 ] ) ) ;
2023-12-20 22:30:06 +00:00
}
2024-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu: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-02-23 00:24:00 +00:00
const showMessage = ( ) = > {
2023-10-18 23:01:15 +01:00
let message : string ;
2024-02-17 05:40:03 +00:00
this . scene . executeWithSeedOffset ( ( ) = > message = Utils . randSeedItem ( victoryMessages ) , this . scene . currentBattle . waveIndex ) ;
2023-10-18 23:01:15 +01:00
const messagePages = message . split ( /\$/g ) . map ( m = > m . trim ( ) ) ;
for ( let p = messagePages . length - 1 ; p >= 0 ; p -- ) {
2024-02-23 00:24:00 +00:00
const originalFunc = showMessageOrEnd ;
showMessageOrEnd = ( ) = > this . scene . ui . showDialogue ( messagePages [ p ] , this . scene . currentBattle . trainer . getName ( ) , null , originalFunc ) ;
2023-10-18 23:01:15 +01:00
}
2024-02-23 00:24:00 +00:00
showMessageOrEnd ( ) ;
} ;
let showMessageOrEnd = ( ) = > this . end ( ) ;
if ( victoryMessages ? . length ) {
if ( this . scene . currentBattle . trainer . config . hasCharSprite ) {
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-02-23 00:24:00 +00:00
} else
showMessage ( ) ;
} else
showMessageOrEnd ( ) ;
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
2023-11-11 02:11:36 +00:00
this . scene . ui . showText ( ` You got ₽ ${ moneyAmount . value . toLocaleString ( 'en-US' ) } \ nfor winning! ` , 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-04-04 22:43:37 +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 ) ;
} ) ;
} )
}
}
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 ( ( ) = > {
this . scene . gameData . saveSystem ( ) . then ( success = > {
if ( success ) {
2024-04-04 22:43:37 +01:00
this . scene . playSound ( 'level_up_fanfare' ) ;
2024-02-22 21:21:34 +00:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . arenaBg . setVisible ( false ) ;
this . scene . ui . fadeIn ( 250 ) . then ( ( ) = > {
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 ) ;
} ) ;
} else
this . scene . reset ( true ) ;
} ) ;
} ) ;
} )
2023-10-20 16:38: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 ;
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-04-04 20:22:05 +01:00
if ( this . victory || ! this . scene . enableRetries )
this . handleClearSession ( ) ;
else {
this . scene . ui . showText ( ` Would you like to retry from the start of the battle? ` , null , ( ) = > {
this . scene . ui . setMode ( Mode . CONFIRM , ( ) = > {
this . scene . ui . fadeOut ( 1250 ) . then ( ( ) = > {
this . scene . reset ( ) ;
this . scene . clearPhaseQueue ( ) ;
this . scene . gameData . loadSession ( this . scene , this . scene . sessionSlotId ) . then ( ( ) = > {
this . scene . pushPhase ( new EncounterPhase ( this . scene , true ) ) ;
const availablePartyMembers = this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) . length ;
this . scene . pushPhase ( new SummonPhase ( this . scene , 0 ) ) ;
if ( this . scene . currentBattle . double && availablePartyMembers > 1 )
this . scene . pushPhase ( new SummonPhase ( this . scene , 1 ) ) ;
if ( this . scene . currentBattle . waveIndex > 1 && this . scene . currentBattle . battleType !== BattleType . TRAINER ) {
this . scene . pushPhase ( new CheckSwitchPhase ( this . scene , 0 , this . scene . currentBattle . double ) ) ;
if ( this . scene . currentBattle . double && availablePartyMembers > 1 )
this . scene . pushPhase ( new CheckSwitchPhase ( this . scene , 1 , this . scene . currentBattle . double ) ) ;
}
this . scene . ui . fadeIn ( 1250 ) ;
this . end ( ) ;
} ) ;
} ) ;
} , ( ) = > this . handleClearSession ( ) , false , 0 , 0 , 1000 ) ;
} ) ;
}
}
handleClearSession ( ) : void {
2024-03-18 00:31:09 +00:00
this . scene . gameData . tryClearSession ( this . scene , this . scene . sessionSlotId ) . then ( ( success : boolean | [ boolean , boolean ] ) = > {
2024-04-21 21:19:11 +01:00
if ( ! success [ 0 ] )
return this . scene . reset ( 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-03-17 02:06:56 +00:00
if ( this . victory && success [ 1 ] ) {
if ( this . scene . gameMode . isClassic ) {
firstClear = this . scene . validateAchv ( achvs . CLASSIC_VICTORY ) ;
this . scene . gameData . gameStats . sessionsWon ++ ;
} else if ( this . scene . gameMode . isDaily && success [ 1 ] )
this . scene . gameData . gameStats . dailyRunSessionsWon ++ ;
2024-01-11 17:26:32 +00:00
}
this . scene . gameData . saveSystem ( ) ;
2024-01-01 03:49:50 +00:00
const fadeDuration = this . victory ? 10000 : 5000 ;
this . scene . fadeOutBgm ( fadeDuration , true ) ;
this . scene . ui . fadeOut ( fadeDuration ) . then ( ( ) = > {
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-02-22 21:21:34 +00:00
this . handleUnlocks ( ) ;
2024-03-17 02:06:56 +00:00
if ( this . victory && ! firstClear && success [ 1 ] )
2024-02-22 21:21:34 +00:00
this . scene . unshiftPhase ( new GameOverModifierRewardPhase ( this . scene , modifierTypes . VOUCHER_PREMIUM ) ) ;
2024-01-01 03:49:50 +00:00
this . scene . reset ( ) ;
2024-03-15 01:49:49 +00:00
this . scene . unshiftPhase ( new TitlePhase ( this . scene ) ) ;
2024-01-01 03:49:50 +00:00
this . end ( ) ;
} ) ;
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 ) {
2023-06-05 16:39:49 +01:00
if ( ! this . scene . gameData . unlocks [ Unlockables . ENDLESS_MODE ] )
this . scene . unshiftPhase ( new UnlockPhase ( this . scene , Unlockables . ENDLESS_MODE ) ) ;
2023-11-04 23:46:48 +00:00
if ( this . scene . getParty ( ) . filter ( p = > p . fusionSpecies ) . length && ! this . scene . gameData . unlocks [ Unlockables . SPLICED_ENDLESS_MODE ] )
this . scene . unshiftPhase ( new UnlockPhase ( this . scene , Unlockables . SPLICED_ENDLESS_MODE ) ) ;
2023-06-05 16:39:49 +01:00
if ( ! this . scene . gameData . unlocks [ Unlockables . MINI_BLACK_HOLE ] )
this . scene . unshiftPhase ( new UnlockPhase ( this . scene , Unlockables . MINI_BLACK_HOLE ) ) ;
}
2023-04-29 06:40:24 +01:00
}
}
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 ;
2023-12-30 23:41:25 +00:00
this . scene . gameData . saveSystem ( ) . then ( success = > {
if ( success ) {
2024-04-04 22:43:37 +01:00
this . scene . playSound ( 'level_up_fanfare' ) ;
2023-12-30 23:41:25 +00:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . arenaBg . setVisible ( false ) ;
this . scene . ui . fadeIn ( 250 ) . then ( ( ) = > {
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 ) ;
} ) ;
} else
this . scene . reset ( true ) ;
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
if ( this . isModal && ! this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) && ! p . isActive ( true ) ) . length )
return super . end ( ) ;
2023-11-25 20:53:38 +00:00
// Override field index to 0 in case of double battle where 2/3 remaining party members fainted at once
const fieldIndex = this . scene . currentBattle . getBattlerCount ( ) === 1 || this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) . length > 1 ? this . fieldIndex : 0 ;
this . scene . ui . setMode ( Mode . PARTY , this . isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH , fieldIndex , ( slotIndex : integer , option : PartyOption ) = > {
2023-05-18 16:11:06 +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 ) ) ;
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 ( ) ;
let 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-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:expGain' , { pokemonName : pokemon.name , exp : exp.value } ) , null , ( ) = > {
2023-04-10 19:12:01 +01:00
const lastLevel = pokemon . level ;
let newLevel : integer ;
pokemon . addExp ( exp . value ) ;
newLevel = pokemon . level ;
if ( newLevel > lastLevel )
this . scene . unshiftPhase ( new LevelUpPhase ( this . scene , this . partyMemberIndex , lastLevel , newLevel ) ) ;
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 ( ) ;
let exp = new Utils . NumberHolder ( this . expValue ) ;
this . scene . applyModifiers ( ExpBoosterModifier , true , exp ) ;
exp . value = Math . floor ( exp . value ) ;
const lastLevel = pokemon . level ;
let newLevel : integer ;
pokemon . addExp ( exp . value ) ;
newLevel = pokemon . level ;
if ( newLevel > lastLevel )
this . scene . unshiftPhase ( new LevelUpPhase ( this . scene , this . partyMemberIndex , lastLevel , newLevel ) ) ;
this . scene . unshiftPhase ( new HidePartyExpBarPhase ( this . scene ) ) ;
pokemon . updateInfo ( ) ;
2024-04-18 05:49:44 +01:00
if ( this . scene . expGainsSpeed < 3 ) {
2024-04-18 05:24:47 +01:00
this . scene . partyExpBar . showPokemonExp ( pokemon , exp . value ) . then ( ( ) = > {
if ( newLevel > lastLevel )
this . end ( ) ;
else
2024-04-18 05:49:44 +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 ;
}
start() {
super . start ( ) ;
2024-01-11 17:26:32 +00:00
if ( this . level > this . scene . gameData . gameStats . highestLevel )
this . scene . gameData . gameStats . highestLevel = this . level ;
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-04-04 22:43:37 +01:00
this . scene . playSound ( 'level_up_fanfare' ) ;
2024-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:levelUp' , { pokemonName : this.getPokemon ( ) . name , level : this.level } ) , null , ( ) = > this . scene . ui . getMessageHandler ( ) . promptLevelUpStats ( this . partyMemberIndex , prevStats , false ) . then ( ( ) = > this . end ( ) ) , null , true ) ;
2023-04-19 19:07:38 +01:00
if ( this . level <= 100 ) {
const levelMoves = this . getPokemon ( ) . getLevelMoves ( this . lastLevel + 1 ) ;
for ( let lm of levelMoves )
2024-03-01 21:21:28 +00:00
this . scene . unshiftPhase ( new LearnMovePhase ( this . scene , this . partyMemberIndex , lm [ 1 ] ) ) ;
2023-04-19 19:07:38 +01:00
}
2023-12-14 17:55:11 +00:00
if ( ! pokemon . pauseEvolutions ) {
const evolution = pokemon . getEvolution ( ) ;
if ( evolution )
2024-01-10 04:34:43 +00:00
this . scene . unshiftPhase ( new EvolutionPhase ( this . scene , pokemon as PlayerPokemon , evolution , this . lastLevel ) ) ;
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
2023-12-07 22:43:56 +00:00
if ( existingMoveIndex > - 1 )
return this . end ( ) ;
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-04-04 22:43:37 +01:00
this . scene . playSound ( 'level_up_fanfare' ) ;
2024-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu: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
} ) ;
} ) ;
} ) ;
} else {
this . scene . ui . setMode ( messageMode ) . then ( ( ) = > {
2024-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:learnMovePrompt' , { pokemonName : pokemon.name , moveName : move.name } ) , null , ( ) = > {
this . scene . ui . showText ( i18next . t ( 'menu:learnMoveLimitReached' , { pokemonName : pokemon.name } ) , null , ( ) = > {
this . scene . ui . showText ( i18next . t ( 'menu:learnMoveReplaceQuestion' , { moveName : move.name } ) , null , ( ) = > {
2023-04-10 19:12:01 +01:00
const noHandler = ( ) = > {
this . scene . ui . setMode ( messageMode ) . then ( ( ) = > {
2024-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu: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-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu: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-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu: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 ( ( ) = > {
this . scene . ui . showText ( '@d{32}1, @d{15}2, and@d{15}… @d{15}… @d{15}… @d{15}@s{pb_bounce_1}Poof!' , null , ( ) = > {
2024-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu:learnMoveForgetSuccess' , { pokemonName : pokemon.name , moveName : pokemon.moveset [ moveIndex ] . getName ( ) } ) , null , ( ) = > {
2023-04-10 19:12:01 +01:00
this . scene . ui . showText ( 'And…' , 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-20 20:46:05 +01:00
export class BerryPhase extends CommonAnimPhase {
2023-05-18 16:11:06 +01:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex , undefined , CommonAnim . USE_ITEM ) ;
2023-04-20 20:46:05 +01:00
}
start() {
2024-03-25 04:21:00 +00:00
let berryModifiers : BerryModifier [ ] ;
2023-04-20 20:46:05 +01:00
2024-04-26 02:40:00 +01:00
const pokemon = this . getPokemon ( ) ;
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 if ( ( berryModifiers = this . scene . applyModifiers ( BerryModifier , this . player , pokemon ) as BerryModifier [ ] ) ) {
2024-03-25 04:21:00 +00:00
for ( let berryModifier of berryModifiers ) {
if ( berryModifier . consumed ) {
if ( ! -- berryModifier . stackCount )
this . scene . removeModifier ( berryModifier ) ;
else
berryModifier . consumed = false ;
this . scene . updateModifiers ( this . player ) ;
}
2023-04-20 20:46:05 +01:00
}
2023-12-26 06:29:05 +00:00
return super . start ( ) ;
2023-04-20 20:46:05 +01:00
}
this . end ( ) ;
}
}
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() {
2023-06-06 15:14:53 +01:00
if ( ! this . skipAnim && ( this . revive || this . getPokemon ( ) . hp ) && this . getPokemon ( ) . getHpRatio ( ) < 1 )
2023-04-16 05:29:55 +01:00
super . start ( ) ;
else
this . end ( ) ;
2023-04-14 06:08:44 +01:00
}
end() {
const pokemon = this . getPokemon ( ) ;
2023-04-23 06:03:09 +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 ;
let lastStatusEffect = StatusEffect . NONE ;
2024-04-22 04:02:10 +01:00
if ( ! fullHp || this . hpHealed < 0 ) {
2023-04-20 20:46:05 +01:00
const hpRestoreMultiplier = new Utils . IntegerHolder ( 1 ) ;
2023-06-06 15:14:53 +01:00
if ( ! this . revive )
this . scene . applyModifiers ( HealingBoosterModifier , this . player , hpRestoreMultiplier ) ;
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 ) {
pokemon . damageAndUpdate ( healAmount . value * - 1 , HitResult . HEAL ) ;
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)
if ( this . preventFullHeal && pokemon . hp + healAmount . value >= pokemon . getMaxHp ( ) )
healAmount . value = ( pokemon . getMaxHp ( ) - pokemon . hp ) - 1 ;
2024-01-08 04:17:24 +00:00
healAmount . value = pokemon . heal ( healAmount . value ) ;
2024-03-01 14:35:36 +00:00
if ( healAmount . value )
this . scene . damageNumberHandler . add ( pokemon , healAmount . value , HitResult . HEAL ) ;
2024-02-19 03:21:57 +00:00
if ( pokemon . isPlayer ( ) ) {
this . scene . validateAchvs ( HealAchv , healAmount ) ;
if ( healAmount . value > this . scene . gameData . gameStats . highestHeal )
this . scene . gameData . gameStats . highestHeal = healAmount . value ;
}
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 ) {
lastStatusEffect = pokemon . status . effect ;
pokemon . resetStatus ( ) ;
pokemon . updateInfo ( ) . then ( ( ) = > super . end ( ) ) ;
2023-04-16 05:29:55 +01:00
} else if ( this . showFullHpMessage )
this . message = getPokemonMessage ( pokemon , ` 's \ nHP is full! ` ) ;
if ( this . message )
2023-04-22 00:30:04 +01:00
this . scene . queueMessage ( this . message ) ;
2023-04-16 05:29:55 +01:00
2024-03-11 17:18:49 +00:00
if ( this . healStatus && lastStatusEffect && ! hasMessage )
this . scene . queueMessage ( getPokemonMessage ( pokemon , getStatusEffectHealText ( lastStatusEffect ) ) ) ;
2024-03-11 22:13:07 +00:00
if ( fullHp && ! lastStatusEffect )
2023-04-16 05:29:55 +01:00
super . end ( ) ;
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
2023-12-07 22:43:56 +00:00
if ( ! pokemon ? . hp )
return this . end ( ) ;
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 ) ;
2023-12-30 02:04:40 +00: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 ) ;
2023-10-21 13:58:39 +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 ,
2023-05-18 16:11:06 +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 : ( ) = > {
this . pokeball . setTexture ( 'pb' , ` ${ pokeballAtlasKey } _opening ` ) ;
this . scene . time . delayedCall ( 17 , ( ) = > this . pokeball . setTexture ( 'pb' , ` ${ pokeballAtlasKey } _open ` ) ) ;
2023-10-21 13:58:39 +01:00
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 ,
2023-04-10 19:12:01 +01:00
ease : 'Sine.easeIn' ,
scale : 0.25 ,
y : 20 ,
onComplete : ( ) = > {
this . pokeball . setTexture ( 'pb' , ` ${ pokeballAtlasKey } _opening ` ) ;
pokemon . setVisible ( false ) ;
2023-10-21 13:58:39 +01:00
this . scene . playSound ( 'pb_catch' ) ;
2023-04-10 19:12:01 +01:00
this . scene . time . delayedCall ( 17 , ( ) = > this . pokeball . setTexture ( 'pb' , ` ${ pokeballAtlasKey } ` ) ) ;
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 ,
ease : 'Cubic.easeOut' ,
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-01-08 04:50:11 +00:00
if ( pokeballMultiplier === - 1 || pokemon . randSeedInt ( 65536 ) < y )
2023-10-21 13:58:39 +01:00
this . scene . playSound ( 'pb_move' ) ;
2023-04-10 19:12:01 +01:00
else {
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 {
this . scene . playSound ( 'pb_lock' ) ;
addPokeballCaptureStars ( this . scene , this . pokeball ) ;
const pbTint = this . scene . add . sprite ( this . pokeball . x , this . pokeball . y , 'pb' , 'pb' ) ;
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 ,
easing : 'Sine.easeOut' ,
onComplete : ( ) = > {
this . scene . tweens . add ( {
targets : pbTint ,
alpha : 0 ,
duration : 200 ,
easing : 'Sine.easeIn' ,
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
2023-10-21 13:58:39 +01:00
this . scene . playSound ( 'pb_rel' ) ;
2023-04-10 19:12:01 +01:00
pokemon . setY ( this . originalY ) ;
2024-04-06 02:12:29 +01:00
if ( pokemon . status ? . effect !== StatusEffect . SLEEP )
pokemon . cry ( pokemon . getHpRatio ( ) > 0.25 ? undefined : { rate : 0.85 } ) ;
2023-04-10 19:12:01 +01:00
pokemon . tint ( getPokeballTintColor ( this . pokeballType ) ) ;
pokemon . setVisible ( true ) ;
pokemon . untint ( 250 , 'Sine.easeOut' ) ;
const pokeballAtlasKey = getPokeballAtlasKey ( this . pokeballType ) ;
this . pokeball . setTexture ( 'pb' , ` ${ pokeballAtlasKey } _opening ` ) ;
this . scene . time . delayedCall ( 17 , ( ) = > this . pokeball . setTexture ( 'pb' , ` ${ pokeballAtlasKey } _open ` ) ) ;
this . scene . tweens . add ( {
targets : pokemon ,
duration : 250 ,
ease : 'Sine.easeOut' ,
scale : 1
} ) ;
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
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 ) ;
if ( pokemon . species . pseudoLegendary || pokemon . species . legendary )
this . scene . validateAchv ( achvs . CATCH_LEGENDARY ) ;
if ( pokemon . species . mythical )
this . scene . validateAchv ( achvs . CATCH_MYTHICAL ) ;
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 ) ;
2023-11-12 05:31:40 +00:00
2024-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu: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 ) ;
2023-11-12 14:27:50 +00:00
if ( this . scene . getParty ( ) . filter ( p = > p . isShiny ( ) ) . length === 6 )
this . scene . validateAchv ( achvs . SHINY_PARTY ) ;
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 ( ) ;
2023-04-21 00:44:56 +01:00
if ( newPokemon )
newPokemon . loadAssets ( ) . then ( end ) ;
else
end ( ) ;
} ) ;
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 = ( ) = > {
2023-10-18 23:01:15 +01:00
this . scene . ui . showText ( ` Your party is full. \ nRelease a Pokémon to make room for ${ pokemon . name } ? ` , null , ( ) = > {
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 ( ( ) = > {
if ( slotIndex < 6 )
addToParty ( ) ;
else
promptRelease ( ) ;
} ) ;
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 ( ) ;
} else
addToParty ( ) ;
} ) ;
2023-04-10 19:12:01 +01:00
} , 0 , true ) ;
}
removePb() {
this . scene . tweens . add ( {
targets : this.pokeball ,
duration : 250 ,
delay : 250 ,
ease : 'Sine.easeIn' ,
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 ) {
2023-10-21 13:58:39 +01:00
this . scene . playSound ( 'flee' ) ;
2024-04-24 00:19:36 +01:00
this . scene . queueMessage ( i18next . t ( 'menu:runAwaySuccess' ) , null , true , 500 ) ;
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 ,
2023-10-31 18:09:33 +00:00
ease : 'Sine.easeIn' ,
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 ) ) ;
2023-05-07 22:05:19 +01:00
} else
2024-04-24 00:19:36 +01:00
this . scene . queueMessage ( i18next . t ( 'menu: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 ( ) ;
2023-11-10 20:51:34 +00:00
if ( ! this . rerollCount )
this . updateSeed ( ) ;
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 ) ;
2023-10-29 20:15:18 +00:00
if ( this . isPlayer ( ) )
this . scene . applyModifiers ( ExtraModifierModifier , true , modifierCount ) ;
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-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu: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 ) {
case 0 :
if ( ! cursor ) {
2024-04-05 15:14:49 +01:00
const rerollCost = this . getRerollCost ( typeOptions , this . scene . lockModifierTiers ) ;
2024-02-04 05:30:19 +00:00
if ( this . scene . money < rerollCost ) {
this . scene . ui . playError ( ) ;
return false ;
} else {
2024-04-04 05:38:02 +01:00
this . scene . unshiftPhase ( new SelectModifierPhase ( this . scene , this . rerollCount + 1 , typeOptions . map ( o = > o . type . tier ) ) ) ;
2024-02-04 05:30:19 +00:00
this . scene . ui . clearText ( ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > super . end ( ) ) ;
this . scene . money -= rerollCost ;
this . scene . updateMoneyText ( ) ;
this . scene . playSound ( 'buy' ) ;
}
2024-04-05 15:14:49 +01:00
} else if ( cursor === 1 ) {
2024-02-04 05:30:19 +00:00
this . scene . ui . setModeWithoutClear ( Mode . PARTY , PartyUiMode . MODIFIER_TRANSFER , - 1 , ( fromSlotIndex : integer , itemIndex : integer , toSlotIndex : integer ) = > {
if ( toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > - 1 ) {
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 ) ) . then ( ( ) = > {
2024-02-04 05:30:19 +00:00
const itemModifiers = this . scene . findModifiers ( m = > m instanceof PokemonHeldItemModifier
&& ( m as PokemonHeldItemModifier ) . getTransferrable ( true ) && ( m as PokemonHeldItemModifier ) . pokemonId === party [ fromSlotIndex ] . id ) as PokemonHeldItemModifier [ ] ;
const itemModifier = itemModifiers [ itemIndex ] ;
2024-03-05 04:59:15 +00:00
this . scene . tryTransferHeldItemModifier ( itemModifier , party [ toSlotIndex ] , true , true ) ;
2024-02-04 05:30:19 +00: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-02-04 05:30:19 +00:00
} , PartyUiHandler . FilterItemMaxStacks ) ;
2024-04-05 15:14:49 +01:00
} else {
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 ;
2024-02-04 05:30:19 +00:00
}
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 ;
}
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 ( ) ;
this . scene . playSound ( 'buy' ) ;
( this . scene . ui . getHandler ( ) as ModifierSelectUiHandler ) . updateCostText ( ) ;
} else
this . scene . ui . playError ( ) ;
} ) ;
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 ( ) ;
} ;
if ( result instanceof Promise )
result . then ( ( ) = > doEnd ( ) ) ;
else
doEnd ( ) ;
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
} ) ;
} 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 ) ) ;
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-01 15:16:58 +01:00
const isPpRestoreModifier = modifierType instanceof PokemonPpRestoreModifierType ;
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
: 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 ] )
: 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
} ) ;
} 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-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-02-04 05:30:19 +00:00
} else
2024-02-15 00:20:12 +00:00
applyModifier ( modifierType . newModifier ( ) ) ;
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 ] ;
for ( let opt of typeOptions )
baseValue += tierValues [ opt . type . tier ] ;
} else
baseValue = 250 ;
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
}
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 ) = > {
return -- egg . hatchWaves < 1
2024-04-21 21:19:11 +01:00
} ) ;
2023-12-20 04:51:48 +00:00
2024-04-05 22:50:01 +01:00
if ( eggsToHatch . length ) {
2024-04-24 00:19:36 +01:00
this . scene . queueMessage ( i18next . t ( 'menu:eggHatching' ) ) ;
2024-04-05 22:50:01 +01:00
for ( let egg of eggsToHatch )
this . scene . unshiftPhase ( new EggHatchPhase ( this . scene , egg ) ) ;
}
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-03-11 04:16:24 +00:00
const count = Math . ceil ( waveIndex / 250 ) ;
for ( let i = 0 ; i < count ; i ++ )
this . scene . addEnemyModifier ( getEnemyBuffModifierForWave ( tier , this . scene . findModifiers ( m = > m instanceof EnemyPersistentModifier , false ) , this . scene ) , true , true ) ;
this . scene . updateModifiers ( false , true ) . then ( ( ) = > this . end ( ) ) ;
2023-04-10 19:12:01 +01:00
}
}
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 ( ) ;
if ( bgmPlaying )
this . scene . fadeOutBgm ( 1000 , false ) ;
this . scene . ui . fadeOut ( 1000 ) . then ( ( ) = > {
for ( let pokemon of this . scene . getParty ( ) ) {
pokemon . hp = pokemon . getMaxHp ( ) ;
pokemon . resetStatus ( ) ;
for ( let move of pokemon . moveset )
move . ppUsed = 0 ;
2023-10-20 16:38:41 +01:00
pokemon . updateInfo ( true ) ;
2023-10-18 23:01:15 +01:00
}
2023-10-26 21:33:59 +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 ( ) ;
if ( this . resumeBgm && bgmPlaying )
this . scene . playBgm ( ) ;
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 ( ) ;
if ( ! this . shownIvs )
return this . end ( ) ;
const pokemon = this . getPokemon ( ) ;
2024-04-24 00:19:36 +01:00
this . scene . ui . showText ( i18next . t ( 'menu: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-02-06 21:15:35 +00:00
this . trainerTypes = trainerTypes ;
2023-12-12 02:46:49 +00:00
}
start() {
super . start ( ) ;
let testMessages : string [ ] = [ ] ;
for ( let t of Object . keys ( trainerConfigs ) ) {
2024-02-06 21:15:35 +00:00
const type = parseInt ( t ) ;
if ( this . trainerTypes . length && ! this . trainerTypes . find ( tt = > tt === type as TrainerType ) )
continue ;
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 = > {
if ( messages ? . length )
testMessages . push ( . . . messages ) ;
} ) ;
}
for ( let message of testMessages )
this . scene . pushPhase ( new TestMessagePhase ( this . scene , message ) ) ;
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
}