2023-11-04 23:46:48 +00:00
|
|
|
import BattleScene from "./battle-scene";
|
2023-05-18 16:11:06 +01:00
|
|
|
import { EnemyPokemon, PlayerPokemon, QueuedMove } from "./pokemon";
|
|
|
|
import { Command } from "./ui/command-ui-handler";
|
2023-03-31 21:04:39 +01:00
|
|
|
import * as Utils from "./utils";
|
2023-10-07 21:08:33 +01:00
|
|
|
import Trainer from "./trainer";
|
2023-10-10 01:20:02 +01:00
|
|
|
import { Species } from "./data/species";
|
|
|
|
import { Moves } from "./data/move";
|
2023-11-04 23:46:48 +00:00
|
|
|
import { TrainerType } from "./data/trainer-type";
|
|
|
|
import { GameMode } from "./game-mode";
|
2023-10-07 21:08:33 +01:00
|
|
|
|
|
|
|
export enum BattleType {
|
|
|
|
WILD,
|
|
|
|
TRAINER
|
|
|
|
}
|
2023-03-31 04:02:35 +01:00
|
|
|
|
2023-05-18 16:11:06 +01:00
|
|
|
export enum BattlerIndex {
|
2023-11-27 16:42:03 +00:00
|
|
|
ATTACKER = -1,
|
2023-05-18 16:11:06 +01:00
|
|
|
PLAYER,
|
|
|
|
PLAYER_2,
|
|
|
|
ENEMY,
|
|
|
|
ENEMY_2
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface TurnCommand {
|
|
|
|
command: Command;
|
|
|
|
cursor?: integer;
|
|
|
|
move?: QueuedMove;
|
|
|
|
targets?: BattlerIndex[];
|
2023-10-27 22:43:53 +01:00
|
|
|
skip?: boolean;
|
2023-05-18 16:11:06 +01:00
|
|
|
args?: any[];
|
|
|
|
};
|
|
|
|
|
|
|
|
interface TurnCommands {
|
|
|
|
[key: integer]: TurnCommand
|
|
|
|
}
|
|
|
|
|
|
|
|
export default class Battle {
|
2023-03-31 04:02:35 +01:00
|
|
|
public waveIndex: integer;
|
2023-10-07 21:08:33 +01:00
|
|
|
public battleType: BattleType;
|
|
|
|
public trainer: Trainer;
|
2023-05-18 16:11:06 +01:00
|
|
|
public enemyLevels: integer[];
|
2023-10-07 21:08:33 +01:00
|
|
|
public enemyParty: EnemyPokemon[];
|
2023-10-23 18:48:56 +01:00
|
|
|
public seenEnemyPartyMemberIds: Set<integer>;
|
2023-05-18 16:11:06 +01:00
|
|
|
public double: boolean;
|
2023-10-18 23:01:15 +01:00
|
|
|
public started: boolean;
|
2023-04-22 00:30:04 +01:00
|
|
|
public turn: integer;
|
2023-05-18 16:11:06 +01:00
|
|
|
public turnCommands: TurnCommands;
|
2023-03-31 04:02:35 +01:00
|
|
|
public playerParticipantIds: Set<integer> = new Set<integer>();
|
2023-05-07 22:05:19 +01:00
|
|
|
public escapeAttempts: integer = 0;
|
2023-10-10 01:20:02 +01:00
|
|
|
public lastMove: Moves;
|
2023-03-31 04:02:35 +01:00
|
|
|
|
2023-10-07 21:08:33 +01:00
|
|
|
constructor(waveIndex: integer, battleType: BattleType, trainer: Trainer, double: boolean) {
|
2023-03-31 04:02:35 +01:00
|
|
|
this.waveIndex = waveIndex;
|
2023-10-07 21:08:33 +01:00
|
|
|
this.battleType = battleType;
|
|
|
|
this.trainer = trainer;
|
2023-10-18 23:01:15 +01:00
|
|
|
this.enemyLevels = battleType !== BattleType.TRAINER
|
|
|
|
? new Array(double ? 2 : 1).fill(null).map(() => this.getLevelForWave())
|
|
|
|
: trainer.getPartyLevels(this.waveIndex);
|
2023-10-07 21:08:33 +01:00
|
|
|
this.enemyParty = [];
|
2023-10-23 18:48:56 +01:00
|
|
|
this.seenEnemyPartyMemberIds = new Set<integer>();
|
2023-05-18 16:11:06 +01:00
|
|
|
this.double = double;
|
|
|
|
this.turn = 0;
|
2023-10-18 23:01:15 +01:00
|
|
|
this.started = false;
|
2023-03-31 04:02:35 +01:00
|
|
|
}
|
|
|
|
|
2023-10-07 21:08:33 +01:00
|
|
|
private getLevelForWave(): integer {
|
2023-04-19 19:07:38 +01:00
|
|
|
let baseLevel = 1 + this.waveIndex / 2 + Math.pow(this.waveIndex / 25, 2);
|
2023-10-29 20:05:17 +00:00
|
|
|
const bossMultiplier = 1.2;
|
2023-03-31 21:04:39 +01:00
|
|
|
|
2023-04-26 21:07:29 +01:00
|
|
|
if (!(this.waveIndex % 10)) {
|
2023-10-27 02:42:53 +01:00
|
|
|
const ret = Math.floor(baseLevel * bossMultiplier);
|
|
|
|
if (this.waveIndex === 200 || !(this.waveIndex % 250))
|
|
|
|
return Math.ceil(ret / 25) * 25;
|
2023-10-29 20:05:17 +00:00
|
|
|
return ret + Math.round(Phaser.Math.RND.realInRange(-1, 1) * Math.floor(this.waveIndex / 10));
|
2023-04-26 21:07:29 +01:00
|
|
|
}
|
2023-03-31 21:04:39 +01:00
|
|
|
|
|
|
|
const deviation = 10 / this.waveIndex;
|
|
|
|
|
2023-10-29 20:05:17 +00:00
|
|
|
return Math.max(Math.round(baseLevel + Math.abs(Utils.randSeedGauss(deviation))), 1);
|
2023-03-31 21:04:39 +01:00
|
|
|
}
|
|
|
|
|
2023-05-18 16:11:06 +01:00
|
|
|
getBattlerCount(): integer {
|
|
|
|
return this.double ? 2 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
incrementTurn(scene: BattleScene): void {
|
2023-04-22 00:30:04 +01:00
|
|
|
this.turn++;
|
2023-05-18 16:11:06 +01:00
|
|
|
this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ]));
|
2023-04-22 00:30:04 +01:00
|
|
|
}
|
|
|
|
|
2023-03-31 21:04:39 +01:00
|
|
|
addParticipant(playerPokemon: PlayerPokemon): void {
|
2023-03-31 04:02:35 +01:00
|
|
|
this.playerParticipantIds.add(playerPokemon.id);
|
|
|
|
}
|
|
|
|
|
2023-03-31 21:04:39 +01:00
|
|
|
removeFaintedParticipant(playerPokemon: PlayerPokemon): void {
|
2023-03-31 04:02:35 +01:00
|
|
|
this.playerParticipantIds.delete(playerPokemon.id);
|
|
|
|
}
|
2023-10-10 01:20:02 +01:00
|
|
|
|
2023-11-04 23:46:48 +00:00
|
|
|
getBgmOverride(scene: BattleScene): string {
|
2023-10-10 01:20:02 +01:00
|
|
|
const battlers = this.enemyParty.slice(0, this.getBattlerCount());
|
2023-10-18 23:01:15 +01:00
|
|
|
if (this.battleType === BattleType.TRAINER) {
|
2023-10-20 16:38:41 +01:00
|
|
|
if (!this.started && this.trainer.config.encounterBgm && this.trainer.config.encounterMessages.length)
|
2023-10-18 23:01:15 +01:00
|
|
|
return `encounter_${this.trainer.getEncounterBgm()}`;
|
|
|
|
return this.trainer.getBattleBgm();
|
|
|
|
}
|
2023-10-10 01:20:02 +01:00
|
|
|
for (let pokemon of battlers) {
|
|
|
|
if (pokemon.species.speciesId === Species.ETERNATUS)
|
|
|
|
return 'battle_final';
|
2023-10-18 23:01:15 +01:00
|
|
|
if (pokemon.species.legendary || pokemon.species.pseudoLegendary || pokemon.species.mythical) {
|
2023-10-10 01:20:02 +01:00
|
|
|
if (pokemon.species.speciesId === Species.KYUREM)
|
|
|
|
return 'battle_legendary_z';
|
2023-10-18 23:01:15 +01:00
|
|
|
if (pokemon.species.legendary)
|
|
|
|
return 'battle_legendary_rz';
|
2023-10-10 01:20:02 +01:00
|
|
|
return 'battle_legendary';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-04 23:46:48 +00:00
|
|
|
if (scene.gameMode === GameMode.CLASSIC && this.waveIndex <= 4)
|
2023-10-18 23:01:15 +01:00
|
|
|
return 'battle_wild';
|
|
|
|
|
2023-10-10 01:20:02 +01:00
|
|
|
return null;
|
|
|
|
}
|
2023-10-18 23:01:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export class FixedBattle extends Battle {
|
|
|
|
constructor(scene: BattleScene, waveIndex: integer, config: FixedBattleConfig) {
|
|
|
|
super(waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer(scene) : null, config.double);
|
|
|
|
if (config.getEnemyParty)
|
|
|
|
this.enemyParty = config.getEnemyParty(scene);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type GetTrainerFunc = (scene: BattleScene) => Trainer;
|
|
|
|
type GetEnemyPartyFunc = (scene: BattleScene) => EnemyPokemon[];
|
|
|
|
|
|
|
|
export class FixedBattleConfig {
|
|
|
|
public battleType: BattleType;
|
|
|
|
public double: boolean;
|
|
|
|
public getTrainer: GetTrainerFunc;
|
|
|
|
public getEnemyParty: GetEnemyPartyFunc;
|
|
|
|
|
|
|
|
setBattleType(battleType: BattleType): FixedBattleConfig {
|
|
|
|
this.battleType = battleType;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
setDouble(double: boolean): FixedBattleConfig {
|
|
|
|
this.double = double;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
setGetTrainerFunc(getTrainerFunc: GetTrainerFunc): FixedBattleConfig {
|
|
|
|
this.getTrainer = getTrainerFunc;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
setGetEnemyPartyFunc(getEnemyPartyFunc: GetEnemyPartyFunc): FixedBattleConfig {
|
|
|
|
this.getEnemyParty = getEnemyPartyFunc;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
interface FixedBattleConfigs {
|
|
|
|
[key: integer]: FixedBattleConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
export const fixedBattles: FixedBattleConfigs = {
|
|
|
|
[5]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
2023-10-20 16:38:41 +01:00
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.YOUNGSTER, !!Utils.randSeedInt(2))),
|
2023-10-20 03:01:12 +01:00
|
|
|
[8]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
2023-10-18 23:01:15 +01:00
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL, true)),
|
|
|
|
[25]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_2, true)),
|
|
|
|
[55]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_3, true)),
|
|
|
|
[95]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, true)),
|
|
|
|
[145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_5, true)),
|
2023-10-20 16:38:41 +01:00
|
|
|
[186]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.SHAUNTAL)),
|
|
|
|
[187]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.MARSHAL)),
|
|
|
|
[188]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.GRIMSLEY)),
|
|
|
|
[189]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.CAITLIN)),
|
|
|
|
[190]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, Phaser.Math.RND.pick([ TrainerType.BLUE, TrainerType.RED, TrainerType.LANCE, TrainerType.STEVEN, TrainerType.WALLACE, TrainerType.CYNTHIA, TrainerType.IRIS, TrainerType.ALDER, TrainerType.CYNTHIA ]))),
|
2023-10-24 04:20:05 +01:00
|
|
|
[195]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
2023-10-18 23:01:15 +01:00
|
|
|
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_6, true))
|
2023-03-31 04:02:35 +01:00
|
|
|
}
|