Add session save data

pull/1/head
Flashfyre 2023-04-28 15:03:42 -04:00
parent fcd711673d
commit a2ecb4089d
12 changed files with 541 additions and 105 deletions

View File

@ -1,6 +1,7 @@
{
"compilerOptions": {
"target": "ES2019",
"target": "ES2020",
"module": "ES2020",
"moduleResolution": "node",
"checkJs": true,
"esModuleInterop": true

View File

@ -27,6 +27,56 @@ import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTrapTag, TrickRoomTag } from "./data/arena-tag";
import { PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAttr, SuppressWeatherEffectAbAttr, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability";
export class CheckLoadPhase extends BattlePhase {
private loaded: boolean;
constructor(scene: BattleScene) {
super(scene);
this.loaded = false;
}
start(): void {
if (!this.scene.gameData.hasSession()) {
this.end();
return;
}
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
this.scene.arena.playBgm();
this.scene.pushPhase(new EncounterPhase(this.scene, this.loaded));
this.scene.pushPhase(new SummonPhase(this.scene));
super.end();
}
}
export class SelectStarterPhase extends BattlePhase {
constructor(scene: BattleScene) {
super(scene);
@ -62,8 +112,12 @@ export class SelectStarterPhase extends BattlePhase {
}
export class EncounterPhase extends BattlePhase {
constructor(scene: BattleScene) {
private loaded: boolean;
constructor(scene: BattleScene, loaded?: boolean) {
super(scene);
this.loaded = !!loaded;
}
start() {
@ -73,7 +127,8 @@ export class EncounterPhase extends BattlePhase {
const battle = this.scene.currentBattle;
const enemySpecies = this.scene.randomSpecies(battle.waveIndex, battle.enemyLevel, true);
battle.enemyPokemon = new EnemyPokemon(this.scene, enemySpecies, battle.enemyLevel);
if (!this.loaded)
battle.enemyPokemon = new EnemyPokemon(this.scene, enemySpecies, battle.enemyLevel);
const enemyPokemon = this.scene.getEnemyPokemon();
enemyPokemon.resetSummonData();
@ -87,10 +142,16 @@ export class EncounterPhase extends BattlePhase {
this.scene.field.moveBelow(enemyPokemon, this.scene.getPlayerPokemon());
enemyPokemon.tint(0, 0.5);
regenerateModifierPoolThresholds(this.scene.getEnemyParty(), false);
this.scene.generateEnemyModifiers();
if (!this.loaded) {
regenerateModifierPoolThresholds(this.scene.getEnemyParty(), false);
this.scene.generateEnemyModifiers();
}
this.scene.ui.setMode(Mode.MESSAGE).then(() => this.doEncounter());
this.scene.ui.setMode(Mode.MESSAGE).then(() => {
if (!this.loaded)
this.scene.gameData.saveSession(this.scene);
this.doEncounter();
});
});
}
@ -989,7 +1050,7 @@ abstract class MoveEffectPhase extends PokemonPhase {
}
return rand <= this.move.getMove().accuracy * accuracyMultiplier;
}
return true;
}
@ -1500,6 +1561,8 @@ export class GameOverPhase extends BattlePhase {
start() {
super.start();
this.scene.gameData.clearSession();
this.scene.time.delayedCall(1000, () => {
const fadeDuration = this.victory ? 10000 : 5000;
this.scene.fadeOutBgm(fadeDuration, true);

View File

@ -1,7 +1,7 @@
import Phaser from 'phaser';
import { Biome } from './data/biome';
import UI from './ui/ui';
import { EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, SelectStarterPhase, MessagePhase } from './battle-phases';
import { EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, SelectStarterPhase, MessagePhase, CheckLoadPhase } from './battle-phases';
import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon';
import PokemonSpecies, { allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species';
import * as Utils from './utils';
@ -45,7 +45,7 @@ export enum Button {
SLOW_DOWN
}
interface PokeballCounts {
export interface PokeballCounts {
[pb: string]: integer;
}
@ -474,16 +474,9 @@ export default class BattleScene extends Phaser.Scene {
this.currentBattle = null;
this.waveCountText.setText(startingWave.toString());
this.waveCountText.setVisible(false);
this.newArena(startingBiome);
const biomeKey = this.arena.getBiomeKey();
this.arenaBg.setTexture(`${biomeKey}_bg`);
this.arenaBgTransition.setTexture(`${biomeKey}_bg`);
this.arenaPlayer.setTexture(`${biomeKey}_a`);
this.arenaPlayerTransition.setTexture(`${biomeKey}_a`);
this.arenaEnemy.setTexture(`${biomeKey}_b`);
this.arenaNextEnemy.setTexture(`${biomeKey}_b`);
this.newArena(startingBiome, true);
this.arenaBgTransition.setPosition(0, 0);
this.arenaPlayer.setPosition(340, 20);
@ -495,32 +488,46 @@ export default class BattleScene extends Phaser.Scene {
this.trainer.setPosition(406, 132);
}
newBattle(): Battle {
if (this.currentBattle) {
this.getEnemyPokemon().destroy();
if (this.currentBattle.waveIndex % 10)
this.pushPhase(new NextEncounterPhase(this));
else {
this.pushPhase(new SelectBiomePhase(this));
this.pushPhase(new NewBiomeEncounterPhase(this));
newBattle(waveIndex?: integer): Battle {
if (!waveIndex) {
if (this.currentBattle) {
this.getEnemyPokemon().destroy();
if (this.currentBattle.waveIndex % 10)
this.pushPhase(new NextEncounterPhase(this));
else {
this.pushPhase(new SelectBiomePhase(this));
this.pushPhase(new NewBiomeEncounterPhase(this));
}
} else {
if (!this.quickStart)
this.pushPhase(new CheckLoadPhase(this));
else {
this.arena.playBgm();
this.pushPhase(new EncounterPhase(this));
this.pushPhase(new SummonPhase(this));
}
}
} else {
if (!this.quickStart) {
this.arena.preloadBgm();
this.pushPhase(new SelectStarterPhase(this));
} else
this.arena.playBgm();
this.pushPhase(new EncounterPhase(this));
this.pushPhase(new SummonPhase(this));
}
this.currentBattle = new Battle((this.currentBattle?.waveIndex || (startingWave - 1)) + 1);
this.currentBattle = new Battle(waveIndex || ((this.currentBattle?.waveIndex || (startingWave - 1)) + 1));
return this.currentBattle;
}
newArena(biome: Biome): Arena {
newArena(biome: Biome, init?: boolean): Arena {
this.arena = new Arena(this, biome, Biome[biome].toLowerCase());
if (init) {
const biomeKey = this.arena.getBiomeKey();
this.arenaBg.setTexture(`${biomeKey}_bg`);
this.arenaBgTransition.setTexture(`${biomeKey}_bg`);
this.arenaPlayer.setTexture(`${biomeKey}_a`);
this.arenaPlayerTransition.setTexture(`${biomeKey}_a`);
this.arenaEnemy.setTexture(`${biomeKey}_b`);
this.arenaNextEnemy.setTexture(`${biomeKey}_b`);
}
return this.arena;
}
@ -529,6 +536,7 @@ export default class BattleScene extends Phaser.Scene {
this.waveCountText.setText(this.currentBattle.waveIndex.toString());
this.waveCountText.setColor(!isBoss ? '#404040' : '#f89890');
this.waveCountText.setShadowColor(!isBoss ? '#ded6b5' : '#984038');
this.waveCountText.setVisible(true);
}
updateWaveCountPosition(): void {
@ -868,19 +876,19 @@ export default class BattleScene extends Phaser.Scene {
return false;
}
getModifiers(modifierType: { new(...args: any[]): Modifier }, player?: boolean): Modifier[] {
getModifiers(modifierType: { new(...args: any[]): Modifier }, player?: boolean): PersistentModifier[] {
if (player === undefined)
player = true;
return (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType);
}
findModifiers(modifierFilter: ModifierPredicate, player?: boolean): Modifier[] {
findModifiers(modifierFilter: ModifierPredicate, player?: boolean): PersistentModifier[] {
if (player === undefined)
player = true;
return (player ? this.modifiers : this.enemyModifiers).filter(m => (modifierFilter as ModifierPredicate)(m));
}
findModifier(modifierFilter: ModifierPredicate, player?: boolean): Modifier {
findModifier(modifierFilter: ModifierPredicate, player?: boolean): PersistentModifier {
if (player === undefined)
player = true;
return (player ? this.modifiers : this.enemyModifiers).find(m => (modifierFilter as ModifierPredicate)(m));

View File

@ -1,11 +1,11 @@
import { CommonAnim, CommonBattleAnim } from "./battle-anims";
import { CommonAnimPhase, DamagePhase, MessagePhase, MovePhase, ObtainStatusEffectPhase, PokemonHealPhase } from "../battle-phases";
import { CommonAnimPhase, DamagePhase, MovePhase, ObtainStatusEffectPhase, PokemonHealPhase } from "../battle-phases";
import { getPokemonMessage } from "../messages";
import Pokemon from "../pokemon";
import { Stat } from "./pokemon-stat";
import { StatusEffect } from "./status-effect";
import * as Utils from "../utils";
import { LapseBattlerTagAttr, Moves, allMoves } from "./move";
import { Moves, allMoves } from "./move";
import { Type } from "./type";
export enum BattlerTagType {
@ -555,7 +555,7 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
case BattlerTagType.NO_CRIT:
return new BattlerTag(tagType, BattlerTagLapseType.AFTER_MOVE, turnCount, sourceMove);
case BattlerTagType.IGNORE_ACCURACY:
return new IgnoreAccuracyTag(turnCount, sourceMove);
return new IgnoreAccuracyTag(sourceMove);
case BattlerTagType.BYPASS_SLEEP:
return new BattlerTag(BattlerTagType.BYPASS_SLEEP, BattlerTagLapseType.TURN_END, turnCount, sourceMove);
case BattlerTagType.IGNORE_FLYING:

View File

@ -16,11 +16,14 @@ export class Status {
public turnCount: integer;
public cureTurn: integer;
constructor(effect: StatusEffect) {
constructor(effect: StatusEffect, turnCount?: integer, cureTurn?: integer) {
this.effect = effect;
this.turnCount = 0;
if (effect === StatusEffect.SLEEP)
this.cureTurn = Utils.randInt(3, 1);
this.turnCount = turnCount === undefined ? 0 : turnCount;
if (cureTurn === undefined) {
if (effect === StatusEffect.SLEEP)
this.cureTurn = Utils.randInt(3, 1);
} else
this.cureTurn = cureTurn;
}
incrementTurn(): void {

View File

@ -24,6 +24,7 @@ export enum ModifierTier {
type NewModifierFunc = (type: ModifierType, args: any[]) => Modifier;
export class ModifierType {
public id: string;
public name: string;
public description: string;
public iconImage: string;
@ -50,6 +51,30 @@ export class ModifierType {
}
}
type ModifierTypeGeneratorFunc = (party: Pokemon[], pregenArgs?: any[]) => ModifierType;
export class ModifierTypeGenerator extends ModifierType {
private genTypeFunc: ModifierTypeGeneratorFunc;
constructor(genTypeFunc: ModifierTypeGeneratorFunc) {
super(null, null, null, null);
this.genTypeFunc = genTypeFunc;
}
generateType(party: Pokemon[], pregenArgs?: any[]) {
const ret = this.genTypeFunc(party, pregenArgs);
if (ret) {
ret.id = this.id;
ret.setTier(this.tier);
}
return ret;
}
}
export interface GeneratedPersistentModifierType {
getPregenArgs(): any[];
}
class AddPokeballModifierType extends ModifierType {
constructor(pokeballType: PokeballType, count: integer, iconImage?: string) {
super(`${count}x ${getPokeballName(pokeballType)}`, `Receive ${getPokeballName(pokeballType)} x${count}`,
@ -168,7 +193,7 @@ export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType {
}
}
export class TempBattleStatBoosterModifierType extends ModifierType {
export class TempBattleStatBoosterModifierType extends ModifierType implements GeneratedPersistentModifierType {
public tempBattleStat: TempBattleStat;
constructor(tempBattleStat: TempBattleStat) {
@ -179,6 +204,26 @@ export class TempBattleStatBoosterModifierType extends ModifierType {
this.tempBattleStat = tempBattleStat;
}
getPregenArgs(): any[] {
return [ this.tempBattleStat ];
}
}
export class BerryModifierType extends PokemonHeldItemModifierType implements GeneratedPersistentModifierType {
private berryType: BerryType;
constructor(berryType: BerryType) {
super(getBerryName(berryType), getBerryEffectDescription(berryType),
(type, args) => new Modifiers.BerryModifier(type, (args[0] as Pokemon).id, berryType),
null, 'berry');
this.berryType = berryType;
}
getPregenArgs(): any[] {
return [ this.berryType ];
}
}
function getAttackTypeBoosterItemName(type: Type) {
@ -222,7 +267,7 @@ function getAttackTypeBoosterItemName(type: Type) {
}
}
export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType {
export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType implements GeneratedPersistentModifierType {
public moveType: Type;
public boostPercent: integer;
@ -234,6 +279,10 @@ export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType {
this.moveType = moveType;
this.boostPercent = boostPercent;
}
getPregenArgs(): any[] {
return [ this.moveType ];
}
}
export class PokemonLevelIncrementModifierType extends PokemonModifierType {
@ -266,7 +315,7 @@ function getBaseStatBoosterItemName(stat: Stat) {
}
}
export class PokemonBaseStatBoosterModifierType extends PokemonHeldItemModifierType {
export class PokemonBaseStatBoosterModifierType extends PokemonHeldItemModifierType implements GeneratedPersistentModifierType {
private stat: Stat;
constructor(name: string, stat: Stat, _iconImage?: string) {
@ -274,6 +323,10 @@ export class PokemonBaseStatBoosterModifierType extends PokemonHeldItemModifierT
this.stat = stat;
}
getPregenArgs(): any[] {
return [ this.stat ];
}
}
class AllPokemonFullHpRestoreModifierType extends ModifierType {
@ -343,7 +396,7 @@ function getEvolutionItemName(evolutionItem: EvolutionItem) {
}
}
export class EvolutionItemModifierType extends PokemonModifierType {
export class EvolutionItemModifierType extends PokemonModifierType implements GeneratedPersistentModifierType {
public evolutionItem: EvolutionItem;
constructor(evolutionItem: EvolutionItem) {
@ -358,27 +411,18 @@ export class EvolutionItemModifierType extends PokemonModifierType {
this.evolutionItem = evolutionItem;
}
}
class ModifierTypeGenerator extends ModifierType {
private genTypeFunc: Function;
constructor(genTypeFunc: Function) {
super(null, null, null, null);
this.genTypeFunc = genTypeFunc;
}
generateType(party: Pokemon[]) {
const ret = this.genTypeFunc(party);
if (ret)
ret.setTier(this.tier);
return ret;
getPregenArgs(): any[] {
return [ this.evolutionItem ];
}
}
class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
constructor() {
super((party: Pokemon[]) => {
super((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs)
return new AttackTypeBoosterModifierType(pregenArgs[0] as Type, 20);
const attackMoveTypes = party.map(p => p.moveset.map(m => m.getMove()).filter(m => m instanceof AttackMove).map(m => m.type)).flat();
const attackMoveTypeWeights = new Map<Type, integer>();
let totalWeight = 0;
@ -417,7 +461,10 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
constructor() {
super((party: Pokemon[]) => {
super((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs)
return new EvolutionItemModifierType(pregenArgs[0] as EvolutionItem);
const evolutionItemPool = party.filter(p => pokemonEvolutions.hasOwnProperty(p.species.speciesId)).map(p => {
const evolutions = pokemonEvolutions[p.species.speciesId]
return evolutions.filter(e => e.item !== EvolutionItem.NONE && (!e.condition || e.condition.predicate(p)));
@ -446,6 +493,7 @@ class WeightedModifierType {
constructor(modifierTypeFunc: ModifierTypeFunc, weight: integer | WeightedModifierTypeWeightFunc) {
this.modifierType = modifierTypeFunc();
this.modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc);
this.weight = weight;
}
@ -485,21 +533,29 @@ const modifierTypes = {
ELIXIR: () => new PokemonAllMovePpRestoreModifierType('ELIXIR', 10),
MAX_ELIXIR: () => new PokemonAllMovePpRestoreModifierType('MAX ELIXIR', -1),
TEMP_STAT_BOOSTER: () => new ModifierTypeGenerator((party: Pokemon[]) => {
TEMP_STAT_BOOSTER: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs)
return new TempBattleStatBoosterModifierType(pregenArgs[0] as TempBattleStat);
const randTempBattleStat = Utils.randInt(7) as TempBattleStat;
return new TempBattleStatBoosterModifierType(randTempBattleStat);
}),
BASE_STAT_BOOSTER: () => new ModifierTypeGenerator((party: Pokemon[]) => {
BASE_STAT_BOOSTER: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs) {
const stat = pregenArgs[0] as Stat;
return new PokemonBaseStatBoosterModifierType(getBaseStatBoosterItemName(stat), stat);
}
const randStat = Utils.randInt(6) as Stat;
return new PokemonBaseStatBoosterModifierType(getBaseStatBoosterItemName(randStat), randStat);
}),
ATTACK_TYPE_BOOSTER: () => new AttackTypeBoosterModifierTypeGenerator(),
BERRY: () => new ModifierTypeGenerator((party: Pokemon[]) => {
BERRY: () => new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs)
return new BerryModifierType(pregenArgs[0] as BerryType);
const berryTypes = Utils.getEnumValues(BerryType);
let randBerryType;
let randBerryType: BerryType;
let rand = Utils.randInt(10);
if (rand < 2)
randBerryType = BerryType.SITRUS;
@ -507,9 +563,7 @@ const modifierTypes = {
randBerryType = BerryType.LUM;
else
randBerryType = berryTypes[Utils.randInt(berryTypes.length - 2) + 2];
return new PokemonHeldItemModifierType(getBerryName(randBerryType), getBerryEffectDescription(randBerryType),
(type, args) => new Modifiers.BerryModifier(type, (args[0] as Pokemon).id, randBerryType),
null, 'berry');
return new BerryModifierType(randBerryType);
}),
TM: () => new ModifierTypeGenerator((party: Pokemon[]) => {
@ -575,11 +629,11 @@ const modifierPool = {
return thresholdPartyMemberCount;
}),
new WeightedModifierType(modifierTypes.ETHER, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => m.getPpRatio() <= 0.2).length).length, 3);
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount * 3;
}),
new WeightedModifierType(modifierTypes.MAX_ETHER, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => m.getPpRatio() <= 0.2).length).length, 3);
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount;
}),
new WeightedModifierType(modifierTypes.TEMP_STAT_BOOSTER, 4),
@ -611,11 +665,11 @@ const modifierPool = {
return thresholdPartyMemberCount;
}),
new WeightedModifierType(modifierTypes.ELIXIR, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => m.getPpRatio() <= 0.2).length).length, 3);
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount * 3;
}),
new WeightedModifierType(modifierTypes.MAX_ELIXIR, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => m.getPpRatio() <= 0.2).length).length, 3);
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount;
}),
new WeightedModifierType(modifierTypes.MAP, (party: Pokemon[]) => {
@ -714,6 +768,10 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], player?: bool
}
}
export function getModifierTypeFuncById(id: string): ModifierTypeFunc {
return modifierTypes[id];
}
export function getPlayerModifierTypeOptionsForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierTypeOption[] {
if (waveIndex % 10 === 0)
return modifierPool[ModifierTier.LUXURY].filter(m => !(m.weight instanceof Function) || m.weight(party)).map(m => new ModifierTypeOption(m.modifierType, false));

View File

@ -92,6 +92,10 @@ export abstract class PersistentModifier extends Modifier {
abstract clone(): PersistentModifier;
getArgs(): any[] {
return [];
}
incrementStack(amount: integer, virtual: boolean): boolean {
if (this.getStackCount() + amount <= this.getMaxStackCount()) {
if (!virtual)
@ -194,17 +198,21 @@ export class TempBattleStatBoosterModifier extends PersistentModifier {
private tempBattleStat: TempBattleStat;
private battlesLeft: integer;
constructor(type: ModifierTypes.TempBattleStatBoosterModifierType, tempBattleStat: TempBattleStat, stackCount?: integer) {
constructor(type: ModifierTypes.TempBattleStatBoosterModifierType, tempBattleStat: TempBattleStat, battlesLeft?: integer, stackCount?: integer) {
super(type, stackCount);
this.tempBattleStat = tempBattleStat;
this.battlesLeft = 5;
this.battlesLeft = battlesLeft || 5;
}
clone(): TempBattleStatBoosterModifier {
return new TempBattleStatBoosterModifier(this.type as ModifierTypes.TempBattleStatBoosterModifierType, this.tempBattleStat, this.stackCount);
}
getArgs(): any[] {
return [ this.tempBattleStat, this.battlesLeft ];
}
apply(args: any[]): boolean {
const tempBattleStat = args[0] as TempBattleStat;
@ -267,6 +275,10 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier {
return this.matchType(modifier) && (modifier as PokemonHeldItemModifier).pokemonId === this.pokemonId;
}
getArgs(): any[] {
return [ this.pokemonId ];
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args.length && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId);
}
@ -332,6 +344,10 @@ export class PokemonBaseStatModifier extends PokemonHeldItemModifier {
return new PokemonBaseStatModifier(this.type as ModifierTypes.PokemonBaseStatBoosterModifierType, this.pokemonId, this.stat, this.stackCount);
}
getArgs(): any[] {
return super.getArgs().concat(this.stat);
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Array<integer>;
}
@ -365,6 +381,10 @@ export class AttackTypeBoosterModifier extends PokemonHeldItemModifier {
return new AttackTypeBoosterModifier(this.type, this.pokemonId, this.moveType, this.boostMultiplier * 100, this.stackCount);
}
getArgs(): any[] {
return super.getArgs().concat([ this.moveType, this.boostMultiplier * 100 ]);
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Utils.NumberHolder;
}
@ -543,6 +563,10 @@ export class BerryModifier extends PokemonHeldItemModifier {
return new BerryModifier(this.type, this.pokemonId, this.berryType, this.stackCount);
}
getArgs(): any[] {
return super.getArgs().concat(this.berryType);
}
shouldApply(args: any[]): boolean {
return !this.consumed && super.shouldApply(args) && getBerryPredicate(this.berryType)(args[0] as Pokemon);
}
@ -818,6 +842,10 @@ export class HealingBoosterModifier extends PersistentModifier {
return new HealingBoosterModifier(this.type, this.multiplier, this.stackCount);
}
getArgs(): any[] {
return [ this.multiplier ];
}
apply(args: any[]): boolean {
const healingMultiplier = args[0] as Utils.IntegerHolder;
for (let s = 0; s < this.getStackCount(); s++)
@ -853,6 +881,10 @@ export class ExpBoosterModifier extends PersistentModifier {
return new ExpBoosterModifier(this.type, this.boostMultiplier * 100, this.stackCount);
}
getArgs(): any[] {
return [ this.boostMultiplier * 100 ];
}
apply(args: any[]): boolean {
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (1 + (this.getStackCount() * this.boostMultiplier)));
@ -880,6 +912,10 @@ export class PokemonExpBoosterModifier extends PokemonHeldItemModifier {
return new PokemonExpBoosterModifier(this.type as ModifierTypes.PokemonExpBoosterModifierType, this.pokemonId, this.boostMultiplier * 100, this.stackCount);
}
getArgs(): any[] {
return super.getArgs().concat(this.boostMultiplier * 100);
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Utils.NumberHolder;
}

View File

@ -24,6 +24,7 @@ import { TempBattleStat } from './data/temp-battle-stat';
import { WeakenMoveTypeTag } from './data/arena-tag';
import { Biome } from './data/biome';
import { Ability, TypeImmunityAbAttr, VariableMovePowerAbAttr, abilities, applyPreAttackAbAttrs, applyPreDefendAbAttrs } from './data/ability';
import PokemonData from './system/pokemon-data';
export default abstract class Pokemon extends Phaser.GameObjects.Container {
public id: integer;
@ -54,7 +55,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
private shinySparkle: Phaser.GameObjects.Sprite;
constructor(scene: BattleScene, x: number, y: number, species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, dataSource?: Pokemon) {
constructor(scene: BattleScene, x: number, y: number, species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, dataSource?: Pokemon | PokemonData) {
super(scene, x, y);
if (!species.isObtainable() && this.isPlayer())
@ -666,7 +667,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
getMoveHistory(): TurnMove[] {
return this.summonData.moveHistory;
return this.battleSummonData.moveHistory;
}
pushMoveHistory(turnMove: TurnMove) {
@ -875,7 +876,7 @@ export class PlayerPokemon extends Pokemon {
public metLevel: integer;
public compatibleTms: Moves[];
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, abilityIndex: integer, formIndex: integer, gender?: Gender, shiny?: boolean, dataSource?: Pokemon) {
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, abilityIndex: integer, formIndex: integer, gender?: Gender, shiny?: boolean, dataSource?: Pokemon | PokemonData) {
super(scene, 106, 148, species, level, abilityIndex, formIndex, gender, shiny, dataSource);
this.metBiome = scene.arena?.biomeType || Biome.TOWN;
@ -942,16 +943,19 @@ export class PlayerPokemon extends Pokemon {
export class EnemyPokemon extends Pokemon {
public aiType: AiType;
constructor(scene: BattleScene, species: PokemonSpecies, level: integer) {
super(scene, -66, 84, species, level, undefined, scene.arena.getFormIndex(species));
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, dataSource?: PokemonData) {
super(scene, -66, 84, species, level, dataSource?.abilityIndex, dataSource ? dataSource.formIndex : scene.arena.getFormIndex(species),
dataSource?.gender, dataSource?.shiny, dataSource);
let prevolution: Species;
let speciesId = species.speciesId;
while ((prevolution = pokemonPrevolutions[speciesId])) {
const evolution = pokemonEvolutions[prevolution].find(pe => pe.speciesId === speciesId);
if (evolution.condition?.enforceFunc)
evolution.condition.enforceFunc(this);
speciesId = prevolution;
if (!dataSource) {
let prevolution: Species;
let speciesId = species.speciesId;
while ((prevolution = pokemonPrevolutions[speciesId])) {
const evolution = pokemonEvolutions[prevolution].find(pe => pe.speciesId === speciesId);
if (evolution.condition?.enforceFunc)
evolution.condition.enforceFunc(this);
speciesId = prevolution;
}
}
this.aiType = AiType.SMART_RANDOM;
@ -1077,7 +1081,6 @@ export interface QueuedMove {
export class PokemonSummonData {
public battleStats: integer[] = [ 0, 0, 0, 0, 0, 0, 0 ];
public moveHistory: TurnMove[] = [];
public moveQueue: QueuedMove[] = [];
public tags: BattlerTag[] = [];
public types: Type[];
@ -1085,6 +1088,7 @@ export class PokemonSummonData {
export class PokemonBattleSummonData {
public turnCount: integer = 1;
public moveHistory: TurnMove[] = [];
}
export class PokemonTurnData {

17
src/system/arena-data.ts Normal file
View File

@ -0,0 +1,17 @@
import { Arena } from "../arena";
import { ArenaTag } from "../data/arena-tag";
import { Biome } from "../data/biome";
import { Weather } from "../data/weather";
export default class ArenaData {
public biome: Biome;
public weather: Weather;
public tags: ArenaTag[];
constructor(source: Arena | any) {
const sourceArena = source instanceof Arena ? source as Arena : null;
this.biome = sourceArena ? sourceArena.biomeType : source.biome;
this.weather = sourceArena ? sourceArena.weather : source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : undefined;
this.tags = sourceArena ? sourceArena.tags : [];
}
}

View File

@ -1,16 +1,34 @@
import BattleScene from "../battle-scene";
import BattleScene, { PokeballCounts } from "../battle-scene";
import { Gender } from "../data/gender";
import Pokemon from "../pokemon";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "../pokemon";
import { pokemonPrevolutions } from "../data/pokemon-evolutions";
import PokemonSpecies, { allSpecies, getPokemonSpecies } from "../data/pokemon-species";
import { Species } from "../data/species";
import * as Utils from "../utils";
import PokemonData from "./pokemon-data";
import { Weather } from "../data/weather";
import PersistentModifierData from "./modifier-data";
import { Biome } from "../data/biome";
import { PokemonHeldItemModifier } from "../modifier/modifier";
import { ArenaTag } from "../data/arena-tag";
import ArenaData from "./arena-data";
interface SaveData {
interface SystemSaveData {
trainerId: integer;
secretId: integer;
dexData: DexData;
timestamp: integer
timestamp: integer;
}
interface SessionSaveData {
party: PokemonData[];
enemyParty: PokemonData[];
modifiers: PersistentModifierData[];
enemyModifiers: PersistentModifierData[];
arena: ArenaData;
pokeballCounts: PokeballCounts;
waveIndex: integer;
timestamp: integer;
}
export interface DexData {
@ -52,14 +70,14 @@ export class GameData {
this.trainerId = Utils.randInt(65536);
this.secretId = Utils.randInt(65536);
this.initDexData();
this.load();
this.loadSystem();
}
private save(): boolean {
private saveSystem(): boolean {
if (this.scene.quickStart)
return false;
const data: SaveData = {
const data: SystemSaveData = {
trainerId: this.trainerId,
secretId: this.secretId,
dexData: this.dexData,
@ -71,12 +89,12 @@ export class GameData {
return true;
}
private load(): boolean {
private loadSystem(): boolean {
if (!localStorage.getItem('data'))
return false;
const data = JSON.parse(atob(localStorage.getItem('data'))) as SaveData;
console.log(data);
const data = JSON.parse(atob(localStorage.getItem('data'))) as SystemSaveData;
console.debug(data);
this.trainerId = data.trainerId;
this.secretId = data.secretId;
@ -89,7 +107,113 @@ export class GameData {
return true;
}
private initDexData() {
saveSession(scene: BattleScene): boolean {
const sessionData = {
party: scene.getParty().map(p => new PokemonData(p)),
enemyParty: scene.getEnemyParty().map(p => new PokemonData(p)),
modifiers: scene.findModifiers(m => true).map(m => new PersistentModifierData(m, true)),
enemyModifiers: scene.findModifiers(m => true, false).map(m => new PersistentModifierData(m, false)),
arena: new ArenaData(scene.arena),
pokeballCounts: scene.pokeballCounts,
waveIndex: scene.currentBattle.waveIndex,
timestamp: new Date().getTime()
} as SessionSaveData;
localStorage.setItem('sessionData', btoa(JSON.stringify(sessionData)));
console.debug('Session data saved');
return true;
}
hasSession() {
return !!localStorage.getItem('sessionData');
}
loadSession(scene: BattleScene): Promise<boolean> {
return new Promise(async (resolve, reject) => {
if (!this.hasSession())
return resolve(false);
try {
const sessionData = JSON.parse(atob(localStorage.getItem('sessionData')), (k: string, v: any) => {
if (k === 'party' || k === 'enemyParty') {
const ret: PokemonData[] = [];
for (let pd of v)
ret.push(new PokemonData(pd));
return ret;
}
if (k === 'modifiers' || k === 'enemyModifiers') {
const player = k === 'modifiers';
const ret: PersistentModifierData[] = [];
for (let md of v)
ret.push(new PersistentModifierData(md, player));
return ret;
}
if (k === 'arena')
return new ArenaData(v);
return v;
}) as SessionSaveData;
console.debug(sessionData);
const loadPokemonAssets: Promise<void>[] = [];
const party = scene.getParty();
party.splice(0, party.length);
for (let p of sessionData.party) {
const pokemon = p.toPokemon(scene) as PlayerPokemon;
pokemon.setVisible(false);
loadPokemonAssets.push(pokemon.loadAssets());
party.push(pokemon);
}
const enemyPokemon = sessionData.enemyParty[0].toPokemon(scene) as EnemyPokemon;
Object.keys(scene.pokeballCounts).forEach((key: string) => {
scene.pokeballCounts[key] = sessionData.pokeballCounts[key] || 0;
});
scene.newArena(sessionData.arena.biome, true);
scene.newBattle(sessionData.waveIndex).enemyPokemon = enemyPokemon;
loadPokemonAssets.push(enemyPokemon.loadAssets());
scene.arena.weather = sessionData.arena.weather;
// TODO
//scene.arena.tags = sessionData.arena.tags;
const modifiersModule = await import('../modifier/modifier');
for (let modifierData of sessionData.modifiers) {
const modifier = modifierData.toModifier(scene, modifiersModule[modifierData.className]);
if (modifier)
scene.addModifier(modifier);
}
for (let enemyModifierData of sessionData.enemyModifiers) {
const modifier = enemyModifierData.toModifier(scene, modifiersModule[enemyModifierData.className]) as PokemonHeldItemModifier;
if (modifier)
scene.addEnemyModifier(modifier);
}
Promise.all(loadPokemonAssets).then(() => resolve(true));
} catch (err) {
reject(err);
return;
}
});
}
clearSession(): void {
localStorage.removeItem('sessionData');
}
private initDexData(): void {
const data: DexData = {};
const initDexSubData = (dexData: DexData, count: integer): DexData[] => {
@ -148,7 +272,7 @@ export class GameData {
const dexEntry = this.getPokemonDexEntry(pokemon);
if (!dexEntry.seen) {
dexEntry.seen = true;
this.save();
this.saveSystem();
}
}
@ -159,7 +283,7 @@ export class GameData {
const newCatch = !this.getDefaultDexEntry(pokemon.species);
dexEntry.caught = true;
this.save();
this.saveSystem();
if (newCatch && !pokemonPrevolutions.hasOwnProperty(pokemon.species.speciesId)) {
this.scene.playSoundWithoutBgm('level_up_fanfare', 1500);

View File

@ -0,0 +1,43 @@
import BattleScene from "../battle-scene";
import { PersistentModifier } from "../modifier/modifier";
import { GeneratedPersistentModifierType, ModifierTypeGenerator, getModifierTypeFuncById } from "../modifier/modifier-type";
export default class ModifierData {
private player: boolean;
private typeId: string;
private typePregenArgs: any[];
private args: any[];
private stackCount: integer;
public className: string;
constructor(source: PersistentModifier | any, player: boolean) {
const sourceModifier = source instanceof PersistentModifier ? source as PersistentModifier : null;
this.player = player;
this.typeId = sourceModifier ? sourceModifier.type.id : source.typeId;
if (sourceModifier) {
if ('getPregenArgs' in source.type)
this.typePregenArgs = (source.type as GeneratedPersistentModifierType).getPregenArgs();
} else if (source.typePregenArgs)
this.typePregenArgs = source.typePregenArgs;
this.args = sourceModifier ? sourceModifier.getArgs() : source.args;
this.stackCount = source.stackCount;
this.className = sourceModifier ? sourceModifier.constructor.name : source.className;
}
toModifier(scene: BattleScene, constructor: any): PersistentModifier {
const typeFunc = getModifierTypeFuncById(this.typeId);
if (!typeFunc)
return null;
let type = typeFunc();
type.id = this.typeId;
if (type instanceof ModifierTypeGenerator)
type = (type as ModifierTypeGenerator).generateType(this.player ? scene.getParty() : scene.getEnemyParty(), this.typePregenArgs);
const ret = Reflect.construct(constructor, ([ type ] as any[]).concat(this.args).concat(this.stackCount)) as PersistentModifier
return ret;
}
}

View File

@ -0,0 +1,79 @@
import BattleScene from "../battle-scene";
import { Gender } from "../data/gender";
import { PokeballType } from "../data/pokeball";
import { getPokemonSpecies } from "../data/pokemon-species";
import { Species } from "../data/species";
import { Status } from "../data/status-effect";
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove, PokemonSummonData } from "../pokemon";
export default class PokemonData {
public id: integer;
public player: boolean;
public species: Species;
public formIndex: integer;
public abilityIndex: integer;
public shiny: boolean;
public pokeball: PokeballType;
public level: integer;
public exp: integer;
public levelExp: integer;
public gender: Gender;
public hp: integer;
public stats: integer[];
public ivs: integer[];
public moveset: PokemonMove[];
public status: Status;
public winCount: integer;
public summonData: PokemonSummonData;
constructor(source: Pokemon | any) {
const sourcePokemon = source instanceof Pokemon ? source as Pokemon : null;
this.id = source.id;
this.player = sourcePokemon ? sourcePokemon.isPlayer() : source.player;
this.species = sourcePokemon ? sourcePokemon.species.speciesId : source.species;
this.formIndex = source.formIndex;
this.abilityIndex = source.abilityIndex;
this.shiny = source.shiny;
this.pokeball = source.pokeball;
this.level = source.level;
this.exp = source.exp;
this.levelExp = source.levelExp;
this.gender = source.gender;
this.hp = source.hp;
this.stats = source.stats;
this.ivs = source.ivs;
this.winCount = source.winCount;
if (sourcePokemon) {
this.moveset = sourcePokemon.moveset;
this.status = sourcePokemon.status;
if (this.player)
this.summonData = sourcePokemon.summonData;
} else {
this.moveset = source.moveset.map((m: any) => {
const move = new PokemonMove(m.moveId, m.ppUsed, m.ppUp);
move.disableTurns = m.disableTurns;
return move;
});
this.status = source.status
? new Status(source.status.effect, source.status.turnCount, source.status.cureTurn)
: undefined;
this.summonData = new PokemonSummonData();
if (source.summonData) {
this.summonData.battleStats = source.summonData.battleStats;
this.summonData.moveQueue = source.summonData.moveQueue;
this.summonData.tags = []; // TODO
this.summonData.types = source.summonData.types;
}
}
}
toPokemon(scene: BattleScene): Pokemon {
const species = getPokemonSpecies(this.species);
if (this.player)
return new PlayerPokemon(scene, species, this.level, this.abilityIndex, this.formIndex, this.gender, this.shiny, this);
return new EnemyPokemon(scene, species, this.level, this);
}
}