diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 1109c822a26..2c6c1a418f6 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -539,13 +539,15 @@ export default class BattleScene extends Phaser.Scene { getPokemonById(pokemonId: integer): Pokemon { const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId); - return findInParty(this.getParty()) || findInParty(this.getEnemyField()); + return findInParty(this.getParty()) || findInParty(this.getEnemyParty()); } reset(): void { this.seed = Utils.randomString(16); console.log('Seed:', this.seed); + this.gameMode = GameMode.CLASSIC; + this.money = startingMoney; this.pokeballCounts = Object.fromEntries(Utils.getEnumValues(PokeballType).filter(p => p <= PokeballType.MASTER_BALL).map(t => [ t, 0 ])); @@ -559,7 +561,7 @@ export default class BattleScene extends Phaser.Scene { for (let p of this.getParty()) p.destroy(); this.party = []; - for (let p of this.getEnemyField()) + for (let p of this.getEnemyParty()) p.destroy(); this.currentBattle = null; @@ -590,7 +592,7 @@ export default class BattleScene extends Phaser.Scene { this.resetSeed(newWaveIndex); - if (fixedBattles.hasOwnProperty(newWaveIndex)) { + if (fixedBattles.hasOwnProperty(newWaveIndex) && this.gameMode === GameMode.CLASSIC) { battleConfig = fixedBattles[newWaveIndex]; newDouble = battleConfig.double; newBattleType = battleConfig.battleType; @@ -598,7 +600,9 @@ export default class BattleScene extends Phaser.Scene { if (newTrainer) this.field.add(newTrainer); } else { - if (battleType === undefined) { + if (this.gameMode === GameMode.ENDLESS) + newBattleType = BattleType.WILD; + else if (battleType === undefined) { if (newWaveIndex > 20 && !(newWaveIndex % 30)) newBattleType = BattleType.TRAINER; else if (newWaveIndex % 10 !== 1 && newWaveIndex % 10) { @@ -644,7 +648,7 @@ export default class BattleScene extends Phaser.Scene { const showTrainer = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER; const availablePartyMemberCount = this.getParty().filter(p => !p.isFainted()).length; if (lastBattle) { - this.getEnemyField().forEach(enemyPokemon => enemyPokemon.destroy()); + this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy()); if (showTrainer) { playerField.forEach((_, p) => this.unshiftPhase(new ReturnPhase(this, p))); this.unshiftPhase(new ShowTrainerPhase(this)); @@ -741,7 +745,7 @@ export default class BattleScene extends Phaser.Scene { } updateUIPositions(): void { - this.waveCountText.setY(-(this.game.canvas.height / 6) + (this.enemyModifiers.length ? 15 : 0)); + this.waveCountText.setY(-(this.game.canvas.height / 6) + (this.enemyModifiers.filter(m => m.isIconVisible(this)).length ? 15 : 0)); this.moneyText.setY(this.waveCountText.y + 10); this.partyExpBar.setY(this.moneyText.y + 15); } @@ -1125,22 +1129,30 @@ export default class BattleScene extends Phaser.Scene { return new Promise(resolve => { const waveIndex = this.currentBattle.waveIndex; const chances = Math.ceil(waveIndex / 10); - const isBoss = !(waveIndex % 10); - let count = 0; - for (let c = 0; c < chances; c++) { - let modifierChance = !isBoss ? 16 : 6; + const isBoss = !(waveIndex % 10) || (this.currentBattle.battleType === BattleType.TRAINER && this.currentBattle.trainer.config.isBoss); + + let modifierChance: integer; + if (this.gameMode === GameMode.CLASSIC) + modifierChance = !isBoss ? 18 : 6; + else + modifierChance = !isBoss ? 12 : 4; + + this.getEnemyParty().forEach((enemyPokemon: EnemyPokemon, i: integer) => { + let pokemonModifierChance = modifierChance; if (this.currentBattle.battleType === BattleType.TRAINER) - modifierChance /= 2; - if (!Utils.randSeedInt(modifierChance)) - count++; - if (count === 12) - break; - } - if (isBoss) - count = Math.max(count, Math.floor(chances / 2)); - const enemyField = this.getEnemyField(); - getEnemyModifierTypesForWave(waveIndex, count, this.getEnemyField(), this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD) - .map(mt => mt.newModifier(enemyField[Utils.randInt(enemyField.length)]).add(this.enemyModifiers, false)); + pokemonModifierChance = Math.ceil(pokemonModifierChance * this.currentBattle.trainer.getPartyMemberModifierChanceMultiplier(i)); + let count = 0; + for (let c = 0; c < chances; c++) { + if (!Utils.randSeedInt(modifierChance)) + count++; + if (count === 12) + break; + } + if (isBoss) + count = Math.max(count, Math.floor(chances / 2)); + getEnemyModifierTypesForWave(waveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD) + .map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false)); + }); this.updateModifiers(false).then(() => resolve()); }); @@ -1172,7 +1184,7 @@ export default class BattleScene extends Phaser.Scene { modifiers.splice(modifiers.indexOf(modifier), 1); } - this.updatePartyForModifiers(player ? this.getParty() : this.getEnemyField().filter(p => p.isActive())).then(() => { + this.updatePartyForModifiers(player ? this.getParty() : this.getEnemyParty()).then(() => { (player ? this.modifierBar : this.enemyModifierBar).updateModifiers(modifiers); if (!player) this.updateUIPositions(); diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 822ae06423b..93aa5273042 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -256,7 +256,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm { let evolutionChance: number; if (ev.wildDelay === SpeciesWildEvolutionDelay.NONE) - evolutionChance = Math.min(0.5 + easeInFunc((level - ev.level) / 40) / 2, 1); + evolutionChance = Math.min(0.5 + easeInFunc(Math.min(level - ev.level, 40) / 40) / 2, 1); else { let preferredMinLevel = ev.wildDelay * 10; let evolutionLevel = ev.level > 1 ? ev.level : Math.floor(preferredMinLevel / 2); diff --git a/src/data/trainer-type.ts b/src/data/trainer-type.ts index d332299c53c..53a7e7c980e 100644 --- a/src/data/trainer-type.ts +++ b/src/data/trainer-type.ts @@ -280,7 +280,8 @@ export class TrainerConfig { public nameFemale: string; public hasGenders: boolean = false; public isDouble: boolean = false; - public staticParty: boolean = false; + public isBoss: boolean = false; + public hasStaticParty: boolean = false; public battleBgm: string; public encounterBgm: string; public femaleEncounterBgm: string; @@ -349,8 +350,13 @@ export class TrainerConfig { return this; } + setBoss(): TrainerConfig { + this.isBoss = true; + return this; + } + setStaticParty(): TrainerConfig { - this.staticParty = true; + this.hasStaticParty = true; return this; } @@ -428,6 +434,7 @@ export class TrainerConfig { this.setPartyMemberFunc(-1, getRandomPartyMemberFunc([ signatureSpecies ])); if (specialtyType !== undefined) this.setSpeciesFilter(p => p.isOfType(specialtyType)); + this.setBoss(); this.setStaticParty(); this.setBattleBgm('battle_gym'); this.setVictoryBgm('victory_gym'); @@ -441,6 +448,7 @@ export class TrainerConfig { this.setSpeciesFilter(p => p.isOfType(specialtyType) && p.baseTotal >= 450); else this.setSpeciesFilter(p => p.baseTotal >= 450); + this.setBoss(); this.setStaticParty(); this.setBattleBgm('battle_elite'); this.setVictoryBgm('victory_gym'); @@ -451,6 +459,7 @@ export class TrainerConfig { this.setPartyTemplates(trainerPartyTemplates.CHAMPION); this.setPartyMemberFunc(-1, getRandomPartyMemberFunc([ signatureSpecies ])); this.setSpeciesFilter(p => p.baseTotal >= 470); + this.setBoss(); this.setStaticParty(); this.setBattleBgm('battle_champion'); this.setVictoryBgm('victory_champion'); @@ -707,7 +716,7 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT ])) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) .setSpeciesFilter(species => species.baseTotal >= 540), - [TrainerType.RIVAL_4]: new TrainerConfig(++t).setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival_2').setPartyTemplates(trainerPartyTemplates.RIVAL_4).setEncounterMessages([ + [TrainerType.RIVAL_4]: new TrainerConfig(++t).setBoss().setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival_2').setPartyTemplates(trainerPartyTemplates.RIVAL_4).setEncounterMessages([ `It's me! You didn't forget about me again did you? $You made it really far! I'm proud of you.\nBut it looks like it's the end of your journey. $You've awoken something in me I never knew was there.\nIt seems like all I do now is train. @@ -721,13 +730,13 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT ])) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) .setSpeciesFilter(species => species.baseTotal >= 540), - [TrainerType.RIVAL_5]: new TrainerConfig(++t).setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival_3').setPartyTemplates(trainerPartyTemplates.RIVAL_5).setEncounterMessages([ `…` ]).setVictoryMessages([ '…' ]) + [TrainerType.RIVAL_5]: new TrainerConfig(++t).setBoss().setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm('battle_rival_3').setPartyTemplates(trainerPartyTemplates.RIVAL_5).setEncounterMessages([ `…` ]).setVictoryMessages([ '…' ]) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT ])) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) .setSpeciesFilter(species => species.baseTotal >= 540) .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.RAYQUAZA ])), - [TrainerType.RIVAL_6]: new TrainerConfig(++t).setStaticParty().setEncounterBgm('final').setBattleBgm('battle_rival_3').setPartyTemplates(trainerPartyTemplates.RIVAL_6) + [TrainerType.RIVAL_6]: new TrainerConfig(++t).setBoss().setStaticParty().setEncounterBgm('final').setBattleBgm('battle_rival_3').setPartyTemplates(trainerPartyTemplates.RIVAL_6) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT ])) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 4abdaa73f8e..3f21898be51 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -31,10 +31,14 @@ export class ModifierBar extends Phaser.GameObjects.Container { updateModifiers(modifiers: PersistentModifier[]) { this.removeAll(true); - for (let modifier of modifiers) { + const visibleIconModifiers = modifiers.filter(m => m.isIconVisible(this.scene as BattleScene)); + + for (let modifier of visibleIconModifiers) { + if (!modifier.isIconVisible(this.scene as BattleScene)) + continue; const icon = modifier.getIcon(this.scene as BattleScene); this.add(icon); - this.setModifierIconPosition(icon, modifiers.length); + this.setModifierIconPosition(icon, visibleIconModifiers.length); } } @@ -116,6 +120,10 @@ export abstract class PersistentModifier extends Modifier { return 99; } + isIconVisible(scene: BattleScene): boolean { + return true; + } + getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container { const container = scene.add.container(0, 0); @@ -329,6 +337,11 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { return true; } + isIconVisible(scene: BattleScene): boolean { + const pokemon = this.getPokemon(scene); + return pokemon instanceof PlayerPokemon || this.getPokemon(scene).isOnField(); + } + getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container { const container = !forSummary ? scene.add.container(0, 0) : super.getIcon(scene); diff --git a/src/trainer.ts b/src/trainer.ts index 3d520ae5555..62df6f4afee 100644 --- a/src/trainer.ts +++ b/src/trainer.ts @@ -132,7 +132,7 @@ export default class Trainer extends Phaser.GameObjects.Container { : this.genNewPartyMemberSpecies(level); ret = new EnemyPokemon(this.scene, species, level); - }, this.config.staticParty ? this.config.getDerivedType() + ((index + 1) << 8) : this.scene.currentBattle.waveIndex + (this.config.getDerivedType() << 10) + ((index + 1) << 8)); + }, this.config.hasStaticParty ? this.config.getDerivedType() + ((index + 1) << 8) : this.scene.currentBattle.waveIndex + (this.config.getDerivedType() << 10) + ((index + 1) << 8)); return ret; } @@ -189,6 +189,21 @@ export default class Trainer extends Phaser.GameObjects.Container { const maxScorePartyMemberIndexes = partyMemberScores.filter(pms => pms[1] === sortedPartyMemberScores[0][1]).map(pms => pms[0]); return maxScorePartyMemberIndexes[Utils.randSeedInt(maxScorePartyMemberIndexes.length)]; } + + getPartyMemberModifierChanceMultiplier(index: integer): number { + switch (this.getPartyTemplate().getStrength(index)) { + case TrainerPartyMemberStrength.WEAKER: + return 0.75; + case TrainerPartyMemberStrength.WEAK: + return 0.675; + case TrainerPartyMemberStrength.AVERAGE: + return 0.5625; + case TrainerPartyMemberStrength.STRONG: + return 0.45; + case TrainerPartyMemberStrength.STRONGER: + return 0.375; + } + } loadAssets(): Promise { return this.config.loadAssets(this.scene, this.female);