Various changes
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 21 KiB |
BIN
public/images/items/binding_band.png
Normal file
After Width: | Height: | Size: 218 B |
BIN
public/images/items/black_glasses.png
Normal file
After Width: | Height: | Size: 248 B |
BIN
public/images/items/choice_scarf.png
Normal file
After Width: | Height: | Size: 404 B |
BIN
public/images/items/choice_specs.png
Normal file
After Width: | Height: | Size: 283 B |
BIN
public/images/items/dawn_stone.png
Normal file
After Width: | Height: | Size: 274 B |
BIN
public/images/items/deep_sea_scale.png
Normal file
After Width: | Height: | Size: 235 B |
BIN
public/images/items/deep_sea_tooth.png
Normal file
After Width: | Height: | Size: 274 B |
BIN
public/images/items/dragon_scale.png
Normal file
After Width: | Height: | Size: 233 B |
BIN
public/images/items/dubious_disc.png
Normal file
After Width: | Height: | Size: 298 B |
BIN
public/images/items/electirizer.png
Normal file
After Width: | Height: | Size: 282 B |
BIN
public/images/items/magmarizer.png
Normal file
After Width: | Height: | Size: 290 B |
BIN
public/images/items/metal_coat.png
Normal file
After Width: | Height: | Size: 233 B |
BIN
public/images/items/oval_stone.png
Normal file
After Width: | Height: | Size: 251 B |
BIN
public/images/items/prism_scale.png
Normal file
After Width: | Height: | Size: 199 B |
BIN
public/images/items/protector.png
Normal file
After Width: | Height: | Size: 293 B |
BIN
public/images/items/razor_claw.png
Normal file
After Width: | Height: | Size: 262 B |
BIN
public/images/items/razor_fang.png
Normal file
After Width: | Height: | Size: 226 B |
BIN
public/images/items/reaper_cloth.png
Normal file
After Width: | Height: | Size: 317 B |
Before Width: | Height: | Size: 454 B After Width: | Height: | Size: 270 B |
BIN
public/images/items/upgrade.png
Normal file
After Width: | Height: | Size: 303 B |
211
src/arena.ts
@ -1,211 +0,0 @@
|
||||
import BattleScene from "./battle-scene";
|
||||
import { default as PokemonSpecies, allSpecies, getPokemonSpecies } from "./pokemon-species";
|
||||
import { Stat } from "./pokemon-stat";
|
||||
import { Species } from "./species";
|
||||
import { Type } from './type';
|
||||
import * as Utils from './utils';
|
||||
|
||||
export enum ArenaType {
|
||||
PLAINS = 1,
|
||||
GRASS,
|
||||
FOREST,
|
||||
WATER,
|
||||
SWAMP,
|
||||
SEA,
|
||||
MOUNTAIN,
|
||||
LAND,
|
||||
CAVE,
|
||||
DESERT,
|
||||
ICE_CAVE,
|
||||
MEADOW,
|
||||
POWER_PLANT,
|
||||
VOLCANO,
|
||||
GRAVEYARD,
|
||||
DOJO,
|
||||
RUINS,
|
||||
ABYSS,
|
||||
SPACE
|
||||
};
|
||||
|
||||
enum PoolTier {
|
||||
COMMON,
|
||||
UNCOMMON,
|
||||
RARE,
|
||||
ULTRA_RARE,
|
||||
LEGENDARY
|
||||
};
|
||||
|
||||
export class Arena {
|
||||
private scene: BattleScene;
|
||||
public arenaType: integer;
|
||||
private bgm: string;
|
||||
|
||||
private pokemonPool: PokemonSpecies[][];
|
||||
|
||||
constructor(scene: BattleScene, arenaType: integer, bgm: string) {
|
||||
this.scene = scene;
|
||||
this.arenaType = arenaType;
|
||||
this.bgm = bgm;
|
||||
|
||||
if (arenaPools.hasOwnProperty(arenaType))
|
||||
this.pokemonPool = arenaPools[arenaType];
|
||||
else {
|
||||
const predicate = arenaPoolPredicates[arenaType] || (() => {});
|
||||
this.pokemonPool = Utils.getEnumValues(PoolTier).map(t => allSpecies.filter(p => predicate(p, t)));
|
||||
}
|
||||
}
|
||||
randomSpecies(waveIndex: integer): PokemonSpecies {
|
||||
const tierValue = Utils.randInt(512);
|
||||
const tier = tierValue >= 156 ? PoolTier.COMMON : tierValue >= 32 ? PoolTier.UNCOMMON : tierValue >= 6 ? PoolTier.RARE : tierValue >= 1 ? PoolTier.ULTRA_RARE : PoolTier.LEGENDARY;
|
||||
const tierPool = this.pokemonPool[tier];
|
||||
let ret: PokemonSpecies;
|
||||
if (!tierPool.length)
|
||||
ret = this.scene.randomSpecies();
|
||||
else {
|
||||
const species = tierPool[Utils.randInt(tierPool.length)];
|
||||
ret = species instanceof PokemonSpecies ? species : getPokemonSpecies(species);
|
||||
}
|
||||
const newSpeciesId = ret.getSpeciesForLevel(5);
|
||||
if (newSpeciesId !== ret.speciesId) {
|
||||
console.log('Replaced', Species[ret.speciesId], 'with', Species[newSpeciesId]);
|
||||
ret = getPokemonSpecies(newSpeciesId);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
playBgm() {
|
||||
this.scene.loadBgm(this.bgm);
|
||||
this.scene.load.once(Phaser.Loader.Events.COMPLETE, () => this.scene.playBgm(this.bgm));
|
||||
}
|
||||
}
|
||||
|
||||
const arenaPools = {
|
||||
[ArenaType.PLAINS]: {
|
||||
[PoolTier.COMMON]: [
|
||||
Species.CATERPIE, Species.METAPOD, Species.WEEDLE, Species.KAKUNA, Species.PIDGEY, Species.RATTATA, Species.SPEAROW, Species.SENTRET, Species.HOOTHOOT, Species.LEDYBA,
|
||||
Species.HOPPIP, Species.SUNKERN, Species.POOCHYENA, Species.ZIGZAGOON, Species.WURMPLE, Species.SILCOON, Species.CASCOON, Species.TAILLOW, Species.STARLY, Species.BIDOOF,
|
||||
Species.KRICKETOT, Species.PATRAT, Species.LILLIPUP, Species.PIDOVE, Species.COTTONEE, Species.PETILIL, Species.MINCCINO, Species.FOONGUS
|
||||
],
|
||||
[PoolTier.UNCOMMON]: [
|
||||
Species.EKANS, Species.NIDORAN_F, Species.NIDORAN_M, Species.ODDISH, Species.PARAS, Species.VENONAT, Species.MEOWTH, Species.BELLSPROUT, Species.PICHU, Species.IGGLYBUFF,
|
||||
Species.LOTAD, Species.SEEDOT, Species.SHROOMISH, Species.NINCADA, Species.AZURILL, Species.WHISMUR, Species.SKITTY, Species.BUDEW, Species.COMBEE, Species.CHERUBI,
|
||||
Species.VENIPEDE
|
||||
],
|
||||
[PoolTier.RARE]: [ Species.ABRA, Species.CLEFFA, Species.WOOPER, Species.RALTS, Species.SURSKIT, Species.SLAKOTH, Species.DUCKLETT ],
|
||||
[PoolTier.ULTRA_RARE]: [ Species.EEVEE, Species.TOGEPI, Species.SMEARGLE, Species.TYROGUE, Species.WYNAUT ],
|
||||
[PoolTier.LEGENDARY]: [ Species.DITTO ]
|
||||
}
|
||||
};
|
||||
|
||||
const arenaPoolPredicates = {
|
||||
[ArenaType.PLAINS]: (p, t) => {
|
||||
if (p.isOfType(Type.GHOST) || p.isOfType(Type.STEEL) || p.isOfType(Type.ICE) || p.isOfType(Type.DRAGON))
|
||||
return false;
|
||||
for (let s in p.baseStats) {
|
||||
let threshold: integer;
|
||||
const stat = parseInt(s);
|
||||
switch (stat) {
|
||||
case Stat.HP:
|
||||
threshold = 70;
|
||||
break;
|
||||
default:
|
||||
threshold = 60;
|
||||
break;
|
||||
}
|
||||
if (p.baseStats[s] > threshold) {
|
||||
if (p.baseTotal <= 300 && p.baseExp <= 75 && (p.isOfType(Type.NORMAL) || p.isOfType(Type.BUG) || p.isOfType(Type.GRASS)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return p.baseTotal <= 320 && p.baseExp <= 75;
|
||||
},
|
||||
[ArenaType.GRASS]: (p, t) => {
|
||||
switch (t) {
|
||||
case PoolTier.ULTRA_RARE:
|
||||
return [ Species.ENTEI, Species.SUICUNE, Species.RAIKOU, Species.LATIOS, Species.LATIAS ].map(s => getPokemonSpecies(s));
|
||||
case PoolTier.LEGENDARY:
|
||||
return [ Species.MEW ].map(s => getPokemonSpecies(s));
|
||||
}
|
||||
return p.isOfType(Type.NORMAL) || p.isOfType(Type.FAIRY) || p.isOfType(Type.GRASS) || p.isOfType(Type.BUG) || p.isOfType(Type.ELECTRIC);
|
||||
},
|
||||
[ArenaType.FOREST]: (p, t) => {
|
||||
switch (t) {
|
||||
case PoolTier.LEGENDARY:
|
||||
return getPokemonSpecies(Species.CELEBI);
|
||||
}
|
||||
return p.isOfType(Type.GRASS) || p.isOfType(Type.BUG) || p.isOfType(Type.POISON);
|
||||
},
|
||||
[ArenaType.WATER]: (p, t) => {
|
||||
switch (t) {
|
||||
case PoolTier.ULTRA_RARE:
|
||||
return [ Species.UXIE, Species.MESPRIT, Species.AZELF ].map(s => getPokemonSpecies(s));
|
||||
}
|
||||
return p.isOfType(Type.WATER)
|
||||
},
|
||||
[ArenaType.SWAMP]: (p, t) => {
|
||||
return p.isOfType(Type.GRASS) || p.isOfType(Type.WATER) || p.isOfType(Type.POISON);
|
||||
},
|
||||
[ArenaType.SEA]: (p, t) => {
|
||||
switch (t) {
|
||||
case PoolTier.ULTRA_RARE:
|
||||
return [ Species.KYOGRE ].map(s => getPokemonSpecies(s));
|
||||
case PoolTier.LEGENDARY:
|
||||
return [ Species.LUGIA ].map(s => getPokemonSpecies(s));
|
||||
}
|
||||
return p.isOfType(Type.WATER);
|
||||
},
|
||||
[ArenaType.MOUNTAIN]: (p, t) => {
|
||||
switch (t) {
|
||||
case PoolTier.ULTRA_RARE:
|
||||
return [ Species.ARTICUNO, Species.ZAPDOS, Species.MOLTRES ].map(s => getPokemonSpecies(s));
|
||||
break;
|
||||
case PoolTier.LEGENDARY:
|
||||
return [ Species.HO_OH, Species.RAYQUAZA ].map(s => getPokemonSpecies(s));
|
||||
}
|
||||
return p.isOfType(Type.FLYING) || p.isOfType(Type.GROUND) || p.isOfType(Type.ROCK) || p.isOfType(Type.ELECTRIC) || p.isOfType(Type.STEEL);
|
||||
},
|
||||
[ArenaType.LAND]: (p, t) => {
|
||||
switch (t) {
|
||||
case PoolTier.ULTRA_RARE:
|
||||
return [ Species.GROUDON ].map(s => getPokemonSpecies(s));
|
||||
}
|
||||
return p.isOfType(Type.GROUND);
|
||||
},
|
||||
[ArenaType.CAVE]: (p, t) => {
|
||||
switch (t) {
|
||||
case PoolTier.ULTRA_RARE:
|
||||
return [ Species.REGIROCK, Species.REGICE, Species.REGISTEEL ].map(s => getPokemonSpecies(s));
|
||||
case PoolTier.LEGENDARY:
|
||||
return [ Species.MEWTWO, Species.REGIGIGAS ].map(s => getPokemonSpecies(s));
|
||||
}
|
||||
return p.isOfType(Type.ROCK);
|
||||
},
|
||||
[ArenaType.DESERT]: (p, t) => {
|
||||
return p.isOfType(Type.GROUND) || p.isOfType(Type.ROCK);
|
||||
},
|
||||
[ArenaType.ICE_CAVE]: (p, t) => {
|
||||
return p.isOfType(Type.ICE);
|
||||
},
|
||||
[ArenaType.MEADOW]: (p, t) => {
|
||||
return p.isOfType(Type.FAIRY);
|
||||
},
|
||||
[ArenaType.POWER_PLANT]: (p, t) => {
|
||||
return p.isOfType(Type.ELECTRIC);
|
||||
},
|
||||
[ArenaType.VOLCANO]: (p, t) => {
|
||||
return p.isOfType(Type.FIRE);
|
||||
},
|
||||
[ArenaType.GRAVEYARD]: (p, t) => {
|
||||
return p.isOfType(Type.GHOST);
|
||||
},
|
||||
[ArenaType.DOJO]: (p, t) => {
|
||||
return p.isOfType(Type.FIGHTING);
|
||||
},
|
||||
[ArenaType.RUINS]: (p, t) => {
|
||||
return p.isOfType(Type.PSYCHIC);
|
||||
},
|
||||
[ArenaType.ABYSS]: (p, t) => {
|
||||
return p.isOfType(Type.DARK);
|
||||
}
|
||||
};
|
@ -6,7 +6,7 @@ import { Mode } from './ui/ui';
|
||||
import { Command } from "./ui/command-ui-handler";
|
||||
import { interp } from "./temp_interpreter";
|
||||
import { Stat } from "./pokemon-stat";
|
||||
import { ExpBoosterModifier, ExtraModifierModifier, getModifierTypesForWave, ModifierType, PokemonModifierType, regenerateModifierPoolThresholds } from "./modifier";
|
||||
import { ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, getModifierTypesForWave, ModifierType, PokemonModifierType, regenerateModifierPoolThresholds } from "./modifier";
|
||||
import PartyUiHandler from "./ui/party-ui-handler";
|
||||
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor as getPokeballTintColor, PokeballType } from "./pokeball";
|
||||
import { pokemonLevelMoves } from "./pokemon-level-moves";
|
||||
@ -68,14 +68,12 @@ export class NextEncounterPhase extends BattlePhase {
|
||||
start() {
|
||||
super.start();
|
||||
|
||||
this.scene.waveIndex++;
|
||||
|
||||
console.log(this.scene.getPlayerPokemon(), this.scene.getParty().map(p => p.name), this.scene.getPlayerPokemon().id)
|
||||
|
||||
this.scene.getEnemyPokemon().destroy();
|
||||
const newEnemyPokemon = new EnemyPokemon(this.scene, this.scene.randomSpecies(true), this.scene.getLevelForWave());
|
||||
const newEnemyPokemon = new EnemyPokemon(this.scene, this.scene.randomSpecies(true), this.scene.getLevelForNextWave());
|
||||
newEnemyPokemon.loadAssets().then(() => {
|
||||
this.scene.setEnemyPokemon(newEnemyPokemon);
|
||||
this.scene.newBattle(newEnemyPokemon);
|
||||
this.scene.field.add(newEnemyPokemon);
|
||||
this.scene.field.moveBelow(newEnemyPokemon, this.scene.getPlayerPokemon());
|
||||
newEnemyPokemon.tint(0, 0.5);
|
||||
@ -169,7 +167,10 @@ export class SummonPhase extends BattlePhase {
|
||||
onComplete: () => {
|
||||
playerPokemon.cry();
|
||||
playerPokemon.getSprite().clearTint();
|
||||
this.scene.time.delayedCall(1000, () => this.end());
|
||||
this.scene.time.delayedCall(1000, () => {
|
||||
this.scene.currentBattle.addParticipant(playerPokemon);
|
||||
this.end();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -245,7 +246,13 @@ export class CheckSwitchPhase extends BattlePhase {
|
||||
super.start();
|
||||
|
||||
this.scene.ui.showText('Will you switch\nPOKéMON?', null, () => {
|
||||
this.scene.ui.setMode(Mode.SWITCH_CHECK, () => this.end());
|
||||
this.scene.ui.setMode(Mode.SWITCH_CHECK, () => {
|
||||
console.log('handler', this.scene.ui.getHandler());
|
||||
console.log(this.scene.ui.getHandler().getCursor())
|
||||
if (this.scene.ui.getHandler().getCursor())
|
||||
this.scene.currentBattle.addParticipant(this.scene.getPlayerPokemon());
|
||||
this.end();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -321,6 +328,20 @@ export abstract class PokemonPhase extends BattlePhase {
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class PartyMemberPokemonPhase extends PokemonPhase {
|
||||
protected partyMemberIndex: integer;
|
||||
|
||||
constructor(scene: BattleScene, partyMemberIndex: integer) {
|
||||
super(scene, true);
|
||||
|
||||
this.partyMemberIndex = partyMemberIndex;
|
||||
}
|
||||
|
||||
getPokemon() {
|
||||
return this.scene.getParty()[this.partyMemberIndex];
|
||||
}
|
||||
}
|
||||
|
||||
abstract class MovePhase extends BattlePhase {
|
||||
protected pokemon: Pokemon;
|
||||
protected move: PokemonMove;
|
||||
@ -474,6 +495,8 @@ export class FaintPhase extends PokemonPhase {
|
||||
onComplete: () => {
|
||||
pokemon.setVisible(false);
|
||||
pokemon.y -= 150;
|
||||
if (pokemon instanceof PlayerPokemon)
|
||||
this.scene.currentBattle.removeFaintedParticipant(pokemon as PlayerPokemon);
|
||||
this.scene.field.remove(pokemon);
|
||||
this.end();
|
||||
}
|
||||
@ -490,8 +513,28 @@ export class VictoryPhase extends PokemonPhase {
|
||||
start() {
|
||||
super.start();
|
||||
|
||||
if (this.scene.getPlayerPokemon().level < 100)
|
||||
this.scene.unshiftPhase(new ExpPhase(this.scene));
|
||||
const participantIds = this.scene.currentBattle.playerParticipantIds;
|
||||
const party = this.scene.getParty();
|
||||
const expShareModifier = this.scene.getModifier(ExpShareModifier) as ExpShareModifier;
|
||||
const expValue = this.scene.getEnemyPokemon().getExpValue(party[0]);
|
||||
for (let pm = 0; pm < party.length; pm++) {
|
||||
const pokemon = party[pm];
|
||||
if (!pokemon.hp)
|
||||
continue;
|
||||
const pId = pokemon.id;
|
||||
const participated = participantIds.has(pId);
|
||||
if (!participated && !expShareModifier)
|
||||
continue;
|
||||
if (pokemon.level < 100) {
|
||||
let expMultiplier = 0;
|
||||
if (participated)
|
||||
expMultiplier += (1 / participantIds.size);
|
||||
if (expShareModifier)
|
||||
expMultiplier += expShareModifier.stackCount * 0.1;
|
||||
console.log(pokemon.species.name, expMultiplier)
|
||||
this.scene.unshiftPhase(new ExpPhase(this.scene, pm, expValue * expMultiplier));
|
||||
}
|
||||
}
|
||||
this.scene.unshiftPhase(new SelectModifierPhase(this.scene));
|
||||
this.scene.unshiftPhase(new NextEncounterPhase(this.scene));
|
||||
|
||||
@ -522,17 +565,22 @@ export class SwitchPhase extends BattlePhase {
|
||||
}
|
||||
}
|
||||
|
||||
export class ExpPhase extends PokemonPhase {
|
||||
constructor(scene: BattleScene) {
|
||||
super(scene, true);
|
||||
export class ExpPhase extends PartyMemberPokemonPhase {
|
||||
private expValue: number;
|
||||
|
||||
constructor(scene: BattleScene, partyMemberIndex: integer, expValue: number) {
|
||||
super(scene, partyMemberIndex);
|
||||
|
||||
this.expValue = expValue;
|
||||
}
|
||||
|
||||
start() {
|
||||
super.start();
|
||||
|
||||
const pokemon = this.getPokemon();
|
||||
let exp = new Utils.IntegerHolder(this.scene.getEnemyPokemon().getExpValue(pokemon.level));
|
||||
let exp = new Utils.NumberHolder(this.expValue);
|
||||
this.scene.applyModifiers(ExpBoosterModifier, exp);
|
||||
exp.value = Math.floor(exp.value);
|
||||
this.scene.ui.showText(`${pokemon.name} gained\n${exp.value} EXP. Points!`, null, () => {
|
||||
const lastLevel = pokemon.level;
|
||||
let newLevel: integer;
|
||||
@ -543,6 +591,8 @@ export class ExpPhase extends PokemonPhase {
|
||||
pokemon.updateInfo(() => this.end());
|
||||
}, null, true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class LevelUpPhase extends PokemonPhase {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Phaser from 'phaser';
|
||||
import { ArenaType, Arena } from './arena';
|
||||
import { Biome, BiomeArena } from './biome';
|
||||
import UI from './ui/ui';
|
||||
import { BattlePhase, EncounterPhase, SummonPhase, CommandPhase } from './battle-phase';
|
||||
import { PlayerPokemon, EnemyPokemon } from './pokemon';
|
||||
@ -9,6 +9,7 @@ import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, P
|
||||
import { PokeballType } from './pokeball';
|
||||
import { Species } from './species';
|
||||
import { initAutoPlay } from './auto-play';
|
||||
import { Battle } from './battle';
|
||||
|
||||
export default class BattleScene extends Phaser.Scene {
|
||||
private auto: boolean;
|
||||
@ -17,7 +18,7 @@ export default class BattleScene extends Phaser.Scene {
|
||||
private phaseQueue: Array<BattlePhase>;
|
||||
private phaseQueuePrepend: Array<BattlePhase>;
|
||||
private currentPhase: BattlePhase;
|
||||
private arena: Arena;
|
||||
private arena: BiomeArena;
|
||||
public field: Phaser.GameObjects.Container;
|
||||
public fieldUI: Phaser.GameObjects.Container;
|
||||
public arenaBg: Phaser.GameObjects.Image;
|
||||
@ -25,12 +26,11 @@ export default class BattleScene extends Phaser.Scene {
|
||||
public arenaEnemy: Phaser.GameObjects.Image;
|
||||
public arenaEnemy2: Phaser.GameObjects.Image;
|
||||
public trainer: Phaser.GameObjects.Sprite;
|
||||
public waveIndex: integer;
|
||||
public currentBattle: Battle;
|
||||
public pokeballCounts = Object.fromEntries(Utils.getEnumValues(PokeballType).map(t => [ t, 0 ]));
|
||||
private party: PlayerPokemon[];
|
||||
private modifierBar: ModifierBar;
|
||||
private modifiers: Modifier[];
|
||||
private enemyPokemon: EnemyPokemon;
|
||||
public uiContainer: Phaser.GameObjects.Container;
|
||||
public ui: UI;
|
||||
|
||||
@ -117,8 +117,8 @@ export default class BattleScene extends Phaser.Scene {
|
||||
this.loadAtlas('party_cancel', 'ui');
|
||||
|
||||
// Load arena images
|
||||
Utils.getEnumValues(ArenaType).map(at => {
|
||||
const atKey = ArenaType[at].toLowerCase();
|
||||
Utils.getEnumValues(Biome).map(at => {
|
||||
const atKey = Biome[at].toLowerCase();
|
||||
this.loadImage(`${atKey}_bg`, 'arenas', `${atKey}_bg.png`);
|
||||
this.loadImage(`${atKey}_a`, 'arenas', `${atKey}_a.png`);
|
||||
this.loadImage(`${atKey}_b`, 'arenas', `${atKey}_b.png`);
|
||||
@ -177,15 +177,15 @@ export default class BattleScene extends Phaser.Scene {
|
||||
this.field = field;
|
||||
|
||||
// Init arena
|
||||
const arenas = Utils.getEnumValues(ArenaType).map(at => new Arena(this, at, ArenaType[at].toLowerCase()));
|
||||
const arenas = Utils.getEnumValues(Biome).map(at => new BiomeArena(this, at, Biome[at].toLowerCase()));
|
||||
const arena = arenas[Utils.randInt(11)];
|
||||
|
||||
this.arena = arena;
|
||||
|
||||
this.arenaBg = this.add.image(0, 0, `${ArenaType[arena.arenaType].toLowerCase()}_bg`);
|
||||
this.arenaPlayer = this.add.image(340, 20, `${ArenaType[arena.arenaType].toLowerCase()}_a`);
|
||||
this.arenaEnemy = this.add.image(-240, 13, `${ArenaType[arena.arenaType].toLowerCase()}_b`);
|
||||
this.arenaEnemy2 = this.add.image(-240, 13, `${ArenaType[arena.arenaType].toLowerCase()}_b`);
|
||||
this.arenaBg = this.add.image(0, 0, `${Biome[arena.biomeType].toLowerCase()}_bg`);
|
||||
this.arenaPlayer = this.add.image(340, 20, `${Biome[arena.biomeType].toLowerCase()}_a`);
|
||||
this.arenaEnemy = this.add.image(-240, 13, `${Biome[arena.biomeType].toLowerCase()}_b`);
|
||||
this.arenaEnemy2 = this.add.image(-240, 13, `${Biome[arena.biomeType].toLowerCase()}_b`);
|
||||
|
||||
[this.arenaBg, this.arenaPlayer, this.arenaEnemy, this.arenaEnemy2].forEach(a => {
|
||||
a.setOrigin(0, 0);
|
||||
@ -209,8 +209,6 @@ export default class BattleScene extends Phaser.Scene {
|
||||
this.add.existing(this.modifierBar);
|
||||
uiContainer.add(this.modifierBar);
|
||||
|
||||
this.waveIndex = 1;
|
||||
|
||||
this.party = [];
|
||||
|
||||
let loadPokemonAssets = [];
|
||||
@ -226,11 +224,12 @@ export default class BattleScene extends Phaser.Scene {
|
||||
|
||||
const enemySpecies = arena.randomSpecies(1);
|
||||
console.log(enemySpecies.name);
|
||||
const enemyPokemon = new EnemyPokemon(this, enemySpecies, this.getLevelForWave());
|
||||
const enemyPokemon = new EnemyPokemon(this, enemySpecies, this.getLevelForNextWave());
|
||||
loadPokemonAssets.push(enemyPokemon.loadAssets());
|
||||
|
||||
this.add.existing(enemyPokemon);
|
||||
this.enemyPokemon = enemyPokemon;
|
||||
|
||||
this.newBattle(enemyPokemon);
|
||||
|
||||
field.add(enemyPokemon);
|
||||
|
||||
@ -297,11 +296,12 @@ export default class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
getEnemyPokemon(): EnemyPokemon {
|
||||
return this.enemyPokemon;
|
||||
return this.currentBattle.enemyPokemon;
|
||||
}
|
||||
|
||||
setEnemyPokemon(enemyPokemon: EnemyPokemon) {
|
||||
this.enemyPokemon = enemyPokemon;
|
||||
newBattle(enemyPokemon: EnemyPokemon): Battle {
|
||||
this.currentBattle = new Battle((this.currentBattle?.waveIndex || 0) + 1, enemyPokemon);
|
||||
return this.currentBattle;
|
||||
}
|
||||
|
||||
randomSpecies(fromArenaPool?: boolean): PokemonSpecies {
|
||||
@ -310,13 +310,14 @@ export default class BattleScene extends Phaser.Scene {
|
||||
: allSpecies[(Utils.randInt(allSpecies.length)) - 1];
|
||||
}
|
||||
|
||||
getLevelForWave() {
|
||||
let averageLevel = 1 + this.waveIndex * 0.25;
|
||||
getLevelForNextWave() {
|
||||
const waveIndex = (this.currentBattle?.waveIndex || 0) + 1;
|
||||
let averageLevel = 1 + waveIndex * 0.25;
|
||||
|
||||
if (this.waveIndex % 10 === 0)
|
||||
if (waveIndex % 10 === 0)
|
||||
return Math.floor(averageLevel * 1.25);
|
||||
|
||||
const deviation = 10 / this.waveIndex;
|
||||
const deviation = 10 / waveIndex;
|
||||
|
||||
return Math.max(Math.round(averageLevel + Utils.randGauss(deviation)), 1);
|
||||
}
|
||||
@ -421,6 +422,10 @@ export default class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
}
|
||||
|
||||
getModifier(modifierType: { new(...args: any[]): Modifier }): Modifier {
|
||||
return this.modifiers.find(m => m instanceof modifierType);
|
||||
}
|
||||
|
||||
applyModifiers(modifierType: { new(...args: any[]): Modifier }, ...args: any[]): void {
|
||||
const modifiers = this.modifiers.filter(m => m instanceof modifierType && m.shouldApply(args));
|
||||
for (let modifier of modifiers) {
|
||||
|
21
src/battle.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { EnemyPokemon, PlayerPokemon } from "./pokemon";
|
||||
|
||||
export class Battle {
|
||||
public waveIndex: integer;
|
||||
public enemyPokemon: EnemyPokemon;
|
||||
public playerParticipantIds: Set<integer> = new Set<integer>();
|
||||
|
||||
constructor(waveIndex: integer, enemyPokemon: EnemyPokemon) {
|
||||
this.waveIndex = waveIndex;
|
||||
this.enemyPokemon = enemyPokemon;
|
||||
}
|
||||
|
||||
addParticipant(playerPokemon: PlayerPokemon) {
|
||||
console.log('add participant', playerPokemon.name)
|
||||
this.playerParticipantIds.add(playerPokemon.id);
|
||||
}
|
||||
|
||||
removeFaintedParticipant(playerPokemon: PlayerPokemon) {
|
||||
this.playerParticipantIds.delete(playerPokemon.id);
|
||||
}
|
||||
}
|
3414
src/biome.ts
Normal file
@ -61,9 +61,14 @@ export abstract class Modifier {
|
||||
abstract apply(args: any[]): boolean;
|
||||
|
||||
incrementStack(): void {
|
||||
if (this.stackCount < this.getMaxStackCount())
|
||||
this.stackCount++;
|
||||
}
|
||||
|
||||
getMaxStackCount(): integer {
|
||||
return 99;
|
||||
}
|
||||
|
||||
getIcon(scene: BattleScene): Phaser.GameObjects.Container {
|
||||
const container = scene.add.container(0, 0);
|
||||
|
||||
@ -83,7 +88,7 @@ export abstract class Modifier {
|
||||
if (this.stackCount <= 1)
|
||||
return null;
|
||||
|
||||
const text = addTextObject(scene, 16, 12, this.stackCount.toString(), TextStyle.PARTY, { fontSize: '66px' });
|
||||
const text = addTextObject(scene, 16, 12, this.stackCount.toString(), TextStyle.PARTY, { fontSize: '66px', color: this.stackCount < this.getMaxStackCount() ? '#484848' : '#e64a18' });
|
||||
text.setStroke('#424242', 16)
|
||||
text.setOrigin(1, 0);
|
||||
|
||||
@ -101,7 +106,7 @@ export abstract class ConsumableModifier extends Modifier {
|
||||
}
|
||||
|
||||
shouldApply(args: any[]): boolean {
|
||||
return args.length === 1 && args[0] instanceof BattleScene;
|
||||
return super.shouldApply(args) && args.length === 1 && args[0] instanceof BattleScene;
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,7 +139,7 @@ export abstract class PokemonModifier extends Modifier {
|
||||
}
|
||||
|
||||
shouldApply(args: any[]): boolean {
|
||||
return args.length && args[0] === this.pokemonId;
|
||||
return super.shouldApply(args) && args.length && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId);
|
||||
}
|
||||
|
||||
getIcon(scene: BattleScene): Phaser.GameObjects.Container {
|
||||
@ -180,12 +185,11 @@ export class PokemonBaseStatModifier extends PokemonModifier {
|
||||
}
|
||||
|
||||
shouldApply(args: any[]): boolean {
|
||||
console.log(args, this.pokemonId, args)
|
||||
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Array<integer>;
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
args[1][this.stat] = Math.min(Math.floor(args[1][this.stat] * (1 + this.stackCount * 0.1)), 999999);
|
||||
args[1][this.stat] = Math.min(Math.floor(args[1][this.stat] * (1 + this.stackCount * 0.2)), 999999);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -215,10 +219,6 @@ export abstract class ConsumablePokemonModifier extends PokemonModifier {
|
||||
add(_modifierBar: ModifierBar, _modifiers: Modifier[]): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
shouldApply(args: any[]): boolean {
|
||||
return args.length === 1 && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId);
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonHpRestoreModifier extends ConsumablePokemonModifier {
|
||||
@ -266,12 +266,26 @@ export class ExpBoosterModifier extends Modifier {
|
||||
}
|
||||
|
||||
apply(args: any[]): boolean {
|
||||
(args[0] as Utils.IntegerHolder).value = Math.floor((args[0] as Utils.IntegerHolder).value * (1 + (this.stackCount * (this.boostMultiplier))));
|
||||
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (1 + (this.stackCount * (this.boostMultiplier))));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExpShareModifier extends Modifier {
|
||||
constructor(type: ModifierType) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
apply(_args: any[]): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
getMaxStackCount(): integer {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
export class ShinyRateBoosterModifier extends Modifier {
|
||||
constructor(type: ModifierType) {
|
||||
super(type);
|
||||
@ -295,6 +309,10 @@ export class ShinyRateBoosterModifier extends Modifier {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getMaxStackCount(): integer {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtraModifierModifier extends Modifier {
|
||||
@ -361,7 +379,7 @@ export class PokemonHpRestoreModifierType extends PokemonModifierType {
|
||||
|
||||
constructor(name: string, restorePercent: integer, newModifierFunc?: Function, iconImage?: string) {
|
||||
super(name, `Restore ${restorePercent} HP or ${restorePercent}% HP for one POKéMON, whichever is higher`,
|
||||
newModifierFunc || ((_type, args) => new PokemonHpRestoreModifier(this, args[0], this.restorePercent, false)),
|
||||
newModifierFunc || ((_type, args) => new PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePercent, false)),
|
||||
(pokemon: PlayerPokemon) => {
|
||||
if (pokemon.hp >= pokemon.getMaxHp())
|
||||
return PartyUiHandler.NoEffectMessage;
|
||||
@ -374,7 +392,7 @@ export class PokemonHpRestoreModifierType extends PokemonModifierType {
|
||||
|
||||
export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
|
||||
constructor(name: string, restorePercent: integer, iconImage?: string) {
|
||||
super(name, restorePercent, (_type, args) => new PokemonHpRestoreModifier(this, args[0], this.restorePercent, true), iconImage);
|
||||
super(name, restorePercent, (_type, args) => new PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePercent, true), iconImage);
|
||||
|
||||
this.description = `Revive one POKéMON and restore ${restorePercent}% HP`;
|
||||
this.selectFilter = (pokemon: PlayerPokemon) => {
|
||||
@ -389,7 +407,7 @@ export class PokemonBaseStatBoosterModifierType extends PokemonModifierType {
|
||||
private stat: Stat;
|
||||
|
||||
constructor(name: string, stat: Stat, _iconImage?: string) {
|
||||
super(name, `Increases one POKéMON's base ${getStatName(stat)} by 10%` , (_type, args) => new PokemonBaseStatModifier(this, args[0], this.stat));
|
||||
super(name, `Increases one POKéMON's base ${getStatName(stat)} by 20%` , (_type, args) => new PokemonBaseStatModifier(this, (args[0] as PlayerPokemon).id, this.stat));
|
||||
|
||||
this.stat = stat;
|
||||
}
|
||||
@ -439,23 +457,27 @@ const modifierPool = {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.getHpRatio() <= 0.75).length;
|
||||
return Math.ceil(thresholdPartyMemberCount / 3);
|
||||
}),
|
||||
].map(m => { m.setTier(ModifierTier.COMMON); return m; }),
|
||||
[ModifierTier.GREAT]: [
|
||||
new WeightedModifierType(new AddPokeballModifierType(PokeballType.GREAT_BALL, 5, 'gb'), 3),
|
||||
new WeightedModifierType(new PokemonReviveModifierType('REVIVE', 50), (party: Array<PlayerPokemon>) => {
|
||||
const faintedPartyMemberCount = party.filter(p => !p.hp).length;
|
||||
return faintedPartyMemberCount * 3;
|
||||
}),
|
||||
new WeightedModifierType(new PokemonReviveModifierType('MAX REVIVE', 100), (party: Array<PlayerPokemon>) => {
|
||||
const faintedPartyMemberCount = party.filter(p => !p.hp).length;
|
||||
return faintedPartyMemberCount;
|
||||
}),
|
||||
new WeightedModifierType(new PokemonHpRestoreModifierType('HYPER POTION', 80), (party: Array<PlayerPokemon>) => {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.getHpRatio() <= 0.6).length;
|
||||
return thresholdPartyMemberCount;
|
||||
}),
|
||||
new PokemonBaseStatBoosterModifierType('HP-UP', Stat.HP),
|
||||
new PokemonBaseStatBoosterModifierType('PROTEIN', Stat.ATK),
|
||||
new PokemonBaseStatBoosterModifierType('IRON', Stat.DEF),
|
||||
new PokemonBaseStatBoosterModifierType('CALCIUM', Stat.SPATK),
|
||||
new PokemonBaseStatBoosterModifierType('ZINC', Stat.SPDEF),
|
||||
new PokemonBaseStatBoosterModifierType('CARBOS', Stat.SPD)
|
||||
].map(m => { m.setTier(ModifierTier.COMMON); return m; }),
|
||||
[ModifierTier.GREAT]: [
|
||||
new AddPokeballModifierType(PokeballType.GREAT_BALL, 5, 'gb'),
|
||||
new WeightedModifierType(new PokemonReviveModifierType('REVIVE', 50), (party: Array<PlayerPokemon>) => {
|
||||
const faintedPartyMemberCount = party.filter(p => !p.hp).length;
|
||||
return faintedPartyMemberCount;
|
||||
}),
|
||||
new WeightedModifierType(new PokemonHpRestoreModifierType('HYPER POTION', 80), (party: Array<PlayerPokemon>) => {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.getHpRatio() <= 0.6).length;
|
||||
return Math.ceil(thresholdPartyMemberCount / 3);
|
||||
})
|
||||
].map(m => { m.setTier(ModifierTier.GREAT); return m; }),
|
||||
[ModifierTier.ULTRA]: [
|
||||
new AddPokeballModifierType(PokeballType.ULTRA_BALL, 5, 'ub'),
|
||||
@ -463,13 +485,14 @@ const modifierPool = {
|
||||
const thresholdPartyMemberCount = party.filter(p => p.getHpRatio() <= 0.5).length;
|
||||
return Math.ceil(thresholdPartyMemberCount / 3);
|
||||
}),
|
||||
new ExpBoosterModifierType('LUCKY EGG', 25)
|
||||
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
|
||||
[ModifierTier.MASTER]: [
|
||||
new AddPokeballModifierType(PokeballType.MASTER_BALL, 1, 'mb'),
|
||||
new WeightedModifierType(new AllPokemonFullReviveModifierType('SACRED ASH'), (party: Array<PlayerPokemon>) => {
|
||||
return party.filter(p => !p.hp).length >= Math.ceil(party.length / 2) ? 1 : 0;
|
||||
}),
|
||||
new ExpBoosterModifierType('LUCKY EGG', 25),
|
||||
new ModifierType('EXP. SHARE', 'All POKéMON in your party gain an additional 10% of a battle\'s EXP. Points', (type, _args) => new ExpShareModifier(type), 'exp_share')
|
||||
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
|
||||
[ModifierTier.MASTER]: [
|
||||
new AddPokeballModifierType(PokeballType.MASTER_BALL, 1, 'mb'),
|
||||
new WeightedModifierType(new ModifierType('SHINY CHARM', 'Dramatically increases the chance of a wild POKéMON being shiny', (type, _args) => new ShinyRateBoosterModifier(type)), 2)
|
||||
].map(m => { m.setTier(ModifierTier.MASTER); return m; }),
|
||||
[ModifierTier.LUXURY]: [
|
||||
|
@ -345,7 +345,7 @@ export const pokemonEvolutions = {
|
||||
new SpeciesEvolution(Species.KIRLIA, 20, null, null)
|
||||
],
|
||||
[Species.KIRLIA]: [
|
||||
new SpeciesEvolution(Species.GARDEVOIR, 30, null, null),
|
||||
new SpeciesEvolution(Species.GARDEVOIR, 30, null, new SpeciesEvolutionCondition((p: Pokemon) => !p.gender, true)),
|
||||
new SpeciesEvolution(Species.GALLADE, 1, "Dawn Stone", new SpeciesEvolutionCondition((p: Pokemon) => p.gender, true), SpeciesWildEvolutionRate.SLOW)
|
||||
],
|
||||
[Species.SURSKIT]: [
|
||||
|
@ -9,7 +9,7 @@ import * as Utils from './utils';
|
||||
import { getTypeDamageMultiplier } from './type';
|
||||
import { getLevelTotalExp } from './exp';
|
||||
import { Stat } from './pokemon-stat';
|
||||
import { PokemonBaseStatModifier as PokemonBaseStatBoosterModifier, ShinyRateBoosterModifier } from './modifier';
|
||||
import { ExpShareModifier, PokemonBaseStatModifier as PokemonBaseStatBoosterModifier, ShinyRateBoosterModifier } from './modifier';
|
||||
import { PokeballType } from './pokeball';
|
||||
|
||||
export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
@ -450,11 +450,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
});
|
||||
}
|
||||
|
||||
getExpValue(victorLevel: integer): integer {
|
||||
// Gen 1-4 formula
|
||||
// return ((this.pokemon.baseExp * this.level) / 7) * (1 / 1)
|
||||
// TODO: Update for exp share
|
||||
return Math.floor(((this.species.baseExp * this.level) / 5) * (1 / 1) * ((Math.round(Math.sqrt(2 * this.level + 10)) * Math.pow(2 * this.level + 10, 2)) / (Math.round(Math.sqrt(this.level + victorLevel + 10)) * Math.pow(this.level + victorLevel + 10, 2)))) + 1;
|
||||
getExpValue(victor: Pokemon): integer {
|
||||
return ((this.species.baseExp * this.level) / 5) * ((Math.round(Math.sqrt(2 * this.level + 10)) * Math.pow(2 * this.level + 10, 2)) / (Math.round(Math.sqrt(this.level + victor.level + 10)) * Math.pow(this.level + victor.level + 10, 2))) + 1;
|
||||
}
|
||||
|
||||
tint(color: number, alpha?: number, duration?: integer, ease?: string) {
|
||||
|
@ -24,6 +24,10 @@ export default abstract class UiHandler {
|
||||
return this.scene.ui;
|
||||
}
|
||||
|
||||
getCursor(): integer {
|
||||
return this.cursor;
|
||||
}
|
||||
|
||||
setCursor(cursor: integer): boolean {
|
||||
const changed = this.cursor !== cursor;
|
||||
if (changed)
|
||||
|
@ -58,6 +58,13 @@ export function getEnumValues(enumType) {
|
||||
return Object.values(enumType).filter(v => !isNaN(parseInt(v.toString()))).map(v => parseInt(v.toString()));
|
||||
}
|
||||
|
||||
export class NumberHolder {
|
||||
public value: number;
|
||||
|
||||
constructor(value: number) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
export class IntegerHolder {
|
||||
public value: integer;
|
||||
|
||||
|