2023-12-30 15:41:25 -08:00
import BattleScene , { bypassLogin , startingLevel , startingWave } from "./battle-scene" ;
2024-02-29 17:08:50 -08:00
import { default as Pokemon , PlayerPokemon , EnemyPokemon , PokemonMove , MoveResult , DamageResult , FieldPosition , HitResult , TurnMove } from "./field/pokemon" ;
2023-04-17 19:44:41 -07:00
import * as Utils from './utils' ;
2024-01-13 09:24:24 -08:00
import { Moves } from "./data/enums/moves" ;
import { allMoves , applyMoveAttrs , BypassSleepAttr , ChargeAttr , applyFilteredMoveAttrs , HitsTagAttr , MissEffectAttr , MoveAttr , MoveCategory , MoveEffectAttr , MoveFlags , MultiHitAttr , OverrideMoveEffectAttr , VariableAccuracyAttr , MoveTarget , OneHitKOAttr , getMoveTargets , MoveTargetSet , MoveEffectTrigger , CopyMoveAttr , AttackMove , SelfStatusMove , DelayedAttackAttr , RechargeAttr } from "./data/move" ;
2023-04-10 11:12:01 -07:00
import { Mode } from './ui/ui' ;
import { Command } from "./ui/command-ui-handler" ;
2023-04-20 12:46:05 -07:00
import { Stat } from "./data/pokemon-stat" ;
2024-02-16 21:40:03 -08:00
import { BerryModifier , ContactHeldItemTransferChanceModifier , EnemyAttackStatusEffectChanceModifier , EnemyInstantReviveChanceModifier , 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 } from "./modifier/modifier" ;
2023-04-11 08:04:39 -07:00
import PartyUiHandler , { PartyOption , PartyUiMode } from "./ui/party-ui-handler" ;
2023-07-04 14:50:51 -07:00
import { doPokeballBounceAnim , getPokeballAtlasKey , getPokeballCatchMultiplier , getPokeballTintColor , PokeballType } from "./data/pokeball" ;
2023-04-20 12:46:05 -07:00
import { CommonAnim , CommonBattleAnim , MoveAnim , initMoveAnim , loadMoveAnimAssets } from "./data/battle-anims" ;
2023-05-19 13:13:11 -07:00
import { StatusEffect , getStatusEffectActivationText , getStatusEffectCatchRateMultiplier , getStatusEffectHealText , getStatusEffectObtainText , getStatusEffectOverlapText } from "./data/status-effect" ;
2023-04-10 11:12:01 -07:00
import { SummaryUiMode } from "./ui/summary-ui-handler" ;
import EvolutionSceneHandler from "./ui/evolution-scene-handler" ;
import { EvolutionPhase } from "./evolution-phase" ;
2024-02-21 20:57:49 -08:00
import { Phase } from "./phase" ;
2023-04-20 12:46:05 -07:00
import { BattleStat , getBattleStatLevelChangeDescription , getBattleStatName } from "./data/battle-stat" ;
2024-01-13 09:24:24 -08:00
import { biomeDepths , biomeLinks } from "./data/biomes" ;
import { Biome } from "./data/enums/biome" ;
import { ModifierTier } from "./modifier/modifier-tier" ;
2024-02-03 21:30:19 -08:00
import { FusePokemonModifierType , ModifierPoolType , ModifierType , ModifierTypeFunc , ModifierTypeOption , PokemonModifierType , PokemonMoveModifierType , RememberMoveModifierType , TmModifierType , getEnemyBuffModifierForWave , getModifierType , getPlayerModifierTypeOptionsForWave , getPlayerShopModifierTypeOptionsForWave , modifierTypes , regenerateModifierPoolThresholds } from "./modifier/modifier-type" ;
2023-04-12 16:09:15 -07:00
import SoundFade from "phaser3-rex-plugins/plugins/soundfade" ;
2024-03-13 14:09:23 -07:00
import { BattlerTagLapseType , EncoreTag , HideSpriteTag as HiddenTag , ProtectedTag , TrappedTag } from "./data/battler-tags" ;
2024-01-13 09:24:24 -08:00
import { BattlerTagType } from "./data/enums/battler-tag-type" ;
2023-04-14 22:32:16 -07:00
import { getPokemonMessage } from "./messages" ;
2023-04-17 19:44:41 -07:00
import { Starter } from "./ui/starter-select-ui-handler" ;
2023-04-20 12:46:05 -07:00
import { Gender } from "./data/gender" ;
import { Weather , WeatherType , getRandomWeatherType , getWeatherDamageMessage , getWeatherLapseMessage } from "./data/weather" ;
import { TempBattleStat } from "./data/temp-battle-stat" ;
2024-01-15 20:29:22 -08:00
import { ArenaTagSide , ArenaTrapTag , MistTag , TrickRoomTag } from "./data/arena-tag" ;
import { ArenaTagType } from "./data/enums/arena-tag-type" ;
2024-03-13 21:40:57 -07:00
import { Abilities , CheckTrappedAbAttr , MoveAbilityBypassAbAttr , 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 } from "./data/ability" ;
2023-04-28 22:40:24 -07:00
import { Unlockables , getUnlockableName } from "./system/unlockables" ;
2024-02-29 17:08:50 -08:00
import { getBiomeKey } from "./field/arena" ;
2023-10-07 13:08:33 -07:00
import { BattleType , BattlerIndex , TurnCommand } from "./battle" ;
2024-01-13 09:24:24 -08:00
import { BattleSpec } from "./enums/battle-spec" ;
2024-03-14 13:26:57 -07:00
import { GameModes } from "./game-mode" ;
2024-01-13 09:24:24 -08:00
import { Species } from "./data/enums/species" ;
2023-11-11 21:31:40 -08:00
import { HealAchv , LevelAchv , MoneyAchv , achvs } from "./system/achv" ;
2024-01-13 09:24:24 -08:00
import { trainerConfigs } from "./data/trainer-config" ;
import { TrainerType } from "./data/enums/trainer-type" ;
2023-12-19 20:51:48 -08:00
import { EggHatchPhase } from "./egg-hatch-phase" ;
import { Egg } from "./data/egg" ;
import { vouchers } from "./system/voucher" ;
2023-12-31 15:30:37 -08:00
import { loggedInUser , updateUserInfo } from "./account" ;
2024-02-06 13:15:35 -08:00
import { GameDataType , PlayerGender } from "./system/game-data" ;
2024-02-29 17:08:50 -08:00
import { addPokeballCaptureStars , addPokeballOpenParticles } from "./field/anims" ;
2024-01-13 09:24:24 -08:00
import { SpeciesFormChangeActiveTrigger , SpeciesFormChangeManualTrigger , SpeciesFormChangeMoveLearnedTrigger , SpeciesFormChangeMoveUsedTrigger } from "./data/pokemon-forms" ;
2024-02-25 16:09:24 -08:00
import { battleSpecDialogue , getCharVariantFromDialogue } from "./data/dialogue" ;
2024-02-03 21:30:19 -08:00
import ModifierSelectUiHandler , { SHOP_OPTIONS_ROW_LIMIT } from "./ui/modifier-select-ui-handler" ;
2024-02-06 13:15:35 -08:00
import { Setting } from "./system/settings" ;
2024-02-13 15:42:11 -08:00
import { Tutorial , handleTutorial } from "./tutorial" ;
2024-03-09 21:14:09 -08:00
import { TerrainType } from "./data/terrain" ;
2023-12-30 15:41:25 -08:00
2024-02-21 20:57:49 -08:00
export class LoginPhase extends Phase {
2023-12-30 15:41:25 -08:00
private showText : boolean ;
constructor ( scene : BattleScene , showText? : boolean ) {
super ( scene ) ;
this . showText = showText === undefined || ! ! showText ;
}
start ( ) : void {
super . start ( ) ;
this . scene . ui . setMode ( Mode . LOADING , { buttonActions : [ ] } ) ;
Utils . executeIf ( bypassLogin || ! ! Utils . getCookie ( Utils . sessionIdKey ) , updateUserInfo ) . then ( success = > {
if ( ! success ) {
if ( this . showText )
this . scene . ui . showText ( 'Log in or create an account to start. No email required!' ) ;
this . scene . playSound ( 'menu_open' ) ;
2024-02-03 21:49:57 -08:00
const loadData = ( ) = > {
2024-02-19 17:36:10 -08:00
updateUserInfo ( ) . then ( ( ) = > this . scene . gameData . loadSystem ( ) . then ( ( ) = > this . end ( ) ) ) ;
2024-02-03 21:49:57 -08:00
} ;
2023-12-30 15:41:25 -08:00
this . scene . ui . setMode ( Mode . LOGIN_FORM , {
buttonActions : [
( ) = > {
this . scene . ui . playSelect ( ) ;
2024-02-03 21:49:57 -08:00
loadData ( ) ;
2023-12-30 15:41:25 -08:00
} , ( ) = > {
this . scene . playSound ( 'menu_open' ) ;
this . scene . ui . setMode ( Mode . REGISTRATION_FORM , {
buttonActions : [
( ) = > {
this . scene . ui . playSelect ( ) ;
2024-02-03 21:49:57 -08:00
loadData ( ) ;
2023-12-30 15:41:25 -08:00
} , ( ) = > {
2023-12-31 15:30:37 -08:00
this . scene . unshiftPhase ( new LoginPhase ( this . scene , false ) ) ;
2023-12-30 15:41:25 -08:00
this . end ( ) ;
}
]
} ) ;
}
]
} ) ;
return null ;
2024-02-19 17:36:10 -08:00
} else {
this . scene . gameData . loadSystem ( ) . then ( success = > {
2024-03-09 13:09:06 -08:00
if ( success || bypassLogin )
2024-02-19 17:36:10 -08:00
this . end ( ) ;
else {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . showText ( 'Failed to load save data. Please reload the page.\nIf this continues, please contact the administrator.' ) ;
}
} ) ;
}
2023-12-30 15:41:25 -08:00
} ) ;
}
2023-12-31 19:49:50 -08:00
end ( ) : void {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2024-02-19 17:36:10 -08:00
if ( ! this . scene . gameData . gender )
this . scene . unshiftPhase ( new SelectGenderPhase ( this . scene ) ) ;
2024-02-13 15:42:11 -08:00
handleTutorial ( this . scene , Tutorial . Intro ) . then ( ( ) = > super . end ( ) ) ;
2023-12-31 19:49:50 -08:00
}
2023-12-30 15:41:25 -08:00
}
2023-04-10 11:12:01 -07:00
2023-12-31 15:30:37 -08:00
// TODO: Remove
2024-02-21 20:57:49 -08:00
export class ConsolidateDataPhase extends Phase {
2023-12-31 15:30:37 -08:00
start ( ) : void {
super . start ( ) ;
Utils . apiFetch ( ` savedata/get?datatype= ${ GameDataType . SYSTEM } ` )
. then ( response = > response . text ( ) )
. then ( response = > {
if ( ! response . length || response [ 0 ] !== '{' ) {
console . log ( 'System data not found: Loading legacy local system data' ) ;
const systemDataStr = atob ( localStorage . getItem ( 'data' ) ) ;
Utils . apiPost ( ` savedata/update?datatype= ${ GameDataType . SYSTEM } ` , systemDataStr )
. then ( response = > response . text ( ) )
. then ( error = > {
if ( error ) {
console . error ( error ) ;
return this . end ( ) ;
}
Utils . apiFetch ( ` savedata/get?datatype= ${ GameDataType . SESSION } ` )
. then ( response = > response . text ( ) )
. then ( response = > {
if ( ! response . length || response [ 0 ] !== '{' ) {
2023-12-31 22:05:20 -08:00
console . log ( 'Session data not found: Loading legacy local session data' ) ;
2023-12-31 15:30:37 -08:00
const sessionDataStr = atob ( localStorage . getItem ( 'sessionData' ) ) ;
Utils . apiPost ( ` savedata/update?datatype= ${ GameDataType . SESSION } ` , sessionDataStr )
. then ( response = > response . text ( ) )
. then ( error = > {
if ( error )
console . error ( error ) ;
2023-12-31 22:05:20 -08:00
window . location = window . location ;
2023-12-31 15:30:37 -08:00
} ) ;
} else
2023-12-31 22:05:20 -08:00
window . location = window . location ;
2023-12-31 15:30:37 -08:00
} ) ;
} ) ;
} else
this . end ( ) ;
} ) ;
}
}
2024-02-21 20:57:49 -08:00
export class CheckLoadPhase extends Phase {
2023-04-28 12:03:42 -07:00
private loaded : boolean ;
constructor ( scene : BattleScene ) {
super ( scene ) ;
this . loaded = false ;
}
start ( ) : void {
2023-12-30 15:41:25 -08:00
super . start ( ) ;
2023-12-31 15:30:37 -08:00
if ( ! loggedInUser ? . hasGameSession )
2023-12-07 14:43:56 -08:00
return this . end ( ) ;
2023-12-30 19:01:46 -08:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2023-04-28 12:03:42 -07:00
this . scene . ui . showText ( 'You currently have a session in progress.\nWould you like to continue where you left off?' , null , ( ) = > {
this . scene . ui . setMode ( Mode . CONFIRM , ( ) = > {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . gameData . loadSession ( this . scene ) . then ( ( success : boolean ) = > {
if ( success ) {
this . loaded = true ;
this . scene . ui . showText ( 'Session loaded successfully.' , null , ( ) = > this . end ( ) ) ;
} else
this . end ( ) ;
} ) . catch ( err = > {
console . error ( err ) ;
this . scene . ui . showText ( 'Your session data could not be loaded.\nIt may be corrupted. Please reload the page.' , null ) ;
} ) ;
} , ( ) = > {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . clearText ( ) ;
this . end ( ) ;
} )
} ) ;
}
end ( ) : void {
if ( ! this . loaded ) {
this . scene . arena . preloadBgm ( ) ;
this . scene . pushPhase ( new SelectStarterPhase ( this . scene ) ) ;
} else
2023-10-09 17:20:02 -07:00
this . scene . playBgm ( ) ;
2023-04-28 12:03:42 -07:00
this . scene . pushPhase ( new EncounterPhase ( this . scene , this . loaded ) ) ;
2023-12-02 07:30:23 -08:00
if ( this . loaded ) {
const availablePartyMembers = this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) . length ;
this . scene . pushPhase ( new SummonPhase ( this . scene , 0 ) ) ;
2023-10-07 13:08:33 -07:00
if ( this . scene . currentBattle . double && availablePartyMembers > 1 )
2023-12-02 07:30:23 -08:00
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 ) ) ;
}
2023-10-03 09:50:31 -07:00
}
2023-04-28 12:03:42 -07:00
2023-12-19 20:51:48 -08:00
for ( let achv of Object . keys ( this . scene . gameData . achvUnlocks ) ) {
if ( vouchers . hasOwnProperty ( achv ) )
this . scene . validateVoucher ( vouchers [ achv ] ) ;
}
2023-04-28 12:03:42 -07:00
super . end ( ) ;
}
}
2024-02-21 20:57:49 -08:00
export class SelectGenderPhase extends Phase {
2024-02-06 13:15:35 -08:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start ( ) : void {
super . start ( ) ;
this . scene . ui . showText ( 'Are you a boy or a girl?' , null , ( ) = > {
this . scene . ui . setMode ( Mode . OPTION_SELECT , {
options : [
{
label : 'Boy' ,
handler : ( ) = > {
this . scene . gameData . gender = PlayerGender . MALE ;
this . scene . gameData . saveSetting ( Setting . Player_Gender , 0 ) ;
this . scene . gameData . saveSystem ( ) . then ( ( ) = > this . end ( ) ) ;
}
} ,
{
label : 'Girl' ,
handler : ( ) = > {
this . scene . gameData . gender = PlayerGender . FEMALE ;
this . scene . gameData . saveSetting ( Setting . Player_Gender , 1 ) ;
this . scene . gameData . saveSystem ( ) . then ( ( ) = > this . end ( ) ) ;
}
}
]
} ) ;
} ) ;
}
}
2024-02-21 20:57:49 -08:00
export class SelectStarterPhase extends Phase {
2023-04-10 11:12:01 -07:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
super . start ( ) ;
2023-10-26 13:33:59 -07:00
this . scene . playBgm ( 'menu' ) ;
2023-04-12 16:09:15 -07:00
2023-04-17 19:44:41 -07:00
this . scene . ui . setMode ( Mode . STARTER_SELECT , ( starters : Starter [ ] ) = > {
2023-04-12 16:09:15 -07:00
const party = this . scene . getParty ( ) ;
const loadPokemonAssets : Promise < void > [ ] = [ ] ;
2023-04-17 19:44:41 -07:00
for ( let starter of starters ) {
2023-11-12 20:47:04 -08:00
const starterProps = this . scene . gameData . getSpeciesDexAttrProps ( starter . species , starter . dexAttr ) ;
2024-02-21 09:38:07 -08:00
const starterFormIndex = Math . min ( starterProps . formIndex , Math . max ( starter . species . forms . length - 1 , 0 ) ) ;
2023-04-17 19:44:41 -07:00
const starterGender = starter . species . malePercent !== null
2023-11-12 20:47:04 -08:00
? ! starterProps . female ? Gender.MALE : Gender.FEMALE
2023-04-17 19:44:41 -07:00
: Gender . GENDERLESS ;
2023-11-12 20:47:04 -08:00
const starterIvs = this . scene . gameData . dexData [ starter . species . speciesId ] . ivs . slice ( 0 ) ;
2024-02-20 15:05:17 -08:00
const starterPokemon = this . scene . addPlayerPokemon ( starter . species , startingLevel , starterProps . abilityIndex , starterFormIndex , starterGender , starterProps . shiny , starterIvs , starter . nature ) ;
2024-02-20 22:03:34 -08:00
starterPokemon . tryPopulateMoveset ( starter . moveset ) ;
2023-10-25 11:15:44 -07:00
if ( starter . pokerus )
starterPokemon . pokerus = true ;
2024-03-14 13:26:57 -07:00
if ( this . scene . gameMode . isSplicedOnly )
2023-11-08 15:36:30 -08:00
starterPokemon . generateFusionSpecies ( true ) ;
2023-04-17 19:44:41 -07:00
starterPokemon . setVisible ( false ) ;
party . push ( starterPokemon ) ;
loadPokemonAssets . push ( starterPokemon . loadAssets ( ) ) ;
2023-04-12 16:09:15 -07:00
}
Promise . all ( loadPokemonAssets ) . then ( ( ) = > {
this . scene . ui . clearText ( ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > {
2023-11-03 21:32:12 -07:00
SoundFade . fadeOut ( this . scene , this . scene . sound . get ( 'menu' ) , 500 , true ) ;
2023-10-09 17:20:02 -07:00
this . scene . time . delayedCall ( 500 , ( ) = > this . scene . playBgm ( ) ) ;
2024-03-14 13:26:57 -07:00
if ( this . scene . gameMode . isClassic )
2024-01-11 09:26:32 -08:00
this . scene . gameData . gameStats . classicSessionsPlayed ++ ;
else
this . scene . gameData . gameStats . endlessSessionsPlayed ++ ;
2023-12-29 18:04:40 -08:00
this . scene . newBattle ( ) ;
2023-04-12 16:09:15 -07:00
this . end ( ) ;
} ) ;
} ) ;
} ) ;
2023-04-10 11:12:01 -07:00
}
}
2024-02-21 20:57:49 -08:00
export class BattlePhase extends Phase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
showEnemyTrainer ( ) : void {
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 08:11:06 -07: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-01-02 18:31:59 -08:00
return aSpeed < bSpeed ? 1 : aSpeed > bSpeed ? - 1 : ! this . scene . currentBattle . randSeedInt ( 2 ) ? - 1 : 1 ;
2023-05-18 08:11:06 -07: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-11 17:55:41 -07:00
const field = this . scene . getField ( true ) . filter ( p = > p . summonData ) ;
2023-05-18 08:11:06 -07:00
field . forEach ( pokemon = > func ( pokemon ) ) ;
}
}
export abstract class PokemonPhase extends FieldPhase {
2023-12-03 21:09:38 -08:00
protected battlerIndex : BattlerIndex | integer ;
2023-05-18 08:11:06 -07:00
protected player : boolean ;
protected fieldIndex : integer ;
2023-12-03 21:09:38 -08:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex | integer ) {
2023-05-18 08:11:06 -07: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-03 21:09:38 -08:00
if ( this . battlerIndex > BattlerIndex . ENEMY_2 )
return this . scene . getPokemonById ( this . battlerIndex ) ;
2023-05-18 08:11:06 -07:00
return this . scene . getField ( ) [ this . battlerIndex ] ;
}
}
export abstract class PartyMemberPokemonPhase extends FieldPhase {
protected partyMemberIndex : integer ;
protected fieldIndex : integer ;
2023-10-07 13:08:33 -07:00
protected player : boolean ;
2023-05-18 08:11:06 -07:00
2023-10-07 13:08:33 -07:00
constructor ( scene : BattleScene , partyMemberIndex : integer , player : boolean ) {
2023-05-18 08:11:06 -07:00
super ( scene ) ;
this . partyMemberIndex = partyMemberIndex ;
this . fieldIndex = partyMemberIndex < this . scene . currentBattle . getBattlerCount ( )
? partyMemberIndex
: - 1 ;
2023-10-07 13:08:33 -07:00
this . player = player ;
2023-05-18 08:11:06 -07:00
}
2023-10-07 13:08:33 -07: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 08:11:06 -07:00
}
}
2023-04-10 11:12:01 -07:00
export class EncounterPhase extends BattlePhase {
2023-04-28 12:03:42 -07:00
private loaded : boolean ;
constructor ( scene : BattleScene , loaded? : boolean ) {
2023-04-10 11:12:01 -07:00
super ( scene ) ;
2023-04-28 12:03:42 -07:00
this . loaded = ! ! loaded ;
2023-04-10 11:12:01 -07:00
}
start() {
super . start ( ) ;
2024-01-11 17:27:50 -08:00
this . scene . initSession ( ) ;
2024-01-13 09:24:24 -08:00
2023-05-18 08:11:06 -07:00
const loadEnemyAssets = [ ] ;
2023-04-10 11:12:01 -07:00
const battle = this . scene . currentBattle ;
2023-05-18 08:11:06 -07:00
battle . enemyLevels . forEach ( ( level , e ) = > {
2023-10-18 15:01:15 -07:00
if ( ! this . loaded ) {
if ( battle . battleType === BattleType . TRAINER )
battle . enemyParty [ e ] = battle . trainer . genPartyMember ( e ) ;
else {
2023-11-08 15:36:30 -08:00
const enemySpecies = this . scene . randomSpecies ( battle . waveIndex , level , true ) ;
2024-01-07 20:17:24 -08:00
battle . enemyParty [ e ] = this . scene . addEnemyPokemon ( enemySpecies , level , false , ! ! this . scene . getEncounterBossSegments ( battle . waveIndex , level , enemySpecies ) ) ;
2024-02-19 19:58:25 -08:00
if ( this . scene . currentBattle . battleSpec === BattleSpec . FINAL_BOSS )
battle . enemyParty [ e ] . ivs = new Array ( 6 ) . fill ( 31 ) ;
2024-01-05 19:24:05 -08:00
this . scene . getParty ( ) . slice ( 0 , ! battle . double ? 1 : 2 ) . reverse ( ) . forEach ( playerPokemon = > {
applyAbAttrs ( SyncEncounterNatureAbAttr , playerPokemon , null , battle . enemyParty [ e ] ) ;
} ) ;
2023-10-18 15:01:15 -07:00
}
}
2023-10-07 13:08:33 -07:00
const enemyPokemon = this . scene . getEnemyParty ( ) [ e ] ;
if ( e < ( battle . double ? 2 : 1 ) ) {
enemyPokemon . setX ( - 66 + enemyPokemon . getFieldPositionOffset ( ) [ 0 ] ) ;
enemyPokemon . resetSummonData ( ) ;
2023-11-12 20:47:04 -08:00
if ( ! this . loaded )
this . scene . gameData . setPokemonSeen ( enemyPokemon ) ;
2023-10-07 13:08:33 -07:00
}
2023-04-17 22:32:26 -07:00
2024-02-26 09:34:45 -08:00
if ( enemyPokemon . species . speciesId === Species . ETERNATUS ) {
2024-03-14 13:26:57 -07:00
if ( this . scene . gameMode . isClassic && ( battle . battleSpec === BattleSpec . FINAL_BOSS || this . scene . gameMode . isWaveFinal ( battle . waveIndex ) ) ) {
2024-02-27 19:50:27 -08:00
if ( battle . battleSpec !== BattleSpec . FINAL_BOSS ) {
2024-02-26 09:34:45 -08:00
enemyPokemon . formIndex = 1 ;
2024-02-27 19:50:27 -08:00
enemyPokemon . updateScale ( ) ;
}
2024-02-26 09:34:45 -08:00
enemyPokemon . setBoss ( ) ;
2024-02-27 19:50:27 -08:00
} else if ( ! ( battle . waveIndex % 1000 ) ) {
2024-01-13 09:24:24 -08:00
enemyPokemon . formIndex = 1 ;
2024-02-27 19:50:27 -08:00
enemyPokemon . updateScale ( ) ;
}
2024-01-07 20:17:24 -08:00
}
2023-10-23 20:20:05 -07:00
2023-05-18 08:11:06 -07:00
loadEnemyAssets . push ( enemyPokemon . loadAssets ( ) ) ;
2023-12-01 14:23:26 -08:00
console . log ( enemyPokemon . name , enemyPokemon . species . speciesId , enemyPokemon . stats ) ;
2023-05-18 08:11:06 -07:00
} ) ;
2023-04-10 11:12:01 -07:00
2023-10-07 13:08:33 -07:00
if ( battle . battleType === BattleType . TRAINER )
2023-10-18 15:01:15 -07:00
loadEnemyAssets . push ( battle . trainer . loadAssets ( ) . then ( ( ) = > battle . trainer . initSprite ( ) ) ) ;
2023-10-07 13:08:33 -07:00
2023-05-18 08:11:06 -07:00
Promise . all ( loadEnemyAssets ) . then ( ( ) = > {
2023-10-07 13:08:33 -07: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 10:48:56 -07:00
battle . seenEnemyPartyMemberIds . add ( enemyPokemon . id ) ;
2023-10-07 13:08:33 -07:00
const playerPokemon = this . scene . getPlayerPokemon ( ) ;
if ( playerPokemon . visible )
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 08:11:06 -07:00
} ) ;
2023-04-10 11:12:01 -07:00
2023-04-28 12:03:42 -07:00
if ( ! this . loaded ) {
2023-10-23 10:48:56 -07:00
regenerateModifierPoolThresholds ( this . scene . getEnemyField ( ) , battle . battleType === BattleType . TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD ) ;
2023-04-28 12:03:42 -07:00
this . scene . generateEnemyModifiers ( ) ;
}
2023-04-20 16:44:56 -07:00
2023-04-28 12:03:42 -07:00
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > {
2023-11-12 20:47:04 -08:00
if ( ! this . loaded ) {
2023-12-30 15:41:25 -08:00
this . scene . gameData . saveSystem ( ) . then ( success = > {
if ( ! success )
return this . scene . reset ( true ) ;
this . scene . gameData . saveSession ( this . scene , true ) . then ( ( ) = > this . doEncounter ( ) ) ;
} ) ;
} else
this . doEncounter ( ) ;
2023-04-28 12:03:42 -07:00
} ) ;
2023-04-10 11:12:01 -07:00
} ) ;
}
doEncounter() {
2023-10-09 17:20:02 -07:00
this . scene . playBgm ( undefined , true ) ;
2023-12-16 07:15:25 -08:00
this . scene . updateModifiers ( false ) ;
2024-01-14 17:47:08 -08:00
this . scene . setFieldScale ( 1 ) ;
2023-12-16 07:15:25 -08:00
2023-11-28 18:35:52 -08:00
/ * i f ( s t a r t i n g W a v e > 1 0 ) {
2023-05-30 06:46:42 -07:00
for ( let m = 0 ; m < Math . min ( Math . floor ( startingWave / 10 ) , 99 ) ; m ++ )
2023-10-31 18:43:22 -07:00
this . scene . addModifier ( getPlayerModifierTypeOptionsForWave ( ( m + 1 ) * 10 , 1 , this . scene . getParty ( ) ) [ 0 ] . type . newModifier ( ) , true ) ;
this . scene . updateModifiers ( true ) ;
2023-11-28 18:35:52 -08:00
} * /
for ( let pokemon of this . scene . getParty ( ) ) {
if ( pokemon )
pokemon . resetBattleData ( ) ;
2023-04-19 11:07:38 -07:00
}
2023-12-29 18:04:40 -08:00
this . scene . arena . trySetWeather ( getRandomWeatherType ( this . scene . arena ) , false ) ;
2023-04-19 11:07:38 -07:00
2023-05-18 08:11:06 -07:00
const enemyField = this . scene . getEnemyField ( ) ;
2023-04-10 11:12:01 -07:00
this . scene . tweens . add ( {
2023-10-07 13:08:33 -07: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 11:12:01 -07:00
duration : 2000 ,
2024-01-13 09:24:24 -08:00
onComplete : ( ) = > {
if ( ! this . tryOverrideForBattleSpec ( ) )
this . doEncounterCommon ( ) ;
}
2023-04-10 11:12:01 -07:00
} ) ;
}
2024-01-13 09:24:24 -08:00
getEncounterMessage ( ) : string {
const enemyField = this . scene . getEnemyField ( ) ;
if ( this . scene . currentBattle . battleSpec === BattleSpec . FINAL_BOSS )
return ` ${ enemyField [ 0 ] . name } appeared. ` ;
if ( this . scene . currentBattle . battleType === BattleType . TRAINER )
2024-02-05 20:05:56 -08:00
return ` ${ this . scene . currentBattle . trainer . getName ( true ) } \ nwould like to battle! ` ;
2024-01-13 09:24:24 -08:00
return enemyField . length === 1
? ` A wild ${ enemyField [ 0 ] . name } appeared! `
: ` A wild ${ enemyField [ 0 ] . name } \ nand ${ enemyField [ 1 ] . name } appeared! ` ;
}
doEncounterCommon ( showEncounterMessage : boolean = true ) {
2023-10-07 13:08:33 -07: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-11 21:31:40 -08:00
if ( enemyPokemon . isShiny ( ) )
this . scene . validateAchv ( achvs . SEE_SHINY ) ;
2023-10-07 13:08:33 -07:00
} ) ;
2024-02-14 20:25:12 -08:00
this . scene . updateFieldScale ( ) ;
2024-01-13 09:24:24 -08:00
if ( showEncounterMessage )
this . scene . ui . showText ( this . getEncounterMessage ( ) , null , ( ) = > this . end ( ) , 1500 ) ;
else
this . end ( ) ;
2023-10-07 13:08:33 -07:00
} else if ( this . scene . currentBattle . battleType === BattleType . TRAINER ) {
2023-10-18 15:01:15 -07: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 09:24:24 -08:00
const doTrainerSummon = ( ) = > {
2024-02-21 20:57:49 -08:00
this . hideEnemyTrainer ( ) ;
2023-12-02 07:30:23 -08:00
const availablePartyMembers = this . scene . getEnemyParty ( ) . filter ( p = > ! p . isFainted ( ) ) . length ;
2023-10-18 15:01:15 -07:00
this . scene . unshiftPhase ( new SummonPhase ( this . scene , 0 , false ) ) ;
2023-12-02 07:30:23 -08:00
if ( this . scene . currentBattle . double && availablePartyMembers > 1 )
2023-10-18 15:01:15 -07:00
this . scene . unshiftPhase ( new SummonPhase ( this . scene , 1 , false ) ) ;
this . end ( ) ;
2024-01-13 09:24:24 -08:00
} ;
if ( showEncounterMessage )
this . scene . ui . showText ( this . getEncounterMessage ( ) , null , doTrainerSummon , 1500 , true ) ;
else
doTrainerSummon ( ) ;
2023-10-18 15:01:15 -07:00
} ;
2024-02-14 11:41:39 -08:00
const encounterMessages = this . scene . currentBattle . trainer . getEncounterMessages ( ) ;
2023-10-18 15:01:15 -07:00
2024-02-14 12:33:02 -08:00
if ( ! encounterMessages ? . length )
2023-10-18 15:01:15 -07:00
doSummon ( ) ;
else {
2024-02-22 15:03:36 -08: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-25 16:09:24 -08:00
this . scene . showFieldOverlay ( 500 ) . then ( ( ) = > this . scene . charSprite . showCharacter ( trainer . getKey ( ) , getCharVariantFromDialogue ( encounterMessages [ 0 ] ) ) . then ( ( ) = > showDialogueAndSummon ( ) ) ) ;
2024-02-22 15:03:36 -08:00
else
showDialogueAndSummon ( ) ;
2023-10-18 15:01:15 -07:00
}
2023-10-07 13:08:33 -07:00
}
}
2023-04-10 11:12:01 -07:00
end() {
2023-05-18 08:11:06 -07:00
const enemyField = this . scene . getEnemyField ( ) ;
enemyField . forEach ( ( enemyPokemon , e ) = > {
2023-11-05 20:48:04 -08:00
if ( enemyPokemon . isShiny ( ) )
2023-05-18 08:11:06 -07:00
this . scene . unshiftPhase ( new ShinySparklePhase ( this . scene , BattlerIndex . ENEMY + e ) ) ;
} ) ;
2023-04-24 11:30:21 -07:00
2023-11-12 09:49:06 -08:00
if ( this . scene . currentBattle . battleType !== BattleType . TRAINER ) {
2023-11-05 19:44:49 -08:00
enemyField . map ( p = > this . scene . pushPhase ( new PostSummonPhase ( this . scene , p . getBattlerIndex ( ) ) ) ) ;
2023-11-12 09:49:06 -08:00
const ivScannerModifier = this . scene . findModifier ( m = > m instanceof IvScannerModifier ) ;
if ( ivScannerModifier )
enemyField . map ( p = > this . scene . pushPhase ( new ScanIvsPhase ( this . scene , p . getBattlerIndex ( ) , Math . min ( ivScannerModifier . getStackCount ( ) , 6 ) ) ) ) ;
}
2023-12-02 07:30:23 -08:00
if ( ! this . loaded ) {
const availablePartyMembers = this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) ;
if ( ! availablePartyMembers [ 0 ] . isOnField ( ) )
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 ) ) ;
if ( ! availablePartyMembers [ 1 ] . isOnField ( ) )
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 11:12:01 -07:00
2024-02-14 07:44:55 -08:00
handleTutorial ( this . scene , Tutorial . Access_Menu ) . then ( ( ) = > super . end ( ) ) ;
2023-04-10 11:12:01 -07:00
}
2024-01-13 09:24:24 -08: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-14 20:38:37 -08:00
this . scene . ui . showDialogue ( battleSpecDialogue [ BattleSpec . FINAL_BOSS ] . encounter , enemy . species . name , null , ( ) = > {
2024-01-13 09:24:24 -08:00
this . doEncounterCommon ( false ) ;
2024-02-13 15:42:11 -08:00
} ) ;
2024-01-13 09:24:24 -08:00
} , 1500 , true ) ;
return true ;
}
return false ;
}
2023-04-10 11:12:01 -07:00
}
export class NextEncounterPhase extends EncounterPhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
doEncounter ( ) : void {
2023-10-09 17:20:02 -07:00
this . scene . playBgm ( undefined , true ) ;
2024-02-26 12:14:47 -08:00
for ( let pokemon of this . scene . getParty ( ) ) {
if ( pokemon )
pokemon . resetBattleData ( ) ;
}
2024-01-14 17:47:08 -08:00
this . scene . arenaNextEnemy . setVisible ( true ) ;
2023-05-18 08:11:06 -07:00
const enemyField = this . scene . getEnemyField ( ) ;
2023-04-10 11:12:01 -07:00
this . scene . tweens . add ( {
2023-10-18 15:01:15 -07:00
targets : [ this . scene . arenaEnemy , this . scene . arenaNextEnemy , this . scene . currentBattle . trainer , enemyField , this . scene . lastEnemyTrainer ] . flat ( ) ,
2023-04-10 11:12:01 -07:00
x : '+=300' ,
duration : 2000 ,
onComplete : ( ) = > {
this . scene . arenaEnemy . setX ( this . scene . arenaNextEnemy . x ) ;
2023-05-07 14:05:19 -07:00
this . scene . arenaEnemy . setAlpha ( 1 ) ;
2023-04-10 11:12:01 -07:00
this . scene . arenaNextEnemy . setX ( this . scene . arenaNextEnemy . x - 300 ) ;
2024-01-14 17:47:08 -08:00
this . scene . arenaNextEnemy . setVisible ( false ) ;
2023-10-22 19:45:48 -07:00
if ( this . scene . lastEnemyTrainer )
this . scene . lastEnemyTrainer . destroy ( ) ;
2023-10-07 13:08:33 -07:00
2024-01-13 09:24:24 -08:00
if ( ! this . tryOverrideForBattleSpec ( ) )
this . doEncounterCommon ( ) ;
2023-04-10 11:12:01 -07:00
}
} ) ;
}
}
export class NewBiomeEncounterPhase extends NextEncounterPhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
doEncounter ( ) : void {
2023-11-28 18:35:52 -08:00
for ( let pokemon of this . scene . getParty ( ) ) {
if ( pokemon )
pokemon . resetBattleData ( ) ;
}
2023-12-29 18:04:40 -08:00
this . scene . arena . trySetWeather ( getRandomWeatherType ( this . scene . arena ) , false ) ;
2023-04-18 21:35:06 -07:00
2023-05-18 08:11:06 -07:00
const enemyField = this . scene . getEnemyField ( ) ;
2023-04-10 11:12:01 -07:00
this . scene . tweens . add ( {
2023-05-18 08:11:06 -07:00
targets : [ this . scene . arenaEnemy , enemyField ] . flat ( ) ,
2023-05-30 09:15:59 -07:00
x : '+=300' ,
2023-04-10 11:12:01 -07:00
duration : 2000 ,
2024-01-13 09:24:24 -08:00
onComplete : ( ) = > {
if ( ! this . tryOverrideForBattleSpec ( ) )
this . doEncounterCommon ( ) ;
}
2023-04-10 11:12:01 -07:00
} ) ;
}
}
2023-07-04 14:50:51 -07: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-22 19:46:05 -08:00
applyPostSummonAbAttrs ( PostSummonAbAttr , pokemon ) . then ( ( ) = > this . end ( ) ) ;
2023-07-04 14:50:51 -07:00
}
}
2023-04-11 21:37:56 -07:00
export class SelectBiomePhase extends BattlePhase {
2023-04-10 11:12:01 -07:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
super . start ( ) ;
2023-04-11 21:37:56 -07:00
const currentBiome = this . scene . arena . biomeType ;
const setNextBiome = ( nextBiome : Biome ) = > {
2023-11-10 18:11:36 -08:00
if ( this . scene . currentBattle . waveIndex % 10 === 1 ) {
this . scene . applyModifiers ( MoneyInterestModifier , true , this . scene ) ;
2023-10-26 16:07:41 -07:00
this . scene . unshiftPhase ( new PartyHealPhase ( this . scene , false ) ) ;
2023-11-10 18:11:36 -08:00
}
2023-04-11 21:37:56 -07:00
this . scene . unshiftPhase ( new SwitchBiomePhase ( this . scene , nextBiome ) ) ;
this . end ( ) ;
} ;
2024-03-14 13:26:57 -07:00
if ( this . scene . gameMode . isClassic && this . scene . gameMode . isWaveFinal ( this . scene . currentBattle . waveIndex + 9 ) )
2023-04-26 14:40:08 -07:00
setNextBiome ( Biome . END ) ;
2024-03-14 13:26:57 -07:00
else if ( this . scene . gameMode . hasRandomBiomes )
2023-12-07 10:41:47 -08:00
setNextBiome ( this . generateNextBiome ( ) ) ;
else if ( Array . isArray ( biomeLinks [ currentBiome ] ) ) {
2023-12-12 12:32:50 -08: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 ) ) {
2023-04-26 16:19:39 -07:00
this . scene . ui . setMode ( Mode . BIOME_SELECT , currentBiome , ( biomeIndex : integer ) = > {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
setNextBiome ( biomes [ biomeIndex ] ) ;
} ) ;
} else
2023-10-18 15:01:15 -07:00
setNextBiome ( biomes [ Utils . randSeedInt ( biomes . length ) ] ) ;
2023-04-26 16:19:39 -07:00
} else
setNextBiome ( biomeLinks [ currentBiome ] as Biome ) ;
2023-04-11 21:37:56 -07:00
}
2023-12-07 10:41:47 -08:00
generateNextBiome ( ) : Biome {
if ( ! ( this . scene . currentBattle . waveIndex % 50 ) )
return Biome . END ;
else {
const relWave = this . scene . currentBattle . waveIndex % 250 ;
2023-12-12 12:32:50 -08:00
const biomes = Utils . getEnumValues ( Biome ) . slice ( 1 , Utils . getEnumValues ( Biome ) . filter ( b = > b >= 40 ) . length * - 1 ) ;
const maxDepth = biomeDepths [ Biome . END ] [ 0 ] - 2 ;
2023-12-07 10:41:47 -08:00
const depthWeights = new Array ( maxDepth + 1 ) . fill ( null )
. map ( ( _ , i : integer ) = > ( ( 1 - Math . min ( Math . abs ( ( i / ( maxDepth - 1 ) ) - ( relWave / 250 ) ) + 0.25 , 1 ) ) / 0.75 ) * 250 ) ;
const biomeThresholds : integer [ ] = [ ] ;
let totalWeight = 0 ;
for ( let biome of biomes ) {
2023-12-12 12:32:50 -08:00
totalWeight += Math . ceil ( depthWeights [ biomeDepths [ biome ] [ 0 ] - 1 ] / biomeDepths [ biome ] [ 1 ] ) ;
2023-12-07 10:41:47 -08:00
biomeThresholds . push ( totalWeight ) ;
}
const randInt = Utils . randSeedInt ( totalWeight ) ;
for ( let biome of biomes ) {
if ( randInt < biomeThresholds [ biome ] )
return biome ;
}
return biomes [ Utils . randSeedInt ( biomes . length ) ] ;
}
}
2023-04-11 21:37:56 -07: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 15:01:15 -07:00
if ( this . nextBiome === undefined )
return this . end ( ) ;
2023-04-10 11:12:01 -07:00
this . scene . tweens . add ( {
2023-11-26 19:22:05 -08:00
targets : [ this . scene . arenaEnemy , this . scene . lastEnemyTrainer ] ,
2023-04-10 11:12:01 -07:00
x : '+=300' ,
duration : 2000 ,
onComplete : ( ) = > {
this . scene . arenaEnemy . setX ( this . scene . arenaEnemy . x - 600 ) ;
2023-04-17 19:44:41 -07:00
this . scene . newArena ( this . nextBiome ) ;
2023-04-10 11:12:01 -07:00
2023-05-09 09:43:28 -07:00
const biomeKey = getBiomeKey ( this . nextBiome ) ;
2023-04-10 11:12:01 -07:00
const bgTexture = ` ${ biomeKey } _bg ` ;
this . scene . arenaBgTransition . setTexture ( bgTexture )
this . scene . arenaBgTransition . setAlpha ( 0 ) ;
this . scene . arenaBgTransition . setVisible ( true ) ;
2023-05-09 09:43:28 -07:00
this . scene . arenaPlayerTransition . setBiome ( this . nextBiome ) ;
2023-04-10 11:12:01 -07:00
this . scene . arenaPlayerTransition . setAlpha ( 0 ) ;
this . scene . arenaPlayerTransition . setVisible ( true ) ;
2023-10-09 17:20:02 -07:00
this . scene . time . delayedCall ( 1000 , ( ) = > this . scene . playBgm ( ) ) ;
2023-04-10 11:12:01 -07:00
this . scene . tweens . add ( {
2023-11-26 19:22:05 -08:00
targets : [ this . scene . arenaPlayer , this . scene . arenaBgTransition , this . scene . arenaPlayerTransition ] ,
2023-04-10 11:12:01 -07:00
duration : 1000 ,
delay : 1000 ,
ease : 'Sine.easeInOut' ,
2023-04-30 08:38:46 -07:00
alpha : ( target : any ) = > target === this . scene . arenaPlayer ? 0 : 1 ,
2023-04-10 11:12:01 -07:00
onComplete : ( ) = > {
this . scene . arenaBg . setTexture ( bgTexture ) ;
2023-05-09 09:43:28 -07:00
this . scene . arenaPlayer . setBiome ( this . nextBiome ) ;
2023-04-30 08:38:46 -07:00
this . scene . arenaPlayer . setAlpha ( 1 ) ;
2023-05-09 09:43:28 -07:00
this . scene . arenaEnemy . setBiome ( this . nextBiome ) ;
2023-05-07 14:05:19 -07:00
this . scene . arenaEnemy . setAlpha ( 1 ) ;
2023-05-09 09:43:28 -07:00
this . scene . arenaNextEnemy . setBiome ( this . nextBiome ) ;
2023-04-10 11:12:01 -07:00
this . scene . arenaBgTransition . setVisible ( false ) ;
this . scene . arenaPlayerTransition . setVisible ( false ) ;
2023-10-22 19:45:48 -07:00
if ( this . scene . lastEnemyTrainer )
this . scene . lastEnemyTrainer . destroy ( ) ;
2023-04-10 11:12:01 -07:00
this . end ( ) ;
}
2023-04-30 08:38:46 -07:00
} ) ;
2023-04-10 11:12:01 -07:00
}
} ) ;
}
}
2023-05-18 08:11:06 -07:00
export class SummonPhase extends PartyMemberPokemonPhase {
2023-10-07 13:08:33 -07:00
constructor ( scene : BattleScene , fieldIndex : integer , player? : boolean ) {
super ( scene , fieldIndex , player !== undefined ? player : true ) ;
2023-04-10 11:12:01 -07:00
}
start() {
super . start ( ) ;
this . preSummon ( ) ;
}
preSummon ( ) : void {
2023-05-31 09:38:55 -07:00
const partyMember = this . getPokemon ( ) ;
if ( partyMember . isFainted ( ) ) {
2023-10-07 13:08:33 -07:00
const party = this . getParty ( ) ;
2023-05-31 09:38:55 -07: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 13:08:33 -07:00
if ( this . player ) {
this . scene . ui . showText ( ` Go! ${ this . getPokemon ( ) . name } ! ` ) ;
2023-10-18 15:01:15 -07:00
if ( this . player )
this . scene . pbTray . hide ( ) ;
2024-02-06 13:15:35 -08:00
this . scene . trainer . setTexture ( ` trainer_ ${ this . scene . gameData . gender === PlayerGender . FEMALE ? 'f' : 'm' } _back_pb ` ) ;
2024-02-05 20:05:56 -08:00
this . scene . time . delayedCall ( 562 , ( ) = > {
this . scene . trainer . setFrame ( '2' ) ;
this . scene . time . delayedCall ( 64 , ( ) = > {
this . scene . trainer . setFrame ( '3' ) ;
} ) ;
} ) ;
2023-10-07 13:08:33 -07:00
this . scene . tweens . add ( {
targets : this.scene.trainer ,
x : - 36 ,
2024-01-14 17:47:08 -08:00
duration : 1000 ,
onComplete : ( ) = > this . scene . trainer . setVisible ( false )
2023-10-07 13:08:33 -07:00
} ) ;
this . scene . time . delayedCall ( 750 , ( ) = > this . summon ( ) ) ;
2023-10-18 15:01:15 -07:00
} else {
this . scene . pbTrayEnemy . hide ( ) ;
2024-02-05 20:05:56 -08:00
this . scene . ui . showText ( ` ${ this . scene . currentBattle . trainer . getName ( true ) } sent out \ n ${ this . getPokemon ( ) . name } ! ` , null , ( ) = > this . summon ( ) ) ;
2023-10-18 15:01:15 -07:00
}
2023-04-10 11:12:01 -07:00
}
summon ( ) : void {
2024-01-04 16:56:26 -08: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 11:12:01 -07:00
pokeball . setVisible ( false ) ;
pokeball . setOrigin ( 0.5 , 0.625 ) ;
this . scene . field . add ( pokeball ) ;
2023-05-18 08:11:06 -07:00
if ( this . fieldIndex === 1 )
2023-10-07 13:08:33 -07: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 08:11:06 -07:00
2023-10-07 13:08:33 -07:00
const fpOffset = pokemon . getFieldPositionOffset ( ) ;
2023-05-18 08:11:06 -07:00
2023-04-10 11:12:01 -07:00
pokeball . setVisible ( true ) ;
2023-05-18 08:11:06 -07:00
this . scene . tweens . add ( {
targets : pokeball ,
duration : 650 ,
2023-10-07 13:08:33 -07:00
x : ( this . player ? 100 : 236 ) + fpOffset [ 0 ]
2023-05-18 08:11:06 -07:00
} ) ;
2023-04-10 11:12:01 -07:00
this . scene . tweens . add ( {
targets : pokeball ,
duration : 150 ,
2023-05-18 08:11:06 -07:00
ease : 'Cubic.easeOut' ,
2023-10-07 13:08:33 -07:00
y : ( this . player ? 70 : 34 ) + fpOffset [ 1 ] ,
2023-04-10 11:12:01 -07:00
onComplete : ( ) = > {
this . scene . tweens . add ( {
targets : pokeball ,
duration : 500 ,
ease : 'Cubic.easeIn' ,
2023-05-18 08:11:06 -07:00
angle : 1440 ,
2023-10-07 13:08:33 -07:00
y : ( this . player ? 132 : 86 ) + fpOffset [ 1 ] ,
2023-04-10 11:12:01 -07:00
onComplete : ( ) = > {
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'pb_rel' ) ;
2023-04-10 11:12:01 -07:00
pokeball . destroy ( ) ;
2023-10-07 13:08:33 -07:00
this . scene . add . existing ( pokemon ) ;
this . scene . field . add ( pokemon ) ;
if ( ! this . player ) {
const playerPokemon = this . scene . getPlayerPokemon ( ) as Pokemon ;
2023-10-27 13:20:51 -07:00
if ( playerPokemon ? . visible )
2023-10-07 13:08:33 -07:00
this . scene . field . moveBelow ( pokemon , playerPokemon ) ;
2023-10-23 10:48:56 -07:00
this . scene . currentBattle . seenEnemyPartyMemberIds . add ( pokemon . id ) ;
2023-10-07 13:08:33 -07:00
}
2024-01-04 20:57:21 -08:00
addPokeballOpenParticles ( this . scene , pokemon . x , pokemon . y - 16 , pokemon . pokeball ) ;
2023-10-31 18:43:22 -07:00
this . scene . updateModifiers ( this . player ) ;
2024-02-14 20:25:12 -08:00
this . scene . updateFieldScale ( ) ;
2023-10-07 13:08:33 -07:00
pokemon . showInfo ( ) ;
pokemon . playAnim ( ) ;
pokemon . setVisible ( true ) ;
2023-10-31 11:09:33 -07:00
pokemon . getSprite ( ) . setVisible ( true ) ;
2023-10-07 13:08:33 -07:00
pokemon . setScale ( 0.5 ) ;
pokemon . tint ( getPokeballTintColor ( pokemon . pokeball ) ) ;
pokemon . untint ( 250 , 'Sine.easeIn' ) ;
2024-02-14 20:25:12 -08:00
this . scene . updateFieldScale ( ) ;
2023-04-10 11:12:01 -07:00
this . scene . tweens . add ( {
2023-10-07 13:08:33 -07:00
targets : pokemon ,
2023-04-10 11:12:01 -07:00
duration : 250 ,
ease : 'Sine.easeIn' ,
2024-02-14 20:25:12 -08:00
scale : pokemon.getSpriteScale ( ) ,
2023-04-10 11:12:01 -07:00
onComplete : ( ) = > {
2023-10-07 13:08:33 -07:00
pokemon . cry ( ) ;
pokemon . getSprite ( ) . clearTint ( ) ;
pokemon . resetSummonData ( ) ;
2023-04-10 11:12:01 -07:00
this . scene . time . delayedCall ( 1000 , ( ) = > this . end ( ) ) ;
}
} ) ;
}
} ) ;
}
} ) ;
}
2023-07-05 13:13:00 -07:00
onEnd ( ) : void {
2023-06-02 08:41:08 -07:00
const pokemon = this . getPokemon ( ) ;
2023-05-05 21:42:01 -07:00
2023-11-05 20:48:04 -08:00
if ( pokemon . isShiny ( ) )
2023-06-02 08:41:08 -07:00
this . scene . unshiftPhase ( new ShinySparklePhase ( this . scene , pokemon . getBattlerIndex ( ) ) ) ;
2023-04-10 11:12:01 -07:00
2023-06-02 08:41:08 -07:00
pokemon . resetTurnData ( ) ;
2023-05-05 21:42:01 -07:00
2024-01-09 20:34:43 -08:00
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeActiveTrigger , true ) ;
2023-07-04 14:50:51 -07:00
this . queuePostSummon ( ) ;
2023-07-05 13:13:00 -07:00
}
queuePostSummon ( ) : void {
this . scene . pushPhase ( new PostSummonPhase ( this . scene , this . getPokemon ( ) . getBattlerIndex ( ) ) ) ;
}
end() {
this . onEnd ( ) ;
2023-05-04 07:25:11 -07:00
2023-04-10 11:12:01 -07:00
super . end ( ) ;
}
}
export class SwitchSummonPhase extends SummonPhase {
private slotIndex : integer ;
private doReturn : boolean ;
2023-04-28 16:26:41 -07:00
private batonPass : boolean ;
2023-04-10 11:12:01 -07:00
2023-10-07 13:08:33 -07:00
private lastPokemon : Pokemon ;
2023-04-28 16:26:41 -07:00
2023-10-07 13:08:33 -07:00
constructor ( scene : BattleScene , fieldIndex : integer , slotIndex : integer , doReturn : boolean , batonPass : boolean , player? : boolean ) {
super ( scene , fieldIndex , player !== undefined ? player : true ) ;
2023-04-10 11:12:01 -07:00
this . slotIndex = slotIndex ;
this . doReturn = doReturn ;
2023-04-28 16:26:41 -07:00
this . batonPass = batonPass ;
2023-04-10 11:12:01 -07:00
}
preSummon ( ) : void {
2024-02-21 20:57:49 -08:00
if ( ! this . player ) {
if ( this . slotIndex === - 1 )
this . slotIndex = this . scene . currentBattle . trainer . getNextSummonIndex ( ) ;
2024-03-06 19:41:55 -08:00
if ( this . slotIndex > - 1 ) {
this . showEnemyTrainer ( ) ;
this . scene . pbTrayEnemy . showPbTray ( this . scene . getEnemyParty ( ) ) ;
}
2024-02-21 20:57:49 -08:00
}
2023-11-26 19:22:05 -08:00
2024-01-14 21:20:26 -08:00
if ( ! this . doReturn || ( this . slotIndex !== - 1 && ! ( this . player ? this . scene . getParty ( ) : this . scene . getEnemyParty ( ) ) [ this . slotIndex ] ) ) {
2024-02-21 20:57:49 -08:00
if ( this . player )
return this . switchAndSummon ( ) ;
else {
this . scene . time . delayedCall ( 750 , ( ) = > this . switchAndSummon ( ) ) ;
return ;
}
2023-04-10 11:12:01 -07:00
}
2023-10-07 13:08:33 -07:00
const pokemon = this . getPokemon ( ) ;
2023-04-10 11:12:01 -07:00
2023-04-28 16:26:41 -07:00
if ( ! this . batonPass )
2024-01-14 21:20:26 -08:00
( this . player ? this . scene . getEnemyField ( ) : this . scene . getPlayerField ( ) ) . forEach ( enemyPokemon = > enemyPokemon . removeTagsBySourceId ( pokemon . id ) ) ;
2023-04-22 19:14:53 -07:00
2024-01-15 21:28:03 -08:00
applyPreSwitchOutAbAttrs ( PreSwitchOutAbAttr , pokemon ) ;
2024-02-05 20:05:56 -08:00
this . scene . ui . showText ( this . player ? ` Come back, ${ pokemon . name } ! ` : ` ${ this . scene . currentBattle . trainer . getName ( true ) } \ nwithdrew ${ pokemon . name } ! ` ) ;
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'pb_rel' ) ;
2023-10-07 13:08:33 -07:00
pokemon . hideInfo ( ) ;
pokemon . tint ( getPokeballTintColor ( pokemon . pokeball ) , 1 , 250 , 'Sine.easeIn' ) ;
2023-04-10 11:12:01 -07:00
this . scene . tweens . add ( {
2023-10-07 13:08:33 -07:00
targets : pokemon ,
2023-04-10 11:12:01 -07:00
duration : 250 ,
ease : 'Sine.easeIn' ,
scale : 0.5 ,
onComplete : ( ) = > {
2023-10-07 13:08:33 -07:00
pokemon . setVisible ( false ) ;
this . scene . field . remove ( pokemon ) ;
2024-01-09 20:34:43 -08:00
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeActiveTrigger , true ) ;
2023-04-10 11:12:01 -07:00
this . scene . time . delayedCall ( 750 , ( ) = > this . switchAndSummon ( ) ) ;
}
} ) ;
}
switchAndSummon() {
2024-01-14 21:20:26 -08:00
const party = this . player ? this . getParty ( ) : this . scene . getEnemyParty ( ) ;
2023-04-10 11:12:01 -07:00
const switchedPokemon = party [ this . slotIndex ] ;
2023-05-18 08:11:06 -07:00
this . lastPokemon = this . getPokemon ( ) ;
2023-05-31 09:38:55 -07:00
if ( this . batonPass && switchedPokemon ) {
2024-01-14 21:20:26 -08:00
( this . player ? this . scene . getEnemyField ( ) : this . scene . getPlayerField ( ) ) . forEach ( enemyPokemon = > enemyPokemon . transferTagsBySourceId ( this . lastPokemon . id , switchedPokemon . id ) ) ;
2023-04-28 16:26:41 -07: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 ;
2023-10-31 11:09:33 -07:00
if ( batonPassModifier )
this . scene . tryTransferHeldItemModifier ( batonPassModifier , switchedPokemon , false , false ) ;
2023-04-28 16:26:41 -07:00
}
}
2023-05-31 09:38:55 -07:00
if ( switchedPokemon ) {
party [ this . slotIndex ] = this . lastPokemon ;
party [ this . fieldIndex ] = switchedPokemon ;
2024-02-21 20:57:49 -08:00
const showTextAndSummon = ( ) = > {
this . scene . ui . showText ( this . player ? ` Go! ${ switchedPokemon . name } ! ` : ` ${ this . scene . currentBattle . trainer . getName ( true ) } sent out \ n ${ this . getPokemon ( ) . name } ! ` ) ;
this . summon ( ) ;
} ;
if ( this . player )
showTextAndSummon ( ) ;
else {
this . scene . time . delayedCall ( 1500 , ( ) = > {
this . hideEnemyTrainer ( ) ;
this . scene . pbTrayEnemy . hide ( ) ;
showTextAndSummon ( ) ;
} ) ;
}
2023-05-31 09:38:55 -07:00
} else
this . end ( ) ;
2023-04-10 11:12:01 -07:00
}
2023-04-28 16:26:41 -07:00
2023-07-05 13:13:00 -07:00
onEnd ( ) : void {
super . onEnd ( ) ;
2023-07-04 14:50:51 -07:00
2023-05-31 09:38:55 -07:00
const pokemon = this . getPokemon ( ) ;
2024-03-08 10:14:08 -08:00
// Compensate for turn spent summoning
2024-03-14 15:03:38 -07:00
if ( pokemon . scene . currentBattle . turn > 1 )
pokemon . battleSummonData . turnCount -- ;
2024-03-08 10:14:08 -08:00
2023-05-31 09:38:55 -07:00
if ( this . batonPass && pokemon )
pokemon . transferSummon ( this . lastPokemon ) ;
2023-04-28 16:26:41 -07:00
2023-05-18 08:11:06 -07:00
this . lastPokemon ? . resetSummonData ( ) ;
2024-01-09 20:34:43 -08:00
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeActiveTrigger , true ) ;
2023-07-05 13:13:00 -07:00
}
2023-04-28 16:26:41 -07:00
2023-07-05 13:13:00 -07:00
queuePostSummon ( ) : void {
this . scene . unshiftPhase ( new PostSummonPhase ( this . scene , this . getPokemon ( ) . getBattlerIndex ( ) ) ) ;
2023-04-28 16:26:41 -07:00
}
2023-04-10 11:12:01 -07:00
}
2023-05-18 08:11:06 -07: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 15:06:12 -07:00
2023-07-05 13:13:00 -07:00
onEnd ( ) : void {
const pokemon = this . getPokemon ( ) ;
pokemon . resetTurnData ( ) ;
pokemon . resetSummonData ( ) ;
2024-01-09 20:34:43 -08:00
2024-02-14 20:25:12 -08:00
this . scene . updateFieldScale ( ) ;
2024-01-09 20:34:43 -08:00
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeActiveTrigger ) ;
2023-07-05 13:13:00 -07:00
}
2023-05-18 08:11:06 -07:00
}
2023-10-18 15:01:15 -07:00
export class ShowTrainerPhase extends BattlePhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
super . start ( ) ;
2024-01-14 17:47:08 -08:00
this . scene . trainer . setVisible ( true )
2024-02-06 13:15:35 -08:00
this . scene . trainer . setTexture ( ` trainer_ ${ this . scene . gameData . gender === PlayerGender . FEMALE ? 'f' : 'm' } _back ` ) ;
2023-10-18 15:01:15 -07:00
this . scene . tweens . add ( {
targets : this.scene.trainer ,
x : 106 ,
duration : 1000 ,
onComplete : ( ) = > this . end ( )
} ) ;
}
}
2023-05-18 08:11:06 -07:00
export class ToggleDoublePositionPhase extends BattlePhase {
private double : boolean ;
constructor ( scene : BattleScene , double : boolean ) {
super ( scene ) ;
this . double = double ;
}
start() {
super . start ( ) ;
2023-07-05 19:33:27 -07:00
const playerPokemon = this . scene . getPlayerField ( ) . find ( p = > p . isActive ( true ) ) ;
2023-11-25 12:53:38 -08:00
if ( playerPokemon ) {
2023-11-25 13:02:47 -08:00
playerPokemon . setFieldPosition ( this . double && this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) . length > 1 ? FieldPosition.LEFT : FieldPosition.CENTER , 500 ) . then ( ( ) = > {
2023-11-25 12:53:38 -08:00
if ( playerPokemon . getFieldIndex ( ) === 1 ) {
const party = this . scene . getParty ( ) ;
party [ 1 ] = party [ 0 ] ;
party [ 0 ] = playerPokemon ;
}
this . end ( ) ;
} ) ;
} else
2023-05-18 08:11:06 -07:00
this . end ( ) ;
}
}
2023-04-10 11:12:01 -07:00
export class CheckSwitchPhase extends BattlePhase {
2023-05-18 08:11:06 -07: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 11:12:01 -07:00
}
start() {
super . start ( ) ;
2023-05-18 08:11:06 -07: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-13 20:50:48 -07:00
super . end ( ) ;
return ;
}
2023-05-18 08:11:06 -07:00
if ( ! this . scene . getParty ( ) . slice ( 1 ) . filter ( p = > p . isActive ( ) ) . length ) {
2023-04-20 08:29:26 -07:00
super . end ( ) ;
return ;
}
2023-05-18 08:11:06 -07:00
if ( pokemon . getTag ( BattlerTagType . FRENZY ) ) {
2023-04-14 22:32:16 -07:00
super . end ( ) ;
return ;
}
2023-10-18 15:01:15 -07:00
this . scene . ui . showText ( ` Will you switch \ n ${ this . useName ? pokemon . name : 'Pokémon' } ? ` , null , ( ) = > {
2023-04-10 11:12:01 -07:00
this . scene . ui . setMode ( Mode . CONFIRM , ( ) = > {
2023-04-19 11:07:38 -07:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2023-05-18 08:11:06 -07:00
this . scene . unshiftPhase ( new SwitchPhase ( this . scene , this . fieldIndex , false , true ) ) ;
2023-04-10 11:12:01 -07:00
this . end ( ) ;
2023-04-19 11:07:38 -07:00
} , ( ) = > {
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . end ( ) ;
} ) ;
2023-04-10 11:12:01 -07:00
} ) ;
}
}
2023-04-13 20:50:48 -07:00
export class SummonMissingPhase extends SummonPhase {
2023-05-18 08:11:06 -07:00
constructor ( scene : BattleScene , fieldIndex : integer ) {
super ( scene , fieldIndex ) ;
2023-04-13 20:50:48 -07:00
}
preSummon ( ) : void {
2023-05-18 08:11:06 -07:00
this . scene . ui . showText ( ` Go! ${ this . getPokemon ( ) . name } ! ` ) ;
2023-04-13 20:50:48 -07:00
this . scene . time . delayedCall ( 250 , ( ) = > this . summon ( ) ) ;
}
}
2023-10-03 09:50:31 -07:00
export class LevelCapPhase extends FieldPhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start ( ) : void {
super . start ( ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > {
this . scene . playSoundWithoutBgm ( 'level_up_fanfare' , 1500 ) ;
this . scene . ui . showText ( ` The level cap \ nhas increased to ${ this . scene . getMaxExpLevel ( ) } ! ` , null , ( ) = > this . end ( ) , null , true ) ;
this . executeForAll ( pokemon = > pokemon . updateInfo ( true ) ) ;
} ) ;
}
}
2023-05-18 08:11:06 -07:00
export class TurnInitPhase extends FieldPhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
2023-04-27 11:30:03 -07:00
2023-05-18 08:11:06 -07:00
start() {
super . start ( ) ;
2023-04-27 11:30:03 -07:00
2023-12-21 17:58:00 -08:00
//this.scene.pushPhase(new MoveAnimTestPhase(this.scene));
2023-06-01 10:54:52 -07:00
this . scene . getField ( ) . forEach ( ( pokemon , i ) = > {
if ( pokemon ? . isActive ( ) ) {
if ( pokemon . isPlayer ( ) )
this . scene . currentBattle . addParticipant ( pokemon as PlayerPokemon ) ;
2023-04-27 11:30:03 -07:00
2023-06-01 10:54:52 -07:00
pokemon . resetTurnData ( ) ;
2023-04-27 11:30:03 -07:00
2023-06-01 10:54:52 -07:00
this . scene . pushPhase ( pokemon . isPlayer ( ) ? new CommandPhase ( this . scene , i ) : new EnemyCommandPhase ( this . scene , i - BattlerIndex . ENEMY ) ) ;
}
2023-05-18 08:11:06 -07:00
} ) ;
this . scene . pushPhase ( new TurnStartPhase ( this . scene ) ) ;
this . end ( ) ;
2023-04-27 11:30:03 -07:00
}
}
export class CommandPhase extends FieldPhase {
2023-05-18 08:11:06 -07:00
protected fieldIndex : integer ;
constructor ( scene : BattleScene , fieldIndex : integer ) {
super ( scene ) ;
this . fieldIndex = fieldIndex ;
2023-04-10 11:12:01 -07:00
}
start() {
super . start ( ) ;
2023-10-27 14:43:53 -07: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 08:11:06 -07:00
const playerPokemon = this . scene . getPlayerField ( ) [ this . fieldIndex ] ;
2023-04-13 09:16:36 -07:00
2023-04-20 18:32:48 -07:00
const moveQueue = playerPokemon . getMoveQueue ( ) ;
while ( moveQueue . length && moveQueue [ 0 ]
2023-05-05 21:42:01 -07:00
&& moveQueue [ 0 ] . move && ( ! playerPokemon . getMoveset ( ) . find ( m = > m . moveId === moveQueue [ 0 ] . move )
2023-10-25 06:41:37 -07:00
|| ! playerPokemon . getMoveset ( ) [ playerPokemon . getMoveset ( ) . findIndex ( m = > m . moveId === moveQueue [ 0 ] . move ) ] . isUsable ( playerPokemon , moveQueue [ 0 ] . ignorePP ) ) )
2023-04-20 18:32:48 -07:00
moveQueue . shift ( ) ;
if ( moveQueue . length ) {
const queuedMove = moveQueue [ 0 ] ;
if ( ! queuedMove . move )
2023-04-30 20:58:16 -07:00
this . handleCommand ( Command . FIGHT , - 1 , false ) ;
2023-04-20 18:32:48 -07:00
else {
2023-05-05 21:42:01 -07:00
const moveIndex = playerPokemon . getMoveset ( ) . findIndex ( m = > m . moveId === queuedMove . move ) ;
2023-10-25 06:41:37 -07:00
if ( moveIndex > - 1 && playerPokemon . getMoveset ( ) [ moveIndex ] . isUsable ( playerPokemon , queuedMove . ignorePP ) ) {
2023-05-18 08:11:06 -07:00
this . handleCommand ( Command . FIGHT , moveIndex , queuedMove . ignorePP , { targets : queuedMove.targets , multiple : queuedMove.targets.length > 1 } ) ;
} else
2023-10-20 08:38:41 -07:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-04-20 18:32:48 -07:00
}
2023-04-19 15:19:55 -07:00
} else
2023-10-20 08:38:41 -07:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-04-10 11:12:01 -07:00
}
2023-04-28 16:26:41 -07:00
handleCommand ( command : Command , cursor : integer , . . . args : any [ ] ) : boolean {
2023-05-18 08:11:06 -07:00
const playerPokemon = this . scene . getPlayerField ( ) [ this . fieldIndex ] ;
const enemyField = this . scene . getEnemyField ( ) ;
2023-04-10 11:12:01 -07:00
let success : boolean ;
2023-04-11 16:08:03 -07:00
2023-04-10 11:12:01 -07:00
switch ( command ) {
case Command . FIGHT :
2023-10-27 05:45:47 -07: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 10:47:32 -08:00
const moveId = ! useStruggle ? cursor > - 1 ? playerPokemon . getMoveset ( ) [ cursor ] . moveId : Moves.NONE : Moves . STRUGGLE ;
2023-12-21 20:57:11 -08:00
const turnCommand : TurnCommand = { command : Command.FIGHT , cursor : cursor , move : { move : moveId , targets : [ ] , ignorePP : args [ 0 ] } , args : args } ;
2023-11-27 10:47:32 -08:00
const moveTargets : MoveTargetSet = args . length < 3 ? getMoveTargets ( playerPokemon , moveId ) : args [ 2 ] ;
if ( ! moveId )
turnCommand . targets = [ this . fieldIndex ] ;
2023-05-18 08:11:06 -07: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 11:12:01 -07:00
success = true ;
2023-05-05 21:42:01 -07:00
} else if ( cursor < playerPokemon . getMoveset ( ) . length ) {
const move = playerPokemon . getMoveset ( ) [ cursor ] ;
2023-10-25 06:41:37 -07:00
if ( playerPokemon . summonData . disabledMove === move . moveId ) {
2023-04-19 19:51:46 -07:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . showText ( ` ${ move . getName ( ) } is disabled! ` , null , ( ) = > {
this . scene . ui . clearText ( ) ;
2023-10-18 20:16:38 -07:00
this . scene . ui . setMode ( Mode . FIGHT , this . fieldIndex ) ;
2023-04-19 19:51:46 -07:00
} , null , true ) ;
}
2023-04-10 11:12:01 -07:00
}
break ;
case Command . BALL :
2023-04-29 13:26:09 -07:00
if ( this . scene . arena . biomeType === Biome . END ) {
2023-10-20 08:38:41 -07:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-04-29 13:26:09 -07:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
2023-10-27 16:56:15 -07:00
this . scene . ui . showText ( ` An unseen force \ nprevents using Poké Balls. ` , null , ( ) = > {
2023-10-18 15:01:15 -07:00
this . scene . ui . showText ( null , 0 ) ;
2023-10-20 08:38:41 -07:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-10-18 15:01:15 -07:00
} , null , true ) ;
} else if ( this . scene . currentBattle . battleType === BattleType . TRAINER ) {
2023-10-20 08:38:41 -07:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-10-18 15:01:15 -07:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . showText ( ` You can't catch \ nanother trainer's Pokémon! ` , null , ( ) = > {
2023-04-29 21:51:33 -07:00
this . scene . ui . showText ( null , 0 ) ;
2023-10-20 08:38:41 -07:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-04-29 13:26:09 -07:00
} , null , true ) ;
2023-10-27 14:43:53 -07:00
} else {
2023-06-02 08:41:08 -07:00
const targets = this . scene . getEnemyField ( ) . filter ( p = > p . isActive ( true ) ) . map ( p = > p . getBattlerIndex ( ) ) ;
2023-10-27 14:43:53 -07:00
if ( targets . length > 1 ) {
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . showText ( ` You can only throw a Poké Ball \ nwhen there is one Pokémon remaining! ` , null , ( ) = > {
this . scene . ui . showText ( null , 0 ) ;
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
} , null , true ) ;
2024-02-29 06:58:07 -08:00
} else if ( cursor < 5 ) {
2024-01-07 20:17:24 -08:00
const targetPokemon = this . scene . getEnemyField ( ) . find ( p = > p . isActive ( true ) ) ;
2024-03-01 20:10:46 -08:00
if ( targetPokemon . isBoss ( ) && targetPokemon . bossSegmentIndex >= 1 ) {
2024-01-07 20:17:24 -08:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . showText ( ` The target Pokémon is too strong to be caught! \ nYou need to weaken it first! ` , null , ( ) = > {
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 14:43:53 -07:00
this . scene . currentBattle . turnCommands [ this . fieldIndex ] . targets = targets ;
if ( this . fieldIndex )
this . scene . currentBattle . turnCommands [ this . fieldIndex - 1 ] . skip = true ;
2024-01-07 20:17:24 -08:00
success = true ;
2023-10-27 14:43:53 -07:00
}
}
2023-04-10 11:12:01 -07:00
}
break ;
case Command . POKEMON :
2023-05-08 07:03:57 -07:00
case Command . RUN :
const isSwitch = command === Command . POKEMON ;
2023-10-27 16:56:15 -07:00
if ( ! isSwitch && this . scene . arena . biomeType === Biome . END ) {
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . showText ( ` An unseen force \ nprevents escape. ` , null , ( ) = > {
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 08:38:41 -07:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-10-18 15:01:15 -07:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
this . scene . ui . showText ( ` You can't run \ nfrom a trainer battle! ` , null , ( ) = > {
2023-04-22 19:14:53 -07:00
this . scene . ui . showText ( null , 0 ) ;
2023-10-20 08:38:41 -07:00
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-04-22 19:14:53 -07:00
} , null , true ) ;
2023-10-18 15:01:15 -07: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-28 22:35:12 -07:00
if ( ! isSwitch && this . fieldIndex )
2023-10-27 14:43:53 -07:00
this . scene . currentBattle . turnCommands [ this . fieldIndex - 1 ] . skip = true ;
2023-10-18 15:01:15 -07:00
} else if ( trapTag ) {
2023-12-14 08:54:56 -08:00
if ( ! isSwitch ) {
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
}
2023-10-18 15:01:15 -07:00
this . scene . ui . showText ( ` ${ this . scene . getPokemonById ( trapTag . sourceId ) . name } 's ${ trapTag . getMoveName ( ) } \ nprevents ${ isSwitch ? 'switching' : 'fleeing' } ! ` , null , ( ) = > {
this . scene . ui . showText ( null , 0 ) ;
2023-12-14 08:54:56 -08:00
if ( ! isSwitch )
this . scene . ui . setMode ( Mode . COMMAND , this . fieldIndex ) ;
2023-10-18 15:01:15 -07:00
} , null , true ) ;
}
}
2023-04-10 11:12:01 -07:00
break ;
}
2023-04-11 21:37:56 -07:00
2023-05-18 08:11:06 -07:00
if ( success )
2023-04-10 11:12:01 -07:00
this . end ( ) ;
return success ;
}
2023-10-18 20:16:38 -07: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-15 21:58:57 -08: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-18 20:16:38 -07:00
getFieldIndex ( ) : integer {
return this . fieldIndex ;
}
2023-05-18 08:11:06 -07:00
getPokemon ( ) : PlayerPokemon {
return this . scene . getPlayerField ( ) [ this . fieldIndex ] ;
}
2023-04-10 11:12:01 -07:00
end() {
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > super . end ( ) ) ;
}
}
2023-05-18 08:11:06 -07: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-01-14 21:20:26 -08:00
const trainer = this . scene . currentBattle . trainer ;
2024-03-08 15:38:04 -08:00
if ( trainer && ! enemyPokemon . getMoveQueue ( ) . length ) {
2024-01-14 21:20:26 -08: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 ) ) ;
if ( enemyPokemon . moveset . find ( m = > m . moveId === Moves . BATON_PASS && m . isUsable ( enemyPokemon ) ) && ( enemyPokemon . summonData . battleStats . reduce ( ( total , stat ) = > total += stat , 0 ) >= 0 || trapTag || trapped . value ) ) {
this . scene . currentBattle . turnCommands [ this . fieldIndex + BattlerIndex . ENEMY ] =
{ command : Command.FIGHT , move : { move : Moves.BATON_PASS , targets : enemyPokemon.getNextTargets ( Moves . BATON_PASS ) } } ;
return this . end ( ) ;
} else if ( ! trapTag && ! trapped . value ) {
const partyMemberScores = trainer . getPartyMemberMatchupScores ( ) ;
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 ) ;
if ( sortedPartyMemberScores [ 0 ] [ 1 ] >= matchupScore * ( trainer . config . isBoss ? 2 : 3 ) ) {
const index = trainer . getNextSummonIndex ( partyMemberScores ) ;
this . scene . currentBattle . turnCommands [ this . fieldIndex + BattlerIndex . ENEMY ] =
{ command : Command.POKEMON , cursor : index , args : [ false ] } ;
return this . end ( ) ;
}
}
}
}
2023-05-18 08:11:06 -07:00
const nextMove = enemyPokemon . getNextMove ( ) ;
this . scene . currentBattle . turnCommands [ this . fieldIndex + BattlerIndex . ENEMY ] =
{ command : Command.FIGHT , move : nextMove } ;
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-27 16:34:55 -07:00
if ( turnCommand . command === Command . BALL && this . fieldIndex )
2023-10-27 14:43:53 -07:00
this . scene . currentBattle . turnCommands [ this . fieldIndex - 1 ] . skip = true ;
2023-05-18 08:11:06 -07: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 ) {
const aPriority = allMoves [ aCommand . move . move ] . priority ;
const bPriority = allMoves [ bCommand . move . move ] . priority ;
if ( aPriority !== bPriority )
return aPriority < bPriority ? 1 : - 1 ;
}
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 14:43:53 -07:00
if ( turnCommand . skip )
continue ;
2023-05-18 08:11:06 -07: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-14 21:20:26 -08:00
this . scene . unshiftPhase ( new SwitchSummonPhase ( this . scene , pokemon . getFieldIndex ( ) , turnCommand . cursor , true , turnCommand . args [ 0 ] as boolean , pokemon . isPlayer ( ) ) ) ;
2023-05-18 08:11:06 -07:00
else
this . scene . unshiftPhase ( new AttemptRunPhase ( this . scene , pokemon . getFieldIndex ( ) ) ) ;
break ;
}
}
2023-07-04 13:16:50 -07:00
if ( this . scene . arena . weather )
this . scene . pushPhase ( new WeatherEffectPhase ( this . scene , this . scene . arena . weather ) ) ;
2023-05-18 08:11:06 -07: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 11:30:03 -07:00
export class TurnEndPhase extends FieldPhase {
2023-04-13 09:16:36 -07:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
2023-04-18 19:09:37 -07:00
super . start ( ) ;
2023-05-18 08:11:06 -07:00
this . scene . currentBattle . incrementTurn ( this . scene ) ;
2023-04-19 15:19:55 -07:00
const handlePokemon = ( pokemon : Pokemon ) = > {
2023-04-21 16:30:04 -07:00
pokemon . lapseTags ( BattlerTagLapseType . TURN_END ) ;
2023-04-19 15:19:55 -07:00
2023-10-25 06:41:37 -07:00
if ( pokemon . summonData . disabledMove && ! -- pokemon . summonData . disabledTurns ) {
this . scene . pushPhase ( new MessagePhase ( this . scene , ` ${ allMoves [ pokemon . summonData . disabledMove ] . name } is disabled \ nno more! ` ) ) ;
2024-02-25 09:45:41 -08:00
pokemon . summonData . disabledMove = Moves . NONE ;
2023-04-19 15:19:55 -07:00
}
2023-04-15 14:40:18 -07:00
2023-04-20 16:44:56 -07:00
const hasUsableBerry = ! ! this . scene . findModifier ( m = > m instanceof BerryModifier && m . shouldApply ( [ pokemon ] ) , pokemon . isPlayer ( ) ) ;
2023-04-20 12:46:05 -07:00
if ( hasUsableBerry )
2023-05-18 08:11:06 -07:00
this . scene . pushPhase ( new BerryPhase ( this . scene , pokemon . getBattlerIndex ( ) ) ) ;
2023-04-20 12:46:05 -07:00
2023-04-20 17:15:16 -07:00
this . scene . applyModifiers ( TurnHealModifier , pokemon . isPlayer ( ) , pokemon ) ;
2023-10-29 14:58:54 -07:00
if ( ! pokemon . isPlayer ( ) ) {
this . scene . applyModifiers ( EnemyTurnHealModifier , false , pokemon ) ;
this . scene . applyModifier ( EnemyStatusEffectHealChanceModifier , false , pokemon ) ;
}
2024-03-09 21:14:09 -08:00
if ( this . scene . arena . terrain ? . terrainType === TerrainType . GRASSY && pokemon . isGrounded ( ) ) {
2024-03-09 18:57:33 -08:00
this . scene . unshiftPhase ( new PokemonHealPhase ( this . scene , pokemon . getBattlerIndex ( ) ,
Math . max ( pokemon . getMaxHp ( ) >> 4 , 1 ) , getPokemonMessage ( pokemon , ' regained\nhealth from the Grassy Terrain!' ) , true ) ) ;
}
2023-05-31 08:20:06 -07:00
applyPostTurnAbAttrs ( PostTurnAbAttr , pokemon ) ;
2023-05-02 12:56:41 -07:00
2023-05-05 13:55:46 -07:00
this . scene . applyModifiers ( TurnHeldItemTransferModifier , pokemon . isPlayer ( ) , pokemon ) ;
2023-04-23 07:24:22 -07:00
2023-04-19 15:19:55 -07:00
pokemon . battleSummonData . turnCount ++ ;
} ;
2023-05-18 08:11:06 -07:00
this . executeForAll ( handlePokemon ) ;
2023-04-21 16:30:04 -07:00
this . scene . arena . lapseTags ( ) ;
2023-04-13 09:16:36 -07:00
2023-04-19 11:07:38 -07:00
if ( this . scene . arena . weather && ! this . scene . arena . weather . lapse ( ) )
this . scene . arena . trySetWeather ( WeatherType . NONE , false ) ;
2023-04-13 09:16:36 -07:00
this . end ( ) ;
}
}
2023-04-18 19:09:37 -07:00
export class BattleEndPhase extends BattlePhase {
start() {
super . start ( ) ;
2024-01-11 09:26:32 -08:00
this . scene . gameData . gameStats . battles ++ ;
if ( this . scene . currentBattle . trainer )
this . scene . gameData . gameStats . trainersDefeated ++ ;
2024-03-14 13:26:57 -07:00
if ( this . scene . gameMode . isEndless && this . scene . currentBattle . waveIndex + 1 > this . scene . gameData . gameStats . highestEndlessWave )
2024-01-11 09:26:32 -08:00
this . scene . gameData . gameStats . highestEndlessWave = this . scene . currentBattle . waveIndex + 1 ;
2023-05-18 08:11:06 -07:00
for ( let pokemon of this . scene . getField ( ) ) {
if ( pokemon )
pokemon . resetBattleSummonData ( ) ;
}
2024-03-07 05:42:04 -08:00
for ( let pokemon of this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) )
2024-03-06 18:05:23 -08:00
applyPostBattleAbAttrs ( PostBattleAbAttr , pokemon ) ;
2023-10-28 10:24:57 -07:00
this . scene . clearEnemyHeldItemModifiers ( ) ;
2023-04-20 16:44:56 -07:00
2024-02-16 21:40:03 -08:00
const lapsingModifiers = this . scene . findModifiers ( m = > m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier ) as ( LapsingPersistentModifier | LapsingPokemonHeldItemModifier ) [ ] ;
2023-06-01 08:22:34 -07:00
for ( let m of lapsingModifiers ) {
2024-02-16 21:40:03 -08:00
const args : any [ ] = [ ] ;
if ( m instanceof LapsingPokemonHeldItemModifier )
args . push ( this . scene . getPokemonById ( m . pokemonId ) ) ;
if ( ! m . lapse ( args ) )
2023-04-18 19:09:37 -07:00
this . scene . removeModifier ( m ) ;
}
this . scene . updateModifiers ( ) . then ( ( ) = > this . end ( ) ) ;
}
}
2023-05-18 08:11:06 -07:00
export class NewBattlePhase extends BattlePhase {
start() {
super . start ( ) ;
2023-04-10 11:12:01 -07:00
2023-05-18 08:11:06 -07:00
this . scene . newBattle ( ) ;
2023-04-10 11:12:01 -07:00
2023-05-18 08:11:06 -07:00
this . end ( ) ;
2023-04-10 11:12:01 -07:00
}
}
2023-04-13 22:08:44 -07:00
export class CommonAnimPhase extends PokemonPhase {
private anim : CommonAnim ;
2023-05-18 08:11:06 -07:00
private targetIndex : integer ;
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , targetIndex : BattlerIndex , anim : CommonAnim ) {
super ( scene , battlerIndex ) ;
2023-04-13 22:08:44 -07:00
this . anim = anim ;
2023-05-18 08:11:06 -07:00
this . targetIndex = targetIndex ;
2023-04-13 22:08:44 -07:00
}
start() {
2023-05-19 10:02:58 -07: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-13 22:08:44 -07:00
this . end ( ) ;
} ) ;
}
}
2023-05-18 08:11:06 -07:00
export class MovePhase extends BattlePhase {
2024-01-13 09:24:24 -08:00
public pokemon : Pokemon ;
2024-02-20 10:15:53 -08:00
public move : PokemonMove ;
2023-05-18 08:11:06 -07:00
protected targets : BattlerIndex [ ] ;
2023-04-16 15:40:32 -07:00
protected followUp : boolean ;
2023-04-30 20:58:16 -07:00
protected ignorePp : boolean ;
2023-04-11 16:08:03 -07:00
protected cancelled : boolean ;
2023-04-10 11:12:01 -07:00
2023-05-18 08:11:06 -07:00
constructor ( scene : BattleScene , pokemon : Pokemon , targets : BattlerIndex [ ] , move : PokemonMove , followUp? : boolean , ignorePp? : boolean ) {
2023-04-10 11:12:01 -07:00
super ( scene ) ;
this . pokemon = pokemon ;
2023-05-18 08:11:06 -07:00
this . targets = targets ;
2023-04-10 11:12:01 -07:00
this . move = move ;
2023-04-16 15:40:32 -07:00
this . followUp = ! ! followUp ;
2023-04-30 20:58:16 -07:00
this . ignorePp = ! ! ignorePp ;
2023-04-11 16:08:03 -07:00
this . cancelled = false ;
2023-04-10 11:12:01 -07:00
}
canMove ( ) : boolean {
2023-10-25 06:41:37 -07:00
return this . pokemon . isActive ( true ) && this . move . isUsable ( this . pokemon , this . ignorePp ) && ! ! this . targets . length ;
2023-04-10 11:12:01 -07:00
}
2023-04-14 22:32:16 -07:00
cancel ( ) : void {
this . cancelled = true ;
}
2023-04-10 11:12:01 -07:00
start() {
super . start ( ) ;
2023-04-19 15:19:55 -07:00
console . log ( Moves [ this . move . moveId ] ) ;
2023-05-04 13:38:56 -07:00
if ( ! this . canMove ( ) ) {
2023-11-27 10:47:32 -08:00
if ( this . move . moveId && this . pokemon . summonData . disabledMove === this . move . moveId )
2023-05-04 13:38:56 -07:00
this . scene . queueMessage ( ` ${ this . move . getName ( ) } is disabled! ` ) ;
2023-12-07 14:43:56 -08:00
return this . end ( ) ;
2023-05-04 13:38:56 -07:00
}
2024-03-13 21:40:57 -07:00
if ( ! this . followUp ) {
const abilityEffectsIgnored = new Utils . BooleanHolder ( false ) ;
this . scene . getField ( true ) . map ( p = > applyAbAttrs ( MoveAbilityBypassAbAttr , p , abilityEffectsIgnored ) ) ;
if ( abilityEffectsIgnored . value )
this . scene . arena . setIgnoreAbilities ( true ) ;
}
2024-03-11 17:55:41 -07: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 05:44:05 -07:00
this . scene . getField ( true ) . filter ( p = > p !== this . pokemon ) . forEach ( p = > applyAbAttrs ( RedirectMoveAbAttr , p , null , this . move . moveId , moveTarget ) ) ;
2024-03-11 17:55:41 -07:00
this . targets [ 0 ] = moveTarget . value ;
}
2023-11-27 08:42:03 -08: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 08:11:06 -07:00
2024-03-11 17:55:41 -07:00
const targets = this . scene . getField ( true ) . filter ( p = > {
if ( this . targets . indexOf ( p . getBattlerIndex ( ) ) > - 1 ) {
2023-05-18 08:11:06 -07:00
const hiddenTag = p . getTag ( HiddenTag ) ;
if ( hiddenTag && ! this . move . getMove ( ) . getAttrs ( HitsTagAttr ) . filter ( hta = > ( hta as HitsTagAttr ) . tagType === hiddenTag . tagType ) . length )
return false ;
return true ;
}
return false ;
} ) ;
2023-04-16 15:40:32 -07:00
2023-04-11 16:08:03 -07:00
const doMove = ( ) = > {
2023-11-15 15:45:10 -08:00
if ( ! this . followUp && this . canMove ( ) ) {
this . pokemon . lapseTags ( BattlerTagLapseType . MOVE ) ;
if ( this . cancelled ) {
this . pokemon . pushMoveHistory ( { move : Moves.NONE , result : MoveResult.FAIL } ) ;
2023-12-07 14:43:56 -08:00
return this . end ( ) ;
2023-11-15 15:45:10 -08:00
}
}
2023-04-20 18:32:48 -07:00
const moveQueue = this . pokemon . getMoveQueue ( ) ;
2023-11-27 10:47:32 -08:00
if ( this . move . moveId )
this . showMoveText ( ) ;
2023-05-18 08:11:06 -07:00
if ( ( moveQueue . length && moveQueue [ 0 ] . move === Moves . NONE ) || ! targets . length ) {
2023-04-20 18:32:48 -07:00
moveQueue . shift ( ) ;
this . cancel ( ) ;
}
2023-04-11 16:08:03 -07:00
if ( this . cancelled ) {
2023-05-18 08:11:06 -07:00
this . pokemon . pushMoveHistory ( { move : Moves.NONE , result : MoveResult.FAIL } ) ;
2023-12-07 14:43:56 -08:00
return this . end ( ) ;
2023-04-11 16:08:03 -07:00
}
2023-04-16 15:40:32 -07:00
2023-10-29 21:16:23 -07:00
if ( ! moveQueue . length || ! moveQueue . shift ( ) . ignorePP ) {
2023-04-13 09:16:36 -07:00
this . move . ppUsed ++ ;
2023-10-29 21:16:23 -07:00
for ( let opponent of this . pokemon . getOpponents ( ) ) {
if ( this . move . ppUsed === this . move . getMove ( ) . pp )
break ;
if ( opponent . getAbility ( ) . id === Abilities . PRESSURE )
this . move . ppUsed ++ ;
}
}
2023-04-16 15:40:32 -07:00
2023-10-09 17:20:02 -07:00
if ( ! allMoves [ this . move . moveId ] . getAttrs ( CopyMoveAttr ) . length )
this . scene . currentBattle . lastMove = this . move . moveId ;
2023-05-18 08:11:06 -07: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 ( ) ) ;
2023-04-24 11:30:21 -07:00
if ( success && this . scene . arena . isMoveWeatherCancelled ( this . move . getMove ( ) ) )
success = false ;
if ( success )
this . scene . unshiftPhase ( this . getEffectPhase ( ) ) ;
else {
2023-05-18 08:11:06 -07:00
this . pokemon . pushMoveHistory ( { move : this.move.moveId , targets : this.targets , result : MoveResult.FAIL , virtual : this.move.virtual } ) ;
2023-11-27 08:42:03 -08:00
this . showFailedText ( ) ;
2023-04-24 11:30:21 -07:00
}
2023-04-15 14:40:18 -07:00
2023-04-11 16:08:03 -07:00
this . end ( ) ;
} ;
2023-04-16 15:40:32 -07:00
if ( ! this . followUp && this . pokemon . status && ! this . pokemon . status . isPostTurn ( ) ) {
2023-04-11 16:08:03 -07:00
this . pokemon . status . incrementTurn ( ) ;
let activated = false ;
let healed = false ;
2023-04-16 15:40:32 -07:00
2023-04-11 16:08:03 -07:00
switch ( this . pokemon . status . effect ) {
case StatusEffect . PARALYSIS :
2024-01-02 18:31:59 -08:00
if ( ! this . pokemon . randSeedInt ( 4 ) ) {
2023-04-11 16:08:03 -07:00
activated = true ;
this . cancelled = true ;
}
break ;
case StatusEffect . SLEEP :
2023-05-18 08:11:06 -07:00
applyMoveAttrs ( BypassSleepAttr , this . pokemon , null , this . move . getMove ( ) ) ;
2023-04-11 16:08:03 -07:00
healed = this . pokemon . status . turnCount === this . pokemon . status . cureTurn ;
2023-04-21 16:30:04 -07:00
activated = ! healed && ! this . pokemon . getTag ( BattlerTagType . BYPASS_SLEEP ) ;
2023-04-11 16:08:03 -07:00
this . cancelled = activated ;
break ;
case StatusEffect . FREEZE :
2024-01-02 18:31:59 -08:00
healed = ! this . pokemon . randSeedInt ( 5 ) ;
2023-04-11 16:08:03 -07:00
activated = ! healed ;
this . cancelled = activated ;
break ;
}
if ( activated ) {
2023-04-21 16:30:04 -07:00
this . scene . queueMessage ( getPokemonMessage ( this . pokemon , getStatusEffectActivationText ( this . pokemon . status . effect ) ) ) ;
2023-05-18 08:11:06 -07:00
this . scene . unshiftPhase ( new CommonAnimPhase ( this . scene , this . pokemon . getBattlerIndex ( ) , undefined , CommonAnim . POISON + ( this . pokemon . status . effect - 1 ) ) ) ;
2023-04-14 22:32:16 -07:00
doMove ( ) ;
2023-04-11 16:08:03 -07:00
} else {
if ( healed ) {
2023-04-21 16:30:04 -07:00
this . scene . queueMessage ( getPokemonMessage ( this . pokemon , getStatusEffectHealText ( this . pokemon . status . effect ) ) ) ;
2023-04-11 16:08:03 -07:00
this . pokemon . resetStatus ( ) ;
2023-04-20 12:46:05 -07:00
this . pokemon . updateInfo ( ) ;
2023-04-11 16:08:03 -07:00
}
doMove ( ) ;
}
} else
doMove ( ) ;
2023-04-10 11:12:01 -07:00
}
2023-04-16 15:40:32 -07:00
2023-04-10 11:12:01 -07:00
getEffectPhase ( ) : MoveEffectPhase {
2023-05-18 08:11:06 -07:00
return new MoveEffectPhase ( this . scene , this . pokemon . getBattlerIndex ( ) , this . targets , this . move ) ;
2023-04-10 11:12:01 -07:00
}
2023-11-27 08:42:03 -08:00
showMoveText ( ) : void {
2023-12-11 18:46:49 -08:00
if ( this . move . getMove ( ) . getAttrs ( ChargeAttr ) . length ) {
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-07 20:17:24 -08:00
if ( this . pokemon . getTag ( BattlerTagType . RECHARGING ) )
return ;
2023-12-11 18:46:49 -08:00
2023-11-27 08:42:03 -08:00
this . scene . queueMessage ( getPokemonMessage ( this . pokemon , ` used \ n ${ this . move . getName ( ) } ! ` ) , 500 ) ;
}
showFailedText ( ) : void {
this . scene . queueMessage ( 'But it failed!' ) ;
}
2023-05-18 08:11:06 -07:00
end() {
if ( ! this . followUp && this . canMove ( ) )
this . scene . unshiftPhase ( new MoveEndPhase ( this . scene , this . pokemon . getBattlerIndex ( ) ) ) ;
2023-04-10 11:12:01 -07:00
2023-05-18 08:11:06 -07:00
super . end ( ) ;
2023-04-10 11:12:01 -07:00
}
}
2023-12-03 21:09:38 -08:00
export class MoveEffectPhase extends PokemonPhase {
2024-03-13 14:09:23 -07:00
public move : PokemonMove ;
2023-05-18 08:11:06 -07:00
protected targets : BattlerIndex [ ] ;
2023-04-10 11:12:01 -07:00
2023-05-18 08:11:06 -07:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , targets : BattlerIndex [ ] , move : PokemonMove ) {
super ( scene , battlerIndex ) ;
2023-04-10 11:12:01 -07:00
this . move = move ;
2023-05-18 08:11:06 -07:00
this . targets = targets ;
2023-04-10 11:12:01 -07:00
}
start() {
super . start ( ) ;
const user = this . getUserPokemon ( ) ;
2023-05-18 08:11:06 -07:00
const targets = this . getTargets ( ) ;
2023-04-10 11:12:01 -07:00
2024-03-08 12:52:33 -08:00
if ( ! user || ! user . scene )
2023-12-07 14:43:56 -08:00
return this . end ( ) ;
2023-04-13 09:16:36 -07:00
const overridden = new Utils . BooleanHolder ( false ) ;
2023-04-10 13:17:25 -07:00
2023-05-18 08:11:06 -07:00
// Assume single target for override
2023-12-03 21:09:38 -08:00
applyMoveAttrs ( OverrideMoveEffectAttr , user , this . getTarget ( ) , this . move . getMove ( ) , overridden , this . move . virtual ) . then ( ( ) = > {
2023-04-10 13:17:25 -07:00
2023-12-07 14:43:56 -08:00
if ( overridden . value )
return this . end ( ) ;
2023-12-03 21:09:38 -08:00
2023-04-21 16:30:04 -07:00
user . lapseTags ( BattlerTagLapseType . MOVE_EFFECT ) ;
2023-04-13 09:16:36 -07:00
if ( user . turnData . hitsLeft === undefined ) {
const hitCount = new Utils . IntegerHolder ( 1 ) ;
2023-05-18 08:11:06 -07:00
// Assume single target for multi hit
applyMoveAttrs ( MultiHitAttr , user , this . getTarget ( ) , this . move . getMove ( ) , hitCount ) ;
2023-05-02 21:00:21 -07:00
user . turnData . hitsLeft = user . turnData . hitCount = hitCount . value ;
2023-04-13 09:16:36 -07:00
}
2023-05-18 08:11:06 -07: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 14:12:39 -08:00
const activeTargets = targets . map ( t = > t . isActive ( true ) ) ;
if ( targets . length === 1 && ! targetHitChecks [ this . targets [ 0 ] ] || ! activeTargets . length ) {
2023-10-30 09:33:20 -07:00
user . turnData . hitCount = 1 ;
user . turnData . hitsLeft = 1 ;
2023-12-05 14:12:39 -08:00
if ( activeTargets . length ) {
this . scene . queueMessage ( getPokemonMessage ( user , '\'s\nattack missed!' ) ) ;
moveHistoryEntry . result = MoveResult . MISS ;
applyMoveAttrs ( MissEffectAttr , user , null , this . move . getMove ( ) ) ;
} else {
this . scene . queueMessage ( 'But it failed!' ) ;
moveHistoryEntry . result = MoveResult . FAIL ;
}
2023-12-07 14:43:56 -08:00
return this . end ( ) ;
2023-04-10 11:12:01 -07:00
}
2023-04-18 09:30:47 -07:00
2023-10-31 11:09:33 -07:00
const applyAttrs : Promise < void > [ ] = [ ] ;
2023-05-18 08:11:06 -07: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 09:33:20 -07:00
user . turnData . hitCount = 1 ;
user . turnData . hitsLeft = 1 ;
2023-05-18 08:11:06 -07: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-24 22:32:48 -07:00
}
2023-05-18 08:11:06 -07:00
2024-03-13 14:09:23 -07: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 08:11:06 -07:00
moveHistoryEntry . result = MoveResult . SUCCESS ;
2023-10-09 17:20:02 -07:00
const hitResult = ! isProtected ? target . apply ( user , this . move ) : HitResult . NO_EFFECT ;
2023-07-10 07:54:22 -07:00
2024-01-09 20:34:43 -08:00
this . scene . triggerPokemonFormChange ( user , SpeciesFormChangeMoveUsedTrigger ) ;
2023-10-31 11:09:33 -07:00
applyAttrs . push ( new Promise ( resolve = > {
applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && ( attr as MoveEffectAttr ) . trigger === MoveEffectTrigger . PRE_APPLY ,
user , target , this . move . getMove ( ) ) . then ( ( ) = > {
if ( hitResult !== HitResult . FAIL ) {
2024-02-27 11:21:17 -08:00
const chargeEffect = ! ! this . move . getMove ( ) . getAttrs ( ChargeAttr ) . find ( ca = > ( ca as ChargeAttr ) . usedChargeEffect ( user , this . getTarget ( ) , this . move . getMove ( ) ) ) ;
2023-10-31 11:09:33 -07: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
&& ( attr as MoveEffectAttr ) . selfTarget , user , target , this . move . getMove ( ) ) ) . then ( ( ) = > {
if ( hitResult !== HitResult . NO_EFFECT ) {
applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && ( attr as MoveEffectAttr ) . trigger === MoveEffectTrigger . POST_APPLY
&& ! ( attr as MoveEffectAttr ) . selfTarget , user , target , this . move . getMove ( ) ) . then ( ( ) = > {
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 ) ;
}
Utils . executeIf ( ! isProtected && ! chargeEffect , ( ) = > applyFilteredMoveAttrs ( ( attr : MoveAttr ) = > attr instanceof MoveEffectAttr && ( attr as MoveEffectAttr ) . trigger === MoveEffectTrigger . HIT ,
user , target , this . move . getMove ( ) ) . then ( ( ) = > {
2023-12-22 18:42:47 -08:00
return Utils . executeIf ( ! target . isFainted ( ) , ( ) = > applyPostDefendAbAttrs ( PostDefendAbAttr , target , user , this . move , hitResult ) . then ( ( ) = > {
2024-02-27 18:34:21 -08:00
if ( ! user . isPlayer ( ) && this . move . getMove ( ) instanceof AttackMove )
2023-10-31 11:09:33 -07:00
user . scene . applyModifiers ( EnemyAttackStatusEffectChanceModifier , false , target ) ;
2023-12-22 18:42:47 -08:00
} ) ) . then ( ( ) = > {
applyPostAttackAbAttrs ( PostAttackAbAttr , user , target , this . move , hitResult ) . then ( ( ) = > {
2024-02-27 18:34:21 -08:00
if ( this . move . getMove ( ) instanceof AttackMove )
2023-12-22 18:42:47 -08:00
this . scene . applyModifiers ( ContactHeldItemTransferChanceModifier , this . player , user , target . getFieldIndex ( ) ) ;
resolve ( ) ;
} ) ;
} ) ;
2023-10-31 11:09:33 -07:00
} )
) . then ( ( ) = > resolve ( ) ) ;
} ) ;
2023-10-31 14:38:44 -07:00
} else
resolve ( ) ;
2023-10-31 11:09:33 -07:00
} ) ;
2023-10-31 14:38:44 -07:00
} else
resolve ( ) ;
2023-10-31 11:09:33 -07:00
} ) ;
} ) ) ;
2023-04-23 07:24:22 -07:00
}
2023-10-31 11:09:33 -07:00
Promise . allSettled ( applyAttrs ) . then ( ( ) = > this . end ( ) ) ;
2023-04-13 09:16:36 -07:00
} ) ;
2023-04-10 11:12:01 -07:00
} ) ;
}
2023-04-10 13:17:25 -07:00
end() {
const user = this . getUserPokemon ( ) ;
2023-12-08 06:51:45 -08:00
if ( user ) {
if ( -- user . turnData . hitsLeft >= 1 && this . getTarget ( ) ? . isActive ( ) )
this . scene . unshiftPhase ( this . getNewHitPhase ( ) ) ;
else {
2023-12-22 14:08:37 -08:00
const hitsTotal = user . turnData . hitCount - Math . max ( user . turnData . hitsLeft , 0 ) ;
if ( hitsTotal > 1 )
this . scene . queueMessage ( ` Hit ${ hitsTotal } time(s)! ` ) ;
2023-12-08 06:51:45 -08:00
this . scene . applyModifiers ( HitHealModifier , this . player , user ) ;
}
2023-04-13 09:16:36 -07:00
}
2023-04-10 13:17:25 -07:00
super . end ( ) ;
}
2023-05-18 08:11:06 -07:00
hitCheck ( target : Pokemon ) : boolean {
2023-05-01 11:41:44 -07:00
if ( this . move . getMove ( ) . moveTarget === MoveTarget . USER )
2023-04-18 09:30:47 -07:00
return true ;
2023-04-13 09:16:36 -07:00
2023-10-30 09:33:20 -07:00
// Hit check only calculated on first hit for multi-hit moves
if ( this . getUserPokemon ( ) . turnData . hitsLeft < this . getUserPokemon ( ) . turnData . hitCount )
return true ;
2023-05-18 08:11:06 -07: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 11:07:38 -07:00
2023-07-04 09:59:58 -07:00
if ( this . getUserPokemon ( ) . getTag ( BattlerTagType . IGNORE_ACCURACY ) && ( this . getUserPokemon ( ) . getLastXMoves ( ) . find ( ( ) = > true ) ? . targets || [ ] ) . indexOf ( target . getBattlerIndex ( ) ) > - 1 )
2023-04-27 17:12:25 -07:00
return true ;
2023-04-19 11:07:38 -07:00
const moveAccuracy = new Utils . NumberHolder ( this . move . getMove ( ) . accuracy ) ;
2023-07-05 09:10:23 -07:00
applyMoveAttrs ( VariableAccuracyAttr , this . getUserPokemon ( ) , target , this . move . getMove ( ) , moveAccuracy ) ;
2023-04-19 11:07:38 -07:00
if ( moveAccuracy . value === - 1 )
return true ;
2023-05-08 15:48:35 -07:00
if ( ! this . move . getMove ( ) . getAttrs ( OneHitKOAttr ) . length && this . scene . arena . getTag ( ArenaTagType . GRAVITY ) )
moveAccuracy . value = Math . floor ( moveAccuracy . value * 1.67 ) ;
2023-04-13 09:16:36 -07:00
2023-07-28 07:23:37 -07:00
const userAccuracyLevel = new Utils . IntegerHolder ( this . getUserPokemon ( ) . summonData . battleStats [ BattleStat . ACC ] ) ;
const targetEvasionLevel = new Utils . IntegerHolder ( target . summonData . battleStats [ BattleStat . EVA ] ) ;
2023-11-05 20:27:40 -08:00
applyAbAttrs ( IgnoreOpponentStatChangesAbAttr , target , null , userAccuracyLevel ) ;
applyAbAttrs ( IgnoreOpponentStatChangesAbAttr , this . getUserPokemon ( ) , null , targetEvasionLevel ) ;
2023-07-28 07:23:37 -07:00
this . scene . applyModifiers ( TempBattleStatBoosterModifier , this . player , TempBattleStat . ACC , userAccuracyLevel ) ;
2024-01-02 18:31:59 -08:00
const rand = this . getUserPokemon ( ) . randSeedInt ( 100 , 1 ) ;
2023-07-28 07:23:37 -07:00
let accuracyMultiplier = 1 ;
if ( userAccuracyLevel . value !== targetEvasionLevel . value ) {
accuracyMultiplier = userAccuracyLevel . value > targetEvasionLevel . value
? ( 3 + Math . min ( userAccuracyLevel . value - targetEvasionLevel . value , 6 ) ) / 3
: 3 / ( 3 + Math . min ( targetEvasionLevel . value - userAccuracyLevel . value , 6 ) ) ;
2023-04-10 13:17:25 -07:00
}
2023-07-28 07:23:37 -07:00
return rand <= moveAccuracy . value * accuracyMultiplier ;
2023-04-10 13:17:25 -07:00
}
2023-04-10 11:12:01 -07:00
getUserPokemon ( ) : Pokemon {
2023-12-03 21:09:38 -08:00
if ( this . battlerIndex > BattlerIndex . ENEMY_2 )
return this . scene . getPokemonById ( this . battlerIndex ) ;
2023-05-18 08:11:06 -07:00
return ( this . player ? this . scene . getPlayerField ( ) : this . scene . getEnemyField ( ) ) [ this . fieldIndex ] ;
2023-04-10 11:12:01 -07:00
}
2023-05-18 08:11:06 -07:00
getTargets ( ) : Pokemon [ ] {
2024-03-11 17:55:41 -07:00
return this . scene . getField ( true ) . filter ( p = > this . targets . indexOf ( p . getBattlerIndex ( ) ) > - 1 ) ;
2023-04-10 11:12:01 -07:00
}
2023-04-10 13:17:25 -07:00
2023-05-18 08:11:06 -07:00
getTarget ( ) : Pokemon {
return this . getTargets ( ) . find ( ( ) = > true ) ;
2023-04-10 11:12:01 -07:00
}
2023-04-10 13:17:25 -07:00
getNewHitPhase() {
2023-05-18 08:11:06 -07:00
return new MoveEffectPhase ( this . scene , this . battlerIndex , this . targets , this . move ) ;
2023-04-10 13:17:25 -07:00
}
2023-04-10 11:12:01 -07:00
}
2023-04-16 15:40:32 -07:00
export class MoveEndPhase extends PokemonPhase {
2023-05-18 08:11:06 -07:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex ) ;
2023-04-16 15:40:32 -07:00
}
start() {
super . start ( ) ;
2024-01-15 20:29:22 -08:00
const pokemon = this . getPokemon ( ) ;
if ( pokemon . isActive ( true ) )
pokemon . lapseTags ( BattlerTagLapseType . AFTER_MOVE ) ;
2023-04-16 15:40:32 -07:00
2024-03-13 21:40:57 -07:00
this . scene . arena . setIgnoreAbilities ( false ) ;
2023-04-16 15:40:32 -07:00
this . end ( ) ;
}
}
2023-04-13 20:04:51 -07:00
export class MoveAnimTestPhase extends BattlePhase {
private moveQueue : Moves [ ] ;
constructor ( scene : BattleScene , moveQueue? : Moves [ ] ) {
super ( scene ) ;
2023-04-20 18:32:48 -07:00
this . moveQueue = moveQueue || Utils . getEnumValues ( Moves ) . slice ( 1 ) ;
2023-04-13 20:04:51 -07: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-24 19:32:12 -07:00
} else if ( player )
console . log ( Moves [ moveId ] ) ;
2023-04-13 20:04:51 -07:00
initMoveAnim ( moveId ) . then ( ( ) = > {
loadMoveAnimAssets ( this . scene , [ moveId ] , true )
. then ( ( ) = > {
2023-11-30 13:37:16 -08: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-13 20:04:51 -07:00
if ( player )
this . playMoveAnim ( moveQueue , false ) ;
else
this . playMoveAnim ( moveQueue , true ) ;
} ) ;
} ) ;
} ) ;
}
}
2023-04-27 11:30:03 -07:00
export class ShowAbilityPhase extends PokemonPhase {
2023-05-18 08:11:06 -07:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex ) ;
2023-04-27 11:30:03 -07:00
}
start() {
2023-12-22 18:42:47 -08:00
super . start ( ) ;
2023-04-27 11:30:03 -07:00
this . scene . abilityBar . showAbility ( this . getPokemon ( ) ) ;
this . end ( ) ;
}
}
2023-04-10 20:15:06 -07:00
export class StatChangePhase extends PokemonPhase {
private stats : BattleStat [ ] ;
2023-04-26 20:33:13 -07:00
private selfTarget : boolean ;
2023-04-10 20:15:06 -07:00
private levels : integer ;
2024-01-15 20:29:22 -08:00
private showMessage : boolean ;
2023-04-10 20:15:06 -07:00
2024-01-15 20:29:22 -08:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , selfTarget : boolean , stats : BattleStat [ ] , levels : integer , showMessage : boolean = true ) {
2023-05-18 08:11:06 -07:00
super ( scene , battlerIndex ) ;
2023-04-10 20:15:06 -07:00
2023-04-27 11:30:03 -07:00
this . selfTarget = selfTarget ;
2024-01-02 18:31:59 -08:00
this . stats = stats ;
2023-04-10 20:15:06 -07:00
this . levels = levels ;
2024-01-15 20:29:22 -08:00
this . showMessage = showMessage ;
2023-04-10 20:15:06 -07:00
}
start() {
const pokemon = this . getPokemon ( ) ;
2023-04-26 20:33:13 -07:00
2024-01-21 13:30:06 -08:00
if ( ! pokemon . isActive ( true ) )
2023-11-26 19:22:05 -08:00
return this . end ( ) ;
2024-01-02 18:31:59 -08:00
const allStats = Utils . getEnumValues ( BattleStat ) ;
const filteredStats = this . stats . map ( s = > s !== BattleStat . RAND ? s : allStats [ pokemon . randSeedInt ( BattleStat . SPD + 1 ) ] ) . filter ( stat = > {
2023-04-26 20:33:13 -07:00
const cancelled = new Utils . BooleanHolder ( false ) ;
if ( ! this . selfTarget && this . levels < 0 )
2024-01-15 20:29:22 -08:00
this . scene . arena . applyTagsForSide ( MistTag , pokemon . isPlayer ( ) ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY , cancelled ) ;
if ( ! cancelled . value && ! this . selfTarget && this . levels < 0 )
2023-05-06 09:13:35 -07:00
applyPreStatChangeAbAttrs ( ProtectStatAbAttr , this . getPokemon ( ) , stat , cancelled ) ;
2023-04-26 20:33:13 -07:00
return ! cancelled . value ;
} ) ;
2023-11-05 19:11:38 -08:00
const levels = new Utils . IntegerHolder ( this . levels ) ;
applyAbAttrs ( StatChangeMultiplierAbAttr , pokemon , null , levels ) ;
2023-04-10 20:15:06 -07:00
const battleStats = this . getPokemon ( ) . summonData . battleStats ;
2023-11-05 19:11:38 -08: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-10 20:15:06 -07:00
const end = ( ) = > {
2024-01-15 20:29:22 -08:00
if ( this . showMessage ) {
const messages = this . getStatChangeMessages ( filteredStats , levels . value , relLevels ) ;
for ( let message of messages )
this . scene . queueMessage ( message ) ;
}
2023-04-10 20:15:06 -07:00
2023-04-26 20:33:13 -07:00
for ( let stat of filteredStats )
2023-11-05 19:11:38 -08:00
pokemon . summonData . battleStats [ stat ] = Math . max ( Math . min ( pokemon . summonData . battleStats [ stat ] + levels . value , 6 ) , - 6 ) ;
2023-04-10 20:15:06 -07:00
this . end ( ) ;
} ;
if ( relLevels . filter ( l = > l ) . length ) {
pokemon . enableMask ( ) ;
const pokemonMaskSprite = pokemon . maskSprite ;
2024-01-14 17:47:08 -08: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-29 18:04:40 -08:00
statSprite . setPipeline ( this . scene . fieldSpritePipeline ) ;
2023-04-10 20:15:06 -07:00
statSprite . setAlpha ( 0 ) ;
statSprite . setScale ( 6 ) ;
statSprite . setOrigin ( 0.5 , 1 ) ;
2023-11-05 19:11:38 -08:00
this . scene . playSound ( ` stat_ ${ levels . value >= 1 ? 'up' : 'down' } ` ) ;
2023-04-10 20:15:06 -07: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 06:41:11 -07:00
2023-04-10 20:15:06 -07:00
this . scene . tweens . add ( {
targets : statSprite ,
duration : 1500 ,
2023-11-05 19:11:38 -08:00
y : ` ${ levels . value >= 1 ? '-' : '+' } = ${ 160 * 6 } `
2023-04-10 20:15:06 -07:00
} ) ;
this . scene . time . delayedCall ( 1750 , ( ) = > {
pokemon . disableMask ( ) ;
end ( ) ;
} ) ;
} else
end ( ) ;
}
2023-11-05 19:11:38 -08:00
getStatChangeMessages ( stats : BattleStat [ ] , levels : integer , relLevels : integer [ ] ) : string [ ] {
2023-04-10 20:15:06 -07:00
const messages : string [ ] = [ ] ;
2023-04-26 20:33:13 -07:00
for ( let s = 0 ; s < stats . length ; s ++ )
2023-11-05 19:11:38 -08:00
messages . push ( getPokemonMessage ( this . getPokemon ( ) , ` 's ${ getBattleStatName ( stats [ s ] ) } ${ getBattleStatLevelChangeDescription ( Math . abs ( relLevels [ s ] ) , levels >= 1 ) } ! ` ) ) ;
2023-04-10 20:15:06 -07:00
return messages ;
}
}
2023-04-19 11:07:38 -07:00
export class WeatherEffectPhase extends CommonAnimPhase {
private weather : Weather ;
2023-04-27 11:30:03 -07:00
constructor ( scene : BattleScene , weather : Weather ) {
2023-05-18 08:11:06 -07:00
super ( scene , undefined , undefined , CommonAnim . SUNNY + ( weather . weatherType - 1 ) ) ;
2023-04-19 11:07:38 -07:00
this . weather = weather ;
}
start() {
if ( this . weather . isDamaging ( ) ) {
2023-04-27 11:30:03 -07:00
const cancelled = new Utils . BooleanHolder ( false ) ;
2023-05-18 08:11:06 -07:00
this . executeForAll ( ( pokemon : Pokemon ) = > applyPreWeatherEffectAbAttrs ( SuppressWeatherEffectAbAttr , pokemon , this . weather , cancelled ) ) ;
2023-04-27 11:30:03 -07:00
if ( ! cancelled . value ) {
const inflictDamage = ( pokemon : Pokemon ) = > {
const cancelled = new Utils . BooleanHolder ( false ) ;
applyPreWeatherEffectAbAttrs ( PreWeatherDamageAbAttr , pokemon , this . weather , cancelled ) ;
if ( cancelled . value )
return ;
2024-03-01 06:35:36 -08:00
const damage = Math . ceil ( pokemon . getMaxHp ( ) / 16 ) ;
2023-04-27 11:30:03 -07:00
this . scene . queueMessage ( getWeatherDamageMessage ( this . weather . weatherType , pokemon ) ) ;
2024-03-01 07:15:43 -08:00
pokemon . damageAndUpdate ( damage ) ;
2023-04-27 11:30:03 -07:00
} ;
2023-05-18 08:11:06 -07:00
this . executeForAll ( ( pokemon : Pokemon ) = > {
2024-02-16 21:40:03 -08:00
const immune = ! pokemon || ! ! pokemon . getTypes ( true ) . filter ( t = > this . weather . isTypeDamageImmune ( t ) ) . length ;
2023-04-27 11:30:03 -07:00
if ( ! immune )
inflictDamage ( pokemon ) ;
} ) ;
}
2023-04-19 11:07:38 -07:00
}
2023-04-27 11:30:03 -07:00
this . scene . ui . showText ( getWeatherLapseMessage ( this . weather . weatherType ) , null , ( ) = > {
2023-05-18 08:11:06 -07:00
this . executeForAll ( ( pokemon : Pokemon ) = > applyPostWeatherLapseAbAttrs ( PostWeatherLapseAbAttr , pokemon , this . weather ) ) ;
2023-04-27 11:30:03 -07:00
super . start ( ) ;
} ) ;
2023-04-19 11:07:38 -07:00
}
}
2023-04-10 11:12:01 -07:00
export class ObtainStatusEffectPhase extends PokemonPhase {
private statusEffect : StatusEffect ;
2023-04-16 15:40:32 -07:00
private cureTurn : integer ;
2023-04-24 11:30:21 -07:00
private sourceText : string ;
2023-04-10 11:12:01 -07:00
2023-05-18 08:11:06 -07:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , statusEffect : StatusEffect , cureTurn? : integer , sourceText? : string ) {
super ( scene , battlerIndex ) ;
2023-04-11 16:08:03 -07:00
this . statusEffect = statusEffect ;
2023-04-16 15:40:32 -07:00
this . cureTurn = cureTurn ;
2023-04-24 11:30:21 -07:00
this . sourceText = sourceText ;
2023-04-10 11:12:01 -07:00
}
start() {
const pokemon = this . getPokemon ( ) ;
2023-04-11 16:08:03 -07:00
if ( ! pokemon . status ) {
if ( pokemon . trySetStatus ( this . statusEffect ) ) {
2023-04-16 15:40:32 -07:00
if ( this . cureTurn )
pokemon . status . cureTurn = this . cureTurn ;
2023-04-11 16:08:03 -07:00
pokemon . updateInfo ( true ) ;
new CommonBattleAnim ( CommonAnim . POISON + ( this . statusEffect - 1 ) , pokemon ) . play ( this . scene , ( ) = > {
2023-04-24 11:30:21 -07:00
this . scene . queueMessage ( getPokemonMessage ( pokemon , getStatusEffectObtainText ( this . statusEffect , this . sourceText ) ) ) ;
2023-04-11 16:08:03 -07:00
if ( pokemon . status . isPostTurn ( ) )
2023-05-18 08:11:06 -07:00
this . scene . pushPhase ( new PostTurnStatusEffectPhase ( this . scene , this . battlerIndex ) ) ;
2023-04-11 16:08:03 -07:00
this . end ( ) ;
} ) ;
return ;
}
} else if ( pokemon . status . effect === this . statusEffect )
2023-04-21 16:30:04 -07:00
this . scene . queueMessage ( getPokemonMessage ( pokemon , getStatusEffectOverlapText ( this . statusEffect ) ) ) ;
2023-04-11 16:08:03 -07:00
this . end ( ) ;
}
}
export class PostTurnStatusEffectPhase extends PokemonPhase {
2023-05-18 08:11:06 -07:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex ) ;
2023-04-11 16:08:03 -07:00
}
start() {
const pokemon = this . getPokemon ( ) ;
2023-06-02 08:41:08 -07:00
if ( pokemon ? . isActive ( true ) && pokemon . status && pokemon . status . isPostTurn ( ) ) {
2023-04-11 16:08:03 -07:00
pokemon . status . incrementTurn ( ) ;
2024-03-01 06:35:36 -08:00
this . scene . queueMessage ( getPokemonMessage ( pokemon , getStatusEffectActivationText ( pokemon . status . effect ) ) ) ;
let damage : integer = 0 ;
switch ( pokemon . status . effect ) {
case StatusEffect . POISON :
case StatusEffect . BURN :
damage = Math . max ( pokemon . getMaxHp ( ) >> 3 , 1 ) ;
break ;
case StatusEffect . TOXIC :
damage = Math . max ( Math . floor ( ( pokemon . getMaxHp ( ) / 16 ) * pokemon . status . turnCount ) , 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 ( ) ) ;
2023-04-10 11:12:01 -07:00
} else
this . end ( ) ;
}
}
2024-02-21 20:57:49 -08:00
export class MessagePhase extends Phase {
2023-04-10 11:12:01 -07:00
private text : string ;
2023-04-11 16:08:03 -07:00
private callbackDelay : integer ;
2023-04-10 11:12:01 -07:00
private prompt : boolean ;
2023-05-07 14:05:19 -07:00
private promptDelay : integer ;
2023-04-10 11:12:01 -07:00
2023-05-07 14:05:19 -07:00
constructor ( scene : BattleScene , text : string , callbackDelay? : integer , prompt? : boolean , promptDelay? : integer ) {
2023-04-10 11:12:01 -07:00
super ( scene ) ;
this . text = text ;
2023-04-11 16:08:03 -07:00
this . callbackDelay = callbackDelay ;
2023-04-10 11:12:01 -07:00
this . prompt = prompt ;
2023-05-07 14:05:19 -07:00
this . promptDelay = promptDelay ;
2023-04-10 11:12:01 -07:00
}
start() {
super . start ( ) ;
2023-10-18 15:01:15 -07: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 14:05:19 -07:00
this . scene . ui . showText ( this . text , null , ( ) = > this . end ( ) , this . callbackDelay || ( this . prompt ? 0 : 1500 ) , this . prompt , this . promptDelay ) ;
2023-04-10 11:12:01 -07:00
}
2023-04-26 22:14:15 -07:00
end() {
if ( this . scene . abilityBar . shown )
this . scene . abilityBar . hide ( ) ;
super . end ( ) ;
}
2023-04-10 11:12:01 -07:00
}
2023-04-14 22:32:16 -07:00
export class DamagePhase extends PokemonPhase {
2024-03-01 06:35:36 -08:00
private amount : integer ;
2023-04-14 22:32:16 -07:00
private damageResult : DamageResult ;
2024-03-01 06:35:36 -08:00
private critical : boolean ;
2023-04-14 22:32:16 -07:00
2024-03-01 06:35:36 -08:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , amount : integer , damageResult? : DamageResult , critical : boolean = false ) {
2023-05-18 08:11:06 -07:00
super ( scene , battlerIndex ) ;
2023-04-14 22:32:16 -07:00
2024-03-01 06:35:36 -08:00
this . amount = amount ;
2023-05-18 08:11:06 -07:00
this . damageResult = damageResult || HitResult . EFFECTIVE ;
2024-03-01 06:35:36 -08:00
this . critical = critical ;
2023-04-14 22:32:16 -07:00
}
start() {
super . start ( ) ;
2023-11-07 19:23:42 -08: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 07:15:43 -08:00
updateAmount ( amount : integer ) : void {
this . amount = amount ;
}
2023-11-07 19:23:42 -08:00
applyDamage() {
2023-04-14 22:32:16 -07:00
switch ( this . damageResult ) {
2023-05-18 08:11:06 -07:00
case HitResult . EFFECTIVE :
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'hit' ) ;
2023-04-14 22:32:16 -07:00
break ;
2023-05-18 08:11:06 -07:00
case HitResult . SUPER_EFFECTIVE :
2023-11-07 19:23:42 -08:00
case HitResult . ONE_HIT_KO :
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'hit_strong' ) ;
2023-04-14 22:32:16 -07:00
break ;
2023-05-18 08:11:06 -07:00
case HitResult . NOT_VERY_EFFECTIVE :
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'hit_weak' ) ;
2023-04-14 22:32:16 -07:00
break ;
}
2024-03-01 06:35:36 -08:00
if ( this . amount )
this . scene . damageNumberHandler . add ( this . getPokemon ( ) , this . amount , this . damageResult , this . critical ) ;
2023-05-18 08:11:06 -07:00
if ( this . damageResult !== HitResult . OTHER ) {
2023-04-18 09:30:47 -07: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-14 22:32:16 -07:00
}
2024-01-13 09:24:24 -08:00
end() {
switch ( this . scene . currentBattle . battleSpec ) {
case BattleSpec . FINAL_BOSS :
const pokemon = this . getPokemon ( ) ;
2024-03-01 20:10:46 -08:00
if ( pokemon instanceof EnemyPokemon && pokemon . isBoss ( ) && ! pokemon . formIndex && pokemon . bossSegmentIndex < 1 ) {
2024-01-13 09:24:24 -08:00
this . scene . fadeOutBgm ( Utils . fixedInt ( 2000 ) , false ) ;
2024-02-14 20:38:37 -08:00
this . scene . ui . showDialogue ( battleSpecDialogue [ BattleSpec . FINAL_BOSS ] . firstStageWin , pokemon . species . name , null , ( ) = > {
2024-01-13 09:24:24 -08:00
this . scene . addEnemyModifier ( getModifierType ( modifierTypes . MINI_BLACK_HOLE ) . newModifier ( pokemon ) as PersistentModifier , false , true ) ;
pokemon . generateAndPopulateMoveset ( 1 ) ;
2024-01-14 17:47:08 -08:00
this . scene . setFieldScale ( 0.75 ) ;
2024-01-13 09:24:24 -08:00
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeManualTrigger , false ) ;
2024-01-13 17:15:55 -08: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 09:24:24 -08:00
super . end ( ) ;
2024-02-13 15:42:11 -08:00
} ) ;
2024-01-13 09:24:24 -08:00
return ;
}
break ;
}
super . end ( ) ;
}
2023-04-14 22:32:16 -07:00
}
2023-04-10 11:12:01 -07:00
export class FaintPhase extends PokemonPhase {
2023-11-07 17:02:42 -08:00
private preventEndure : boolean ;
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , preventEndure? : boolean ) {
2023-05-18 08:11:06 -07:00
super ( scene , battlerIndex ) ;
2023-11-07 17:02:42 -08:00
this . preventEndure = preventEndure ;
2023-04-10 11:12:01 -07:00
}
start() {
super . start ( ) ;
2023-06-06 05:16:07 -07:00
const pokemon = this . getPokemon ( ) ;
2023-11-07 17:02:42 -08:00
if ( ! this . preventEndure ) {
const instantReviveModifier = this . scene . applyModifier ( PokemonInstantReviveModifier , this . player , this . getPokemon ( ) ) as PokemonInstantReviveModifier ;
2023-06-06 05:16:07 -07:00
2023-11-07 17:02:42 -08:00
if ( instantReviveModifier ) {
if ( ! -- instantReviveModifier . stackCount )
this . scene . removeModifier ( instantReviveModifier ) ;
this . scene . updateModifiers ( this . player ) ;
return this . end ( ) ;
}
2023-10-29 13:05:17 -07:00
2023-11-07 17:02:42 -08:00
if ( ! pokemon . isPlayer ( ) ) {
const enemyInstantReviveModifiers = this . scene . findModifiers ( m = > m instanceof EnemyInstantReviveChanceModifier , false ) ;
for ( let modifier of enemyInstantReviveModifiers ) {
2024-03-10 21:16:24 -07:00
const maxRevive = ( modifier as EnemyInstantReviveChanceModifier ) . fullHeal ;
const prop = maxRevive ? 'maxRevived' : 'revived' ;
if ( pokemon . battleData [ prop ] )
continue ;
if ( modifier . shouldApply ( [ pokemon ] ) && modifier . apply ( [ pokemon ] ) ) {
pokemon . battleData [ prop ] = true ;
2023-11-07 17:02:42 -08:00
return this . end ( ) ;
2024-03-10 21:16:24 -07:00
}
2023-11-07 17:02:42 -08:00
}
2023-10-29 13:05:17 -07:00
}
2023-06-06 05:16:07 -07:00
}
2024-01-13 09:24:24 -08:00
if ( ! this . tryOverrideForBattleSpec ( ) )
this . doFaint ( ) ;
}
doFaint ( ) : void {
const pokemon = this . getPokemon ( ) ;
2023-06-06 05:16:07 -07:00
this . scene . queueMessage ( getPokemonMessage ( pokemon , ' fainted!' ) , null , true ) ;
2023-04-14 22:32:16 -07:00
2023-04-21 19:59:09 -07:00
if ( this . player ) {
2024-02-20 11:39:25 -08:00
const nonFaintedPartyMembers = this . scene . getParty ( ) . filter ( p = > ! p . isFainted ( ) ) ;
const nonFaintedPartyMemberCount = nonFaintedPartyMembers . length ;
2023-05-18 08:11:06 -07:00
if ( ! nonFaintedPartyMemberCount )
this . scene . unshiftPhase ( new GameOverPhase ( this . scene ) ) ;
2024-02-20 11:39:25 -08:00
else if ( nonFaintedPartyMemberCount >= this . scene . currentBattle . getBattlerCount ( ) || ( this . scene . currentBattle . double && ! nonFaintedPartyMembers [ 0 ] . isActive ( true ) ) )
2023-11-15 15:45:10 -08:00
this . scene . pushPhase ( new SwitchPhase ( this . scene , this . fieldIndex , true , false ) ) ;
2024-02-20 11:39:25 -08:00
if ( nonFaintedPartyMemberCount === 1 && this . scene . currentBattle . double )
2023-07-05 19:23:50 -07:00
this . scene . unshiftPhase ( new ToggleDoublePositionPhase ( this . scene , true ) ) ;
2023-10-07 13:08:33 -07:00
} else {
2023-05-29 09:24:38 -07:00
this . scene . unshiftPhase ( new VictoryPhase ( this . scene , this . battlerIndex ) ) ;
2023-10-07 13:08:33 -07:00
if ( this . scene . currentBattle . battleType === BattleType . TRAINER ) {
2023-11-15 15:45:10 -08:00
const hasReservePartyMember = ! ! this . scene . getEnemyParty ( ) . filter ( p = > p . isActive ( ) && ! p . isOnField ( ) ) . length ;
if ( hasReservePartyMember )
2023-11-26 19:22:05 -08:00
this . scene . pushPhase ( new SwitchSummonPhase ( this . scene , this . fieldIndex , - 1 , false , false , false ) ) ;
2023-10-07 13:08:33 -07:00
}
}
2023-04-13 20:04:51 -07:00
2023-04-21 16:30:04 -07:00
pokemon . lapseTags ( BattlerTagLapseType . FAINT ) ;
2024-03-11 17:55:41 -07:00
this . scene . getField ( true ) . filter ( p = > p !== pokemon ) . forEach ( p = > p . removeTagsBySourceId ( pokemon . id ) ) ;
2023-04-13 09:16:36 -07:00
2023-04-10 11:12:01 -07:00
pokemon . faintCry ( ( ) = > {
2023-12-21 20:00:45 -08:00
const friendshipDecrease = new Utils . IntegerHolder ( 10 ) ;
pokemon . friendship = Math . max ( pokemon . friendship - friendshipDecrease . value , 0 ) ;
2023-04-10 11:12:01 -07:00
pokemon . hideInfo ( ) ;
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'faint' ) ;
2023-04-10 11:12:01 -07: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 15:40:32 -07:00
pokemon . trySetStatus ( StatusEffect . FAINT ) ;
2023-04-14 22:32:16 -07:00
if ( pokemon . isPlayer ( ) )
2023-04-10 11:12:01 -07:00
this . scene . currentBattle . removeFaintedParticipant ( pokemon as PlayerPokemon ) ;
2024-03-06 18:05:23 -08:00
else
this . scene . currentBattle . addPostBattleLoot ( pokemon as EnemyPokemon ) ;
2023-04-10 11:12:01 -07:00
this . scene . field . remove ( pokemon ) ;
this . end ( ) ;
}
} ) ;
} ) ;
}
2024-01-13 09:24:24 -08:00
tryOverrideForBattleSpec ( ) : boolean {
switch ( this . scene . currentBattle . battleSpec ) {
case BattleSpec . FINAL_BOSS :
2024-01-29 19:07:34 -08:00
if ( ! this . player ) {
const enemy = this . getPokemon ( ) ;
2024-03-01 06:35:36 -08:00
if ( enemy . formIndex )
2024-02-14 20:38:37 -08:00
this . scene . ui . showDialogue ( battleSpecDialogue [ BattleSpec . FINAL_BOSS ] . secondStageWin , enemy . species . name , null , ( ) = > this . doFaint ( ) ) ;
2024-03-01 06:35:36 -08: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-29 19:07:34 -08:00
}
2024-03-01 06:35:36 -08:00
return true ;
2024-01-13 09:24:24 -08:00
}
}
return false ;
}
2023-04-10 11:12:01 -07:00
}
export class VictoryPhase extends PokemonPhase {
2023-05-29 09:24:38 -07:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex ) ;
2023-04-10 11:12:01 -07:00
}
start() {
super . start ( ) ;
2024-01-11 09:26:32 -08:00
this . scene . gameData . gameStats . pokemonDefeated ++ ;
2023-04-10 11:12:01 -07:00
const participantIds = this . scene . currentBattle . playerParticipantIds ;
const party = this . scene . getParty ( ) ;
2023-04-18 19:09:37 -07:00
const expShareModifier = this . scene . findModifier ( m = > m instanceof ExpShareModifier ) as ExpShareModifier ;
2023-04-19 19:51:46 -07:00
const expBalanceModifier = this . scene . findModifier ( m = > m instanceof ExpBalanceModifier ) as ExpBalanceModifier ;
2023-04-28 13:35:03 -07:00
const multipleParticipantExpBonusModifier = this . scene . findModifier ( m = > m instanceof MultipleParticipantExpBonusModifier ) as MultipleParticipantExpBonusModifier ;
2023-10-03 09:50:31 -07:00
const expPartyMembers = party . filter ( p = > p . hp && p . level < this . scene . getMaxExpLevel ( ) ) ;
2023-04-19 19:51:46 -07:00
const partyMemberExp = [ ] ;
2023-11-24 13:27:26 -08: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-21 20:00:45 -08: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 13:27:26 -08: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-19 19:51:46 -07:00
}
2023-11-24 13:27:26 -08:00
if ( expBalanceModifier ) {
let totalLevel = 0 ;
let totalExp = 0 ;
expPartyMembers . forEach ( ( expPartyMember , epm ) = > {
totalExp += partyMemberExp [ epm ] ;
totalLevel += expPartyMember . level ;
} ) ;
2023-04-19 19:51:46 -07:00
2023-11-24 13:27:26 -08:00
const medianLevel = Math . floor ( totalLevel / expPartyMembers . length ) ;
2023-04-19 19:51:46 -07:00
2023-11-24 13:27:26 -08:00
const recipientExpPartyMemberIndexes = [ ] ;
expPartyMembers . forEach ( ( expPartyMember , epm ) = > {
if ( expPartyMember . level <= medianLevel )
recipientExpPartyMemberIndexes . push ( epm ) ;
} ) ;
2023-04-19 19:51:46 -07:00
2023-11-24 13:27:26 -08:00
const splitExp = Math . floor ( totalExp / recipientExpPartyMemberIndexes . length ) ;
2023-04-19 19:51:46 -07:00
2023-11-24 13:27:26 -08:00
expPartyMembers . forEach ( ( _partyMember , pm ) = > {
partyMemberExp [ pm ] = recipientExpPartyMemberIndexes . indexOf ( pm ) > - 1 ? splitExp : 0 ;
} ) ;
}
2023-04-19 19:51:46 -07:00
2023-11-24 13:27:26 -08:00
for ( let pm = 0 ; pm < expPartyMembers . length ; pm ++ ) {
const exp = partyMemberExp [ pm ] ;
2023-04-19 19:51:46 -07:00
2023-11-24 13:27:26 -08: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 11:12:01 -07:00
}
}
2023-05-18 08:11:06 -07:00
2024-03-07 05:42:04 -08:00
if ( ! this . scene . getEnemyParty ( ) . find ( p = > this . scene . currentBattle . battleType ? ! p ? . isFainted ( true ) : p . isOnField ( ) ) ) {
2023-05-18 08:11:06 -07:00
this . scene . pushPhase ( new BattleEndPhase ( this . scene ) ) ;
2023-10-18 15:01:15 -07:00
if ( this . scene . currentBattle . battleType === BattleType . TRAINER )
this . scene . pushPhase ( new TrainerVictoryPhase ( this . scene ) ) ;
2023-12-19 20:51:48 -08:00
this . scene . pushPhase ( new EggLapsePhase ( this . scene ) ) ;
2024-03-14 13:26:57 -07:00
if ( this . scene . gameMode . isEndless || ! this . scene . gameMode . isWaveFinal ( this . scene . currentBattle . waveIndex ) ) {
2023-11-28 13:59:40 -08:00
if ( this . scene . currentBattle . waveIndex % 10 )
2023-10-25 11:15:44 -07:00
this . scene . pushPhase ( new SelectModifierPhase ( this . scene ) ) ;
2023-11-28 13:59:40 -08:00
else {
2024-03-14 13:26:57 -07:00
const superExpWave = ! this . scene . gameMode . isEndless ? 20 : 10 ;
2024-01-04 16:37:07 -08: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 13:26:57 -07:00
if ( this . scene . gameMode . isEndless && ! ( this . scene . currentBattle . waveIndex % 50 ) ) {
2023-12-31 20:19:06 -08:00
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , ! ( this . scene . currentBattle . waveIndex % 250 ) ? modifierTypes.VOUCHER_PREMIUM : modifierTypes.VOUCHER_PLUS ) ) ;
2023-11-05 07:56:09 -08:00
this . scene . pushPhase ( new AddEnemyBuffModifierPhase ( this . scene ) ) ;
2023-12-19 20:51:48 -08:00
}
2023-11-28 13:59:40 -08:00
}
2023-05-18 08:11:06 -07:00
this . scene . pushPhase ( new NewBattlePhase ( this . scene ) ) ;
} else
this . scene . pushPhase ( new GameOverPhase ( this . scene , true ) ) ;
}
2023-04-10 11:12:01 -07:00
this . end ( ) ;
}
}
2023-10-18 15:01:15 -07:00
export class TrainerVictoryPhase extends BattlePhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
2023-10-20 08:38:41 -07:00
this . scene . playBgm ( this . scene . currentBattle . trainer . config . victoryBgm ) ;
2023-11-10 12:51:34 -08:00
this . scene . unshiftPhase ( new MoneyRewardPhase ( this . scene , this . scene . currentBattle . trainer . config . moneyMultiplier ) ) ;
2023-10-20 08:38:41 -07:00
const modifierRewardFuncs = this . scene . currentBattle . trainer . config . modifierRewardFuncs ;
for ( let modifierRewardFunc of modifierRewardFuncs )
2023-10-25 11:15:44 -07:00
this . scene . unshiftPhase ( new ModifierRewardPhase ( this . scene , modifierRewardFunc ) ) ;
2023-10-18 15:01:15 -07:00
2023-12-20 14:30:06 -08: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 )
this . scene . pushPhase ( new ModifierRewardPhase ( this . scene , modifierTypes . VOUCHER ) ) ;
}
2024-02-05 20:05:56 -08:00
this . scene . ui . showText ( ` You defeated \ n ${ this . scene . currentBattle . trainer . getName ( true ) } ! ` , null , ( ) = > {
2024-02-14 11:41:39 -08:00
const victoryMessages = this . scene . currentBattle . trainer . getVictoryMessages ( ) ;
2024-02-22 16:24:00 -08:00
const showMessage = ( ) = > {
2023-10-18 15:01:15 -07:00
let message : string ;
2024-02-16 21:40:03 -08:00
this . scene . executeWithSeedOffset ( ( ) = > message = Utils . randSeedItem ( victoryMessages ) , this . scene . currentBattle . waveIndex ) ;
2023-10-18 15:01:15 -07:00
const messagePages = message . split ( /\$/g ) . map ( m = > m . trim ( ) ) ;
for ( let p = messagePages . length - 1 ; p >= 0 ; p -- ) {
2024-02-22 16:24:00 -08:00
const originalFunc = showMessageOrEnd ;
showMessageOrEnd = ( ) = > this . scene . ui . showDialogue ( messagePages [ p ] , this . scene . currentBattle . trainer . getName ( ) , null , originalFunc ) ;
2023-10-18 15:01:15 -07:00
}
2024-02-22 16:24:00 -08: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-25 16:09:24 -08:00
this . scene . showFieldOverlay ( 500 ) . then ( ( ) = > this . scene . charSprite . showCharacter ( this . scene . currentBattle . trainer . getKey ( ) , getCharVariantFromDialogue ( victoryMessages [ 0 ] ) ) . then ( ( ) = > showMessage ( ) ) ) ;
2024-02-22 16:24:00 -08:00
} else
showMessage ( ) ;
} else
showMessageOrEnd ( ) ;
2023-10-18 15:01:15 -07:00
} , null , true ) ;
2024-02-21 20:57:49 -08:00
this . showEnemyTrainer ( ) ;
2023-10-18 15:01:15 -07:00
}
}
2023-11-10 12:51:34 -08:00
export class MoneyRewardPhase extends BattlePhase {
private moneyMultiplier : number ;
constructor ( scene : BattleScene , moneyMultiplier : number ) {
super ( scene ) ;
this . moneyMultiplier = moneyMultiplier ;
}
start() {
2024-02-03 21:30:19 -08:00
const moneyAmount = new Utils . IntegerHolder ( this . scene . getWaveMoneyAmount ( this . moneyMultiplier ) ) ;
2024-01-18 14:22:18 -08:00
2023-11-10 18:11:36 -08:00
this . scene . applyModifiers ( MoneyMultiplierModifier , true , moneyAmount ) ;
2023-11-10 12:51:34 -08:00
2023-11-10 18:11:36 -08:00
this . scene . money += moneyAmount . value ;
2023-11-10 12:51:34 -08:00
this . scene . updateMoneyText ( ) ;
2023-11-11 21:31:40 -08:00
this . scene . validateAchvs ( MoneyAchv ) ;
2023-11-10 18:11:36 -08:00
this . scene . ui . showText ( ` You got ₽ ${ moneyAmount . value . toLocaleString ( 'en-US' ) } \ nfor winning! ` , null , ( ) = > this . end ( ) , null , true ) ;
2023-11-10 12:51:34 -08:00
}
}
2023-10-20 08:38:41 -07:00
export class ModifierRewardPhase extends BattlePhase {
2024-02-22 13:21:34 -08:00
protected modifierType : ModifierType ;
2023-10-20 08:38:41 -07:00
2023-10-25 11:15:44 -07:00
constructor ( scene : BattleScene , modifierTypeFunc : ModifierTypeFunc ) {
2023-10-20 08:38:41 -07:00
super ( scene ) ;
2023-10-28 10:24:57 -07:00
this . modifierType = getModifierType ( modifierTypeFunc ) ;
2023-10-20 08:38:41 -07:00
}
start() {
super . start ( ) ;
2024-02-22 13:21:34 -08:00
this . doReward ( ) . then ( ( ) = > this . end ( ) ) ;
}
2023-10-20 08:38:41 -07:00
2024-02-22 13:21:34 -08:00
doReward ( ) : Promise < void > {
return new Promise < void > ( resolve = > {
const newModifier = this . modifierType . newModifier ( ) ;
this . scene . addModifier ( newModifier ) . then ( ( ) = > {
this . scene . playSoundWithoutBgm ( 'item_fanfare' ) ;
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 ) {
this . scene . playSoundWithoutBgm ( 'level_up_fanfare' ) ;
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 08:38:41 -07:00
}
}
2023-04-21 19:59:09 -07:00
export class GameOverPhase extends BattlePhase {
2023-04-26 14:40:08 -07:00
private victory : boolean ;
constructor ( scene : BattleScene , victory? : boolean ) {
2023-04-21 19:59:09 -07:00
super ( scene ) ;
2023-04-26 14:40:08 -07:00
this . victory = ! ! victory ;
2023-04-21 19:59:09 -07:00
}
start() {
super . start ( ) ;
2023-12-31 19:49:50 -08:00
this . scene . gameData . clearSession ( ) . then ( ( ) = > {
this . scene . time . delayedCall ( 1000 , ( ) = > {
2024-02-21 21:34:54 -08:00
let firstClear = false ;
2024-01-11 09:26:32 -08:00
if ( this . victory ) {
2024-02-21 21:34:54 -08:00
firstClear = this . scene . validateAchv ( achvs . CLASSIC_VICTORY ) ;
2024-01-11 09:26:32 -08:00
this . scene . gameData . gameStats . sessionsWon ++ ;
}
this . scene . gameData . saveSystem ( ) ;
2023-12-31 19:49:50 -08:00
const fadeDuration = this . victory ? 10000 : 5000 ;
this . scene . fadeOutBgm ( fadeDuration , true ) ;
this . scene . ui . fadeOut ( fadeDuration ) . then ( ( ) = > {
this . scene . clearPhaseQueue ( ) ;
this . scene . ui . clearText ( ) ;
2024-02-22 13:21:34 -08:00
this . handleUnlocks ( ) ;
if ( this . victory && ! firstClear )
this . scene . unshiftPhase ( new GameOverModifierRewardPhase ( this . scene , modifierTypes . VOUCHER_PREMIUM ) ) ;
2023-12-31 19:49:50 -08:00
this . scene . reset ( ) ;
this . scene . unshiftPhase ( new CheckLoadPhase ( this . scene ) ) ;
this . end ( ) ;
} ) ;
2023-04-21 19:59:09 -07:00
} ) ;
} ) ;
}
2023-04-28 22:40:24 -07:00
2024-02-22 13:21:34 -08:00
handleUnlocks ( ) : void {
2023-06-05 08:39:49 -07:00
if ( this . victory ) {
if ( ! this . scene . gameData . unlocks [ Unlockables . ENDLESS_MODE ] )
this . scene . unshiftPhase ( new UnlockPhase ( this . scene , Unlockables . ENDLESS_MODE ) ) ;
2023-11-04 16:46:48 -07: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 08:39:49 -07:00
if ( ! this . scene . gameData . unlocks [ Unlockables . MINI_BLACK_HOLE ] )
this . scene . unshiftPhase ( new UnlockPhase ( this . scene , Unlockables . MINI_BLACK_HOLE ) ) ;
}
2023-04-28 22:40:24 -07:00
}
}
2024-02-21 20:57:49 -08:00
export class UnlockPhase extends Phase {
2023-04-28 22:40:24 -07: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 15:41:25 -08:00
this . scene . gameData . saveSystem ( ) . then ( success = > {
if ( success ) {
this . scene . playSoundWithoutBgm ( 'level_up_fanfare' ) ;
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-28 22:40:24 -07:00
} ) ;
} ) ;
}
2023-04-21 19:59:09 -07:00
}
2023-04-10 11:12:01 -07:00
export class SwitchPhase extends BattlePhase {
2023-05-18 08:11:06 -07:00
protected fieldIndex : integer ;
2023-04-10 11:12:01 -07:00
private isModal : boolean ;
private doReturn : boolean ;
2023-05-18 08:11:06 -07:00
constructor ( scene : BattleScene , fieldIndex : integer , isModal : boolean , doReturn : boolean ) {
2023-04-10 11:12:01 -07:00
super ( scene ) ;
2023-05-18 08:11:06 -07:00
this . fieldIndex = fieldIndex ;
2023-04-10 11:12:01 -07:00
this . isModal = isModal ;
this . doReturn = doReturn ;
}
start() {
super . start ( ) ;
2024-02-20 11:39:25 -08: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 12:53:38 -08: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 08:11:06 -07:00
if ( slotIndex >= this . scene . currentBattle . getBattlerCount ( ) && slotIndex < 6 )
2023-11-25 12:53:38 -08:00
this . scene . unshiftPhase ( new SwitchSummonPhase ( this . scene , fieldIndex , slotIndex , this . doReturn , option === PartyOption . PASS_BATON ) ) ;
2023-04-10 11:12:01 -07:00
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > super . end ( ) ) ;
} , PartyUiHandler . FilterNonFainted ) ;
}
}
2023-10-07 13:08:33 -07:00
export class ExpPhase extends PlayerPartyMemberPokemonPhase {
2023-04-10 11:12:01 -07: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-20 16:44:56 -07:00
this . scene . applyModifiers ( ExpBoosterModifier , true , exp ) ;
2023-04-10 11:12:01 -07:00
exp . value = Math . floor ( exp . value ) ;
this . scene . ui . showText ( ` ${ pokemon . name } gained \ n ${ exp . value } EXP. Points! ` , null , ( ) = > {
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 14:24:28 -07:00
}
2023-10-07 13:08:33 -07:00
export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase {
2023-10-04 14:24:28 -07: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 ( ) ;
this . scene . partyExpBar . showPokemonExp ( pokemon , exp . value ) . then ( ( ) = > {
if ( newLevel > lastLevel )
this . end ( ) ;
else
setTimeout ( ( ) = > this . end ( ) , 500 ) ;
} ) ;
}
}
export class HidePartyExpBarPhase extends BattlePhase {
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
2023-04-10 11:12:01 -07:00
2023-10-04 14:24:28 -07:00
start() {
super . start ( ) ;
2023-04-10 11:12:01 -07:00
2023-10-04 14:24:28 -07:00
this . scene . partyExpBar . hide ( ) . then ( ( ) = > this . end ( ) ) ;
}
2023-04-10 11:12:01 -07:00
}
2023-10-07 13:08:33 -07:00
export class LevelUpPhase extends PlayerPartyMemberPokemonPhase {
2023-04-10 11:12:01 -07: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 09:26:32 -08:00
if ( this . level > this . scene . gameData . gameStats . highestLevel )
this . scene . gameData . gameStats . highestLevel = this . level ;
2023-11-11 21:31:40 -08:00
this . scene . validateAchvs ( LevelAchv , new Utils . IntegerHolder ( this . level ) ) ;
2023-04-10 11:12:01 -07:00
const pokemon = this . getPokemon ( ) ;
const prevStats = pokemon . stats . slice ( 0 ) ;
pokemon . calculateStats ( ) ;
pokemon . updateInfo ( ) ;
2023-04-17 22:32:26 -07:00
this . scene . playSoundWithoutBgm ( 'level_up_fanfare' , 1500 ) ;
2023-11-12 09:49:06 -08:00
this . scene . ui . showText ( ` ${ this . getPokemon ( ) . name } grew to \ nLv. ${ this . level } ! ` , null , ( ) = > this . scene . ui . getMessageHandler ( ) . promptLevelUpStats ( this . partyMemberIndex , prevStats , false ) . then ( ( ) = > this . end ( ) ) , null , true ) ;
2023-04-19 11:07:38 -07:00
if ( this . level <= 100 ) {
const levelMoves = this . getPokemon ( ) . getLevelMoves ( this . lastLevel + 1 ) ;
for ( let lm of levelMoves )
2024-03-01 13:21:28 -08:00
this . scene . unshiftPhase ( new LearnMovePhase ( this . scene , this . partyMemberIndex , lm [ 1 ] ) ) ;
2023-04-19 11:07:38 -07:00
}
2023-12-14 09:55:11 -08:00
if ( ! pokemon . pauseEvolutions ) {
const evolution = pokemon . getEvolution ( ) ;
if ( evolution )
2024-01-09 20:34:43 -08:00
this . scene . unshiftPhase ( new EvolutionPhase ( this . scene , pokemon as PlayerPokemon , evolution , this . lastLevel ) ) ;
2023-12-14 09:55:11 -08:00
}
2023-04-10 11:12:01 -07:00
}
}
2023-10-07 13:08:33 -07:00
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
2023-04-10 11:12:01 -07: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-20 18:32:48 -07:00
const move = allMoves [ this . moveId ] ;
2023-04-10 11:12:01 -07:00
2023-05-05 21:42:01 -07:00
const existingMoveIndex = pokemon . getMoveset ( ) . findIndex ( m = > m ? . moveId === move . id ) ;
2023-04-10 11:12:01 -07:00
2023-12-07 14:43:56 -08:00
if ( existingMoveIndex > - 1 )
return this . end ( ) ;
2023-04-10 11:12:01 -07:00
2023-05-05 21:42:01 -07:00
const emptyMoveIndex = pokemon . getMoveset ( ) . length < 4
? pokemon . getMoveset ( ) . length
: pokemon . getMoveset ( ) . findIndex ( m = > m === null ) ;
2023-04-10 11:12:01 -07:00
const messageMode = this . scene . ui . getHandler ( ) instanceof EvolutionSceneHandler
? Mode . EVOLUTION_SCENE
: Mode . MESSAGE ;
if ( emptyMoveIndex > - 1 ) {
2023-05-05 21:42:01 -07:00
pokemon . setMove ( emptyMoveIndex , this . moveId ) ;
2023-04-11 16:08:03 -07:00
initMoveAnim ( this . moveId ) . then ( ( ) = > {
2023-04-10 11:12:01 -07:00
loadMoveAnimAssets ( this . scene , [ this . moveId ] , true )
. then ( ( ) = > {
this . scene . ui . setMode ( messageMode ) . then ( ( ) = > {
2023-04-17 22:32:26 -07:00
this . scene . playSoundWithoutBgm ( 'level_up_fanfare' , 1500 ) ;
2024-01-09 20:34:43 -08:00
this . scene . ui . showText ( ` ${ pokemon . name } learned \ n ${ move . name } ! ` , null , ( ) = > {
this . scene . triggerPokemonFormChange ( pokemon , SpeciesFormChangeMoveLearnedTrigger , true ) ;
this . end ( ) ;
} , messageMode === Mode . EVOLUTION_SCENE ? 1000 : null , true ) ;
2023-04-10 11:12:01 -07:00
} ) ;
} ) ;
} ) ;
} else {
this . scene . ui . setMode ( messageMode ) . then ( ( ) = > {
this . scene . ui . showText ( ` ${ pokemon . name } wants to learn the \ nmove ${ move . name } . ` , null , ( ) = > {
this . scene . ui . showText ( ` However, ${ pokemon . name } already \ nknows four moves. ` , null , ( ) = > {
this . scene . ui . showText ( ` Should a move be deleted and \ nreplaced with ${ move . name } ? ` , null , ( ) = > {
const noHandler = ( ) = > {
this . scene . ui . setMode ( messageMode ) . then ( ( ) = > {
this . scene . ui . showText ( ` Stop trying to teach \ n ${ move . name } ? ` , null , ( ) = > {
this . scene . ui . setModeWithoutClear ( Mode . CONFIRM , ( ) = > {
this . scene . ui . setMode ( messageMode ) ;
this . scene . ui . showText ( ` ${ pokemon . name } did not learn the \ nmove ${ move . name } . ` , null , ( ) = > this . end ( ) , null , true ) ;
} , ( ) = > {
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 ) ;
this . scene . ui . showText ( 'Which move should be forgotten?' , null , ( ) = > {
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 , ( ) = > {
this . scene . ui . showText ( ` ${ pokemon . name } forgot how to \ nuse ${ pokemon . moveset [ moveIndex ] . getName ( ) } . ` , null , ( ) = > {
this . scene . ui . showText ( 'And…' , null , ( ) = > {
2023-05-05 21:42:01 -07:00
pokemon . setMove ( moveIndex , Moves . NONE ) ;
2023-04-10 11:12:01 -07: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 12:46:05 -07:00
export class BerryPhase extends CommonAnimPhase {
2023-05-18 08:11:06 -07:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex , undefined , CommonAnim . USE_ITEM ) ;
2023-04-20 12:46:05 -07:00
}
start() {
let berryModifier : BerryModifier ;
2023-04-20 16:44:56 -07:00
if ( ( berryModifier = this . scene . applyModifier ( BerryModifier , this . player , this . getPokemon ( ) ) as BerryModifier ) ) {
if ( berryModifier . consumed ) {
if ( ! -- berryModifier . stackCount )
this . scene . removeModifier ( berryModifier ) ;
else
berryModifier . consumed = false ;
this . scene . updateModifiers ( this . player ) ;
2023-04-20 12:46:05 -07:00
}
2023-12-25 22:29:05 -08:00
return super . start ( ) ;
2023-04-20 12:46:05 -07:00
}
this . end ( ) ;
}
}
2023-04-13 22:08:44 -07:00
export class PokemonHealPhase extends CommonAnimPhase {
private hpHealed : integer ;
2023-04-15 21:29:55 -07:00
private message : string ;
private showFullHpMessage : boolean ;
private skipAnim : boolean ;
2023-06-06 07:14:53 -07:00
private revive : boolean ;
2024-03-11 10:18:49 -07:00
private healStatus : boolean ;
2023-04-13 22:08:44 -07:00
2024-03-11 10:18:49 -07:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex , hpHealed : integer , message : string , showFullHpMessage : boolean , skipAnim : boolean = false , revive : boolean = false , healStatus : boolean = false ) {
2023-05-18 08:11:06 -07:00
super ( scene , battlerIndex , undefined , CommonAnim . HEALTH_UP ) ;
2023-04-13 22:08:44 -07:00
this . hpHealed = hpHealed ;
2023-04-15 21:29:55 -07:00
this . message = message ;
this . showFullHpMessage = showFullHpMessage ;
2024-03-11 10:18:49 -07:00
this . skipAnim = skipAnim ;
this . revive = revive ;
this . healStatus = healStatus ;
2023-04-15 21:29:55 -07:00
}
start() {
2023-06-06 07:14:53 -07:00
if ( ! this . skipAnim && ( this . revive || this . getPokemon ( ) . hp ) && this . getPokemon ( ) . getHpRatio ( ) < 1 )
2023-04-15 21:29:55 -07:00
super . start ( ) ;
else
this . end ( ) ;
2023-04-13 22:08:44 -07:00
}
end() {
const pokemon = this . getPokemon ( ) ;
2023-04-22 22:03:09 -07:00
2023-06-06 07:14:53 -07:00
if ( ! pokemon . isOnField ( ) || ( ! this . revive && ! pokemon . isActive ( ) ) ) {
2023-04-22 22:03:09 -07:00
super . end ( ) ;
return ;
}
2023-04-13 22:08:44 -07:00
2023-04-15 21:29:55 -07:00
const fullHp = pokemon . getHpRatio ( ) >= 1 ;
2024-03-11 10:18:49 -07:00
const hasMessage = ! ! this . message ;
let lastStatusEffect = StatusEffect . NONE ;
2023-04-15 21:29:55 -07:00
if ( ! fullHp ) {
2023-04-20 12:46:05 -07:00
const hpRestoreMultiplier = new Utils . IntegerHolder ( 1 ) ;
2023-06-06 07:14:53 -07:00
if ( ! this . revive )
this . scene . applyModifiers ( HealingBoosterModifier , this . player , hpRestoreMultiplier ) ;
2024-02-20 08:42:05 -08:00
const healAmount = new Utils . NumberHolder ( Math . floor ( this . hpHealed * hpRestoreMultiplier . value ) ) ;
2024-01-07 20:17:24 -08:00
healAmount . value = pokemon . heal ( healAmount . value ) ;
2024-03-01 06:35:36 -08:00
if ( healAmount . value )
this . scene . damageNumberHandler . add ( pokemon , healAmount . value , HitResult . HEAL ) ;
2024-02-18 19:21:57 -08: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 10:18:49 -07:00
if ( this . healStatus && ! this . revive && pokemon . status ) {
lastStatusEffect = pokemon . status . effect ;
pokemon . resetStatus ( ) ;
}
2023-04-15 21:29:55 -07:00
pokemon . updateInfo ( ) . then ( ( ) = > super . end ( ) ) ;
2024-03-11 15:13:07 -07:00
} else if ( this . healStatus && ! this . revive && pokemon . status ) {
lastStatusEffect = pokemon . status . effect ;
pokemon . resetStatus ( ) ;
pokemon . updateInfo ( ) . then ( ( ) = > super . end ( ) ) ;
2023-04-15 21:29:55 -07:00
} else if ( this . showFullHpMessage )
this . message = getPokemonMessage ( pokemon , ` 's \ nHP is full! ` ) ;
if ( this . message )
2023-04-21 16:30:04 -07:00
this . scene . queueMessage ( this . message ) ;
2023-04-15 21:29:55 -07:00
2024-03-11 10:18:49 -07:00
if ( this . healStatus && lastStatusEffect && ! hasMessage )
this . scene . queueMessage ( getPokemonMessage ( pokemon , getStatusEffectHealText ( lastStatusEffect ) ) ) ;
2024-03-11 15:13:07 -07:00
if ( fullHp && ! lastStatusEffect )
2023-04-15 21:29:55 -07:00
super . end ( ) ;
2023-04-13 22:08:44 -07:00
}
}
2023-05-18 08:11:06 -07:00
export class AttemptCapturePhase extends PokemonPhase {
2023-04-10 11:12:01 -07:00
private pokeballType : PokeballType ;
private pokeball : Phaser.GameObjects.Sprite ;
private originalY : number ;
2023-05-18 08:11:06 -07:00
constructor ( scene : BattleScene , targetIndex : integer , pokeballType : PokeballType ) {
super ( scene , BattlerIndex . ENEMY + targetIndex ) ;
2023-04-10 11:12:01 -07:00
this . pokeballType = pokeballType ;
}
start() {
super . start ( ) ;
2024-01-07 20:17:24 -08:00
const pokemon = this . getPokemon ( ) as EnemyPokemon ;
2023-05-18 08:11:06 -07:00
2023-12-07 14:43:56 -08:00
if ( ! pokemon ? . hp )
return this . end ( ) ;
2023-05-18 08:11:06 -07:00
2023-04-10 11:12:01 -07:00
this . scene . pokeballCounts [ this . pokeballType ] -- ;
this . originalY = pokemon . y ;
2024-02-20 07:16:09 -08:00
const _3m = 3 * pokemon . getMaxHp ( ) ;
2023-04-10 11:12:01 -07:00
const _2h = 2 * pokemon . hp ;
const catchRate = pokemon . species . catchRate ;
const pokeballMultiplier = getPokeballCatchMultiplier ( this . pokeballType ) ;
2023-04-18 19:09:37 -07:00
const statusMultiplier = pokemon . status ? getStatusEffectCatchRateMultiplier ( pokemon . status . effect ) : 1 ;
2023-04-10 11:12:01 -07: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 08:11:06 -07:00
const fpOffset = pokemon . getFieldPositionOffset ( ) ;
2023-04-10 11:12:01 -07:00
const pokeballAtlasKey = getPokeballAtlasKey ( this . pokeballType ) ;
2023-12-29 18:04:40 -08:00
this . pokeball = this . scene . addFieldSprite ( 16 , 80 , 'pb' , pokeballAtlasKey ) ;
2023-04-10 11:12:01 -07:00
this . pokeball . setOrigin ( 0.5 , 0.625 ) ;
this . scene . field . add ( this . pokeball ) ;
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'pb_throw' ) ;
2023-04-10 11:12:01 -07:00
this . scene . time . delayedCall ( 300 , ( ) = > {
2023-06-06 07:14:53 -07:00
this . scene . field . moveBelow ( this . pokeball as Phaser . GameObjects . GameObject , pokemon ) ;
2023-04-10 11:12:01 -07:00
} ) ;
2024-01-04 20:57:21 -08:00
2023-04-10 11:12:01 -07:00
this . scene . tweens . add ( {
targets : this.pokeball ,
2023-05-18 08:11:06 -07:00
x : { value : 236 + fpOffset [ 0 ] , ease : 'Linear' } ,
y : { value : 16 + fpOffset [ 1 ] , ease : 'Cubic.easeOut' } ,
2023-04-10 11:12:01 -07:00
duration : 500 ,
onComplete : ( ) = > {
this . pokeball . setTexture ( 'pb' , ` ${ pokeballAtlasKey } _opening ` ) ;
this . scene . time . delayedCall ( 17 , ( ) = > this . pokeball . setTexture ( 'pb' , ` ${ pokeballAtlasKey } _open ` ) ) ;
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'pb_rel' ) ;
2023-04-10 11:12:01 -07:00
pokemon . tint ( getPokeballTintColor ( this . pokeballType ) ) ;
2024-01-04 20:57:21 -08:00
addPokeballOpenParticles ( this . scene , this . pokeball . x , this . pokeball . y , this . pokeballType ) ;
2023-04-10 11:12:01 -07:00
this . scene . tweens . add ( {
targets : pokemon ,
2024-01-04 20:57:21 -08:00
duration : 500 ,
2023-04-10 11:12:01 -07:00
ease : 'Sine.easeIn' ,
scale : 0.25 ,
y : 20 ,
onComplete : ( ) = > {
this . pokeball . setTexture ( 'pb' , ` ${ pokeballAtlasKey } _opening ` ) ;
pokemon . setVisible ( false ) ;
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'pb_catch' ) ;
2023-04-10 11:12:01 -07:00
this . scene . time . delayedCall ( 17 , ( ) = > this . pokeball . setTexture ( 'pb' , ` ${ pokeballAtlasKey } ` ) ) ;
2024-01-04 16:56:26 -08:00
const doShake = ( ) = > {
2023-04-10 11:12:01 -07: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 13:07:29 -07:00
if ( ! pokemon . species . isObtainable ( ) ) {
shakeCounter . stop ( ) ;
this . failCatch ( shakeCount ) ;
} else if ( shakeCount ++ < 3 ) {
2024-01-07 20:50:11 -08:00
if ( pokeballMultiplier === - 1 || pokemon . randSeedInt ( 65536 ) < y )
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'pb_move' ) ;
2023-04-10 11:12:01 -07:00
else {
shakeCounter . stop ( ) ;
2023-04-10 22:31:18 -07:00
this . failCatch ( shakeCount ) ;
2023-04-10 11:12:01 -07:00
}
2024-01-04 20:57:21 -08: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 11:12:01 -07:00
} ,
2023-04-20 16:44:56 -07:00
onComplete : ( ) = > this . catch ( )
2023-04-10 11:12:01 -07:00
} ) ;
2024-01-04 16:56:26 -08:00
} ;
2023-04-10 11:12:01 -07:00
this . scene . time . delayedCall ( 250 , ( ) = > doPokeballBounceAnim ( this . scene , this . pokeball , 16 , 72 , 350 , doShake ) ) ;
}
} ) ;
}
} ) ;
}
2023-04-10 22:31:18 -07:00
failCatch ( shakeCount : integer ) {
2023-05-18 08:11:06 -07:00
const pokemon = this . getPokemon ( ) ;
2023-04-10 11:12:01 -07:00
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'pb_rel' ) ;
2023-04-10 11:12:01 -07:00
pokemon . setY ( this . originalY ) ;
pokemon . cry ( ) ;
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 08:11:06 -07:00
const pokemon = this . getPokemon ( ) as EnemyPokemon ;
2023-05-29 09:24:38 -07:00
this . scene . unshiftPhase ( new VictoryPhase ( this . scene , this . battlerIndex ) ) ;
2023-11-11 21:31:40 -08:00
2023-12-05 20:31:34 -08:00
const speciesForm = ! pokemon . fusionSpecies ? pokemon . getSpeciesForm ( ) : pokemon . getFusionSpeciesForm ( ) ;
2023-12-05 14:12:39 -08:00
if ( speciesForm . abilityHidden && ( pokemon . fusionSpecies ? pokemon.fusionAbilityIndex : pokemon.abilityIndex ) === speciesForm . getAbilityCount ( ) - 1 )
2023-11-11 21:31:40 -08: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 06:27:50 -08:00
2024-03-07 19:43:15 -08:00
this . scene . pokemonInfoContainer . show ( pokemon , true ) ;
2023-12-19 20:51:48 -08:00
this . scene . gameData . updateSpeciesDexIvs ( pokemon . species . speciesId , pokemon . ivs ) ;
2023-11-11 21:31:40 -08:00
2023-04-10 11:12:01 -07:00
this . scene . ui . showText ( ` ${ pokemon . name } was caught! ` , null , ( ) = > {
2023-04-10 13:52:27 -07:00
const end = ( ) = > {
2024-03-07 19:43:15 -08:00
this . scene . pokemonInfoContainer . hide ( ) ;
2023-04-10 11:12:01 -07:00
this . removePb ( ) ;
this . end ( ) ;
2023-04-10 13:52:27 -07:00
} ;
2023-07-03 07:59:56 -07:00
const removePokemon = ( ) = > {
2024-02-18 07:53:32 -08:00
this . scene . getPlayerField ( ) . filter ( p = > p . isActive ( true ) ) . forEach ( playerPokemon = > playerPokemon . removeTagsBySourceId ( pokemon . id ) ) ;
2023-07-03 07:59:56 -07:00
pokemon . hp = 0 ;
pokemon . trySetStatus ( StatusEffect . FAINT ) ;
2023-10-28 10:24:57 -07:00
this . scene . clearEnemyHeldItemModifiers ( ) ;
2023-07-03 07:59:56 -07:00
this . scene . field . remove ( pokemon , true ) ;
} ;
2023-04-10 22:31:18 -07:00
const addToParty = ( ) = > {
2024-01-04 16:56:26 -08:00
const newPokemon = pokemon . addToParty ( this . pokeballType ) ;
2023-04-20 16:44:56 -07:00
const modifiers = this . scene . findModifiers ( m = > m instanceof PokemonHeldItemModifier , false ) ;
2023-11-12 06:27:50 -08:00
if ( this . scene . getParty ( ) . filter ( p = > p . isShiny ( ) ) . length === 6 )
this . scene . validateAchv ( achvs . SHINY_PARTY ) ;
2023-10-31 18:43:22 -07:00
Promise . all ( modifiers . map ( m = > this . scene . addModifier ( m , true ) ) ) . then ( ( ) = > {
this . scene . updateModifiers ( true ) ;
2023-07-03 07:59:56 -07:00
removePokemon ( ) ;
2023-04-20 16:44:56 -07:00
if ( newPokemon )
newPokemon . loadAssets ( ) . then ( end ) ;
else
end ( ) ;
} ) ;
2023-04-10 22:31:18 -07:00
} ;
2023-04-18 10:36:11 -07:00
Promise . all ( [ pokemon . hideInfo ( ) , this . scene . gameData . setPokemonCaught ( pokemon ) ] ) . then ( ( ) = > {
2023-04-17 22:32:26 -07:00
if ( this . scene . getParty ( ) . length === 6 ) {
const promptRelease = ( ) = > {
2023-10-18 15:01:15 -07:00
this . scene . ui . showText ( ` Your party is full. \ nRelease a Pokémon to make room for ${ pokemon . name } ? ` , null , ( ) = > {
2023-04-17 22:32:26 -07:00
this . scene . ui . setMode ( Mode . CONFIRM , ( ) = > {
2023-05-18 08:11:06 -07:00
this . scene . ui . setMode ( Mode . PARTY , PartyUiMode . RELEASE , this . fieldIndex , ( slotIndex : integer , _option : PartyOption ) = > {
2023-04-17 22:32:26 -07:00
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > {
if ( slotIndex < 6 )
addToParty ( ) ;
else
promptRelease ( ) ;
} ) ;
2023-04-10 22:31:18 -07:00
} ) ;
2023-04-17 22:32:26 -07:00
} , ( ) = > {
2023-07-03 07:59:56 -07:00
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > {
removePokemon ( ) ;
end ( ) ;
} ) ;
2023-04-10 22:31:18 -07:00
} ) ;
} ) ;
2023-04-17 22:32:26 -07:00
} ;
promptRelease ( ) ;
} else
addToParty ( ) ;
} ) ;
2023-04-10 11:12:01 -07: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 08:11:06 -07:00
export class AttemptRunPhase extends PokemonPhase {
constructor ( scene : BattleScene , fieldIndex : integer ) {
super ( scene , fieldIndex ) ;
2023-05-07 14:05:19 -07:00
}
start() {
super . start ( ) ;
2023-05-18 08:11:06 -07:00
const playerPokemon = this . getPokemon ( ) ;
const enemyField = this . scene . getEnemyField ( ) ;
2023-10-28 22:28:56 -07:00
const enemySpeed = enemyField . reduce ( ( total : integer , enemyPokemon : Pokemon ) = > total + enemyPokemon . getStat ( Stat . SPD ) , 0 ) / enemyField . length ;
2023-05-07 14:05:19 -07:00
2023-12-22 14:08:37 -08: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 14:05:19 -07:00
2024-01-02 18:31:59 -08:00
if ( playerPokemon . randSeedInt ( 256 ) < escapeChance . value ) {
2023-10-21 05:58:39 -07:00
this . scene . playSound ( 'flee' ) ;
2023-05-07 14:05:19 -07:00
this . scene . queueMessage ( 'You got away safely!' , null , true , 500 ) ;
this . scene . tweens . add ( {
2023-05-18 08:11:06 -07:00
targets : [ this . scene . arenaEnemy , enemyField ] . flat ( ) ,
2023-05-07 14:05:19 -07:00
alpha : 0 ,
duration : 250 ,
2023-10-31 11:09:33 -07:00
ease : 'Sine.easeIn' ,
onComplete : ( ) = > enemyField . forEach ( enemyPokemon = > enemyPokemon . destroy ( ) )
2023-05-07 14:05:19 -07:00
} ) ;
2023-12-26 09:01:56 -08:00
this . scene . clearEnemyHeldItemModifiers ( ) ;
2023-06-01 10:54:52 -07:00
enemyField . forEach ( enemyPokemon = > {
2023-10-31 11:09:33 -07:00
enemyPokemon . hideInfo ( ) . then ( ( ) = > enemyPokemon . destroy ( ) ) ;
2023-06-01 10:54:52 -07:00
enemyPokemon . hp = 0 ;
2023-11-03 21:32:12 -07:00
enemyPokemon . trySetStatus ( StatusEffect . FAINT ) ;
2023-06-01 10:54:52 -07:00
} ) ;
2023-05-07 14:05:19 -07:00
this . scene . pushPhase ( new BattleEndPhase ( this . scene ) ) ;
2023-05-18 08:11:06 -07:00
this . scene . pushPhase ( new NewBattlePhase ( this . scene ) ) ;
2023-05-07 14:05:19 -07:00
} else
this . scene . queueMessage ( 'You can\'t escape!' , null , true ) ;
this . end ( ) ;
}
}
2023-04-10 11:12:01 -07:00
export class SelectModifierPhase extends BattlePhase {
2023-11-10 12:51:34 -08:00
private rerollCount : integer ;
constructor ( scene : BattleScene , rerollCount : integer = 0 ) {
2023-04-10 11:12:01 -07:00
super ( scene ) ;
2023-11-10 12:51:34 -08:00
this . rerollCount = rerollCount ;
2023-04-10 11:12:01 -07:00
}
start() {
super . start ( ) ;
2023-11-10 12:51:34 -08:00
if ( ! this . rerollCount )
this . updateSeed ( ) ;
2023-10-18 15:01:15 -07:00
2023-04-10 11:12:01 -07:00
const party = this . scene . getParty ( ) ;
2023-10-29 13:05:17 -07:00
regenerateModifierPoolThresholds ( party , this . getPoolType ( ) ) ;
2023-04-10 11:12:01 -07:00
const modifierCount = new Utils . IntegerHolder ( 3 ) ;
2023-10-29 13:15:18 -07:00
if ( this . isPlayer ( ) )
this . scene . applyModifiers ( ExtraModifierModifier , true , modifierCount ) ;
2023-10-29 13:05:17 -07:00
const typeOptions : ModifierTypeOption [ ] = this . getModifierTypeOptions ( modifierCount . value ) ;
2023-04-10 11:12:01 -07:00
2024-02-03 21:30:19 -08:00
const modifierSelectCallback = ( rowCursor : integer , cursor : integer ) = > {
if ( rowCursor < 0 || cursor < 0 ) {
2023-04-10 11:12:01 -07:00
this . scene . ui . setMode ( Mode . MESSAGE ) ;
super . end ( ) ;
2023-11-10 12:51:34 -08:00
return true ;
2023-04-10 11:12:01 -07:00
}
2024-02-03 21:30:19 -08:00
let modifierType : ModifierType ;
let cost : integer ;
switch ( rowCursor ) {
case 0 :
if ( ! cursor ) {
const rerollCost = this . getRerollCost ( ) ;
if ( this . scene . money < rerollCost ) {
this . scene . ui . playError ( ) ;
return false ;
} else {
this . scene . unshiftPhase ( new SelectModifierPhase ( this . scene , this . rerollCount + 1 ) ) ;
this . scene . ui . clearText ( ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) . then ( ( ) = > super . end ( ) ) ;
this . scene . money -= rerollCost ;
this . scene . updateMoneyText ( ) ;
this . scene . playSound ( 'buy' ) ;
}
} else {
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-03-04 20:59:15 -08:00
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) , typeOptions , modifierSelectCallback , this . getRerollCost ( ) ) . then ( ( ) = > {
2024-02-03 21:30:19 -08: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-04 20:59:15 -08:00
this . scene . tryTransferHeldItemModifier ( itemModifier , party [ toSlotIndex ] , true , true ) ;
2024-02-03 21:30:19 -08:00
} ) ;
} else
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) , typeOptions , modifierSelectCallback , this . getRerollCost ( ) ) ;
} , PartyUiHandler . FilterItemMaxStacks ) ;
}
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-03-14 14:15:01 -07:00
this . scene . addModifier ( modifier , false , playSound ) ;
if ( cost ) {
this . scene . money -= cost ;
this . scene . updateMoneyText ( ) ;
this . scene . playSound ( 'buy' ) ;
( this . scene . ui . getHandler ( ) as ModifierSelectUiHandler ) . updateCostText ( ) ;
} else {
this . scene . ui . clearText ( ) ;
this . scene . ui . setMode ( Mode . MESSAGE ) ;
super . end ( ) ;
}
2024-02-03 21:30:19 -08:00
} ;
2023-04-10 11:12:01 -07:00
if ( modifierType instanceof PokemonModifierType ) {
2023-11-03 21:32:12 -07: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-03 21:30:19 -08:00
applyModifier ( modifier , true ) ;
2023-11-03 21:32:12 -07:00
} ) ;
} else
2023-11-10 12:51:34 -08:00
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) , typeOptions , modifierSelectCallback , this . getRerollCost ( ) ) ;
2023-11-03 21:32:12 -07:00
} , modifierType . selectFilter ) ;
} else {
const pokemonModifierType = modifierType as PokemonModifierType ;
const isMoveModifier = modifierType instanceof PokemonMoveModifierType ;
const isTmModifier = modifierType instanceof TmModifierType ;
2023-11-08 14:30:07 -08:00
const isRememberMoveModifier = modifierType instanceof RememberMoveModifierType ;
2023-11-03 21:32:12 -07:00
const partyUiMode = isMoveModifier ? PartyUiMode . MOVE_MODIFIER
2023-11-08 14:30:07 -08:00
: isTmModifier ? PartyUiMode . TM_MODIFIER
: isRememberMoveModifier ? PartyUiMode . REMEMBER_MOVE_MODIFIER
: PartyUiMode . MODIFIER ;
2023-11-03 21:32:12 -07: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 14:30:07 -08:00
? ! isRememberMoveModifier
? modifierType . newModifier ( party [ slotIndex ] )
: modifierType . newModifier ( party [ slotIndex ] , option as integer )
2023-11-03 21:32:12 -07:00
: modifierType . newModifier ( party [ slotIndex ] , option - PartyOption . MOVE_1 ) ;
2024-02-03 21:30:19 -08:00
applyModifier ( modifier , true ) ;
2023-11-03 21:32:12 -07:00
} ) ;
} else
2023-11-10 12:51:34 -08:00
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) , typeOptions , modifierSelectCallback , this . getRerollCost ( ) ) ;
2023-11-03 21:32:12 -07:00
} , pokemonModifierType . selectFilter , modifierType instanceof PokemonMoveModifierType ? ( modifierType as PokemonMoveModifierType ) . moveSelectFilter : undefined , tmMoveId ) ;
}
2024-02-03 21:30:19 -08:00
} else
2024-02-14 16:20:12 -08:00
applyModifier ( modifierType . newModifier ( ) ) ;
2023-11-10 12:51:34 -08:00
2024-02-03 21:30:19 -08:00
return ! cost ;
2023-04-10 11:12:01 -07:00
} ;
2023-11-10 12:51:34 -08:00
this . scene . ui . setMode ( Mode . MODIFIER_SELECT , this . isPlayer ( ) , typeOptions , modifierSelectCallback , this . getRerollCost ( ) ) ;
2023-10-29 13:05:17 -07:00
}
updateSeed ( ) : void {
this . scene . resetSeed ( ) ;
}
isPlayer ( ) : boolean {
return true ;
}
2023-11-10 12:51:34 -08:00
getRerollCost ( ) : integer {
return Math . ceil ( this . scene . currentBattle . waveIndex / 10 ) * 250 * Math . pow ( 2 , this . rerollCount ) ;
}
2023-10-29 13:05:17 -07:00
getPoolType ( ) : ModifierPoolType {
return ModifierPoolType . PLAYER ;
}
getModifierTypeOptions ( modifierCount : integer ) : ModifierTypeOption [ ] {
return getPlayerModifierTypeOptionsForWave ( this . scene . currentBattle . waveIndex , modifierCount , this . scene . getParty ( ) ) ;
}
addModifier ( modifier : Modifier ) : Promise < void > {
2023-10-31 18:43:22 -07:00
return this . scene . addModifier ( modifier , false , true ) ;
2023-10-29 13:05:17 -07:00
}
}
2024-02-21 20:57:49 -08:00
export class EggLapsePhase extends Phase {
2023-12-19 20:51:48 -08:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
super . start ( ) ;
const eggsToHatch : Egg [ ] = [ ] ;
for ( let egg of this . scene . gameData . eggs ) {
if ( -- egg . hatchWaves < 1 )
eggsToHatch . push ( egg ) ;
}
if ( eggsToHatch . length )
this . scene . queueMessage ( 'Oh?' ) ;
for ( let egg of eggsToHatch )
this . scene . unshiftPhase ( new EggHatchPhase ( this . scene , egg ) ) ;
this . end ( ) ;
}
}
2024-02-21 20:57:49 -08:00
export class AddEnemyBuffModifierPhase extends Phase {
2023-10-29 13:05:17 -07:00
constructor ( scene : BattleScene ) {
super ( scene ) ;
}
start() {
2023-11-05 07:56:09 -08:00
super . start ( ) ;
2023-10-29 13:05:17 -07:00
2023-11-05 07:56:09 -08:00
const waveIndex = this . scene . currentBattle . waveIndex ;
const tier = ! ( waveIndex % 1000 ) ? ModifierTier . ULTRA : ! ( waveIndex % 250 ) ? ModifierTier.GREAT : ModifierTier.COMMON ;
2023-10-29 13:05:17 -07:00
2023-11-05 07:56:09 -08:00
regenerateModifierPoolThresholds ( this . scene . getEnemyParty ( ) , ModifierPoolType . ENEMY_BUFF ) ;
2024-03-10 21:16:24 -07: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 11:12:01 -07:00
}
}
2023-10-18 15:01:15 -07: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 08:38:41 -07:00
pokemon . updateInfo ( true ) ;
2023-10-18 15:01:15 -07:00
}
2023-10-26 13:33:59 -07:00
const healSong = this . scene . playSoundWithoutBgm ( 'heal' ) ;
2023-10-18 15:01:15 -07:00
this . scene . time . delayedCall ( healSong . totalDuration * 1000 , ( ) = > {
healSong . destroy ( ) ;
if ( this . resumeBgm && bgmPlaying )
this . scene . playBgm ( ) ;
this . scene . ui . fadeIn ( 500 ) . then ( ( ) = > this . end ( ) ) ;
} ) ;
} ) ;
}
}
2023-04-10 11:12:01 -07:00
export class ShinySparklePhase extends PokemonPhase {
2023-05-18 08:11:06 -07:00
constructor ( scene : BattleScene , battlerIndex : BattlerIndex ) {
super ( scene , battlerIndex ) ;
2023-04-10 11:12:01 -07:00
}
start() {
super . start ( ) ;
this . getPokemon ( ) . sparkle ( ) ;
this . scene . time . delayedCall ( 1000 , ( ) = > this . end ( ) ) ;
}
2023-10-18 15:01:15 -07:00
}
2023-11-12 09:49:06 -08: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 ( ) ;
this . scene . ui . showText ( ` Use IV Scanner on ${ pokemon . name } ? ` , null , ( ) = > {
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-11 18:46:49 -08:00
export class TrainerMessageTestPhase extends BattlePhase {
2024-02-06 13:15:35 -08:00
private trainerTypes : TrainerType [ ] ;
constructor ( scene : BattleScene , . . . trainerTypes : TrainerType [ ] ) {
2023-12-11 18:46:49 -08:00
super ( scene ) ;
2024-02-06 13:15:35 -08:00
this . trainerTypes = trainerTypes ;
2023-12-11 18:46:49 -08:00
}
start() {
super . start ( ) ;
let testMessages : string [ ] = [ ] ;
for ( let t of Object . keys ( trainerConfigs ) ) {
2024-02-06 13:15:35 -08:00
const type = parseInt ( t ) ;
if ( this . trainerTypes . length && ! this . trainerTypes . find ( tt = > tt === type as TrainerType ) )
continue ;
const config = trainerConfigs [ type ] ;
2023-12-11 18:46:49 -08: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 15:01:15 -07:00
export class TestMessagePhase extends MessagePhase {
constructor ( scene : BattleScene , message : string ) {
super ( scene , message , null , true ) ;
}
2023-04-10 11:12:01 -07:00
}