Add WiP trainer logic

This commit is contained in:
Flashfyre committed 2023-10-07 16:08:33 -04:00
1 parent 798f6cc53f
commit 48797cb20f
151 files changed
+114315 -129

No files matched your search

+168 -104
View File
@@ -28,7 +28,7 @@ import { ArenaTagType, ArenaTrapTag, TrickRoomTag } from "./data/arena-tag";
import { CheckTrappedAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, SuppressWeatherEffectAbAttr, applyCheckTrappedAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability";
import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./arena";
import { BattlerIndex, TurnCommand } from "./battle";
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
import { GameMode } from "./game-mode";
export class CheckLoadPhase extends BattlePhase {
@@ -74,13 +74,15 @@ export class CheckLoadPhase extends BattlePhase {
} else
this.scene.arena.playBgm();
const availablePartyMembers = this.scene.getParty().filter(p => !p.isFainted()).length;
this.scene.pushPhase(new EncounterPhase(this.scene, this.loaded));
this.scene.pushPhase(new SummonPhase(this.scene, 0));
if (this.scene.currentBattle.double && this.scene.getParty().filter(p => !p.isFainted()).length > 1)
if (this.scene.currentBattle.double && availablePartyMembers > 1)
this.scene.pushPhase(new SummonPhase(this.scene, 1));
if (this.scene.currentBattle.waveIndex > 1) {
this.scene.pushPhase(new CheckSwitchPhase(this.scene, 0, this.scene.currentBattle.double));
if (this.scene.currentBattle.double)
if (this.scene.currentBattle.double && availablePartyMembers > 1)
this.scene.pushPhase(new CheckSwitchPhase(this.scene, 1, this.scene.currentBattle.double));
}
@@ -175,18 +177,44 @@ export abstract class PokemonPhase extends FieldPhase {
export abstract class PartyMemberPokemonPhase extends FieldPhase {
protected partyMemberIndex: integer;
protected fieldIndex: integer;
protected player: boolean;
constructor(scene: BattleScene, partyMemberIndex: integer) {
constructor(scene: BattleScene, partyMemberIndex: integer, player: boolean) {
super(scene);
this.partyMemberIndex = partyMemberIndex;
this.fieldIndex = partyMemberIndex < this.scene.currentBattle.getBattlerCount()
? partyMemberIndex
: -1;
this.player = player;
}
getPokemon() {
return this.scene.getParty()[this.partyMemberIndex];
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;
}
}
@@ -211,26 +239,38 @@ export class EncounterPhase extends BattlePhase {
battle.enemyLevels.forEach((level, e) => {
const enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true);
if (!this.loaded)
battle.enemyField[e] = new EnemyPokemon(this.scene, enemySpecies, level);
const enemyPokemon = this.scene.getEnemyField()[e];
enemyPokemon.resetSummonData();
this.scene.gameData.setPokemonSeen(enemyPokemon);
battle.enemyParty[e] = new EnemyPokemon(this.scene, enemySpecies, level);
const enemyPokemon = this.scene.getEnemyParty()[e];
if (e < (battle.double ? 2 : 1)) {
enemyPokemon.setX(-66 + enemyPokemon.getFieldPositionOffset()[0]);
enemyPokemon.resetSummonData();
this.scene.gameData.setPokemonSeen(enemyPokemon);
}
loadEnemyAssets.push(enemyPokemon.loadAssets());
console.log(enemyPokemon.species.name, enemyPokemon.species.speciesId, enemyPokemon.stats);
});
if (battle.battleType === BattleType.TRAINER)
loadEnemyAssets.push(battle.trainer.loadAssets());
Promise.all(loadEnemyAssets).then(() => {
battle.enemyField.forEach((enemyPokemon, e) => {
this.scene.field.add(enemyPokemon);
const playerPokemon = this.scene.getPlayerPokemon();
if (playerPokemon.visible)
this.scene.field.moveBelow(enemyPokemon as Pokemon, playerPokemon);
enemyPokemon.tint(0, 0.5);
if (battle.enemyField.length > 1)
enemyPokemon.setFieldPosition(e ? FieldPosition.RIGHT : FieldPosition.LEFT);
battle.enemyParty.forEach((enemyPokemon, e) => {
if (e < (battle.double ? 2 : 1)) {
if (battle.battleType === BattleType.WILD) {
this.scene.field.add(enemyPokemon);
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);
}
});
if (!this.loaded) {
@@ -256,23 +296,47 @@ export class EncounterPhase extends BattlePhase {
const enemyField = this.scene.getEnemyField();
this.scene.tweens.add({
targets: [ this.scene.arenaEnemy, enemyField, this.scene.arenaPlayer, this.scene.trainer ].flat(),
x: (_target, _key, value, fieldIndex: integer) => fieldIndex < 1 + (enemyField.length) ? value + 300 : value - 300,
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,
duration: 2000,
onComplete: () => {
enemyField.forEach(enemyPokemon => {
enemyPokemon.untint(100, 'Sine.easeOut');
enemyPokemon.cry();
enemyPokemon.showInfo();
});
const text = enemyField.length === 1
? `A wild ${enemyField[0].name} appeared!`
: `A wild ${enemyField[0].name}\nand ${enemyField[1].name} appeared!`;
this.scene.ui.showText(text, null, () => this.end(), 1500);
}
onComplete: () => this.doEncounterCommon()
});
}
doEncounterCommon() {
const enemyField = this.scene.getEnemyField();
if (this.scene.currentBattle.battleType === BattleType.WILD) {
enemyField.forEach(enemyPokemon => {
enemyPokemon.untint(100, 'Sine.easeOut');
enemyPokemon.cry();
enemyPokemon.showInfo();
});
let text = enemyField.length === 1
? `A wild ${enemyField[0].name} appeared!`
: `A wild ${enemyField[0].name}\nand ${enemyField[1].name} appeared!`;
this.scene.ui.showText(text, null, () => this.end(), 1500);
} else if (this.scene.currentBattle.battleType === BattleType.TRAINER) {
this.scene.currentBattle.trainer.untint(100, 'Sine.easeOut');
this.scene.currentBattle.trainer.playAnim();
const text = `${this.scene.currentBattle.trainer.getName()}\nwould like to battle!`;
this.scene.ui.showText(text, null, () => {
this.scene.tweens.add({
targets: this.scene.currentBattle.trainer,
x: '+=16',
y: '-=16',
alpha: 0,
ease: 'Sine.easeInOut',
duration: 750
});
this.scene.unshiftPhase(new SummonPhase(this.scene, 0, false));
if (this.scene.currentBattle.double)
this.scene.unshiftPhase(new SummonPhase(this.scene, 1, false));
this.end();
}, 1500);
}
}
end() {
const enemyField = this.scene.getEnemyField();
@@ -298,22 +362,15 @@ export class NextEncounterPhase extends EncounterPhase {
doEncounter(): void {
const enemyField = this.scene.getEnemyField();
this.scene.tweens.add({
targets: [ this.scene.arenaEnemy, this.scene.arenaNextEnemy, enemyField ].flat(),
targets: [ this.scene.arenaEnemy, this.scene.arenaNextEnemy, this.scene.currentBattle.trainer, enemyField ].flat(),
x: '+=300',
duration: 2000,
onComplete: () => {
this.scene.arenaEnemy.setX(this.scene.arenaNextEnemy.x);
this.scene.arenaEnemy.setAlpha(1);
this.scene.arenaNextEnemy.setX(this.scene.arenaNextEnemy.x - 300);
enemyField.forEach(enemyPokemon => {
enemyPokemon.untint(100, 'Sine.easeOut');
enemyPokemon.cry();
enemyPokemon.showInfo();
});
const text = enemyField.length === 1
? `A wild ${enemyField[0].name} appeared!`
: `A wild ${enemyField[0].name}\nand ${enemyField[1].name} appeared!`;
this.scene.ui.showText(text, null, () => this.end(), 1500);
this.doEncounterCommon();
}
});
}
@@ -332,17 +389,7 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase {
targets: [ this.scene.arenaEnemy, enemyField ].flat(),
x: '+=300',
duration: 2000,
onComplete: () => {
enemyField.forEach(enemyPokemon => {
enemyPokemon.untint(100, 'Sine.easeOut');
enemyPokemon.cry();
enemyPokemon.showInfo();
});
const text = enemyField.length === 1
? `A wild ${enemyField[0].name} appeared!`
: `A wild ${enemyField[0].name}\nand ${enemyField[1].name} appeared!`;
this.scene.ui.showText(text, null, () => this.end(), 1500);
}
onComplete: () => this.doEncounterCommon()
});
}
}
@@ -461,8 +508,8 @@ export class SwitchBiomePhase extends BattlePhase {
}
export class SummonPhase extends PartyMemberPokemonPhase {
constructor(scene: BattleScene, fieldIndex: integer) {
super(scene, fieldIndex);
constructor(scene: BattleScene, fieldIndex: integer, player?: boolean) {
super(scene, fieldIndex, player !== undefined ? player : true);
}
start() {
@@ -474,78 +521,89 @@ export class SummonPhase extends PartyMemberPokemonPhase {
preSummon(): void {
const partyMember = this.getPokemon();
if (partyMember.isFainted()) {
const party = this.scene.getParty();
const party = this.getParty();
const nonFaintedIndex = party.slice(this.partyMemberIndex).findIndex(p => !p.isFainted()) + this.partyMemberIndex;
const nonFaintedPartyMember = party[nonFaintedIndex];
party[nonFaintedIndex] = partyMember;
party[this.partyMemberIndex] = nonFaintedPartyMember;
}
this.scene.ui.showText(`Go! ${this.getPokemon().name}!`);
this.scene.trainer.play('trainer_m_pb');
this.scene.tweens.add({
targets: this.scene.trainer,
x: -36,
duration: 1000
});
this.scene.time.delayedCall(750, () => this.summon());
if (this.player) {
this.scene.ui.showText(`Go! ${this.getPokemon().name}!`);
if (this.player)
this.scene.trainer.play('trainer_m_pb');
this.scene.tweens.add({
targets: this.scene.trainer,
x: -36,
duration: 1000
});
this.scene.time.delayedCall(750, () => this.summon());
} else
this.scene.ui.showText(`${this.scene.currentBattle.trainer.getName()} sent out\n${this.getPokemon().name}!`, null, () => this.summon());
}
summon(): void {
const pokeball = this.scene.add.sprite(36, 80, 'pb', 'pb');
const pokeball = this.scene.add.sprite(this.player ? 36 : 248, this.player ? 80 : 44, 'pb', 'pb');
pokeball.setVisible(false);
pokeball.setOrigin(0.5, 0.625);
this.scene.field.add(pokeball);
const playerPokemon = this.getPokemon();
const pokemon = this.getPokemon();
if (this.fieldIndex === 1)
playerPokemon.setFieldPosition(FieldPosition.RIGHT, 0);
else
playerPokemon.setFieldPosition(!this.scene.currentBattle.double ? FieldPosition.CENTER : FieldPosition.LEFT);
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);
}
const fpOffset = playerPokemon.getFieldPositionOffset();
const fpOffset = pokemon.getFieldPositionOffset();
pokeball.setVisible(true);
this.scene.tweens.add({
targets: pokeball,
duration: 650,
x: 100 + fpOffset[0]
x: (this.player ? 100 : 236) + fpOffset[0]
});
this.scene.tweens.add({
targets: pokeball,
duration: 150,
ease: 'Cubic.easeOut',
y: 70 + fpOffset[1],
y: (this.player ? 70 : 34) + fpOffset[1],
onComplete: () => {
this.scene.tweens.add({
targets: pokeball,
duration: 500,
ease: 'Cubic.easeIn',
angle: 1440,
y: 132 + fpOffset[1],
y: (this.player ? 132 : 86) + fpOffset[1],
onComplete: () => {
this.scene.sound.play('pb_rel');
pokeball.destroy();
this.scene.add.existing(playerPokemon);
this.scene.field.add(playerPokemon);
playerPokemon.showInfo();
playerPokemon.playAnim();
playerPokemon.setVisible(true);
playerPokemon.setScale(0.5);
playerPokemon.tint(getPokeballTintColor(playerPokemon.pokeball));
playerPokemon.untint(250, 'Sine.easeIn');
this.scene.add.existing(pokemon);
this.scene.field.add(pokemon);
if (!this.player) {
const playerPokemon = this.scene.getPlayerPokemon() as Pokemon;
if (playerPokemon.visible)
this.scene.field.moveBelow(pokemon, playerPokemon);
}
pokemon.showInfo();
pokemon.playAnim();
pokemon.setVisible(true);
pokemon.setScale(0.5);
pokemon.tint(getPokeballTintColor(pokemon.pokeball));
pokemon.untint(250, 'Sine.easeIn');
this.scene.tweens.add({
targets: playerPokemon,
targets: pokemon,
duration: 250,
ease: 'Sine.easeIn',
scale: 1,
onComplete: () => {
playerPokemon.cry();
playerPokemon.getSprite().clearTint();
playerPokemon.resetSummonData();
pokemon.cry();
pokemon.getSprite().clearTint();
pokemon.resetSummonData();
this.scene.time.delayedCall(1000, () => this.end());
}
});
@@ -582,10 +640,10 @@ export class SwitchSummonPhase extends SummonPhase {
private doReturn: boolean;
private batonPass: boolean;
private lastPokemon: PlayerPokemon;
private lastPokemon: Pokemon;
constructor(scene: BattleScene, fieldIndex: integer, slotIndex: integer, doReturn: boolean, batonPass: boolean) {
super(scene, fieldIndex);
constructor(scene: BattleScene, fieldIndex: integer, slotIndex: integer, doReturn: boolean, batonPass: boolean, player?: boolean) {
super(scene, fieldIndex, player !== undefined ? player : true);
this.slotIndex = slotIndex;
this.doReturn = doReturn;
@@ -598,30 +656,30 @@ export class SwitchSummonPhase extends SummonPhase {
return;
}
const playerPokemon = this.getPokemon();
const pokemon = this.getPokemon();
if (!this.batonPass)
this.scene.getEnemyField().forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(playerPokemon.id));
this.scene.getEnemyField().forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(pokemon.id));
this.scene.ui.showText(`Come back, ${playerPokemon.name}!`);
this.scene.ui.showText(this.player ? `Come back, ${pokemon.name}!` : `${this.scene.currentBattle.trainer.getName()}\nwithdrew ${pokemon.name}!`);
this.scene.sound.play('pb_rel');
playerPokemon.hideInfo();
playerPokemon.tint(getPokeballTintColor(playerPokemon.pokeball), 1, 250, 'Sine.easeIn');
pokemon.hideInfo();
pokemon.tint(getPokeballTintColor(pokemon.pokeball), 1, 250, 'Sine.easeIn');
this.scene.tweens.add({
targets: playerPokemon,
targets: pokemon,
duration: 250,
ease: 'Sine.easeIn',
scale: 0.5,
onComplete: () => {
playerPokemon.setVisible(false);
this.scene.field.remove(playerPokemon);
pokemon.setVisible(false);
this.scene.field.remove(pokemon);
this.scene.time.delayedCall(750, () => this.switchAndSummon());
}
});
}
switchAndSummon() {
const party = this.scene.getParty();
const party = this.getParty();
const switchedPokemon = party[this.slotIndex];
this.lastPokemon = this.getPokemon();
if (this.batonPass && switchedPokemon) {
@@ -635,7 +693,7 @@ export class SwitchSummonPhase extends SummonPhase {
if (switchedPokemon) {
party[this.slotIndex] = this.lastPokemon;
party[this.fieldIndex] = switchedPokemon;
this.scene.ui.showText(`Go! ${switchedPokemon.name}!`);
this.scene.ui.showText(this.player ? `Go! ${switchedPokemon.name}!` : `${this.scene.currentBattle.trainer.getName()} sent out\n${this.getPokemon().name}!`);
this.summon();
} else
this.end();
@@ -1800,8 +1858,14 @@ export class FaintPhase extends PokemonPhase {
this.scene.unshiftPhase(new SwitchPhase(this.scene, this.fieldIndex, true, false));
else if (nonFaintedPartyMemberCount === 1 && this.scene.currentBattle.double)
this.scene.unshiftPhase(new ToggleDoublePositionPhase(this.scene, true));
} else
} else {
this.scene.unshiftPhase(new VictoryPhase(this.scene, this.battlerIndex));
if (this.scene.currentBattle.battleType === BattleType.TRAINER) {
const nonFaintedPartyMemberCount = this.scene.getParty().filter(p => !p.isFainted()).length;
if (nonFaintedPartyMemberCount >= this.scene.currentBattle.getBattlerCount())
this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, this.fieldIndex, this.scene.currentBattle.trainer.getNextSummonIndex(), false, false, false));
}
}
pokemon.lapseTags(BattlerTagLapseType.FAINT);
this.scene.getField().filter(p => p !== pokemon && p?.isActive(true)).forEach(p => p.removeTagsBySourceId(pokemon.id));
@@ -1897,7 +1961,7 @@ export class VictoryPhase extends PokemonPhase {
}
}
if (!this.scene.getEnemyField().filter(p => !p?.isFainted(true)).length) {
if (!this.scene.getEnemyParty().filter(p => !p?.isFainted(true)).length) {
this.scene.pushPhase(new BattleEndPhase(this.scene));
if (this.scene.gameMode === GameMode.ENDLESS || this.scene.currentBattle.waveIndex < this.scene.finalWave) {
this.scene.pushPhase(new SelectModifierPhase(this.scene));
@@ -1999,7 +2063,7 @@ export class SwitchPhase extends BattlePhase {
}
}
export class ExpPhase extends PartyMemberPokemonPhase {
export class ExpPhase extends PlayerPartyMemberPokemonPhase {
private expValue: number;
constructor(scene: BattleScene, partyMemberIndex: integer, expValue: number) {
@@ -2027,7 +2091,7 @@ export class ExpPhase extends PartyMemberPokemonPhase {
}
}
export class ShowPartyExpBarPhase extends PartyMemberPokemonPhase {
export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase {
private expValue: number;
constructor(scene: BattleScene, partyMemberIndex: integer, expValue: number) {
@@ -2074,7 +2138,7 @@ export class HidePartyExpBarPhase extends BattlePhase {
}
}
export class LevelUpPhase extends PartyMemberPokemonPhase {
export class LevelUpPhase extends PlayerPartyMemberPokemonPhase {
private lastLevel: integer;
private level: integer;
@@ -2105,7 +2169,7 @@ export class LevelUpPhase extends PartyMemberPokemonPhase {
}
}
export class LearnMovePhase extends PartyMemberPokemonPhase {
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
private moveId: Moves;
constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Moves) {
+43 -9
View File
@@ -1,7 +1,7 @@
import Phaser from 'phaser';
import { Biome } from './data/biome';
import UI from './ui/ui';
import { EncounterPhase, SummonPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, CheckLoadPhase, TurnInitPhase, ReturnPhase, ToggleDoublePositionPhase, CheckSwitchPhase, PostSummonPhase, LevelCapPhase } from './battle-phases';
import { EncounterPhase, SummonPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, CheckLoadPhase, TurnInitPhase, ReturnPhase, ToggleDoublePositionPhase, CheckSwitchPhase, LevelCapPhase } from './battle-phases';
import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon';
import PokemonSpecies, { allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species';
import * as Utils from './utils';
@@ -19,10 +19,13 @@ import { Moves, initMoves } from './data/move';
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave } from './modifier/modifier-type';
import AbilityBar from './ui/ability-bar';
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, applyAbAttrs, initAbilities } from './data/ability';
import Battle from './battle';
import Battle, { BattleType } from './battle';
import { GameMode } from './game-mode';
import SpritePipeline from './pipelines/sprite';
import PartyExpBar from './ui/party-exp-bar';
import { TrainerType, trainerConfigs } from './data/trainer-type';
import Trainer from './trainer';
import TrainerData from './system/trainer-data';
const enableAuto = true;
const quickStart = false;
@@ -232,6 +235,13 @@ export default class BattleScene extends Phaser.Scene {
this.loadImage('trainer_m', 'trainer');
this.loadAtlas('trainer_m_pb', 'trainer');
Utils.getEnumValues(TrainerType).map(tt => {
const config = trainerConfigs[tt];
this.loadAtlas(config.getKey(), 'trainer');
if (config.isDouble)
this.loadAtlas(config.getKey(true), 'trainer');
});
// Load pokemon-related images
this.loadImage(`pkmn__back__sub`, 'pokemon/back', 'sub.png');
this.loadImage(`pkmn__sub`, 'pokemon', 'sub.png');
@@ -465,12 +475,17 @@ export default class BattleScene extends Phaser.Scene {
return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1));
}
getEnemyParty(): EnemyPokemon[] {
return this.currentBattle?.enemyParty || [];
}
getEnemyPokemon(): EnemyPokemon {
return this.getEnemyField().find(p => p.isActive());
}
getEnemyField(): EnemyPokemon[] {
return this.currentBattle?.enemyField || [];
const party = this.getEnemyParty();
return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1));
}
getField(): Pokemon[] {
@@ -517,15 +532,32 @@ export default class BattleScene extends Phaser.Scene {
this.trainer.setPosition(406, 132);
}
newBattle(waveIndex?: integer, double?: boolean): Battle {
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle {
let newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (startingWave - 1)) + 1);
let newDouble: boolean;
let newBattleType: BattleType;
let newTrainer: Trainer;
if (battleType === undefined)
newBattleType = BattleType.WILD;
else
newBattleType = battleType;
if (newBattleType === BattleType.TRAINER) {
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, TrainerType.SWIMMER, !!Utils.randInt(2));
this.field.add(newTrainer);
}
if (double === undefined && newWaveIndex > 1) {
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8);
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
this.getPlayerField().forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance));
newDouble = !Utils.randInt(doubleChance.value);
if (newBattleType === BattleType.WILD) {
const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8);
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
this.getPlayerField().forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance));
newDouble = !Utils.randInt(doubleChance.value);
} else if (newBattleType === BattleType.TRAINER) {
console.log(newTrainer, newTrainer.config);
newDouble = newTrainer.config.isDouble;
}
} else
newDouble = !!double;
@@ -533,7 +565,7 @@ export default class BattleScene extends Phaser.Scene {
const maxExpLevel = this.getMaxExpLevel();
this.currentBattle = new Battle(newWaveIndex, newDouble);
this.currentBattle = new Battle(newWaveIndex, newBattleType, newTrainer, newDouble);
this.currentBattle.incrementTurn(this);
if (!waveIndex) {
@@ -559,6 +591,8 @@ export default class BattleScene extends Phaser.Scene {
}
}
console.log(lastBattle, newDouble)
if ((lastBattle?.double || false) !== newDouble) {
const availablePartyMemberCount = this.getParty().filter(p => !p.isFainted()).length;
if (newDouble) {
+15 -5
View File
@@ -2,6 +2,12 @@ import BattleScene, { PokeballCounts } from "./battle-scene";
import { EnemyPokemon, PlayerPokemon, QueuedMove } from "./pokemon";
import { Command } from "./ui/command-ui-handler";
import * as Utils from "./utils";
import Trainer from "./trainer";
export enum BattleType {
WILD,
TRAINER
}
export enum BattlerIndex {
PLAYER,
@@ -24,8 +30,10 @@ interface TurnCommands {
export default class Battle {
public waveIndex: integer;
public battleType: BattleType;
public trainer: Trainer;
public enemyLevels: integer[];
public enemyField: EnemyPokemon[];
public enemyParty: EnemyPokemon[];
public double: boolean;
public turn: integer;
public turnCommands: TurnCommands;
@@ -33,15 +41,17 @@ export default class Battle {
public playerParticipantIds: Set<integer> = new Set<integer>();
public escapeAttempts: integer = 0;
constructor(waveIndex: integer, double: boolean) {
constructor(waveIndex: integer, battleType: BattleType, trainer: Trainer, double: boolean) {
this.waveIndex = waveIndex;
this.enemyLevels = new Array(double ? 2 : 1).fill(null).map(() => this.getLevelForWave());
this.enemyField = [];
this.battleType = battleType;
this.trainer = trainer;
this.enemyLevels = new Array(battleType !== BattleType.TRAINER ? double ? 2 : 1 : trainer.config.genPartySize()).fill(null).map(() => this.getLevelForWave());
this.enemyParty = [];
this.double = double;
this.turn = 0;
}
private getLevelForWave(): number {
private getLevelForWave(): integer {
let baseLevel = 1 + this.waveIndex / 2 + Math.pow(this.waveIndex / 25, 2);
if (!(this.waveIndex % 10)) {
+179
View File
@@ -0,0 +1,179 @@
import BattleScene from "../battle-scene";
import * as Utils from "../utils";
export enum TrainerType {
ACE_TRAINER = 1,
ARTIST,
BACKERS,
BACKPACKER,
BAKER,
BATTLE_GIRL,
BEAUTY,
BIKER,
BLACKBELT,
BREEDER,
CLERK,
CYCLIST,
DANCER,
DEPOT_AGENT,
DOCTOR,
FISHERMAN,
GENTLEMAN,
GUITARIST,
HARLEQUIN,
HIKER,
HOOLIGANS,
HOOPSTER,
INFIELDER,
JANITOR,
LADY,
LASS,
LINEBACKER,
MAID,
MUSICIAN,
NURSE,
NURSERY_AIDE,
OFFICER,
PARASOL_LADY,
PILOT,
POKEFAN,
PRESCHOOLER,
PSYCHIC,
RANGER,
RICH_BOY,
ROUGHNECK,
SCIENTIST,
SMASHER,
SNOW_WORKER,
SOCIALITE,
STRIKER,
STUDENT,
SWIMMER,
TWINS,
VETERAN,
WAITER,
WORKER,
YOUNGSTER,
CYNTHIA
}
export class TrainerConfig {
public trainerType: TrainerType;
public hasGenders: boolean = false;
public isDouble: boolean = false;
constructor(trainerType: TrainerType) {
this.trainerType = trainerType;
}
public getKey(female?: boolean): string {
let ret = TrainerType[this.trainerType].toString().toLowerCase();
if (this.hasGenders)
ret += `_${female ? 'f' : 'm'}`;
return ret;
}
public setHasGenders(): TrainerConfig {
this.hasGenders = true;
return this;
}
public setDouble(): TrainerConfig {
this.isDouble = true;
return this;
}
public getName(): string {
return Utils.toPokemonUpperCase(TrainerType[this.trainerType].toString().replace(/\_/g, ' '));
}
public genPartySize(): integer {
// TODO
return this.isDouble ? 2 : 1;
}
loadAssets(scene: BattleScene, female: boolean): Promise<void> {
return new Promise(resolve => {
const trainerKey = this.getKey(female);
scene.loadAtlas(trainerKey, 'trainer');
scene.load.once(Phaser.Loader.Events.COMPLETE, () => {
const originalWarn = console.warn;
// Ignore warnings for missing frames, because there will be a lot
console.warn = () => {};
const frameNames = scene.anims.generateFrameNames(trainerKey, { zeroPad: 4, suffix: ".png", start: 1, end:24 });
console.warn = originalWarn;
scene.anims.create({
key: trainerKey,
frames: frameNames,
frameRate: 12,
repeat: -1
});
resolve();
});
if (!scene.load.isLoading())
scene.load.start();
});
}
}
let t = 0;
interface TrainerConfigs {
[key: integer]: TrainerConfig
}
export const trainerConfigs: TrainerConfigs = {
[TrainerType.ACE_TRAINER]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.ARTIST]: new TrainerConfig(++t),
[TrainerType.BACKERS]: new TrainerConfig(++t).setHasGenders().setDouble(),
[TrainerType.BACKPACKER]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.BAKER]: new TrainerConfig(++t),
[TrainerType.BATTLE_GIRL]: new TrainerConfig(++t),
[TrainerType.BEAUTY]: new TrainerConfig(++t),
[TrainerType.BIKER]: new TrainerConfig(++t),
[TrainerType.BLACKBELT]: new TrainerConfig(++t),
[TrainerType.BREEDER]: new TrainerConfig(++t).setHasGenders().setDouble(),
[TrainerType.CLERK]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.CYCLIST]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.DANCER]: new TrainerConfig(++t),
[TrainerType.DEPOT_AGENT]: new TrainerConfig(++t),
[TrainerType.DOCTOR]: new TrainerConfig(++t),
[TrainerType.FISHERMAN]: new TrainerConfig(++t),
[TrainerType.GENTLEMAN]: new TrainerConfig(++t),
[TrainerType.GUITARIST]: new TrainerConfig(++t),
[TrainerType.HARLEQUIN]: new TrainerConfig(++t),
[TrainerType.HIKER]: new TrainerConfig(++t),
[TrainerType.HOOLIGANS]: new TrainerConfig(++t).setDouble(),
[TrainerType.HOOPSTER]: new TrainerConfig(++t),
[TrainerType.INFIELDER]: new TrainerConfig(++t),
[TrainerType.JANITOR]: new TrainerConfig(++t),
[TrainerType.LADY]: new TrainerConfig(++t),
[TrainerType.LASS]: new TrainerConfig(++t),
[TrainerType.LINEBACKER]: new TrainerConfig(++t),
[TrainerType.MAID]: new TrainerConfig(++t),
[TrainerType.MUSICIAN]: new TrainerConfig(++t),
[TrainerType.NURSE]: new TrainerConfig(++t),
[TrainerType.NURSERY_AIDE]: new TrainerConfig(++t),
[TrainerType.OFFICER]: new TrainerConfig(++t),
[TrainerType.PARASOL_LADY]: new TrainerConfig(++t),
[TrainerType.PILOT]: new TrainerConfig(++t),
[TrainerType.POKEFAN]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.PRESCHOOLER]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.PSYCHIC]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.RANGER]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.RICH_BOY]: new TrainerConfig(++t),
[TrainerType.ROUGHNECK]: new TrainerConfig(++t),
[TrainerType.SCIENTIST]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.SMASHER]: new TrainerConfig(++t),
[TrainerType.SNOW_WORKER]: new TrainerConfig(++t),
[TrainerType.SOCIALITE]: new TrainerConfig(++t),
[TrainerType.STRIKER]: new TrainerConfig(++t),
[TrainerType.STUDENT]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.SWIMMER]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.TWINS]: new TrainerConfig(++t).setDouble(),
[TrainerType.VETERAN]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.WAITER]: new TrainerConfig(++t).setHasGenders(),
[TrainerType.WORKER]: new TrainerConfig(++t),
[TrainerType.YOUNGSTER]: new TrainerConfig(++t),
[TrainerType.CYNTHIA]: new TrainerConfig(++t),
}
+3 -3
View File
@@ -1,5 +1,5 @@
import BattleScene from "../battle-scene";
import Pokemon from "../pokemon";
import Trainer from "../trainer";
const spriteFragShader = `
#ifdef GL_FRAGMENT_PRECISION_HIGH
@@ -150,7 +150,7 @@ export default class SpritePipeline extends Phaser.Renderer.WebGL.Pipelines.Mult
const tone = data['tone'] as number[];
const hasShadow = data['hasShadow'] as boolean;
const position = sprite.parentContainer instanceof Pokemon
const position = sprite.parentContainer instanceof Pokemon || sprite.parentContainer instanceof Trainer
? [ sprite.parentContainer.x, sprite.parentContainer.y ]
: [ sprite.x, sprite.y ];
position[0] += -(sprite.width - sprite.frame.width) / 2 + sprite.frame.x;
@@ -176,7 +176,7 @@ export default class SpritePipeline extends Phaser.Renderer.WebGL.Pipelines.Mult
const hasShadow = sprite.pipelineData['hasShadow'] as boolean;
if (hasShadow) {
const baseY = (sprite.parentContainer instanceof Pokemon
const baseY = (sprite.parentContainer instanceof Pokemon || sprite.parentContainer instanceof Trainer
? sprite.parentContainer.y
: sprite.y + sprite.height / 2) * 6;
const bottomPadding = Math.ceil(sprite.height * 0.05) * 6;
+14 -2
View File
@@ -406,7 +406,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
getTypes(): Type[] {
const types = [];
if (this.summonData.types)
if (this.summonData?.types)
this.summonData.types.forEach(t => types.push(t));
else {
const speciesForm = this.getSpeciesForm();
@@ -447,6 +447,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return getTypeDamageMultiplier(moveType, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(moveType, types[1]) : 1) as TypeDamageMultiplier;
}
getMatchupScore(pokemon: Pokemon): number {
const types = this.getTypes();
const enemyTypes = pokemon.getTypes();
let atkScore = pokemon.getAttackMoveEffectiveness(types[0]);
let defScore = 1 / this.getAttackMoveEffectiveness(enemyTypes[0]);
if (types.length > 1)
atkScore *= pokemon.getAttackMoveEffectiveness(types[1]);
if (enemyTypes.length > 1)
defScore *= (1 / this.getAttackMoveEffectiveness(enemyTypes[1]));
return (atkScore + defScore) * (this.getHpRatio() + (1 - pokemon.getHpRatio()));
}
getEvolution(): SpeciesEvolution {
if (!pokemonEvolutions.hasOwnProperty(this.species.speciesId))
return null;
@@ -1142,7 +1154,7 @@ export class EnemyPokemon extends Pokemon {
public aiType: AiType;
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, dataSource?: PokemonData) {
super(scene, -66, 84, species, level, dataSource?.abilityIndex, dataSource ? dataSource.formIndex : scene.arena.getFormIndex(species),
super(scene, 236, 84, species, level, dataSource?.abilityIndex, dataSource ? dataSource.formIndex : scene.arena.getFormIndex(species),
dataSource?.gender, dataSource?.shiny, dataSource);
if (!dataSource) {
+23 -6
View File
@@ -11,6 +11,9 @@ import { PokemonHeldItemModifier } from "../modifier/modifier";
import ArenaData from "./arena-data";
import { Unlockables } from "./unlockables";
import { GameMode } from "../game-mode";
import { BattleType } from "../battle";
import TrainerData from "./trainer-data";
import { trainerConfigs } from "../data/trainer-type";
interface SystemSaveData {
trainerId: integer;
@@ -23,12 +26,15 @@ interface SystemSaveData {
interface SessionSaveData {
gameMode: GameMode;
party: PokemonData[];
enemyParty: PokemonData[];
enemyField: PokemonData[];
modifiers: PersistentModifierData[];
enemyModifiers: PersistentModifierData[];
arena: ArenaData;
pokeballCounts: PokeballCounts;
waveIndex: integer;
battleType: BattleType;
trainer: TrainerData;
timestamp: integer;
}
@@ -130,12 +136,14 @@ export class GameData {
const sessionData = {
gameMode: scene.gameMode,
party: scene.getParty().map(p => new PokemonData(p)),
enemyField: scene.getEnemyField().map(p => new PokemonData(p)),
enemyParty: scene.getEnemyParty().map(p => new PokemonData(p)),
modifiers: scene.findModifiers(() => true).map(m => new PersistentModifierData(m, true)),
enemyModifiers: scene.findModifiers(() => true, false).map(m => new PersistentModifierData(m, false)),
arena: new ArenaData(scene.arena),
pokeballCounts: scene.pokeballCounts,
waveIndex: scene.currentBattle.waveIndex,
battleType: scene.currentBattle.battleType,
trainer: scene.currentBattle.battleType == BattleType.TRAINER ? new TrainerData(scene.currentBattle.trainer) : null,
timestamp: new Date().getTime()
} as SessionSaveData;
@@ -157,13 +165,16 @@ export class GameData {
try {
const sessionData = JSON.parse(atob(localStorage.getItem('sessionData')), (k: string, v: any) => {
if (k === 'party' || k === 'enemyField') {
if (k === 'party' || k === 'enemyParty' || k === 'enemyField') {
const ret: PokemonData[] = [];
for (let pd of v)
ret.push(new PokemonData(pd));
return ret;
}
if (k === 'trainer')
return v ? new TrainerData(v) : null;
if (k === 'modifiers' || k === 'enemyModifiers') {
const player = k === 'modifiers';
const ret: PersistentModifierData[] = [];
@@ -198,12 +209,18 @@ export class GameData {
scene.pokeballCounts[key] = sessionData.pokeballCounts[key] || 0;
});
scene.newArena(sessionData.arena.biome, true);
const battle = scene.newBattle(sessionData.waveIndex, sessionData.enemyField.length > 1);
// TODO: Remove this
if (sessionData.enemyField)
sessionData.enemyParty = sessionData.enemyField;
sessionData.enemyField.forEach((enemyData, e) => {
scene.newArena(sessionData.arena.biome, true);
const battleType = sessionData.battleType || 0;
const battle = scene.newBattle(sessionData.waveIndex, battleType, sessionData.trainer, battleType === BattleType.TRAINER ? trainerConfigs[sessionData.trainer.trainerType].isDouble : sessionData.enemyParty.length > 1);
sessionData.enemyParty.forEach((enemyData, e) => {
const enemyPokemon = enemyData.toPokemon(scene) as EnemyPokemon;
battle.enemyField[e] = enemyPokemon;
battle.enemyParty[e] = enemyPokemon;
loadPokemonAssets.push(enemyPokemon.loadAssets());
});
+18
View File
@@ -0,0 +1,18 @@
import BattleScene from "../battle-scene";
import { TrainerType } from "../data/trainer-type";
import Trainer from "../trainer";
export default class TrainerData {
public trainerType: TrainerType;
public female: boolean;
constructor(source: Trainer | any) {
const sourceTrainer = source instanceof Trainer ? source as Trainer : null;
this.trainerType = sourceTrainer ? sourceTrainer.config.trainerType : source.trainerType;
this.female = source.female;
}
toTrainer(scene: BattleScene): Trainer {
return new Trainer(scene, this.trainerType, this.female);
}
}
+124
View File
@@ -0,0 +1,124 @@
import BattleScene from "./battle-scene";
import { TrainerConfig, TrainerType, trainerConfigs } from "./data/trainer-type";
import * as Utils from "./utils";
export default class Trainer extends Phaser.GameObjects.Container {
public config: TrainerConfig;
public female: boolean;
constructor(scene: BattleScene, trainerType: TrainerType, female?: boolean) {
super(scene, -72, 80);
this.config = trainerConfigs[trainerType];
this.female = female;
const getSprite = (hasShadow?: boolean) => {
const ret = this.scene.add.sprite(0, 0, this.getKey());
ret.setOrigin(0.5, 1);
ret.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: !!hasShadow });
return ret;
};
const sprite = getSprite(true);
const tintSprite = getSprite();
tintSprite.setVisible(false);
this.add(sprite);
this.add(tintSprite);
}
getKey(): string {
return this.config.getKey(this.female);
}
getName(): string {
return this.config.getName();
}
getNextSummonIndex(): integer {
const party = this.scene.getEnemyParty();
const nonFaintedPartyMembers = party.slice(this.scene.currentBattle.getBattlerCount()).filter(p => !p.isFainted());
const partyMemberScores = nonFaintedPartyMembers.map(p => {
const playerField = this.scene.getPlayerField();
let score = 0;
for (let playerPokemon of playerField)
score += p.getMatchupScore(playerPokemon);
score /= playerField.length;
return [ party.indexOf(p), score ];
});
const sortedPartyMemberScores = partyMemberScores.slice(0);
sortedPartyMemberScores.sort((a, b) => {
const scoreA = a[1];
const scoreB = b[1];
return scoreA < scoreB ? 1 : scoreA > scoreB ? -1 : 0;
});
const maxScorePartyMemberIndexes = partyMemberScores.filter(pms => pms[1] === sortedPartyMemberScores[0][1]).map(pms => pms[0]);
return maxScorePartyMemberIndexes[Utils.randInt(maxScorePartyMemberIndexes.length)];
}
loadAssets(): Promise<void> {
return this.config.loadAssets(this.scene, this.female);
}
playAnim(): void {
const trainerAnimConfig = {
key: this.scene.currentBattle.trainer.getKey(),
repeat: 0
};
this.getSprite().play(trainerAnimConfig);
this.getTintSprite().play(trainerAnimConfig);
}
getSprite(): Phaser.GameObjects.Sprite {
return this.getAt(0) as Phaser.GameObjects.Sprite;
}
getTintSprite(): Phaser.GameObjects.Sprite {
return this.getAt(1) as Phaser.GameObjects.Sprite;
}
tint(color: number, alpha?: number, duration?: integer, ease?: string): void {
const tintSprite = this.getTintSprite();
tintSprite.setTintFill(color);
tintSprite.setVisible(true);
if (duration) {
tintSprite.setAlpha(0);
this.scene.tweens.add({
targets: tintSprite,
alpha: alpha || 1,
duration: duration,
ease: ease || 'Linear'
});
} else
tintSprite.setAlpha(alpha);
}
untint(duration: integer, ease?: string): void {
const tintSprite = this.getTintSprite();
if (duration) {
this.scene.tweens.add({
targets: tintSprite,
alpha: 0,
duration: duration,
ease: ease || 'Linear',
onComplete: () => {
tintSprite.setVisible(false);
tintSprite.setAlpha(1);
}
});
} else {
tintSprite.setVisible(false);
tintSprite.setAlpha(1);
}
}
}
export default interface Trainer {
scene: BattleScene
}