pokerogue/src/field/arena.ts

832 lines
25 KiB
TypeScript
Raw Normal View History

2024-02-29 20:08:50 -05:00
import BattleScene from "../battle-scene";
import { BiomePoolTier, PokemonPools, BiomeTierTrainerPools, biomePokemonPools, biomeTrainerPools } from "../data/biomes";
2024-02-29 20:08:50 -05:00
import * as Utils from "../utils";
import PokemonSpecies, { getPokemonSpecies } from "../data/pokemon-species";
import { Weather, WeatherType, getTerrainClearMessage, getTerrainStartMessage, getWeatherClearMessage, getWeatherStartMessage } from "../data/weather";
import { CommonAnimPhase } from "../phases";
2024-02-29 20:08:50 -05:00
import { CommonAnim } from "../data/battle-anims";
import { Type } from "../data/type";
import Move from "../data/move";
import { ArenaTag, ArenaTagSide, ArenaTrapTag, getArenaTag } from "../data/arena-tag";
2024-02-29 20:08:50 -05:00
import { BattlerIndex } from "../battle";
import { Terrain, TerrainType } from "../data/terrain";
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
2024-03-28 16:24:11 -04:00
import Pokemon from "./pokemon";
import * as Overrides from "../overrides";
import { WeatherChangedEvent, TerrainChangedEvent, TagAddedEvent, TagRemovedEvent } from "../events/arena";
import { ArenaTagType } from "#enums/arena-tag-type";
import { Biome } from "#enums/biome";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { TimeOfDay } from "#enums/time-of-day";
import { TrainerType } from "#enums/trainer-type";
2023-04-16 20:41:52 -04:00
export class Arena {
2023-04-21 19:30:04 -04:00
public scene: BattleScene;
2023-04-16 20:41:52 -04:00
public biomeType: Biome;
public weather: Weather;
public terrain: Terrain;
2023-04-21 19:30:04 -04:00
public tags: ArenaTag[];
public bgm: string;
public ignoreAbilities: boolean;
private lastTimeOfDay: TimeOfDay;
private pokemonPool: PokemonPools;
private trainerPool: BiomeTierTrainerPools;
2023-04-16 20:41:52 -04:00
public readonly eventTarget: EventTarget = new EventTarget();
2023-04-16 20:41:52 -04:00
constructor(scene: BattleScene, biome: Biome, bgm: string) {
this.scene = scene;
this.biomeType = biome;
2023-04-21 19:30:04 -04:00
this.tags = [];
2023-04-16 20:41:52 -04:00
this.bgm = bgm;
this.trainerPool = biomeTrainerPools[biome];
this.updatePoolsForTimeOfDay();
}
init() {
const biomeKey = getBiomeKey(this.biomeType);
this.scene.arenaPlayer.setBiome(this.biomeType);
this.scene.arenaPlayerTransition.setBiome(this.biomeType);
this.scene.arenaEnemy.setBiome(this.biomeType);
this.scene.arenaNextEnemy.setBiome(this.biomeType);
this.scene.arenaBg.setTexture(`${biomeKey}_bg`);
this.scene.arenaBgTransition.setTexture(`${biomeKey}_bg`);
// Redo this on initialise because during save/load the current wave isn't always
// set correctly during construction
this.updatePoolsForTimeOfDay();
}
updatePoolsForTimeOfDay(): void {
const timeOfDay = this.getTimeOfDay();
if (timeOfDay !== this.lastTimeOfDay) {
this.pokemonPool = {};
for (const 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;
}
2023-04-16 20:41:52 -04:00
}
2024-06-05 11:02:41 -04:00
randomSpecies(waveIndex: integer, level: integer, attempt?: integer, luckValue?: integer): PokemonSpecies {
2024-03-16 22:06:56 -04:00
const overrideSpecies = this.scene.gameMode.getOverrideSpecies(waveIndex);
if (overrideSpecies) {
2024-03-16 22:06:56 -04:00
return overrideSpecies;
}
2024-01-07 23:17:24 -05:00
const isBoss = !!this.scene.getEncounterBossSegments(waveIndex, level) && !!this.pokemonPool[BiomePoolTier.BOSS].length
2024-03-14 16:26:57 -04:00
&& (this.biomeType !== Biome.END || this.scene.gameMode.isClassic || this.scene.gameMode.isWaveFinal(waveIndex));
2024-06-05 11:02:41 -04:00
const randVal = isBoss ? 64 : 512;
// luck influences encounter rarity
let luckModifier = 0;
if (typeof luckValue !== "undefined") {
2024-06-05 11:21:47 -04:00
luckModifier = luckValue * (isBoss ? 0.5 : 2);
2024-06-05 11:02:41 -04:00
}
const tierValue = Utils.randSeedInt(randVal - luckModifier);
2023-04-16 20:41:52 -04:00
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]);
2023-04-16 20:41:52 -04:00
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;
2023-04-20 01:04:36 -04:00
let regen = false;
if (!tierPool.length) {
ret = this.scene.randomSpecies(waveIndex, level);
} else {
const entry = tierPool[Utils.randSeedInt(tierPool.length)];
2023-04-16 20:41:52 -04:00
let species: Species;
if (typeof entry === "number") {
2023-04-16 20:41:52 -04:00
species = entry as Species;
} else {
2023-04-16 20:41:52 -04:00
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 {
2023-04-16 20:41:52 -04:00
species = speciesIds[0];
}
2023-04-16 20:41:52 -04:00
break;
}
}
}
2024-05-24 01:45:04 +02:00
2023-04-16 20:41:52 -04:00
ret = getPokemonSpecies(species);
2023-04-20 01:04:36 -04:00
if (ret.subLegendary || ret.legendary || ret.mythical) {
2023-04-20 01:04:36 -04:00
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;
2023-04-20 01:04:36 -04:00
}
}
}
if (regen && (attempt || 0) < 10) {
console.log("Incompatible level: regenerating...");
2023-04-20 01:04:36 -04:00
return this.randomSpecies(waveIndex, level, (attempt || 0) + 1);
2023-04-16 20:41:52 -04:00
}
2023-04-20 01:04:36 -04:00
2024-03-28 14:05:15 -04:00
const newSpeciesId = ret.getWildSpeciesForLevel(level, true, isBoss, this.scene.gameMode);
2023-04-16 20:41:52 -04:00
if (newSpeciesId !== ret.speciesId) {
console.log("Replaced", Species[ret.speciesId], "with", Species[newSpeciesId]);
2023-04-16 20:41:52 -04:00
ret = getPokemonSpecies(newSpeciesId);
}
return ret;
}
randomTrainerType(waveIndex: integer): TrainerType {
2024-03-16 22:06:56 -04:00
const isBoss = !!this.trainerPool[BiomePoolTier.BOSS].length
&& this.scene.gameMode.isTrainerBoss(waveIndex, this.biomeType, this.scene.offsetGym);
console.log(isBoss, this.trainerPool);
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)];
}
2023-10-30 14:57:23 -04:00
getSpeciesFormIndex(species: PokemonSpecies): integer {
2023-10-28 00:18:44 -04:00
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.ROTOM:
switch (this.biomeType) {
case Biome.VOLCANO:
return 1;
case Biome.SEA:
return 2;
case Biome.ICE_CAVE:
return 3;
case Biome.MOUNTAIN:
return 4;
case Biome.TALL_GRASS:
return 5;
}
break;
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;
}
break;
2023-10-28 00:18:44 -04:00
}
2023-10-30 14:57:23 -04:00
return 0;
}
2023-04-21 19:30:04 -04:00
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:
case Biome.JUNGLE:
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:
case Biome.CONSTRUCTION_SITE:
return Type.FIGHTING;
case Biome.FACTORY:
case Biome.LABORATORY:
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;
default:
return Type.UNKNOWN;
2023-04-21 19:30:04 -04:00
}
}
getBgTerrainColorRatioForBiome(): number {
switch (this.biomeType) {
case Biome.SPACE:
return 1;
case Biome.END:
return 0;
}
2024-04-02 20:25:35 -04:00
return 131 / 180;
}
/**
* Sets weather to the override specified in overrides.ts
* @param weather new weather to set of type WeatherType
* @returns true to force trySetWeather to return true
*/
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;
}
/**
* Attempts to set a new weather to the battle
* @param weather new weather to set of type WeatherType
* @param hasPokemonSource is the new weather from a pokemon
* @returns true if new weather set, false if no weather provided or attempting to set the same weather as currently in use
*/
trySetWeather(weather: WeatherType, hasPokemonSource: boolean): boolean {
if (Overrides.WEATHER_OVERRIDE) {
return this.trySetWeatherOverride(Overrides.WEATHER_OVERRIDE);
}
2024-05-24 01:45:04 +02:00
if (this.weather?.weatherType === (weather || undefined)) {
2023-04-16 20:41:52 -04:00
return false;
}
2023-04-16 20:41:52 -04:00
const oldWeatherType = this.weather?.weatherType || WeatherType.NONE;
this.weather = weather ? new Weather(weather, hasPokemonSource ? 5 : 0) : null;
this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType, this.weather?.turnsLeft));
2024-05-24 01:45:04 +02:00
if (this.weather) {
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1)));
2023-04-21 19:30:04 -04:00
this.scene.queueMessage(getWeatherStartMessage(weather));
} else {
2023-04-21 19:30:04 -04:00
this.scene.queueMessage(getWeatherClearMessage(oldWeatherType));
}
this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => {
pokemon.findAndRemoveTags(t => "weatherTypes" in t && !(t.weatherTypes as WeatherType[]).find(t => t === weather));
applyPostWeatherChangeAbAttrs(PostWeatherChangeAbAttr, pokemon, weather);
});
2024-05-24 01:45:04 +02:00
2023-04-16 20:41:52 -04:00
return true;
}
trySetTerrain(terrain: TerrainType, hasPokemonSource: boolean, ignoreAnim: boolean = false): boolean {
if (this.terrain?.terrainType === (terrain || undefined)) {
return false;
}
const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE;
this.terrain = terrain ? new Terrain(terrain, hasPokemonSource ? 5 : 0) : null;
this.eventTarget.dispatchEvent(new TerrainChangedEvent(oldTerrainType,this.terrain?.terrainType, this.terrain?.turnsLeft));
2024-05-24 01:45:04 +02:00
if (this.terrain) {
if (!ignoreAnim) {
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.MISTY_TERRAIN + (terrain - 1)));
}
this.scene.queueMessage(getTerrainStartMessage(terrain));
} else {
this.scene.queueMessage(getTerrainClearMessage(oldTerrainType));
}
this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => {
pokemon.findAndRemoveTags(t => "terrainTypes" in t && !(t.terrainTypes as TerrainType[]).find(t => t === terrain));
applyPostTerrainChangeAbAttrs(PostTerrainChangeAbAttr, pokemon, terrain);
});
2024-05-24 01:45:04 +02:00
return true;
}
2023-04-21 19:30:04 -04:00
isMoveWeatherCancelled(move: Move) {
return this.weather && !this.weather.isEffectSuppressed(this.scene) && this.weather.isMoveWeatherCancelled(move);
2023-04-21 19:30:04 -04:00
}
isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move) {
return this.terrain && this.terrain.isMoveTerrainCancelled(user, targets, move);
}
getTerrainType() : TerrainType {
return this.terrain?.terrainType || TerrainType.NONE;
}
getAttackTypeMultiplier(attackType: Type, grounded: boolean): number {
let weatherMultiplier = 1;
if (this.weather && !this.weather.isEffectSuppressed(this.scene)) {
weatherMultiplier = this.weather.getAttackTypeMultiplier(attackType);
}
let terrainMultiplier = 1;
if (this.terrain && grounded) {
terrainMultiplier = this.terrain.getAttackTypeMultiplier(attackType);
}
return weatherMultiplier * terrainMultiplier;
}
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;
}
}
2023-12-29 21:04:40 -05:00
getTimeOfDay(): TimeOfDay {
2023-04-16 20:41:52 -04:00
switch (this.biomeType) {
case Biome.ABYSS:
return TimeOfDay.NIGHT;
2023-12-29 21:04:40 -05:00
}
const waveCycle = ((this.scene.currentBattle?.waveIndex || 0) + this.scene.waveCycleOffset) % 40;
2023-12-29 21:04:40 -05:00
if (waveCycle < 15) {
2023-12-29 21:04:40 -05:00
return TimeOfDay.DAY;
}
2023-12-29 21:04:40 -05:00
if (waveCycle < 20) {
return TimeOfDay.DUSK;
}
2023-12-29 21:04:40 -05:00
if (waveCycle < 35) {
2023-12-29 21:04:40 -05:00
return TimeOfDay.NIGHT;
}
2023-12-29 21:04:40 -05:00
return TimeOfDay.DAWN;
2023-12-29 21:04:40 -05:00
}
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;
2023-04-16 20:41:52 -04:00
}
}
overrideTint(): [integer, integer, integer] {
switch (Overrides.ARENA_TINT_OVERRIDE) {
case TimeOfDay.DUSK:
return [ 98, 48, 73 ].map(c => Math.round((c + 128) / 2)) as [integer, integer, integer];
break;
case (TimeOfDay.NIGHT):
return [ 64, 64, 64 ];
break;
case TimeOfDay.DAWN:
case TimeOfDay.DAY:
default:
return [ 128, 128, 128 ];
break;
}
}
2023-12-29 21:04:40 -05:00
getDayTint(): [integer, integer, integer] {
if (Overrides.ARENA_TINT_OVERRIDE !== null) {
return this.overrideTint();
}
2023-12-29 21:04:40 -05:00
switch (this.biomeType) {
case Biome.ABYSS:
return [ 64, 64, 64 ];
default:
return [ 128, 128, 128 ];
2023-12-29 21:04:40 -05:00
}
}
getDuskTint(): [integer, integer, integer] {
if (Overrides.ARENA_TINT_OVERRIDE) {
return this.overrideTint();
}
if (!this.isOutside()) {
2023-12-29 21:04:40 -05:00
return [ 0, 0, 0 ];
}
2023-12-29 21:04:40 -05:00
switch (this.biomeType) {
default:
return [ 98, 48, 73 ].map(c => Math.round((c + 128) / 2)) as [integer, integer, integer];
2023-12-29 21:04:40 -05:00
}
}
getNightTint(): [integer, integer, integer] {
if (Overrides.ARENA_TINT_OVERRIDE) {
return this.overrideTint();
}
2023-12-29 21:04:40 -05:00
switch (this.biomeType) {
case Biome.ABYSS:
case Biome.SPACE:
case Biome.END:
return this.getDayTint();
2023-12-29 21:04:40 -05:00
}
if (!this.isOutside()) {
2023-12-29 21:04:40 -05:00
return [ 64, 64, 64 ];
}
2023-12-29 21:04:40 -05:00
switch (this.biomeType) {
default:
return [ 48, 48, 98 ];
2023-12-29 21:04:40 -05:00
}
}
setIgnoreAbilities(ignoreAbilities: boolean = true): void {
this.ignoreAbilities = ignoreAbilities;
}
applyTagsForSide(tagType: ArenaTagType | { new(...args: any[]): ArenaTag }, side: ArenaTagSide, ...args: any[]): void {
let tags = typeof tagType === "string"
2023-04-21 19:30:04 -04:00
? 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));
}
2024-05-24 01:45:04 +02:00
applyTags(tagType: ArenaTagType | { new(...args: any[]): ArenaTag }, ...args: any[]): void {
2024-02-09 14:48:20 -05:00
this.applyTagsForSide(tagType, ArenaTagSide.BOTH, ...args);
}
2023-04-21 19:30:04 -04:00
addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide = ArenaTagSide.BOTH, quiet: boolean = false, targetIndex?: BattlerIndex): boolean {
const existingTag = this.getTagOnSide(tagType, side);
2023-04-21 19:30:04 -04:00
if (existingTag) {
existingTag.onOverlap(this);
if (existingTag instanceof ArenaTrapTag) {
const { tagType, side, turnCount, layers, maxLayers } = existingTag as ArenaTrapTag;
this.eventTarget.dispatchEvent(new TagAddedEvent(tagType, side, turnCount, layers, maxLayers));
}
2023-04-21 19:30:04 -04:00
return false;
}
const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side);
2023-04-21 19:30:04 -04:00
this.tags.push(newTag);
newTag.onAdd(this, quiet);
2023-04-21 19:30:04 -04:00
const { layers = 0, maxLayers = 0 } = newTag instanceof ArenaTrapTag ? newTag : {};
this.eventTarget.dispatchEvent(new TagAddedEvent(newTag.tagType, newTag.side, newTag.turnCount, layers, maxLayers));
2023-04-21 19:30:04 -04:00
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"
2024-02-22 14:59:59 -05:00
? 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));
2023-04-21 19:30:04 -04:00
}
findTags(tagPredicate: (t: ArenaTag) => boolean): ArenaTag[] {
return this.findTagsOnSide(tagPredicate, ArenaTagSide.BOTH);
}
findTagsOnSide(tagPredicate: (t: ArenaTag) => boolean, side: ArenaTagSide): ArenaTag[] {
return this.tags.filter(t => tagPredicate(t) && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side));
}
2023-04-21 19:30:04 -04:00
lapseTags(): void {
2024-03-17 23:47:46 -04:00
this.tags.filter(t => !(t.lapse(this))).forEach(t => {
2023-04-21 19:30:04 -04:00
t.onRemove(this);
2024-03-17 23:47:46 -04:00
this.tags.splice(this.tags.indexOf(t), 1);
this.eventTarget.dispatchEvent(new TagRemovedEvent(t.tagType, t.side, t.turnCount));
2023-04-21 19:30:04 -04:00
});
}
2024-03-17 23:47:46 -04:00
removeTag(tagType: ArenaTagType): boolean {
const tags = this.tags;
const tag = tags.find(t => t.tagType === tagType);
if (tag) {
tag.onRemove(this);
tags.splice(tags.indexOf(tag), 1);
this.eventTarget.dispatchEvent(new TagRemovedEvent(tag.tagType, tag.side, tag.turnCount));
2024-03-17 23:47:46 -04:00
}
return !!tag;
}
removeTagOnSide(tagType: ArenaTagType, side: ArenaTagSide, quiet: boolean = false): boolean {
const tag = this.getTagOnSide(tagType, side);
if (tag) {
tag.onRemove(this, quiet);
this.tags.splice(this.tags.indexOf(tag), 1);
this.eventTarget.dispatchEvent(new TagRemovedEvent(tag.tagType, tag.side, tag.turnCount));
}
return !!tag;
}
2024-05-24 01:45:04 +02:00
removeAllTags(): void {
while (this.tags.length) {
this.tags[0].onRemove(this);
this.eventTarget.dispatchEvent(new TagRemovedEvent(this.tags[0].tagType, this.tags[0].side, this.tags[0].turnCount));
this.tags.splice(0, 1);
}
}
2023-04-16 20:41:52 -04:00
preloadBgm(): void {
this.scene.loadBgm(this.bgm);
}
2023-04-25 23:56:38 -04:00
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;
2024-05-09 19:05:19 -04:00
case Biome.METROPOLIS:
return 141.470;
case Biome.FOREST:
return 4.294;
case Biome.SEA:
return 1.672;
case Biome.SWAMP:
return 4.461;
2023-05-11 12:16:10 -04:00
case Biome.BEACH:
return 3.462;
case Biome.LAKE:
return 5.350;
2023-05-11 12:16:10 -04:00
case Biome.SEABED:
return 2.629;
2023-05-30 09:46:42 -04:00
case Biome.MOUNTAIN:
return 4.018;
2023-05-11 10:31:39 -04:00
case Biome.BADLANDS:
return 17.790;
2023-05-11 12:16:10 -04:00
case Biome.CAVE:
return 14.240;
case Biome.DESERT:
return 1.143;
2023-05-15 08:45:26 -04:00
case Biome.ICE_CAVE:
return 15.010;
2023-05-31 10:21:38 -04:00
case Biome.MEADOW:
return 3.891;
2024-03-17 16:47:49 -04:00
case Biome.POWER_PLANT:
return 2.810;
2023-05-31 10:21:38 -04:00
case Biome.VOLCANO:
return 5.116;
2023-05-31 10:21:38 -04:00
case Biome.GRAVEYARD:
return 3.232;
case Biome.DOJO:
return 6.205;
2023-06-01 13:54:52 -04:00
case Biome.FACTORY:
return 4.985;
2023-06-06 11:09:34 -04:00
case Biome.RUINS:
return 2.270;
2023-05-30 10:21:09 -04:00
case Biome.WASTELAND:
return 6.336;
2023-06-05 23:25:17 -04:00
case Biome.ABYSS:
return 5.130;
case Biome.SPACE:
return 21.347;
2024-01-07 23:54:49 -05:00
case Biome.CONSTRUCTION_SITE:
return 1.222;
2024-05-09 19:05:19 -04:00
case Biome.JUNGLE:
return 0.000;
case Biome.FAIRY_CAVE:
return 4.542;
case Biome.TEMPLE:
return 2.547;
2024-05-09 19:05:19 -04:00
case Biome.ISLAND:
return 2.751;
case Biome.LABORATORY:
return 114.862;
case Biome.SLUM:
return 1.221;
case Biome.SNOWY_FOREST:
return 3.047;
}
}
}
export function getBiomeKey(biome: Biome): string {
return Biome[biome].toLowerCase();
}
export function getBiomeHasProps(biomeType: Biome): boolean {
switch (biomeType) {
case Biome.METROPOLIS:
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.POWER_PLANT:
case Biome.VOLCANO:
case Biome.GRAVEYARD:
case Biome.FACTORY:
case Biome.RUINS:
case Biome.WASTELAND:
case Biome.ABYSS:
case Biome.CONSTRUCTION_SITE:
case Biome.JUNGLE:
case Biome.FAIRY_CAVE:
case Biome.TEMPLE:
case Biome.SNOWY_FOREST:
case Biome.ISLAND:
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", null, 1);
this.base.setOrigin(0, 0);
this.props = !player ?
new Array(3).fill(null).map(() => {
const ret = scene.addFieldSprite(0, 0, "plains_b", null, 1);
ret.setOrigin(0, 0);
ret.setVisible(false);
return ret;
}) : [];
}
setBiome(biome: Biome, propValue?: integer): void {
const hasProps = getBiomeHasProps(biome);
const biomeKey = getBiomeKey(biome);
const baseKey = `${biomeKey}_${this.player ? "a" : "b"}`;
2024-05-24 01:45:04 +02:00
if (biome !== this.biome) {
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 });
2024-05-24 22:57:28 -04:00
if (!(this.scene.anims.exists(baseKey))) {
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 });
2024-05-24 22:57:28 -04:00
if (!(this.scene.anims.exists(propKey))) {
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, (this.scene as BattleScene).waveSeed);
}
}
}