From 216cae16cd93459ede0beef691f65234ee48fcc8 Mon Sep 17 00:00:00 2001 From: RedstonewolfX <108761527+RedstonewolfX@users.noreply.github.com> Date: Thu, 11 Jul 2024 21:04:34 -0400 Subject: [PATCH] Log everything except the logging part --- src/battle-scene.ts | 2 + src/events/arena.ts | 921 +++++++++++++++++++++++++++++++++++++++----- src/field/arena.ts | 2 +- src/logger.ts | 445 +++++++++++++++++++-- src/phases.ts | 116 ++++-- 5 files changed, 1323 insertions(+), 163 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 03216101f27..131cb0c91b6 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -67,6 +67,7 @@ import { Species } from "#enums/species"; import { UiTheme } from "#enums/ui-theme"; import { TimedEventManager } from "#app/timed-event-manager.js"; import i18next from "i18next"; +import * as LoggerTools from "./logger" export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -1585,6 +1586,7 @@ export default class BattleScene extends SceneBase { if (fromArenaPool) { return this.arena.randomSpecies(waveIndex, level,null , getPartyLuckValue(this.party)); } + LoggerTools.rarities[LoggerTools.rarityslot[0]] = "" const filteredSpecies = speciesFilter ? [...new Set(allSpecies.filter(s => s.isCatchable()).filter(speciesFilter).map(s => { if (!filterAllEvolutions) { while (pokemonPrevolutions.hasOwnProperty(s.speciesId)) { diff --git a/src/events/arena.ts b/src/events/arena.ts index 67b423f3b75..8658b35d756 100644 --- a/src/events/arena.ts +++ b/src/events/arena.ts @@ -1,103 +1,850 @@ -import { ArenaTagSide } from "#app/data/arena-tag.js"; +import BattleScene from "../battle-scene"; +import { BiomePoolTier, PokemonPools, BiomeTierTrainerPools, biomePokemonPools, biomeTrainerPools } from "../data/biomes"; +import { Constructor } from "#app/utils"; +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"; +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"; +import { BattlerIndex } from "../battle"; +import { Terrain, TerrainType } from "../data/terrain"; +import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability"; +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 { TerrainType } from "#app/data/terrain.js"; -import { WeatherType } from "#app/data/weather.js"; +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"; +import * as LoggerTools from "../logger" -/** Alias for all {@linkcode ArenaEvent} type strings */ -export enum ArenaEventType { - /** Triggers when a {@linkcode WeatherType} is added, overlapped, or removed */ - WEATHER_CHANGED = "onWeatherChanged", - /** Triggers when a {@linkcode TerrainType} is added, overlapped, or removed */ - TERRAIN_CHANGED = "onTerrainChanged", +export class Arena { + public scene: BattleScene; + public biomeType: Biome; + public weather: Weather; + public terrain: Terrain; + public tags: ArenaTag[]; + public bgm: string; + public ignoreAbilities: boolean; - /** Triggers when a {@linkcode ArenaTagType} is added */ - TAG_ADDED = "onTagAdded", - /** Triggers when a {@linkcode ArenaTagType} is removed */ - TAG_REMOVED = "onTagRemoved", -} + private lastTimeOfDay: TimeOfDay; -/** - * Base container class for all {@linkcode ArenaEventType} events - * @extends Event - */ -export class ArenaEvent extends Event { - /** The total duration of the {@linkcode ArenaEventType} */ - public duration: number; - constructor(eventType: ArenaEventType, duration: number) { - super(eventType); + public pokemonPool: PokemonPools; + private trainerPool: BiomeTierTrainerPools; - this.duration = duration; + public readonly eventTarget: EventTarget = new EventTarget(); + + constructor(scene: BattleScene, biome: Biome, bgm: string) { + this.scene = scene; + this.biomeType = biome; + this.tags = []; + this.bgm = bgm; + this.trainerPool = biomeTrainerPools[biome]; + this.updatePoolsForTimeOfDay(); } -} -/** - * Container class for {@linkcode ArenaEventType.WEATHER_CHANGED} events - * @extends ArenaEvent -*/ -export class WeatherChangedEvent extends ArenaEvent { - /** The {@linkcode WeatherType} being overridden */ - public oldWeatherType: WeatherType; - /** The {@linkcode WeatherType} being set */ - public newWeatherType: WeatherType; - constructor(oldWeatherType: WeatherType, newWeatherType: WeatherType, duration: number) { - super(ArenaEventType.WEATHER_CHANGED, duration); - this.oldWeatherType = oldWeatherType; - this.newWeatherType = newWeatherType; + 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(); } -} -/** - * Container class for {@linkcode ArenaEventType.TERRAIN_CHANGED} events - * @extends ArenaEvent -*/ -export class TerrainChangedEvent extends ArenaEvent { - /** The {@linkcode TerrainType} being overridden */ - public oldTerrainType: TerrainType; - /** The {@linkcode TerrainType} being set */ - public newTerrainType: TerrainType; - constructor(oldTerrainType: TerrainType, newTerrainType: TerrainType, duration: number) { - super(ArenaEventType.TERRAIN_CHANGED, duration); - this.oldTerrainType = oldTerrainType; - this.newTerrainType = newTerrainType; + 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; + } + } + + randomSpecies(waveIndex: integer, level: integer, attempt?: integer, luckValue?: integer): PokemonSpecies { + const overrideSpecies = this.scene.gameMode.getOverrideSpecies(waveIndex); + if (overrideSpecies) { + return overrideSpecies; + } + const isBoss = !!this.scene.getEncounterBossSegments(waveIndex, level) && !!this.pokemonPool[BiomePoolTier.BOSS].length + && (this.biomeType !== Biome.END || this.scene.gameMode.isClassic || this.scene.gameMode.isWaveFinal(waveIndex)); + const randVal = isBoss ? 64 : 512; + // luck influences encounter rarity + let luckModifier = 0; + if (typeof luckValue !== "undefined") { + luckModifier = luckValue * (isBoss ? 0.5 : 2); + } + const tierValue = Utils.randSeedInt(randVal - luckModifier); + 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]); + var tiernames = [ + "Common", + "Uncommon", + "Rare", + "Super Rare", + "Ultra Rare", + "Common", + "Rare", + "Super Rare", + "Ultra Rare", + ] + LoggerTools.rarities[LoggerTools.rarityslot[0]] = tiernames[tier] + console.log(tiernames[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.subLegendary || 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.getWildSpeciesForLevel(level, true, isBoss, this.scene.gameMode); + if (newSpeciesId !== ret.speciesId) { + console.log("Replaced", Species[ret.speciesId], "with", Species[newSpeciesId]); + ret = getPokemonSpecies(newSpeciesId); + } + return ret; + } + + randomTrainerType(waveIndex: integer): TrainerType { + 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)]; + } + + 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.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; + } + + 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: + 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; + } + } + + getBgTerrainColorRatioForBiome(): number { + switch (this.biomeType) { + case Biome.SPACE: + return 1; + case Biome.END: + return 0; + } + + 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); + } + + if (this.weather?.weatherType === (weather || undefined)) { + return false; + } + + 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)); + + 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)); + } + + 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); + }); + + 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)); + + 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); + }); + + return true; + } + + isMoveWeatherCancelled(move: Move) { + return this.weather && !this.weather.isEffectSuppressed(this.scene) && this.weather.isMoveWeatherCancelled(move); + } + + 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; + } + + /** + * Gets the denominator for the chance for a trainer spawn + * @returns n where 1/n is the chance of a trainer battle + */ + 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.waveCycleOffset) % 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; + } + } + + 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; + } + } + + getDayTint(): [integer, integer, integer] { + if (Overrides.ARENA_TINT_OVERRIDE !== null) { + return this.overrideTint(); + } + switch (this.biomeType) { + case Biome.ABYSS: + return [ 64, 64, 64 ]; + default: + return [ 128, 128, 128 ]; + } + } + + getDuskTint(): [integer, integer, integer] { + if (Overrides.ARENA_TINT_OVERRIDE) { + return this.overrideTint(); + } + 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] { + if (Overrides.ARENA_TINT_OVERRIDE) { + return this.overrideTint(); + } + 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 ]; + } + } + + setIgnoreAbilities(ignoreAbilities: boolean = true): void { + this.ignoreAbilities = ignoreAbilities; + } + + applyTagsForSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide, ...args: unknown[]): 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 | Constructor, ...args: unknown[]): void { + this.applyTagsForSide(tagType, ArenaTagSide.BOTH, ...args); + } + + 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); + 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)); + } + + return false; + } + + const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side); + this.tags.push(newTag); + newTag.onAdd(this, quiet); + + const { layers = 0, maxLayers = 0 } = newTag instanceof ArenaTrapTag ? newTag : {}; + + this.eventTarget.dispatchEvent(new TagAddedEvent(newTag.tagType, newTag.side, newTag.turnCount, layers, maxLayers)); + + return true; + } + + getTag(tagType: ArenaTagType | Constructor): ArenaTag { + return this.getTagOnSide(tagType, ArenaTagSide.BOTH); + } + + getTagOnSide(tagType: ArenaTagType | Constructor, 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)); + } + + 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)); + } + + lapseTags(): void { + this.tags.filter(t => !(t.lapse(this))).forEach(t => { + t.onRemove(this); + this.tags.splice(this.tags.indexOf(t), 1); + + this.eventTarget.dispatchEvent(new TagRemovedEvent(t.tagType, t.side, t.turnCount)); + }); + } + + 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)); + } + 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; + } + + + 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); + } + } + + 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 141.470; + 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.600; + 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 20.036; + case Biome.CONSTRUCTION_SITE: + return 1.222; + case Biome.JUNGLE: + return 0.000; + case Biome.FAIRY_CAVE: + return 4.542; + case Biome.TEMPLE: + return 2.547; + 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; + } } } -/** - * Container class for {@linkcode ArenaEventType.TAG_ADDED} events - * @extends ArenaEvent -*/ -export class TagAddedEvent extends ArenaEvent { - /** The {@linkcode ArenaTagType} being added */ - public arenaTagType: ArenaTagType; - /** The {@linkcode ArenaTagSide} the tag is being placed on */ - public arenaTagSide: ArenaTagSide; - /** The current number of layers of the arena trap. */ - public arenaTagLayers: number; - /** The maximum amount of layers of the arena trap. */ - public arenaTagMaxLayers: number; +export function getBiomeKey(biome: Biome): string { + return Biome[biome].toLowerCase(); +} - constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number, arenaTagLayers?: number, arenaTagMaxLayers?: number) { - super(ArenaEventType.TAG_ADDED, duration); +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; + } - this.arenaTagType = arenaTagType; - this.arenaTagSide = arenaTagSide; - this.arenaTagLayers = arenaTagLayers; - this.arenaTagMaxLayers = arenaTagMaxLayers; - } -} -/** - * Container class for {@linkcode ArenaEventType.TAG_REMOVED} events - * @extends ArenaEvent -*/ -export class TagRemovedEvent extends ArenaEvent { - /** The {@linkcode ArenaTagType} being removed */ - public arenaTagType: ArenaTagType; - /** The {@linkcode ArenaTagSide} the tag was being placed on */ - public arenaTagSide: ArenaTagSide; - constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number) { - super(ArenaEventType.TAG_REMOVED, duration); - - this.arenaTagType = arenaTagType; - this.arenaTagSide = arenaTagSide; + 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"}`; + + 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 }); + 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 }); + 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); + } } } diff --git a/src/field/arena.ts b/src/field/arena.ts index ddb3499b3ae..51dba1089e9 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -33,7 +33,7 @@ export class Arena { private lastTimeOfDay: TimeOfDay; - private pokemonPool: PokemonPools; + public pokemonPool: PokemonPools; private trainerPool: BiomeTierTrainerPools; public readonly eventTarget: EventTarget = new EventTarget(); diff --git a/src/logger.ts b/src/logger.ts index cc861e9cc62..c93bc31cf20 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -8,8 +8,14 @@ import { OptionSelectItem } from "./ui/abstact-option-select-ui-handler"; import { TrainerType } from "#enums/trainer-type"; import { Modifier, PokemonHeldItemModifier } from "./modifier/modifier"; import Battle from "./battle"; -import { getBiomeName } from "./data/biomes"; +import { getBiomeName, PokemonPools, SpeciesTree } from "./data/biomes"; import { trainerConfigs } from "./data/trainer-config"; +import { Mode } from "./ui/ui"; +import { TitlePhase } from "./phases"; +import { Item } from "pokenode-ts"; +import Trainer from "./field/trainer"; +import { Species } from "./enums/species"; +import { junit } from "node:test/reporters"; /** * All logs. @@ -17,15 +23,24 @@ import { trainerConfigs } from "./data/trainer-config"; * Format: [filename, localStorage key, name, header, item sprite, header suffix] */ export const logs: string[][] = [ - ["instructions.txt", "path_log", "Steps", "Run Steps", "blunder_policy", ""], - ["encounters.csv", "enc_log", "Encounters", "Encounter Data", "ub", ",,,,,,,,,,,,,,,,"], + //["instructions.txt", "path_log", "Steps", "Run Steps", "blunder_policy", ""], + //["encounters.csv", "enc_log", "Encounters", "Encounter Data", "ub", ",,,,,,,,,,,,,,,,"], + ["drpd.json", "drpd", "DRPD", "", "wide_lens", ""], + //["drpd1.json", "drpd1", "DRPD 1", "", "wide_lens", ""], + //["drpd2.json", "drpd2", "DRPD 2", "", "wide_lens", ""], + //["drpd3.json", "drpd3", "DRPD 3", "", "wide_lens", ""], + //["drpd4.json", "drpd4", "DRPD 4", "", "wide_lens", ""], + //["drpd5.json", "drpd5", "DRPD 5", "", "wide_lens", ""], ] -export var logKeys: string[] = [ +export const logKeys: string[] = [ "i", // Instructions/steps "e", // Encounters "d", // Debug ]; +export const rarities = [] +export const rarityslot = [0] + export var StoredLog: DRPD = undefined; export const DRPD_Version = "1.0.0" @@ -38,8 +53,7 @@ export interface DRPD { authors: string[], date: string, waves: Wave[], - starters?: PokeData[], - filename: string + starters?: PokeData[] } export interface Wave { id: integer, @@ -91,7 +105,7 @@ export interface ItemData { quantity: integer, } -export function newDocument(name: string = "Untitled Run " + (new Date().getUTCMonth() + 1 < 10 ? "0" : "") + (new Date().getUTCMonth() + 1) + "/" + (new Date().getUTCDate() < 10 ? "0" : "") + new Date().getUTCDate() + "/" + new Date().getUTCFullYear(), authorName: string | string[] = "Write your name here"): DRPD { +export function newDocument(name: string = "Untitled Run", authorName: string | string[] = "Write your name here"): DRPD { return { version: DRPD_Version, title: name, @@ -99,7 +113,6 @@ export function newDocument(name: string = "Untitled Run " + (new Date().getUTCM date: (new Date().getUTCMonth() + 1 < 10 ? "0" : "") + (new Date().getUTCMonth() + 1) + "-" + (new Date().getUTCDate() < 10 ? "0" : "") + new Date().getUTCDate() + "-" + new Date().getUTCFullYear(), waves: new Array(50), starters: new Array(3), - filename: (new Date().getUTCMonth() + 1 < 10 ? "0" : "") + (new Date().getUTCMonth() + 1) + "-" + (new Date().getUTCDate() < 10 ? "0" : "") + new Date().getUTCDate() + "-" + new Date().getUTCFullYear() + "_untitled" } } export function importDocument(drpd: string): DRPD { @@ -177,6 +190,13 @@ export function exportWave(scene: BattleScene): Wave { } return ret; } +export function exportTrainer(trainer: Trainer): TrainerData { + return { + id: trainer.config.trainerType, + name: trainer.name, + type: trainer.config.getTitle(0, trainer.variant) + } +} export const byteSize = str => new Blob([str]).size const filesizes = ["b", "kb", "mb", "gb", "tb"] @@ -191,24 +211,29 @@ export function getSize(str: string) { } export function generateOption(i: integer): OptionSelectItem { - if (logs[i][4] != "") { - return { - label: `Export ${logs[i][2]} (${getSize(localStorage.getItem(logs[i][1]))})`, - handler: () => { - downloadLogByID(i) - return false; - }, - item: logs[i][4] - } - } else { - return { - label: `Export ${logs[i][2]} (${getSize(localStorage.getItem(logs[i][1]))})`, - handler: () => { - downloadLogByID(i) - return false; - } + var filename: string = (JSON.parse(localStorage.getItem(logs[i][1])) as DRPD).title + var op: OptionSelectItem = { + label: ` Export ${filename} (${getSize(localStorage.getItem(logs[i][1]))})`, + handler: () => { + downloadLogByID(i) + return false; } } + if (logs[i][4] != "") { + op.item = logs[i][4] + } + return op; +} +export function generateAddOption(i: integer, scene: BattleScene, o: TitlePhase) { + var op: OptionSelectItem = { + label: "Generate log " + logs[i][0], + handler: () => { + localStorage.setItem(logs[i][1], JSON.stringify(newDocument())) + o.callEnd(); + return true; + } + } + return op; } /** @@ -240,22 +265,25 @@ export function clearLog(keyword: string) { * @param keyword The identifier key for the log you want to reste */ export function downloadLog(keyword: string) { - var d = localStorage.getItem(logs[logKeys.indexOf(keyword)][1]) - // logs[logKeys.indexOf(keyword)][1] - const blob = new Blob([ d ], {type: "text/json"}); + var d = JSON.parse(localStorage.getItem(logs[logKeys.indexOf(keyword)][1])) + const blob = new Blob([ JSON.stringify(d) ], {type: "text/json"}); const link = document.createElement("a"); link.href = window.URL.createObjectURL(blob); - link.download = `${logs[logKeys.indexOf(keyword)][0]}`; + var date: string = (d as DRPD).date + var filename: string = date[0] + date[1] + "_" + date[3] + date[4] + "_" + date[6] + date[7] + date[8] + date[9] + "_route.json" + link.download = `${filename}`; link.click(); link.remove(); } export function downloadLogByID(i: integer) { console.log(i) - var d = localStorage.getItem(logs[i][1]) - const blob = new Blob([ d ], {type: "text/json"}); + var d = JSON.parse(localStorage.getItem(logs[i][1])) + const blob = new Blob([ printDRPD("", "", d as DRPD) ], {type: "text/json"}); const link = document.createElement("a"); link.href = window.URL.createObjectURL(blob); - link.download = `${logs[i][0]}`; + var date: string = (d as DRPD).date + var filename: string = date[0] + date[1] + "_" + date[3] + date[4] + "_" + date[6] + date[7] + date[8] + date[9] + "_route.json" + link.download = `${filename}`; link.click(); link.remove(); } @@ -263,20 +291,149 @@ export function logTeam(scene: BattleScene, floor: integer = undefined) { if (floor == undefined) floor = scene.currentBattle.waveIndex var team = scene.getEnemyParty() if (team[0].hasTrainer()) { - var sprite = scene.currentBattle.trainer.config.getSpriteKey() - var trainerCat = Utils.getEnumKeys(TrainerType)[Utils.getEnumValues(TrainerType).indexOf(scene.currentBattle.trainer.config.trainerType)] - setRow("e", floor + ",0," + sprite + ",trainer," + trainerCat + ",,,,,,,,,,,,", floor, 0) + //var sprite = scene.currentBattle.trainer.config.getSpriteKey() + //var trainerCat = Utils.getEnumKeys(TrainerType)[Utils.getEnumValues(TrainerType).indexOf(scene.currentBattle.trainer.config.trainerType)] + //setRow("e", floor + ",0," + sprite + ",trainer," + trainerCat + ",,,,,,,,,,,,", floor, 0) } else { for (var i = 0; i < team.length; i++) { - logPokemon(scene, floor, i, team[i]) + logPokemon(scene, floor, i, team[i], rarities[i]) } if (team.length == 1) { //setRow("e", ",,,,,,,,,,,,,,,,", floor, 1) } } } -export function logPokemon(scene: BattleScene, floor: integer = undefined, slot: integer, pokemon: EnemyPokemon) { +export function getWave(drpd: DRPD, floor: integer, scene: BattleScene) { + var wv: Wave; + var insertPos: integer; + for (var i = 0; i < drpd.waves.length; i++) { + if (drpd.waves[i] != undefined) { + if (drpd.waves[i].id == floor) { + wv = drpd.waves[i] + if (wv.pokemon == undefined) wv.pokemon = [] + } + } else if (insertPos == undefined) { + insertPos = i + } + } + if (wv == undefined && insertPos != undefined) { + drpd.waves[insertPos] = { + id: floor, + reload: false, + //type: floor % 10 == 0 ? "boss" : (floor % 10 == 5 ? "trainer" : "wild"), + type: floor % 10 == 0 ? "boss" : "wild", + double: scene.currentBattle.double, + actions: [], + shop: "", + biome: getBiomeName(scene.arena.biomeType), + pokemon: [] + } + wv = drpd.waves[insertPos] + } + drpd.waves.sort((a, b) => { + if (a == undefined) return 1; // empty values move to the bottom + if (b == undefined) return -1; // empty values move to the bottom + return a.id - b.id + }) + if (wv == undefined) { + console.error("Out of wave slots??") + scene.ui.showText("Out of wave slots!\nClearing duplicates...", null, () => { + for (var i = 0; i < drpd.waves.length - 1; i++) { + if (drpd.waves[i] != undefined && drpd.waves[i+1] != undefined) { + if (drpd.waves[i].id == drpd.waves[i+1].id) { + drpd.waves[i+1] = undefined + drpd.waves.sort((a, b) => { + if (a == undefined) return 1; // empty values move to the bottom + if (b == undefined) return -1; // empty values move to the bottom + return a.id - b.id + }) + } + } + } + if (drpd.waves[49] != undefined) { + scene.ui.showText("No space!\nPress F12 for info") + console.error("There should have been 50 slots, but somehow the program ran out of space.") + console.error("Go yell at @redstonewolf8557 to fix this") + } else { + for (var i = 0; i < drpd.waves.length; i++) { + if (drpd.waves[i] != undefined) { + if (drpd.waves[i].id == floor) { + wv = drpd.waves[i] + if (wv.pokemon == undefined) wv.pokemon = [] + } + } else if (insertPos == undefined) { + insertPos = i + } + } + if (wv == undefined && insertPos != undefined) { + drpd.waves[insertPos] = { + id: floor, + reload: false, + //type: floor % 10 == 0 ? "boss" : (floor % 10 == 5 ? "trainer" : "wild"), + type: floor % 10 == 0 ? "boss" : "wild", + double: scene.currentBattle.double, + actions: [], + shop: "", + biome: getBiomeName(scene.arena.biomeType), + pokemon: [] + } + wv = drpd.waves[insertPos] + } + drpd.waves.sort((a, b) => { + if (a == undefined) return 1; // empty values move to the bottom + if (b == undefined) return -1; // empty values move to the bottom + return a.id - b.id + }) + if (wv == undefined) { + scene.ui.showText("Failed to make space\nPress F12 for info") + console.error("There should be space to store a new wave, but the program failed to find space anyways") + console.error("Go yell at @redstonewolf8557 to fix this") + } + } + }) + } + return wv; +} +/* +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; + } + } +}*/ +function checkForPokeInBiome(species: Species, pool: (Species | SpeciesTree)[]): boolean { + //console.log(species, pool) + for (var i = 0; i < pool.length; i++) { + if (typeof pool[i] === "number") { + //console.log(pool[i] + " == " + species + "? " + (pool[i] == species)) + if (pool[i] == species) return true; + } else { + var k = Object.keys(pool[i]) + //console.log(pool[i], k) + for (var j = 0; j < k.length; j++) { + //console.log(pool[i][k[j]] + " == " + species + "? " + (pool[i][k[j]] == species)) + if (pool[i][k[j]] == species) return true; + } + } + } + return false; +} +export function logPokemon(scene: BattleScene, floor: integer = undefined, slot: integer, pokemon: EnemyPokemon, encounterRarity?: string) { if (floor == undefined) floor = scene.currentBattle.waveIndex + /* var modifiers: string[] = [] var mods = pokemon.getHeldItems() for (var i = 0; i < mods.length; i++) { @@ -305,7 +462,88 @@ export function logPokemon(scene: BattleScene, floor: integer = undefined, slot: //console.log(idx, data.slice(0, idx), newLine, data.slice(idx)) setRow("e", newLine, floor, slot) //console.log(localStorage.getItem(logs[logKeys.indexOf("e")][1]).split("\n")) + */ + if (localStorage.getItem("drpd") == null) localStorage.setItem("drpd", JSON.stringify(newDocument())) + var drpd: DRPD = JSON.parse(localStorage.getItem("drpd")) as DRPD; + var wv: Wave = getWave(drpd, floor, scene) + var pk: PokeData = exportPokemon(pokemon, encounterRarity) + if (wv.pokemon[slot] != undefined) { + if (encounterRarity == "" || encounterRarity == undefined) { + if (wv.pokemon[slot].rarity != undefined && wv.pokemon[slot].rarity != "???") pk.rarity = wv.pokemon[slot].rarity + else { + var biome = scene.arena.biomeType + console.log(scene.arena.pokemonPool) + var tiernames = [ + "Common", + "Uncommon", + "Rare", + "Super Rare", + "Ultra Rare", + "Common", + "Rare", + "Super Rare", + "Ultra Rare", + ] + for (var i = 0; i < tiernames.length; i++) { + if (checkForPokeInBiome(wv.pokemon[slot].id, scene.arena.pokemonPool[i]) == true) { + console.log("Autofilled rarity for " + pk.name + " as " + tiernames[i]) + pk.rarity = tiernames[i] + } + } + } + } + if (JSON.stringify(wv.pokemon[slot]) != JSON.stringify(pk)) { + console.log("A different Pokemon already exists in this slot! Flagging as a reload") + wv.reload = true + } + } + if (pk.rarity == undefined) { + var biome = scene.arena.biomeType + console.log(scene.arena.pokemonPool) + var tiernames = [ + "Common", + "Uncommon", + "Rare", + "Super Rare", + "Ultra Rare", + "Common", + "Rare", + "Super Rare", + "Ultra Rare", + ] + for (var i = 0; i < tiernames.length; i++) { + if (checkForPokeInBiome(wv.pokemon[slot].id, scene.arena.pokemonPool[i]) == true) { + console.log("Autofilled rarity for " + pk.name + " as " + tiernames[i]) + pk.rarity = tiernames[i] + } + } + } + wv.pokemon[slot] = pk; + console.log(drpd) + localStorage.setItem("drpd", JSON.stringify(drpd)) } +export function logTrainer(scene: BattleScene, floor: integer = undefined) { + if (localStorage.getItem("drpd") == null) localStorage.setItem("drpd", JSON.stringify(newDocument())) + var drpd: DRPD = JSON.parse(localStorage.getItem("drpd")) as DRPD; + var wv: Wave = getWave(drpd, floor, scene) + var t: TrainerData = exportTrainer(scene.currentBattle.trainer) + wv.trainer = t + wv.type = "trainer" + console.log(drpd) + localStorage.setItem("drpd", JSON.stringify(drpd)) +} +export function logPlayerTeam(scene: BattleScene) { + if (localStorage.getItem("drpd") == null) localStorage.setItem("drpd", JSON.stringify(newDocument())) + var drpd: DRPD = JSON.parse(localStorage.getItem("drpd")) as DRPD; + //var wv: Wave = getWave(drpd, 1, scene) + var P = scene.getParty() + for (var i = 0; i < P.length; i++) { + drpd.starters[i] = exportPokemon(P[i]) + } + console.log(drpd) + localStorage.setItem("drpd", JSON.stringify(drpd)) +} + export function dataSorter(a: string, b: string) { var da = a.split(",") var db = b.split(",") @@ -384,4 +622,139 @@ export function setRow(keyword: string, newLine: string, floor: integer, slot: i console.log(i + " " + data[i]) } localStorage.setItem(logs[logKeys.indexOf(keyword)][1], data.slice(0, idx).join("\n") + "\n" + newLine + (data.slice(idx).length == 0 ? "" : "\n") + data.slice(idx).join("\n")); +} +export function printDRPD(inData: string, indent: string, drpd: DRPD): string { + inData += indent + "{" + inData += "\n" + indent + " \"version\": \"" + drpd.version + "\"" + inData += ",\n" + indent + " \"title\": \"" + drpd.title + "\"" + inData += ",\n" + indent + " \"authors\": [\"" + drpd.authors.join("\", \"") + "\"]" + inData += ",\n" + indent + " \"date\": \"" + drpd.date + "\"" + inData += ",\n" + indent + " \"waves\": [\n" + var isFirst = true + for (var i = 0; i < drpd.waves.length; i++) { + if (drpd.waves[i] != undefined) { + if (isFirst) { + isFirst = false; + } else { + inData += ",\n" + } + inData = printWave(inData, indent + " ", drpd.waves[i]) + } + } + inData += "\n" + indent + " ]\n" + indent + "}" + return inData; +} +function printWave(inData: string, indent: string, wave: Wave): string { + inData += indent + "{" + inData += "\n" + indent + " \"id\": " + wave.id + "" + inData += ",\n" + indent + " \"reload\": " + wave.reload + "" + inData += ",\n" + indent + " \"type\": \"" + wave.type + "\"" + inData += ",\n" + indent + " \"double\": " + wave.double + "" + inData += ",\n" + indent + " \"actions\": [\n" + var isFirst = true + for (var i = 0; i < wave.actions.length; i++) { + if (wave.actions[i] != undefined) { + if (isFirst) { + isFirst = false; + } else { + inData += "," + } + inData += "\n " + indent + "\"" + wave.actions[i] + "\"" + } + } + if (!isFirst) inData += "\n" + inData += indent + " ]" + inData += ",\n " + indent + "\"shop\": \"" + wave.shop + "\"" + inData += ",\n " + indent + "\"biome\": \"" + wave.biome + "\"" + if (wave.trainer) + inData += ",\n " + indent + "\"trainer\": " + wave.trainer + if (wave.pokemon) { + inData += ",\n " + indent + "\"pokemon\": [\n" + isFirst = true + for (var i = 0; i < wave.pokemon.length; i++) { + if (wave.pokemon[i] != undefined) { + if (isFirst) { + isFirst = false; + } else { + inData += ",\n" + } + inData = printPoke(inData, indent + " ", wave.pokemon[i]) + } + } + } + inData += "\n" + indent + " ]\n" + indent + "}" + return inData; +} +function printPoke(inData: string, indent: string, pokemon: PokeData) { + var itemdata: string = "" + inData += indent + "{" + inData += "\n" + indent + " \"id\": " + pokemon.id + inData += ",\n" + indent + " \"name\": \"" + pokemon.name + "\"" + inData += ",\n" + indent + " \"ability\": \"" + pokemon.ability + "\"" + inData += ",\n" + indent + " \"isHiddenAbility\": " + pokemon.isHiddenAbility + inData += ",\n" + indent + " \"passiveAbility\": \"" + pokemon.passiveAbility + "\"" + inData += ",\n" + indent + " \"nature\": \n" + inData = printNature(inData, indent + " ", pokemon.nature) + inData += ",\n" + indent + " \"gender\": \"" + pokemon.gender + "\"" + inData += ",\n" + indent + " \"rarity\": \"" + pokemon.rarity + "\"" + inData += ",\n" + indent + " \"captured\": " + pokemon.captured + inData += ",\n" + indent + " \"level\": " + pokemon.level + if (pokemon.items.length > 0) { + inData += ",\n" + indent + " \"items\": [\n" + var isFirst = true + for (var i = 0; i < pokemon.items.length; i++) { + if (pokemon.items[i] != undefined) { + if (isFirst) { + isFirst = false; + } else { + inData += "," + } + inData = printItem(inData, indent + " ", pokemon.items[i]) + } + } + if (!isFirst) inData += "\n" + inData += indent + " ]" + } else { + inData += ",\n" + indent + " \"items\": []" + } + inData += ",\n" + indent + " \"ivs\": " + inData = printIV(inData, indent + " ", pokemon.ivs) + //inData += ",\n" + indent + " \"rarity\": " + pokemon.rarity + inData += "\n" + indent + "}" + return inData; +} +function printNature(inData: string, indent: string, nature: NatureData) { + inData += indent + "{" + inData += "\n" + indent + " \"name\": \"" + nature.name + "\"" + inData += ",\n" + indent + " \"increased\": \"" + nature.increased + "\"" + inData += ",\n" + indent + " \"decreased\": \"" + nature.decreased + "\"" + inData += "\n" + indent + "}" + return inData; +} +function printIV(inData: string, indent: string, iv: IVData) { + inData += "{" + inData += "\n" + indent + " \"hp\": " + iv.hp + inData += ",\n" + indent + " \"atk\": " + iv.atk + inData += ",\n" + indent + " \"def\": " + iv.def + inData += ",\n" + indent + " \"spatk\": " + iv.spatk + inData += ",\n" + indent + " \"spdef\": " + iv.spdef + inData += ",\n" + indent + " \"speed\": " + iv.speed + inData += "\n" + indent + "}" + return inData; +} +function printTrainer(inData: string, indent: string, trainer: TrainerData) { + inData += indent + "{" + inData += "\n" + indent + " \"id\": \"" + trainer.id + "\"" + inData += ",\n" + indent + " \"name\": \"" + trainer.name + "\"" + inData += ",\n" + indent + " \"type\": \"" + trainer.type + "\"" + inData += "\n" + indent + "}" + return inData; +} +function printItem(inData: string, indent: string, item: ItemData) { + inData += indent + "{" + inData += "\n" + indent + " \"id\": \"" + item.id + "\"" + inData += ",\n" + indent + " \"name\": \"" + item.name + "\"" + inData += ",\n" + indent + " \"quantity\": " + item.quantity + inData += "\n" + indent + "}" + return inData; } \ No newline at end of file diff --git a/src/phases.ts b/src/phases.ts index f65dd406337..f81791ccef8 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -342,6 +342,7 @@ export class TitlePhase extends Phase { start(): void { super.start(); + console.log(LoggerTools.importDocument(JSON.stringify(LoggerTools.newDocument()))) this.scene.ui.clearText(); this.scene.ui.fadeIn(250); @@ -394,8 +395,61 @@ export class TitlePhase extends Phase { return saves.map(f => f[1]); } + callEnd(): boolean { + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + super.end(); + return true; + } + + showLoggerOptions(txt: string, options: OptionSelectItem[]): boolean { + this.scene.ui.showText("Export or clear game logs.", null, () => this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: options })); + return true; + } + + logMenu(): boolean { + const options: OptionSelectItem[] = []; + for (var i = 0; i < LoggerTools.logs.length; i++) { + if (localStorage.getItem(LoggerTools.logs[i][1]) != null) { + options.push(LoggerTools.generateOption(i) as OptionSelectItem) + } else { + //options.push(LoggerTools.generateAddOption(i, this.scene, this)) + } + } + for (var i = 0; i < LoggerTools.logs.length; i++) { + if (localStorage.getItem(LoggerTools.logs[i][1]) != null) { + //options.push(LoggerTools.generateOption(i, this.scene, this.logMenu) as OptionSelectItem) + } else { + options.push(LoggerTools.generateAddOption(i, this.scene, this)) + } + } + options.push({ + label: "Delete all", + handler: () => { + for (var i = 0; i < LoggerTools.logs.length; i++) { + if (localStorage.getItem(LoggerTools.logs[i][1]) != null) { + localStorage.removeItem(LoggerTools.logs[i][1]) + } + } + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + super.end(); + return true; + } + }, { + label: i18next.t("menu:cancel"), + handler: () => { + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + super.end(); + return true; + } + }); + this.scene.ui.showText("Export or clear game logs.", null, () => this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: options })); + return true; + } + showOptions(): void { - var hasFile = true const options: OptionSelectItem[] = []; if (false) if (loggedInUser.lastSessionSlot > -1) { @@ -434,7 +488,15 @@ export class TitlePhase extends Phase { } else { console.log("Failed to get last save") this.getLastSave(true) - hasFile = false + if (loggedInUser.lastSessionSlot > -1) { + options.push({ + label: i18next.t("continue", null, { ns: "menu"}), + handler: () => { + this.loadSaveSlot(this.lastSessionData ? -1 : loggedInUser.lastSessionSlot); + return true; + } + }); + } } } options.push({ @@ -500,45 +562,9 @@ export class TitlePhase extends Phase { }, { label: "Manage Logs", handler: () => { - const options: OptionSelectItem[] = []; - for (var i = 0; i < LoggerTools.logs.length; i++) { - if (localStorage.getItem(LoggerTools.logs[i][1]) != null) - options.push(LoggerTools.generateOption(i) as OptionSelectItem) - } - options.push({ - label: `Export all (${options.length})`, - handler: () => { - for (var i = 0; i < LoggerTools.logKeys.length; i++) { - LoggerTools.downloadLog(LoggerTools.logKeys[i]) - } - return false; - } - }, { - label: `Reset all (${LoggerTools.logKeys.length})`, - handler: () => { - for (var i = 0; i < LoggerTools.logKeys.length; i++) { - LoggerTools.clearLog(LoggerTools.logKeys[i]) - } - this.scene.clearPhaseQueue(); - this.scene.pushPhase(new TitlePhase(this.scene)); - super.end(); - return true; - } - }, { - label: i18next.t("menu:cancel"), - handler: () => { - this.scene.clearPhaseQueue(); - this.scene.pushPhase(new TitlePhase(this.scene)); - super.end(); - return true; - } - }); - this.scene.ui.showText("Export or clear game logs.", null, () => this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: options })); - return true; + return this.logMenu() } }) - // If the player has no save data (as determined above), hide the "Load Game" button - if (hasFile || true) options.push({ label: i18next.t("menu:loadGame"), handler: () => { @@ -1128,11 +1154,16 @@ export class EncounterPhase extends BattlePhase { let totalBst = 0; + while (LoggerTools.rarities.length > 0) { + LoggerTools.rarities.pop() + } + LoggerTools.rarityslot[0] = 0 battle.enemyLevels.forEach((level, e) => { if (!this.loaded) { if (battle.battleType === BattleType.TRAINER) { battle.enemyParty[e] = battle.trainer.genPartyMember(e); } else { + LoggerTools.rarityslot[0] = e const enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true); battle.enemyParty[e] = this.scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!this.scene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies)); if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { @@ -1172,6 +1203,7 @@ export class EncounterPhase extends BattlePhase { console.log(enemyPokemon.name, enemyPokemon.species.speciesId, enemyPokemon.stats); }); + console.log(LoggerTools.rarities) if (this.scene.getParty().filter(p => p.isShiny()).length === 6) { this.scene.validateAchv(achvs.SHINY_PARTY); @@ -1293,6 +1325,12 @@ export class EncounterPhase extends BattlePhase { const enemyField = this.scene.getEnemyField(); LoggerTools.logTeam(this.scene, this.scene.currentBattle.waveIndex) + if (this.scene.getEnemyParty()[0].hasTrainer()) { + LoggerTools.logTrainer(this.scene, this.scene.currentBattle.waveIndex) + } + if (this.scene.currentBattle.waveIndex == 1) { + LoggerTools.logPlayerTeam(this.scene) + } if (this.scene.currentBattle.battleType === BattleType.WILD) { enemyField.forEach(enemyPokemon => {