Minor refactoring
This commit is contained in:
1 parent
8d314b199f
commit
f48464d48d
41 files changed
+115
-111
No files matched your search
@@ -0,0 +1,178 @@
|
||||
import BattleScene from "../battle-scene";
|
||||
import { PokeballType } from "../data/pokeball";
|
||||
import * as Utils from "../utils";
|
||||
|
||||
export function addPokeballOpenParticles(scene: BattleScene, x: number, y: number, pokeballType: PokeballType): void {
|
||||
switch (pokeballType) {
|
||||
case PokeballType.POKEBALL:
|
||||
doDefaultPbOpenParticles(scene, x, y, 48);
|
||||
break;
|
||||
case PokeballType.GREAT_BALL:
|
||||
doDefaultPbOpenParticles(scene, x, y, 96);
|
||||
break;
|
||||
case PokeballType.ULTRA_BALL:
|
||||
case PokeballType.ROGUE_BALL:
|
||||
doUbOpenParticles(scene, x, y);
|
||||
break;
|
||||
case PokeballType.MASTER_BALL:
|
||||
doMbOpenParticles(scene, x, y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function doDefaultPbOpenParticles(scene: BattleScene, x: number, y: number, radius: number) {
|
||||
const pbOpenParticlesFrameNames = scene.anims.generateFrameNames('pb_particles', { start: 0, end: 3, suffix: '.png' });
|
||||
scene.anims.create({
|
||||
key: 'pb_open_particle',
|
||||
frames: pbOpenParticlesFrameNames,
|
||||
frameRate: 16,
|
||||
repeat: -1
|
||||
});
|
||||
|
||||
const addParticle = (index: integer) => {
|
||||
const particle = scene.add.sprite(x, y, 'pb_open_particle');
|
||||
scene.field.add(particle);
|
||||
const angle = index * 45;
|
||||
const [ xCoord, yCoord ] = [ radius * Math.cos(angle * Math.PI / 180), radius * Math.sin(angle * Math.PI / 180) ];
|
||||
scene.tweens.add({
|
||||
targets: particle,
|
||||
x: x + xCoord,
|
||||
y: y + yCoord,
|
||||
duration: 575
|
||||
});
|
||||
particle.play({
|
||||
key: 'pb_open_particle',
|
||||
startFrame: (index + 3) % 4,
|
||||
frameRate: Math.floor(16 * scene.gameSpeed)
|
||||
});
|
||||
scene.tweens.add({
|
||||
targets: particle,
|
||||
delay: 500,
|
||||
duration: 75,
|
||||
alpha: 0,
|
||||
ease: 'Sine.easeIn',
|
||||
onComplete: () => particle.destroy()
|
||||
});
|
||||
};
|
||||
|
||||
let particleCount = 0;
|
||||
scene.time.addEvent({
|
||||
delay: 20,
|
||||
repeat: 16,
|
||||
callback: () => addParticle(++particleCount)
|
||||
});
|
||||
}
|
||||
|
||||
function doUbOpenParticles(scene: BattleScene, x: number, y: number) {
|
||||
let particles: Phaser.GameObjects.Image[] = [];
|
||||
for (let i = 0; i < 10; i++)
|
||||
particles.push(doFanOutParticle(scene, i * 25, x, y, 1, 1, 5, 8));
|
||||
|
||||
scene.tweens.add({
|
||||
targets: particles,
|
||||
delay: 750,
|
||||
duration: 250,
|
||||
alpha: 0,
|
||||
ease: 'Sine.easeIn',
|
||||
onComplete: () => {
|
||||
for (let particle of particles)
|
||||
particle.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function doMbOpenParticles(scene: BattleScene, x: number, y: number) {
|
||||
let particles: Phaser.GameObjects.Image[] = [];
|
||||
for (let j = 0; j < 2; j++) {
|
||||
for (let i = 0; i < 8; i++)
|
||||
particles.push(doFanOutParticle(scene, i * 32, x, y, j ? 1 : 2, j ? 2 : 1, 8, 4));
|
||||
|
||||
scene.tweens.add({
|
||||
targets: particles,
|
||||
delay: 750,
|
||||
duration: 250,
|
||||
alpha: 0,
|
||||
ease: 'Sine.easeIn',
|
||||
onComplete: () => {
|
||||
for (let particle of particles)
|
||||
particle.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function doFanOutParticle(scene: BattleScene, trigIndex: integer, x: integer, y: integer, xSpeed: integer, ySpeed: integer, angle: integer, frameIndex: integer): Phaser.GameObjects.Image {
|
||||
let f = 0;
|
||||
|
||||
const particle = scene.add.image(x, y, 'pb_particles', `${frameIndex}.png`);
|
||||
scene.field.add(particle);
|
||||
|
||||
const updateParticle = () => {
|
||||
if (!particle.scene)
|
||||
return particleTimer.remove();
|
||||
particle.x = x + sin(trigIndex, f * xSpeed);
|
||||
particle.y = y + cos(trigIndex, f * ySpeed);
|
||||
trigIndex = (trigIndex + angle);
|
||||
f++;
|
||||
};
|
||||
|
||||
const particleTimer = scene.tweens.addCounter({
|
||||
repeat: -1,
|
||||
duration: Utils.getFrameMs(1),
|
||||
onRepeat: () => {
|
||||
updateParticle();
|
||||
}
|
||||
});
|
||||
|
||||
return particle;
|
||||
}
|
||||
|
||||
export function addPokeballCaptureStars(scene: BattleScene, pokeball: Phaser.GameObjects.Sprite): void {
|
||||
const addParticle = () => {
|
||||
const particle = scene.add.sprite(pokeball.x, pokeball.y, 'pb_particles', '4.png');
|
||||
particle.setOrigin(pokeball.originX, pokeball.originY);
|
||||
particle.setAlpha(0.5);
|
||||
scene.field.add(particle);
|
||||
|
||||
scene.tweens.add({
|
||||
targets: particle,
|
||||
y: pokeball.y - 10,
|
||||
ease: 'Sine.easeOut',
|
||||
duration: 250,
|
||||
onComplete: () => {
|
||||
scene.tweens.add({
|
||||
targets: particle,
|
||||
y: pokeball.y,
|
||||
alpha: 0,
|
||||
ease: 'Sine.easeIn',
|
||||
duration: 250
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const dist = Utils.randGauss(25);
|
||||
scene.tweens.add({
|
||||
targets: particle,
|
||||
x: pokeball.x + dist,
|
||||
duration: 500
|
||||
});
|
||||
|
||||
scene.tweens.add({
|
||||
targets: particle,
|
||||
alpha: 0,
|
||||
delay: 425,
|
||||
duration: 75,
|
||||
onComplete: () => particle.destroy()
|
||||
});
|
||||
};
|
||||
|
||||
new Array(3).fill(null).map(() => addParticle());
|
||||
}
|
||||
|
||||
export function sin(index: integer, amplitude: integer): number {
|
||||
return amplitude * Math.sin(index * (Math.PI / 128));
|
||||
}
|
||||
|
||||
export function cos(index: integer, amplitude: integer): number {
|
||||
return amplitude * Math.cos(index * (Math.PI / 128));
|
||||
}
|
||||
@@ -0,0 +1,638 @@
|
||||
import BattleScene from "../battle-scene";
|
||||
import { BiomePoolTier, BiomeTierPokemonPools, PokemonPools, BiomeTierTrainerPools, biomePokemonPools, biomeTrainerPools } from "../data/biomes";
|
||||
import { Biome } from "../data/enums/biome";
|
||||
import * as Utils from "../utils";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "../data/pokemon-species";
|
||||
import { Species } from "../data/enums/species";
|
||||
import { Weather, WeatherType, getWeatherClearMessage, getWeatherStartMessage } from "../data/weather";
|
||||
import { CommonAnimPhase } from "../phases";
|
||||
import { CommonAnim } from "../data/battle-anims";
|
||||
import { Type } from "../data/type";
|
||||
import Move from "../data/move";
|
||||
import { ArenaTag, ArenaTagSide, getArenaTag } from "../data/arena-tag";
|
||||
import { ArenaTagType } from "../data/enums/arena-tag-type";
|
||||
import { GameMode } from "../game-mode";
|
||||
import { TrainerType } from "../data/enums/trainer-type";
|
||||
import { BattlerIndex } from "../battle";
|
||||
import { Moves } from "../data/enums/moves";
|
||||
import { TimeOfDay } from "../data/enums/time-of-day";
|
||||
|
||||
const WEATHER_OVERRIDE = WeatherType.NONE;
|
||||
|
||||
export class Arena {
|
||||
public scene: BattleScene;
|
||||
public biomeType: Biome;
|
||||
public weather: Weather;
|
||||
public tags: ArenaTag[];
|
||||
public bgm: string;
|
||||
|
||||
private lastTimeOfDay: TimeOfDay;
|
||||
|
||||
private pokemonPool: PokemonPools;
|
||||
private trainerPool: BiomeTierTrainerPools;
|
||||
|
||||
constructor(scene: BattleScene, biome: Biome, bgm: string) {
|
||||
this.scene = scene;
|
||||
this.biomeType = biome;
|
||||
this.tags = [];
|
||||
this.bgm = bgm;
|
||||
this.trainerPool = biomeTrainerPools[biome];
|
||||
this.updatePoolsForTimeOfDay();
|
||||
}
|
||||
|
||||
updatePoolsForTimeOfDay(): void {
|
||||
const timeOfDay = this.getTimeOfDay();
|
||||
if (timeOfDay !== this.lastTimeOfDay) {
|
||||
this.pokemonPool = {};
|
||||
for (let tier of Object.keys(biomePokemonPools[this.biomeType]))
|
||||
this.pokemonPool[tier] = Object.assign([], biomePokemonPools[this.biomeType][tier][TimeOfDay.ALL]).concat(biomePokemonPools[this.biomeType][tier][timeOfDay]);
|
||||
this.lastTimeOfDay = timeOfDay;
|
||||
}
|
||||
}
|
||||
|
||||
randomSpecies(waveIndex: integer, level: integer, attempt?: integer): PokemonSpecies {
|
||||
const isBoss = !!this.scene.getEncounterBossSegments(waveIndex, level) && !!this.pokemonPool[BiomePoolTier.BOSS].length
|
||||
&& (this.biomeType !== Biome.END || this.scene.gameMode === GameMode.CLASSIC || waveIndex % 250 === 0);
|
||||
const tierValue = Utils.randSeedInt(!isBoss ? 512 : 64);
|
||||
let tier = !isBoss
|
||||
? tierValue >= 156 ? BiomePoolTier.COMMON : tierValue >= 32 ? BiomePoolTier.UNCOMMON : tierValue >= 6 ? BiomePoolTier.RARE : tierValue >= 1 ? BiomePoolTier.SUPER_RARE : BiomePoolTier.ULTRA_RARE
|
||||
: tierValue >= 20 ? BiomePoolTier.BOSS : tierValue >= 6 ? BiomePoolTier.BOSS_RARE : tierValue >= 1 ? BiomePoolTier.BOSS_SUPER_RARE : BiomePoolTier.BOSS_ULTRA_RARE;
|
||||
console.log(BiomePoolTier[tier]);
|
||||
while (!this.pokemonPool[tier].length) {
|
||||
console.log(`Downgraded rarity tier from ${BiomePoolTier[tier]} to ${BiomePoolTier[tier - 1]}`);
|
||||
tier--;
|
||||
}
|
||||
const tierPool = this.pokemonPool[tier];
|
||||
let ret: PokemonSpecies;
|
||||
let regen = false;
|
||||
if (!tierPool.length)
|
||||
ret = this.scene.randomSpecies(waveIndex, level);
|
||||
else {
|
||||
const entry = tierPool[Utils.randSeedInt(tierPool.length)];
|
||||
let species: Species;
|
||||
if (typeof entry === 'number')
|
||||
species = entry as Species;
|
||||
else {
|
||||
const levelThresholds = Object.keys(entry);
|
||||
for (let l = levelThresholds.length - 1; l >= 0; l--) {
|
||||
const levelThreshold = parseInt(levelThresholds[l]);
|
||||
if (level >= levelThreshold) {
|
||||
const speciesIds = entry[levelThreshold];
|
||||
if (speciesIds.length > 1)
|
||||
species = speciesIds[Utils.randSeedInt(speciesIds.length)];
|
||||
else
|
||||
species = speciesIds[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = getPokemonSpecies(species);
|
||||
|
||||
if (ret.pseudoLegendary || ret.legendary || ret.mythical) {
|
||||
switch (true) {
|
||||
case (ret.baseTotal >= 720):
|
||||
regen = level < 90;
|
||||
break;
|
||||
case (ret.baseTotal >= 670):
|
||||
regen = level < 70;
|
||||
break;
|
||||
case (ret.baseTotal >= 580):
|
||||
regen = level < 50;
|
||||
break;
|
||||
default:
|
||||
regen = level < 30;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (regen && (attempt || 0) < 10) {
|
||||
console.log('Incompatible level: regenerating...');
|
||||
return this.randomSpecies(waveIndex, level, (attempt || 0) + 1);
|
||||
}
|
||||
|
||||
const newSpeciesId = ret.getSpeciesForLevel(level, true, false, isBoss);
|
||||
if (newSpeciesId !== ret.speciesId) {
|
||||
console.log('Replaced', Species[ret.speciesId], 'with', Species[newSpeciesId]);
|
||||
ret = getPokemonSpecies(newSpeciesId);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
randomTrainerType(waveIndex: integer): TrainerType {
|
||||
const isBoss = (waveIndex % 30) === 20 && !!this.trainerPool[BiomePoolTier.BOSS].length
|
||||
&& (this.biomeType !== Biome.END || this.scene.gameMode === GameMode.CLASSIC || waveIndex % 250 === 0);
|
||||
const tierValue = Utils.randSeedInt(!isBoss ? 512 : 64);
|
||||
let tier = !isBoss
|
||||
? tierValue >= 156 ? BiomePoolTier.COMMON : tierValue >= 32 ? BiomePoolTier.UNCOMMON : tierValue >= 6 ? BiomePoolTier.RARE : tierValue >= 1 ? BiomePoolTier.SUPER_RARE : BiomePoolTier.ULTRA_RARE
|
||||
: tierValue >= 20 ? BiomePoolTier.BOSS : tierValue >= 6 ? BiomePoolTier.BOSS_RARE : tierValue >= 1 ? BiomePoolTier.BOSS_SUPER_RARE : BiomePoolTier.BOSS_ULTRA_RARE;
|
||||
console.log(BiomePoolTier[tier]);
|
||||
while (tier && !this.trainerPool[tier].length) {
|
||||
console.log(`Downgraded trainer rarity tier from ${BiomePoolTier[tier]} to ${BiomePoolTier[tier - 1]}`);
|
||||
tier--;
|
||||
}
|
||||
const tierPool = this.trainerPool[tier] || [];
|
||||
return !tierPool.length ? TrainerType.BREEDER : tierPool[Utils.randSeedInt(tierPool.length)];
|
||||
}
|
||||
|
||||
getSpeciesFormIndex(species: PokemonSpecies): integer {
|
||||
switch (species.speciesId) {
|
||||
case Species.BURMY:
|
||||
case Species.WORMADAM:
|
||||
switch (this.biomeType) {
|
||||
case Biome.BEACH:
|
||||
return 1;
|
||||
case Biome.SLUM:
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
case Species.SCATTERBUG:
|
||||
case Species.SPEWPA:
|
||||
case Species.VIVILLON:
|
||||
return 0;
|
||||
case Species.LYCANROC:
|
||||
const timeOfDay = this.getTimeOfDay();
|
||||
switch (timeOfDay) {
|
||||
case TimeOfDay.DAY:
|
||||
case TimeOfDay.DAWN:
|
||||
return 0;
|
||||
case TimeOfDay.DUSK:
|
||||
return 2;
|
||||
case TimeOfDay.NIGHT:
|
||||
return 1;
|
||||
}
|
||||
case Species.CALYREX:
|
||||
switch (this.biomeType) {
|
||||
case Biome.SNOWY_FOREST:
|
||||
return 1;
|
||||
case Biome.GRAVEYARD:
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
getTypeForBiome() {
|
||||
switch (this.biomeType) {
|
||||
case Biome.TOWN:
|
||||
case Biome.PLAINS:
|
||||
case Biome.METROPOLIS:
|
||||
return Type.NORMAL;
|
||||
case Biome.GRASS:
|
||||
case Biome.TALL_GRASS:
|
||||
return Type.GRASS;
|
||||
case Biome.FOREST:
|
||||
return Type.BUG;
|
||||
case Biome.SLUM:
|
||||
case Biome.SWAMP:
|
||||
return Type.POISON;
|
||||
case Biome.SEA:
|
||||
case Biome.BEACH:
|
||||
case Biome.LAKE:
|
||||
case Biome.SEABED:
|
||||
return Type.WATER;
|
||||
case Biome.MOUNTAIN:
|
||||
return Type.FLYING;
|
||||
case Biome.BADLANDS:
|
||||
return Type.GROUND;
|
||||
case Biome.CAVE:
|
||||
case Biome.DESERT:
|
||||
return Type.ROCK;
|
||||
case Biome.ICE_CAVE:
|
||||
case Biome.SNOWY_FOREST:
|
||||
return Type.ICE;
|
||||
case Biome.MEADOW:
|
||||
case Biome.FAIRY_CAVE:
|
||||
case Biome.ISLAND:
|
||||
return Type.FAIRY;
|
||||
case Biome.POWER_PLANT:
|
||||
return Type.ELECTRIC;
|
||||
case Biome.VOLCANO:
|
||||
return Type.FIRE;
|
||||
case Biome.GRAVEYARD:
|
||||
case Biome.TEMPLE:
|
||||
return Type.GHOST;
|
||||
case Biome.DOJO:
|
||||
return Type.FIGHTING;
|
||||
case Biome.FACTORY:
|
||||
return Type.STEEL;
|
||||
case Biome.RUINS:
|
||||
case Biome.SPACE:
|
||||
return Type.PSYCHIC;
|
||||
case Biome.WASTELAND:
|
||||
case Biome.END:
|
||||
return Type.DRAGON;
|
||||
case Biome.ABYSS:
|
||||
return Type.DARK;
|
||||
}
|
||||
}
|
||||
|
||||
trySetWeatherOverride(weather: WeatherType): boolean {
|
||||
this.weather = new Weather(weather, 0);
|
||||
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1)));
|
||||
this.scene.queueMessage(getWeatherStartMessage(weather));
|
||||
return true
|
||||
}
|
||||
|
||||
trySetWeather(weather: WeatherType, viaMove: boolean): boolean {
|
||||
// override hook for debugging
|
||||
if (WEATHER_OVERRIDE) {
|
||||
return this.trySetWeatherOverride(WEATHER_OVERRIDE);
|
||||
}
|
||||
|
||||
if (this.weather?.weatherType === (weather || undefined))
|
||||
return false;
|
||||
|
||||
const oldWeatherType = this.weather?.weatherType || WeatherType.NONE;
|
||||
|
||||
this.weather = weather ? new Weather(weather, viaMove ? 5 : 0) : null;
|
||||
|
||||
if (this.weather) {
|
||||
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1)));
|
||||
this.scene.queueMessage(getWeatherStartMessage(weather));
|
||||
} else
|
||||
this.scene.queueMessage(getWeatherClearMessage(oldWeatherType));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isMoveWeatherCancelled(move: Move) {
|
||||
return this.weather && !this.weather.isEffectSuppressed(this.scene) && this.weather.isMoveWeatherCancelled(move);
|
||||
}
|
||||
|
||||
getAttackTypeMultiplier(attackType: Type): number {
|
||||
if (!this.weather || this.weather.isEffectSuppressed(this.scene))
|
||||
return 1;
|
||||
|
||||
return this.weather.getAttackTypeMultiplier(attackType);
|
||||
}
|
||||
|
||||
getTrainerChance(): integer {
|
||||
switch (this.biomeType) {
|
||||
case Biome.METROPOLIS:
|
||||
return 2;
|
||||
case Biome.SLUM:
|
||||
case Biome.BEACH:
|
||||
case Biome.DOJO:
|
||||
case Biome.CONSTRUCTION_SITE:
|
||||
return 4;
|
||||
case Biome.PLAINS:
|
||||
case Biome.GRASS:
|
||||
case Biome.LAKE:
|
||||
case Biome.CAVE:
|
||||
return 6;
|
||||
case Biome.TALL_GRASS:
|
||||
case Biome.FOREST:
|
||||
case Biome.SEA:
|
||||
case Biome.SWAMP:
|
||||
case Biome.MOUNTAIN:
|
||||
case Biome.BADLANDS:
|
||||
case Biome.DESERT:
|
||||
case Biome.MEADOW:
|
||||
case Biome.POWER_PLANT:
|
||||
case Biome.GRAVEYARD:
|
||||
case Biome.FACTORY:
|
||||
case Biome.SNOWY_FOREST:
|
||||
return 8;
|
||||
case Biome.ICE_CAVE:
|
||||
case Biome.VOLCANO:
|
||||
case Biome.RUINS:
|
||||
case Biome.WASTELAND:
|
||||
case Biome.JUNGLE:
|
||||
case Biome.FAIRY_CAVE:
|
||||
return 12;
|
||||
case Biome.SEABED:
|
||||
case Biome.ABYSS:
|
||||
case Biome.SPACE:
|
||||
case Biome.TEMPLE:
|
||||
return 16;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
getTimeOfDay(): TimeOfDay {
|
||||
switch (this.biomeType) {
|
||||
case Biome.ABYSS:
|
||||
return TimeOfDay.NIGHT;
|
||||
}
|
||||
|
||||
const waveCycle = ((this.scene.currentBattle?.waveIndex || 0) + this.scene.getWaveCycleOffset()) % 40;
|
||||
|
||||
if (waveCycle < 15)
|
||||
return TimeOfDay.DAY;
|
||||
|
||||
if (waveCycle < 20)
|
||||
return TimeOfDay.DUSK;
|
||||
|
||||
if (waveCycle < 35)
|
||||
return TimeOfDay.NIGHT;
|
||||
|
||||
return TimeOfDay.DAWN;
|
||||
}
|
||||
|
||||
isOutside(): boolean {
|
||||
switch (this.biomeType) {
|
||||
case Biome.SEABED:
|
||||
case Biome.CAVE:
|
||||
case Biome.ICE_CAVE:
|
||||
case Biome.POWER_PLANT:
|
||||
case Biome.DOJO:
|
||||
case Biome.FACTORY:
|
||||
case Biome.ABYSS:
|
||||
case Biome.FAIRY_CAVE:
|
||||
case Biome.TEMPLE:
|
||||
case Biome.LABORATORY:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
getDayTint(): [integer, integer, integer] {
|
||||
switch (this.biomeType) {
|
||||
case Biome.ABYSS:
|
||||
return [ 64, 64, 64 ];
|
||||
default:
|
||||
return [ 128, 128, 128 ];
|
||||
}
|
||||
}
|
||||
|
||||
getDuskTint(): [integer, integer, integer] {
|
||||
if (!this.isOutside())
|
||||
return [ 0, 0, 0 ];
|
||||
|
||||
switch (this.biomeType) {
|
||||
default:
|
||||
return [ 98, 48, 73 ].map(c => Math.round((c + 128) / 2)) as [integer, integer, integer];
|
||||
}
|
||||
}
|
||||
|
||||
getNightTint(): [integer, integer, integer] {
|
||||
switch (this.biomeType) {
|
||||
case Biome.ABYSS:
|
||||
case Biome.SPACE:
|
||||
case Biome.END:
|
||||
return this.getDayTint();
|
||||
}
|
||||
|
||||
if (!this.isOutside())
|
||||
return [ 64, 64, 64 ];
|
||||
|
||||
switch (this.biomeType) {
|
||||
default:
|
||||
return [ 48, 48, 98 ];
|
||||
}
|
||||
}
|
||||
|
||||
applyTagsForSide(tagType: ArenaTagType | { new(...args: any[]): ArenaTag }, side: ArenaTagSide, ...args: any[]): void {
|
||||
let tags = typeof tagType === 'string'
|
||||
? this.tags.filter(t => t.tagType === tagType)
|
||||
: this.tags.filter(t => t instanceof tagType);
|
||||
if (side !== ArenaTagSide.BOTH)
|
||||
tags = tags.filter(t => t.side === side);
|
||||
tags.forEach(t => t.apply(this, args));
|
||||
}
|
||||
|
||||
applyTags(tagType: ArenaTagType | { new(...args: any[]): ArenaTag }, ...args: any[]): void {
|
||||
this.applyTagsForSide(tagType, ArenaTagSide.BOTH, ...args);
|
||||
}
|
||||
|
||||
addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide = ArenaTagSide.BOTH, targetIndex?: BattlerIndex): boolean {
|
||||
const existingTag = this.getTag(tagType);
|
||||
if (existingTag) {
|
||||
existingTag.onOverlap(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side);
|
||||
this.tags.push(newTag);
|
||||
newTag.onAdd(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getTag(tagType: ArenaTagType | { new(...args: any[]): ArenaTag }): ArenaTag {
|
||||
return this.getTagOnSide(tagType, ArenaTagSide.BOTH);
|
||||
}
|
||||
|
||||
getTagOnSide(tagType: ArenaTagType | { new(...args: any[]): ArenaTag }, side: ArenaTagSide): ArenaTag {
|
||||
return typeof(tagType) === 'string'
|
||||
? this.tags.find(t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side))
|
||||
: this.tags.find(t => t instanceof tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side));
|
||||
}
|
||||
|
||||
lapseTags(): void {
|
||||
const tags = this.tags;
|
||||
tags.filter(t => !(t.lapse(this))).forEach(t => {
|
||||
t.onRemove(this);
|
||||
tags.splice(tags.indexOf(t), 1);
|
||||
});
|
||||
}
|
||||
|
||||
removeAllTags(): void {
|
||||
for (let t of this.tags) {
|
||||
t.onRemove(this);
|
||||
this.tags.splice(this.tags.indexOf(t), 1);
|
||||
}
|
||||
}
|
||||
|
||||
preloadBgm(): void {
|
||||
this.scene.loadBgm(this.bgm);
|
||||
}
|
||||
|
||||
getBgmLoopPoint(): number {
|
||||
switch (this.biomeType) {
|
||||
case Biome.TOWN:
|
||||
return 7.288;
|
||||
case Biome.PLAINS:
|
||||
return 7.693;
|
||||
case Biome.GRASS:
|
||||
return 1.995;
|
||||
case Biome.TALL_GRASS:
|
||||
return 9.608;
|
||||
case Biome.METROPOLIS:
|
||||
return 4.867;
|
||||
case Biome.FOREST:
|
||||
return 4.294;
|
||||
case Biome.SEA:
|
||||
return 1.672;
|
||||
case Biome.SWAMP:
|
||||
return 4.461;
|
||||
case Biome.BEACH:
|
||||
return 3.462;
|
||||
case Biome.LAKE:
|
||||
return 5.350;
|
||||
case Biome.SEABED:
|
||||
return 2.629;
|
||||
case Biome.MOUNTAIN:
|
||||
return 4.018;
|
||||
case Biome.BADLANDS:
|
||||
return 17.790;
|
||||
case Biome.CAVE:
|
||||
return 14.240;
|
||||
case Biome.DESERT:
|
||||
return 1.143;
|
||||
case Biome.ICE_CAVE:
|
||||
return 15.010;
|
||||
case Biome.MEADOW:
|
||||
return 3.891;
|
||||
case Biome.POWER_PLANT:
|
||||
return 2.810;
|
||||
case Biome.VOLCANO:
|
||||
return 5.116;
|
||||
case Biome.GRAVEYARD:
|
||||
return 3.232;
|
||||
case Biome.DOJO:
|
||||
return 6.205;
|
||||
case Biome.FACTORY:
|
||||
return 4.985;
|
||||
case Biome.RUINS:
|
||||
return 2.270;
|
||||
case Biome.WASTELAND:
|
||||
return 6.336;
|
||||
case Biome.ABYSS:
|
||||
return 5.130;
|
||||
case Biome.SPACE:
|
||||
return 21.347;
|
||||
case Biome.CONSTRUCTION_SITE:
|
||||
return 1.222;
|
||||
case Biome.JUNGLE:
|
||||
return 2.477;
|
||||
case Biome.FAIRY_CAVE:
|
||||
return 4.542;
|
||||
case Biome.TEMPLE:
|
||||
return 2.547;
|
||||
case Biome.ISLAND:
|
||||
return 2.751;
|
||||
case Biome.LABORATORY:
|
||||
return 0.797;
|
||||
case Biome.SLUM:
|
||||
return 1.221;
|
||||
case Biome.SNOWY_FOREST:
|
||||
return 3.047;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getBiomeKey(biome: Biome): string {
|
||||
switch (biome) {
|
||||
case Biome.POWER_PLANT:
|
||||
return 'ruins';
|
||||
case Biome.METROPOLIS:
|
||||
return 'slum';
|
||||
case Biome.JUNGLE:
|
||||
return 'tall_grass';
|
||||
case Biome.ISLAND:
|
||||
return 'beach';
|
||||
}
|
||||
return Biome[biome].toLowerCase();
|
||||
}
|
||||
|
||||
export function getBiomeHasProps(biomeType: Biome): boolean {
|
||||
switch (biomeType) {
|
||||
case Biome.BEACH:
|
||||
case Biome.LAKE:
|
||||
case Biome.SEABED:
|
||||
case Biome.MOUNTAIN:
|
||||
case Biome.BADLANDS:
|
||||
case Biome.CAVE:
|
||||
case Biome.DESERT:
|
||||
case Biome.ICE_CAVE:
|
||||
case Biome.MEADOW:
|
||||
case Biome.VOLCANO:
|
||||
case Biome.GRAVEYARD:
|
||||
case Biome.FACTORY:
|
||||
case Biome.RUINS:
|
||||
case Biome.WASTELAND:
|
||||
case Biome.ABYSS:
|
||||
case Biome.CONSTRUCTION_SITE:
|
||||
case Biome.FAIRY_CAVE:
|
||||
case Biome.TEMPLE:
|
||||
case Biome.LABORATORY:
|
||||
case Biome.END:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export class ArenaBase extends Phaser.GameObjects.Container {
|
||||
public player: boolean;
|
||||
public biome: Biome;
|
||||
public propValue: integer;
|
||||
public base: Phaser.GameObjects.Sprite;
|
||||
public props: Phaser.GameObjects.Sprite[];
|
||||
|
||||
constructor(scene: BattleScene, player: boolean) {
|
||||
super(scene, 0, 0);
|
||||
|
||||
this.player = player;
|
||||
|
||||
this.base = scene.addFieldSprite(0, 0, 'plains_a');
|
||||
this.base.setOrigin(0, 0);
|
||||
|
||||
this.props = !player ?
|
||||
new Array(3).fill(null).map(() => {
|
||||
const ret = scene.addFieldSprite(0, 0, 'plains_b');
|
||||
ret.setOrigin(0, 0);
|
||||
ret.setVisible(false);
|
||||
return ret;
|
||||
}) : [];
|
||||
}
|
||||
|
||||
setBiome(biome: Biome, propValue?: integer): void {
|
||||
if (this.biome === biome)
|
||||
return;
|
||||
|
||||
const hasProps = getBiomeHasProps(biome);
|
||||
const biomeKey = getBiomeKey(biome);
|
||||
const baseKey = `${biomeKey}_${this.player ? 'a' : 'b'}`;
|
||||
|
||||
this.base.setTexture(baseKey);
|
||||
|
||||
if (this.base.texture.frameTotal > 1) {
|
||||
const baseFrameNames = this.scene.anims.generateFrameNames(baseKey, { zeroPad: 4, suffix: ".png", start: 1, end: this.base.texture.frameTotal - 1 });
|
||||
this.scene.anims.create({
|
||||
key: baseKey,
|
||||
frames: baseFrameNames,
|
||||
frameRate: 12,
|
||||
repeat: -1
|
||||
});
|
||||
this.base.play(baseKey);
|
||||
} else
|
||||
this.base.stop();
|
||||
|
||||
this.add(this.base);
|
||||
|
||||
if (!this.player) {
|
||||
(this.scene as BattleScene).executeWithSeedOffset(() => {
|
||||
this.propValue = propValue === undefined
|
||||
? hasProps ? Utils.randSeedInt(8) : 0
|
||||
: propValue;
|
||||
this.props.forEach((prop, p) => {
|
||||
const propKey = `${biomeKey}_b${hasProps ? `_${p + 1}` : ''}`;
|
||||
prop.setTexture(propKey);
|
||||
|
||||
if (hasProps && prop.texture.frameTotal > 1) {
|
||||
const propFrameNames = this.scene.anims.generateFrameNames(propKey, { zeroPad: 4, suffix: ".png", start: 1, end: prop.texture.frameTotal - 1 });
|
||||
this.scene.anims.create({
|
||||
key: propKey,
|
||||
frames: propFrameNames,
|
||||
frameRate: 12,
|
||||
repeat: -1
|
||||
});
|
||||
prop.play(propKey);
|
||||
} else
|
||||
prop.stop();
|
||||
|
||||
prop.setVisible(hasProps && !!(this.propValue & (1 << p)));
|
||||
this.add(prop);
|
||||
});
|
||||
}, (this.scene as BattleScene).currentBattle?.waveIndex || 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import BattleScene from "../battle-scene";
|
||||
import Pokemon from "./pokemon";
|
||||
import * as Utils from "../utils";
|
||||
|
||||
export default class PokemonSpriteSparkleHandler {
|
||||
private sprites: Set<Phaser.GameObjects.Sprite>;
|
||||
|
||||
setup(scene: BattleScene): void {
|
||||
this.sprites = new Set();
|
||||
|
||||
scene.tweens.addCounter({
|
||||
duration: Utils.fixedInt(200),
|
||||
from: 0,
|
||||
to: 1,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
onRepeat: () => this.onLapse()
|
||||
});
|
||||
}
|
||||
|
||||
onLapse(): void {
|
||||
Array.from(this.sprites.values()).filter(s => !s.scene).map(s => this.sprites.delete(s));
|
||||
for (let s of this.sprites.values()) {
|
||||
if (!s.pipelineData['teraColor'] || !(s.pipelineData['teraColor'] as number[]).find(c => c))
|
||||
continue;
|
||||
if (!s.visible || (s.parentContainer instanceof Pokemon && !s.parentContainer.parentContainer))
|
||||
continue;
|
||||
const pokemon = s.parentContainer instanceof Pokemon ? s.parentContainer as Pokemon : null;
|
||||
const parent = (pokemon || s).parentContainer;
|
||||
const texture = s.texture;
|
||||
const [ width, height ] = [ texture.source[0].width, texture.source[0].height ];
|
||||
const [ pixelX, pixelY ] = [ Utils.randInt(width), Utils.randInt(height) ];
|
||||
const ratioX = s.width / width;
|
||||
const ratioY = s.height / height;
|
||||
const pixel = texture.manager.getPixel(pixelX, pixelY, texture.key, '__BASE');
|
||||
if (pixel.alpha) {
|
||||
const [ xOffset, yOffset ] = [ -s.originX * s.width, -s.originY * s.height];
|
||||
const sparkle = (s.scene as BattleScene).addFieldSprite(((pokemon?.x || 0) + s.x + pixelX * ratioX + xOffset), ((pokemon?.y || 0) + s.y + pixelY * ratioY + yOffset), 'tera_sparkle');
|
||||
sparkle.pipelineData['ignoreTimeTint'] = s.pipelineData['ignoreTimeTint'];
|
||||
sparkle.play('tera_sparkle');
|
||||
const teraColor = s.pipelineData['teraColor'] as number[];
|
||||
parent.add(sparkle);
|
||||
s.scene.time.delayedCall(Utils.fixedInt(Math.floor((1000 / 12) * 13)), () => sparkle.destroy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void {
|
||||
if (!Array.isArray(sprites))
|
||||
sprites = [ sprites ];
|
||||
for (let s of sprites) {
|
||||
if (this.sprites.has(s))
|
||||
continue;
|
||||
this.sprites.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
remove(sprites: Phaser.GameObjects.Sprite | Phaser.GameObjects.Sprite[]): void {
|
||||
if (!Array.isArray(sprites))
|
||||
sprites = [ sprites ];
|
||||
for (let s of sprites) {
|
||||
this.sprites.delete(s);
|
||||
}
|
||||
}
|
||||
|
||||
removeAll(): void {
|
||||
for (let s of this.sprites.values())
|
||||
this.sprites.delete(s);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,343 @@
|
||||
import BattleScene from "../battle-scene";
|
||||
import { pokemonPrevolutions } from "../data/pokemon-evolutions";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "../data/pokemon-species";
|
||||
import { TrainerConfig, TrainerPartyCompoundTemplate, TrainerPartyMemberStrength, TrainerPartyTemplate, TrainerPoolTier, trainerConfigs, trainerPartyTemplates } from "../data/trainer-config";
|
||||
import { TrainerType } from "../data/enums/trainer-type";
|
||||
import { EnemyPokemon } from "./pokemon";
|
||||
import * as Utils from "../utils";
|
||||
import { PersistentModifier } from "../modifier/modifier";
|
||||
|
||||
export default class Trainer extends Phaser.GameObjects.Container {
|
||||
public config: TrainerConfig;
|
||||
public female: boolean;
|
||||
public partyTemplateIndex: integer;
|
||||
|
||||
constructor(scene: BattleScene, trainerType: TrainerType, female?: boolean, partyTemplateIndex?: integer) {
|
||||
super(scene, -72, 80);
|
||||
this.config = trainerConfigs.hasOwnProperty(trainerType)
|
||||
? trainerConfigs[trainerType]
|
||||
: trainerConfigs[TrainerType.ACE_TRAINER];
|
||||
this.female = female;
|
||||
this.partyTemplateIndex = Math.min(partyTemplateIndex !== undefined ? partyTemplateIndex : Utils.randSeedWeightedItem(this.config.partyTemplates.map((_, i) => i)),
|
||||
this.config.partyTemplates.length - 1);
|
||||
|
||||
console.log(Object.keys(trainerPartyTemplates)[Object.values(trainerPartyTemplates).indexOf(this.getPartyTemplate())]);
|
||||
|
||||
const getSprite = (hasShadow?: boolean) => {
|
||||
const ret = this.scene.addFieldSprite(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(includeTitle: boolean = false): string {
|
||||
let name = this.config.getName(this.female);
|
||||
return includeTitle && this.config.title ? `${this.config.title} ${name}` : name;
|
||||
}
|
||||
|
||||
getBattleBgm(): string {
|
||||
return this.config.battleBgm;
|
||||
}
|
||||
|
||||
getEncounterBgm(): string {
|
||||
return !this.female ? this.config.encounterBgm : this.config.femaleEncounterBgm || this.config.encounterBgm;
|
||||
}
|
||||
|
||||
getEncounterMessages(): string[] {
|
||||
return !this.female || !this.config.femaleEncounterMessages ? this.config.encounterMessages : this.config.femaleEncounterMessages;
|
||||
}
|
||||
|
||||
getVictoryMessages(): string[] {
|
||||
return !this.female || !this.config.femaleVictoryMessages ? this.config.victoryMessages : this.config.femaleVictoryMessages;
|
||||
}
|
||||
|
||||
getDefeatMessages(): string[] {
|
||||
return !this.female || !this.config.femaleDefeatMessages ? this.config.defeatMessages : this.config.femaleDefeatMessages;
|
||||
}
|
||||
|
||||
getPartyTemplate(): TrainerPartyTemplate {
|
||||
if (this.config.partyTemplateFunc)
|
||||
return this.config.partyTemplateFunc(this.scene);
|
||||
return this.config.partyTemplates[this.partyTemplateIndex];
|
||||
}
|
||||
|
||||
getPartyLevels(waveIndex: integer): integer[] {
|
||||
const ret = [];
|
||||
const partyTemplate = this.getPartyTemplate();
|
||||
|
||||
let baseLevel = 1 + waveIndex / 2 + Math.pow(waveIndex / 25, 2);
|
||||
|
||||
for (let i = 0; i < partyTemplate.size; i++) {
|
||||
let multiplier = 1;
|
||||
|
||||
const strength = partyTemplate.getStrength(i);
|
||||
|
||||
switch (strength) {
|
||||
case TrainerPartyMemberStrength.WEAKER:
|
||||
multiplier = 0.95;
|
||||
break;
|
||||
case TrainerPartyMemberStrength.WEAK:
|
||||
multiplier = 1.0;
|
||||
break;
|
||||
case TrainerPartyMemberStrength.AVERAGE:
|
||||
multiplier = 1.1;
|
||||
break;
|
||||
case TrainerPartyMemberStrength.STRONG:
|
||||
multiplier = 1.2;
|
||||
break;
|
||||
case TrainerPartyMemberStrength.STRONGER:
|
||||
multiplier = 1.25;
|
||||
break;
|
||||
}
|
||||
|
||||
let levelOffset = 0;
|
||||
|
||||
if (strength < TrainerPartyMemberStrength.STRONG) {
|
||||
multiplier = Math.min(multiplier + 0.025 * Math.floor(waveIndex / 25), 1.2);
|
||||
levelOffset = -Math.floor((waveIndex / 50) * (TrainerPartyMemberStrength.STRONG - strength));
|
||||
}
|
||||
|
||||
const level = Math.ceil(baseLevel * multiplier) + levelOffset;
|
||||
ret.push(level);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
genPartyMember(index: integer): EnemyPokemon {
|
||||
const battle = this.scene.currentBattle;
|
||||
const level = battle.enemyLevels[index];
|
||||
|
||||
let ret: EnemyPokemon;
|
||||
|
||||
this.scene.executeWithSeedOffset(() => {
|
||||
const template = this.getPartyTemplate();
|
||||
|
||||
if (this.config.partyMemberFuncs.hasOwnProperty(index)) {
|
||||
ret = this.config.partyMemberFuncs[index](this.scene, level);
|
||||
return;
|
||||
}
|
||||
if (this.config.partyMemberFuncs.hasOwnProperty(index - template.size)) {
|
||||
ret = this.config.partyMemberFuncs[index - template.size](this.scene, level);
|
||||
return;
|
||||
}
|
||||
|
||||
let offset = 0;
|
||||
|
||||
if (template instanceof TrainerPartyCompoundTemplate) {
|
||||
for (let innerTemplate of template.templates) {
|
||||
if (offset + innerTemplate.size > index)
|
||||
break;
|
||||
offset += innerTemplate.size;
|
||||
}
|
||||
}
|
||||
|
||||
const species = template.isSameSpecies(index) && index > offset
|
||||
? getPokemonSpecies(battle.enemyParty[offset].species.getSpeciesForLevel(level, false, true, this.config.isBoss))
|
||||
: this.genNewPartyMemberSpecies(level);
|
||||
|
||||
ret = this.scene.addEnemyPokemon(species, level, true);
|
||||
}, this.config.hasStaticParty ? this.config.getDerivedType() + ((index + 1) << 8) : this.scene.currentBattle.waveIndex + (this.config.getDerivedType() << 10) + (((!this.config.useSameSeedForAllMembers ? index : 0) + 1) << 8));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
genNewPartyMemberSpecies(level: integer, attempt?: integer): PokemonSpecies {
|
||||
const battle = this.scene.currentBattle;
|
||||
const template = this.getPartyTemplate();
|
||||
|
||||
let species: PokemonSpecies;
|
||||
if (this.config.speciesPools) {
|
||||
const tierValue = Utils.randSeedInt(512);
|
||||
let tier = tierValue >= 156 ? TrainerPoolTier.COMMON : tierValue >= 32 ? TrainerPoolTier.UNCOMMON : tierValue >= 6 ? TrainerPoolTier.RARE : tierValue >= 1 ? TrainerPoolTier.SUPER_RARE : TrainerPoolTier.ULTRA_RARE
|
||||
console.log(TrainerPoolTier[tier]);
|
||||
while (!this.config.speciesPools.hasOwnProperty(tier) || !this.config.speciesPools[tier].length) {
|
||||
console.log(`Downgraded trainer Pokemon rarity tier from ${TrainerPoolTier[tier]} to ${TrainerPoolTier[tier - 1]}`);
|
||||
tier--;
|
||||
}
|
||||
const tierPool = this.config.speciesPools[tier];
|
||||
species = getPokemonSpecies(Utils.randSeedItem(tierPool));
|
||||
} else
|
||||
species = this.scene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter);
|
||||
|
||||
let ret = getPokemonSpecies(species.getSpeciesForLevel(level, true, true, this.config.isBoss));
|
||||
let retry = false;
|
||||
|
||||
console.log(ret.getName());
|
||||
|
||||
if (pokemonPrevolutions.hasOwnProperty(species.speciesId) && ret.speciesId !== species.speciesId)
|
||||
retry = true;
|
||||
else if (template.isBalanced(battle.enemyParty.length)) {
|
||||
const partyMemberTypes = battle.enemyParty.map(p => p.getTypes(true)).flat();
|
||||
if (partyMemberTypes.indexOf(ret.type1) > -1 || (ret.type2 !== null && partyMemberTypes.indexOf(ret.type2) > -1))
|
||||
retry = true;
|
||||
}
|
||||
|
||||
if (!retry && this.config.specialtyTypes.length && !this.config.specialtyTypes.find(t => ret.isOfType(t))) {
|
||||
retry = true;
|
||||
console.log('Attempting reroll of species evolution to fit specialty type...');
|
||||
let evoAttempt = 0;
|
||||
while (retry && evoAttempt++ < 10) {
|
||||
ret = getPokemonSpecies(species.getSpeciesForLevel(level, true, true, this.config.isBoss));
|
||||
console.log(ret.name);
|
||||
if (this.config.specialtyTypes.find(t => ret.isOfType(t)))
|
||||
retry = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (retry && (attempt || 0) < 10) {
|
||||
console.log('Rerolling party member...')
|
||||
ret = this.genNewPartyMemberSpecies(level, (attempt || 0) + 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
getPartyMemberMatchupScores(): [integer, 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;
|
||||
let ret: [integer, integer];
|
||||
for (let playerPokemon of playerField) {
|
||||
score += p.getMatchupScore(playerPokemon);
|
||||
if (playerPokemon.species.legendary)
|
||||
score /= 2;
|
||||
}
|
||||
score /= playerField.length;
|
||||
ret = [ party.indexOf(p), score ];
|
||||
return ret;
|
||||
});
|
||||
|
||||
return partyMemberScores;
|
||||
}
|
||||
|
||||
getSortedPartyMemberMatchupScores(partyMemberScores: [integer, integer][] = this.getPartyMemberMatchupScores()) {
|
||||
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;
|
||||
});
|
||||
|
||||
return sortedPartyMemberScores;
|
||||
}
|
||||
|
||||
getNextSummonIndex(partyMemberScores: [integer, integer][] = this.getPartyMemberMatchupScores()): integer {
|
||||
const sortedPartyMemberScores = this.getSortedPartyMemberMatchupScores(partyMemberScores);
|
||||
|
||||
const maxScorePartyMemberIndexes = partyMemberScores.filter(pms => pms[1] === sortedPartyMemberScores[0][1]).map(pms => pms[0]);
|
||||
|
||||
if (maxScorePartyMemberIndexes.length > 1) {
|
||||
let rand: integer;
|
||||
this.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(maxScorePartyMemberIndexes.length), this.scene.currentBattle.turn << 2);
|
||||
return maxScorePartyMemberIndexes[rand];
|
||||
}
|
||||
|
||||
return maxScorePartyMemberIndexes[0];
|
||||
}
|
||||
|
||||
getPartyMemberModifierChanceMultiplier(index: integer): number {
|
||||
switch (this.getPartyTemplate().getStrength(index)) {
|
||||
case TrainerPartyMemberStrength.WEAKER:
|
||||
return 0.75;
|
||||
case TrainerPartyMemberStrength.WEAK:
|
||||
return 0.675;
|
||||
case TrainerPartyMemberStrength.AVERAGE:
|
||||
return 0.5625;
|
||||
case TrainerPartyMemberStrength.STRONG:
|
||||
return 0.45;
|
||||
case TrainerPartyMemberStrength.STRONGER:
|
||||
return 0.375;
|
||||
}
|
||||
}
|
||||
|
||||
genModifiers(party: EnemyPokemon[]): PersistentModifier[] {
|
||||
if (this.config.genModifiersFunc)
|
||||
return this.config.genModifiersFunc(party);
|
||||
return [];
|
||||
}
|
||||
|
||||
loadAssets(): Promise<void> {
|
||||
return this.config.loadAssets(this.scene, this.female);
|
||||
}
|
||||
|
||||
initSprite(): void {
|
||||
this.getSprite().setTexture(this.getKey()).setFrame(0);
|
||||
this.getTintSprite().setTexture(this.getKey()).setFrame(0);
|
||||
}
|
||||
|
||||
playAnim(): void {
|
||||
const trainerAnimConfig = {
|
||||
key: this.getKey(),
|
||||
repeat: 0,
|
||||
startFrame: 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
|
||||
}
|
||||
Reference in new issue
Block a user