Add daily runs (WiP)

pull/16/head
Flashfyre 2024-03-16 22:06:56 -04:00
parent 81f9e2da36
commit 56f4a71ca6
14 changed files with 332 additions and 139 deletions

View File

@ -756,7 +756,7 @@ export default class BattleScene extends Phaser.Scene {
}
reset(clearScene?: boolean): void {
this.setSeed(Utils.randomString(16));
this.setSeed(Utils.randomString(24));
console.log('Seed:', this.seed);
this.gameMode = gameModes[GameModes.CLASSIC];
@ -835,35 +835,9 @@ export default class BattleScene extends Phaser.Scene {
} else {
if (!this.gameMode.hasTrainers)
newBattleType = BattleType.WILD;
else if (battleType === undefined) {
if ((newWaveIndex % 30) === 20 && !this.gameMode.isWaveFinal(newWaveIndex))
newBattleType = BattleType.TRAINER;
else if (newWaveIndex % 10 !== 1 && newWaveIndex % 10) {
const trainerChance = this.arena.getTrainerChance();
let allowTrainerBattle = true;
if (trainerChance) {
const waveBase = Math.floor(newWaveIndex / 10) * 10;
for (let w = Math.max(newWaveIndex - 3, waveBase + 2); w <= Math.min(newWaveIndex + 3, waveBase + 9); w++) {
if (w === newWaveIndex)
continue;
if ((w % 30) === 20 || fixedBattles.hasOwnProperty(w)) {
allowTrainerBattle = false;
break;
} else if (w < newWaveIndex) {
this.executeWithSeedOffset(() => {
const waveTrainerChance = this.arena.getTrainerChance();
if (!Utils.randSeedInt(waveTrainerChance))
allowTrainerBattle = false;
}, w);
if (!allowTrainerBattle)
break;
}
}
}
newBattleType = allowTrainerBattle && trainerChance && !Utils.randSeedInt(trainerChance) ? BattleType.TRAINER : BattleType.WILD;
} else
newBattleType = BattleType.WILD;
} else
else if (battleType === undefined)
newBattleType = this.gameMode.isWaveTrainer(newWaveIndex, this.arena) ? BattleType.TRAINER : BattleType.WILD;
else
newBattleType = battleType;
if (newBattleType === BattleType.TRAINER) {
@ -897,7 +871,7 @@ export default class BattleScene extends Phaser.Scene {
//this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6));
if (!waveIndex && lastBattle) {
const isNewBiome = !(lastBattle.waveIndex % 10);
const isNewBiome = !(lastBattle.waveIndex % 10) || (this.gameMode.isDaily && lastBattle.waveIndex === 49);
const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS;
this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy());
this.trySpreadPokerus();
@ -1030,6 +1004,9 @@ export default class BattleScene extends Phaser.Scene {
}
getEncounterBossSegments(waveIndex: integer, level: integer, species?: PokemonSpecies, forceBoss: boolean = false): integer {
if (this.gameMode.isDaily && this.gameMode.isWaveFinal(waveIndex))
return 5;
let isBoss: boolean;
if (forceBoss || (species && (species.pseudoLegendary || species.legendary || species.mythical)))
isBoss = true;
@ -1164,7 +1141,7 @@ export default class BattleScene extends Phaser.Scene {
getMaxExpLevel(ignoreLevelCap?: boolean): integer {
if (ignoreLevelCap)
return Number.MAX_SAFE_INTEGER;
const lastWaveIndex = Math.ceil((this.currentBattle?.waveIndex || 1) / 10) * 10;
const lastWaveIndex = Math.ceil((this.gameMode.getWaveForDifficulty(this.currentBattle?.waveIndex || 1)) / 10) * 10;
const baseLevel = (1 + lastWaveIndex / 2 + Math.pow(lastWaveIndex / 25, 2)) * 1.2;
return Math.ceil(baseLevel / 2) * 2 + 2;
}

View File

@ -96,12 +96,18 @@ export default class Battle {
const ret = Math.floor(baseLevel * bossMultiplier);
if (this.battleSpec === BattleSpec.FINAL_BOSS || !(this.waveIndex % 250))
return Math.ceil(ret / 25) * 25;
return ret + Math.round(Phaser.Math.RND.realInRange(-1, 1) * Math.floor(levelWaveIndex / 10));
let levelOffset = 0;
if (!this.gameMode.isWaveFinal(this.waveIndex))
levelOffset = Math.round(Phaser.Math.RND.realInRange(-1, 1) * Math.floor(levelWaveIndex / 10));
return ret + levelOffset;
}
let levelOffset = 0;
const deviation = 10 / levelWaveIndex;
levelOffset = Math.abs(this.randSeedGaussForLevel(deviation));
return Math.max(Math.round(baseLevel + Math.abs(this.randSeedGaussForLevel(deviation))), 1);
return Math.max(Math.round(baseLevel + levelOffset), 1);
}
randSeedGaussForLevel(value: number): number {

View File

@ -11,12 +11,19 @@ export interface DailyRunConfig {
starters: Starter;
}
export function getDailyRunSeed(scene: BattleScene): string {
return 'Test';//Utils.randomString(16)
export function fetchDailyRunSeed(): Promise<string> {
return new Promise<string>(resolve => {
Utils.apiFetch('daily/seed').then(response => {
if (!response.ok) {
resolve(null);
return;
}
return response.text();
}).then(seed => resolve(seed));
});
}
export function getDailyRunStarters(scene: BattleScene): Starter[] {
const seed = getDailyRunSeed(scene);
export function getDailyRunStarters(scene: BattleScene, seed: string): Starter[] {
const starters: Starter[] = [];
scene.executeWithSeedOffset(() => {
@ -25,13 +32,15 @@ export function getDailyRunStarters(scene: BattleScene): Starter[] {
starterWeights.push(Utils.randSeedInt(9 - starterWeights[0], 1));
starterWeights.push(10 - (starterWeights[0] + starterWeights[1]));
const startingLevel = gameModes[GameModes.DAILY].getStartingLevel();
for (let s = 0; s < starterWeights.length; s++) {
const weight = starterWeights[s];
const weightSpecies = Object.keys(speciesStarters)
.map(s => parseInt(s) as Species)
.filter(s => speciesStarters[s] === weight);
const starterSpecies = getPokemonSpecies(Phaser.Math.RND.pick(weightSpecies));
const pokemon = new PlayerPokemon(scene, starterSpecies, gameModes[GameModes.DAILY].getStartingLevel(), undefined, undefined, undefined, undefined, undefined, undefined, undefined);
const starterSpecies = getPokemonSpecies(getPokemonSpecies(Utils.randSeedItem(weightSpecies)).getSpeciesForLevel(startingLevel, true, true, false, true));
const pokemon = new PlayerPokemon(scene, starterSpecies, startingLevel, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
const starter: Starter = {
species: starterSpecies,
dexAttr: pokemon.getDexAttr(),
@ -42,6 +51,5 @@ export function getDailyRunStarters(scene: BattleScene): Starter[] {
pokemon.destroy();
}
}, 0, seed);
return starters;
}

View File

@ -402,7 +402,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm {
return this.name;
}
getSpeciesForLevel(level: integer, allowEvolving: boolean = false, forTrainer: boolean = false, isBoss: boolean = false): Species {
getSpeciesForLevel(level: integer, allowEvolving: boolean = false, forTrainer: boolean = false, isBoss: boolean = false, player: boolean = false): Species {
const prevolutionLevels = this.getPrevolutionLevels();
if (prevolutionLevels.length) {
@ -437,11 +437,15 @@ export default class PokemonSpecies extends PokemonSpeciesForm {
if (!forTrainer && isRegionalEvolution)
evolutionChance = 0;
else if (ev.wildDelay === SpeciesWildEvolutionDelay.NONE) {
const maxLevelDiff = forTrainer || isBoss ? forTrainer && isBoss ? 10 : 20 : 40;
const minChance = forTrainer ? 0.5 : 0.75;
evolutionChance = Math.min(minChance + easeInFunc(Math.min(level - ev.level, maxLevelDiff) / maxLevelDiff) * (1 - minChance), 1);
if (player)
evolutionChance = 1;
else {
const maxLevelDiff = forTrainer || isBoss ? forTrainer && isBoss ? 10 : 20 : 40;
const minChance = forTrainer ? 0.5 : 0.75;
evolutionChance = Math.min(minChance + easeInFunc(Math.min(level - ev.level, maxLevelDiff) / maxLevelDiff) * (1 - minChance), 1);
}
} else {
let preferredMinLevel = (ev.level - 1) + ev.wildDelay * (forTrainer || isBoss ? forTrainer && isBoss ? 5 : 10 : 20);
let preferredMinLevel = (ev.level - 1) + ev.wildDelay * (player ? 0 : forTrainer || isBoss ? forTrainer && isBoss ? 5 : 10 : 20);
let evolutionLevel = ev.level > 1 ? ev.level : Math.floor(preferredMinLevel / 2);
if (ev.level <= 1 && pokemonPrevolutions.hasOwnProperty(this.speciesId)) {

View File

@ -455,7 +455,7 @@ interface TrainerConfigs {
}
function getWavePartyTemplate(scene: BattleScene, ...templates: TrainerPartyTemplate[]) {
return templates[Math.min(Math.max(Math.ceil(((scene.currentBattle?.waveIndex || startingWave) - 20) / 30), 0), templates.length - 1)];
return templates[Math.min(Math.max(Math.ceil((scene.gameMode.getWaveForDifficulty(scene.currentBattle?.waveIndex || startingWave, true) - 20) / 30), 0), templates.length - 1)];
}
function getGymLeaderPartyTemplate(scene: BattleScene) {

View File

@ -53,6 +53,9 @@ export class Arena {
}
randomSpecies(waveIndex: integer, level: integer, attempt?: 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 tierValue = Utils.randSeedInt(!isBoss ? 512 : 64);
@ -123,8 +126,9 @@ export class Arena {
}
randomTrainerType(waveIndex: integer): TrainerType {
const isBoss = (waveIndex % 30) === 20 && !!this.trainerPool[BiomePoolTier.BOSS].length
&& (this.biomeType !== Biome.END || this.scene.gameMode.isClassic || this.scene.gameMode.isWaveFinal(waveIndex));
const isBoss = !!this.trainerPool[BiomePoolTier.BOSS].length
&& this.scene.gameMode.isTrainerBoss(waveIndex, this.biomeType);
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

View File

@ -1073,9 +1073,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (source.getTag(BattlerTagType.CRIT_BOOST))
critLevel.value += 2;
const critChance = Math.ceil(16 / Math.pow(2, critLevel.value));
const blockCrit = new Utils.BooleanHolder(false);
applyAbAttrs(BlockCritAbAttr, this, null);
isCritical = !blockCrit.value && !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !this.scene.currentBattle.randSeedInt(critChance));
isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !this.scene.currentBattle.randSeedInt(critChance));
if (isCritical) {
const blockCrit = new Utils.BooleanHolder(false);
applyAbAttrs(BlockCritAbAttr, this, null, blockCrit);
if (blockCrit.value)
isCritical = false;
}
}
const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this));
const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source));

View File

@ -78,7 +78,8 @@ export default class Trainer extends Phaser.GameObjects.Container {
const ret = [];
const partyTemplate = this.getPartyTemplate();
let baseLevel = 1 + waveIndex / 2 + Math.pow(waveIndex / 25, 2);
const difficultyWaveIndex = this.scene.gameMode.getWaveForDifficulty(waveIndex);
let baseLevel = 1 + difficultyWaveIndex / 2 + Math.pow(difficultyWaveIndex / 25, 2);
for (let i = 0; i < partyTemplate.size; i++) {
let multiplier = 1;
@ -106,8 +107,8 @@ export default class Trainer extends Phaser.GameObjects.Container {
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));
multiplier = Math.min(multiplier + 0.025 * Math.floor(difficultyWaveIndex / 25), 1.2);
levelOffset = -Math.floor((difficultyWaveIndex / 50) * (TrainerPartyMemberStrength.STRONG - strength));
}
const level = Math.ceil(baseLevel * multiplier) + levelOffset;

View File

@ -1,5 +1,10 @@
import { fixedBattles } from "./battle";
import BattleScene, { STARTING_BIOME_OVERRIDE, STARTING_LEVEL_OVERRIDE, STARTING_MONEY_OVERRIDE } from "./battle-scene";
import { Biome } from "./data/enums/biome";
import { Species } from "./data/enums/species";
import PokemonSpecies, { allSpecies } from "./data/pokemon-species";
import { Arena } from "./field/arena";
import * as Utils from "./utils";
export enum GameModes {
CLASSIC,
@ -14,6 +19,7 @@ interface GameModeConfig {
isDaily?: boolean;
hasTrainers?: boolean;
hasFixedBattles?: boolean;
hasNoShop?: boolean;
hasRandomBiomes?: boolean;
hasRandomBosses?: boolean;
isSplicedOnly?: boolean;
@ -26,6 +32,7 @@ export class GameMode implements GameModeConfig {
public isDaily: boolean;
public hasTrainers: boolean;
public hasFixedBattles: boolean;
public hasNoShop: boolean;
public hasRandomBiomes: boolean;
public hasRandomBosses: boolean;
public isSplicedOnly: boolean;
@ -59,15 +66,66 @@ export class GameMode implements GameModeConfig {
}
}
getWaveForDifficulty(waveIndex: integer): integer {
getWaveForDifficulty(waveIndex: integer, ignoreCurveChanges: boolean = false): integer {
switch (this.modeId) {
case GameModes.DAILY:
return waveIndex + 30 + Math.floor(waveIndex / 5);
return waveIndex + 30 + (!ignoreCurveChanges ? Math.floor(waveIndex / 5) : 0);
default:
return waveIndex;
}
}
isWaveTrainer(waveIndex: integer, arena: Arena): boolean {
if (this.isDaily)
return waveIndex % 10 === 5 || (!(waveIndex % 10) && waveIndex > 10 && !this.isWaveFinal(waveIndex));
if ((waveIndex % 30) === 20 && !this.isWaveFinal(waveIndex))
return true;
else if (waveIndex % 10 !== 1 && waveIndex % 10) {
const trainerChance = arena.getTrainerChance();
let allowTrainerBattle = true;
if (trainerChance) {
const waveBase = Math.floor(waveIndex / 10) * 10;
for (let w = Math.max(waveIndex - 3, waveBase + 2); w <= Math.min(waveIndex + 3, waveBase + 9); w++) {
if (w === waveIndex)
continue;
if ((w % 30) === 20 || fixedBattles.hasOwnProperty(w)) {
allowTrainerBattle = false;
break;
} else if (w < waveIndex) {
arena.scene.executeWithSeedOffset(() => {
const waveTrainerChance = arena.getTrainerChance();
if (!Utils.randSeedInt(waveTrainerChance))
allowTrainerBattle = false;
}, w);
if (!allowTrainerBattle)
break;
}
}
}
return allowTrainerBattle && trainerChance && !Utils.randSeedInt(trainerChance);
}
return false;
}
isTrainerBoss(waveIndex: integer, biomeType: Biome): boolean {
switch (this.modeId) {
case GameModes.DAILY:
return waveIndex > 10 && waveIndex < 50 && !(waveIndex % 10);
default:
return (waveIndex % 30) === 20 && (biomeType !== Biome.END || this.isClassic || this.isWaveFinal(waveIndex));
}
}
getOverrideSpecies(waveIndex: integer): PokemonSpecies {
if (this.isDaily && this.isWaveFinal(waveIndex)) {
const allFinalBossSpecies = allSpecies.filter(s => (s.pseudoLegendary || s.legendary || s.mythical)
&& s.baseTotal >= 600 && s.speciesId !== Species.ETERNATUS && s.speciesId !== Species.ARCEUS);
return Utils.randSeedItem(allFinalBossSpecies);
}
return null;
}
isWaveFinal(waveIndex: integer): boolean {
switch (this.modeId) {
case GameModes.CLASSIC:
@ -109,5 +167,5 @@ export const gameModes = Object.freeze({
[GameModes.CLASSIC]: new GameMode(GameModes.CLASSIC, { isClassic: true, hasTrainers: true, hasFixedBattles: true }),
[GameModes.ENDLESS]: new GameMode(GameModes.ENDLESS, { isEndless: true, hasRandomBiomes: true, hasRandomBosses: true }),
[GameModes.SPLICED_ENDLESS]: new GameMode(GameModes.SPLICED_ENDLESS, { isEndless: true, hasRandomBiomes: true, hasRandomBosses: true, isSplicedOnly: true }),
[GameModes.DAILY]: new GameMode(GameModes.DAILY, { isDaily: true, hasTrainers: true })
[GameModes.DAILY]: new GameMode(GameModes.DAILY, { isDaily: true, hasTrainers: true, hasNoShop: true })
});

View File

@ -12,7 +12,6 @@ import * as Utils from '../utils';
import { TempBattleStat, getTempBattleStatBoosterItemName, getTempBattleStatName } from '../data/temp-battle-stat';
import { BerryType, getBerryEffectDescription, getBerryName } from '../data/berry';
import { Unlockables } from '../system/unlockables';
import { GameModes } from '../game-mode';
import { StatusEffect, getStatusEffectDescriptor } from '../data/status-effect';
import { SpeciesFormKey } from '../data/pokemon-species';
import BattleScene from '../battle-scene';
@ -29,7 +28,8 @@ export enum ModifierPoolType {
PLAYER,
WILD,
TRAINER,
ENEMY_BUFF
ENEMY_BUFF,
DAILY_STARTER
}
type NewModifierFunc = (type: ModifierType, args: any[]) => Modifier;
@ -775,7 +775,7 @@ export const modifierTypes = {
if (!party[0].scene.getModifiers(Modifiers.TerastallizeAccessModifier).length)
return null;
let type: Type;
if (!Utils.randInt(3)) {
if (!Utils.randSeedInt(3)) {
const partyMemberTypes = party.map(p => p.getTypes(false, true)).flat();
type = Utils.randSeedItem(partyMemberTypes);
} else
@ -886,7 +886,11 @@ export const modifierTypes = {
ENEMY_FUSED_CHANCE: () => new ModifierType('Fusion Token', 'Adds a 1% chance that a wild Pokémon will be a fusion', (type, _args) => new Modifiers.EnemyFusionChanceModifier(type, 1), 'wl_custom_spliced'),
};
const modifierPool = {
interface ModifierPool {
[tier: string]: WeightedModifierType[]
}
const modifierPool: ModifierPool = {
[ModifierTier.COMMON]: [
new WeightedModifierType(modifierTypes.POKEBALL, 6),
new WeightedModifierType(modifierTypes.RARE_CANDY, 2),
@ -1015,12 +1019,10 @@ const modifierPool = {
new WeightedModifierType(modifierTypes.VOUCHER_PLUS, 8),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => !party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 24 : 0, 24),
new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => party[0].scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE] ? 1 : 0, 1),
].map(m => { m.setTier(ModifierTier.MASTER); return m; }),
[ModifierTier.LUXURY]: [
].map(m => { m.setTier(ModifierTier.LUXURY); return m; }),
].map(m => { m.setTier(ModifierTier.MASTER); return m; })
};
const wildModifierPool = {
const wildModifierPool: ModifierPool = {
[ModifierTier.COMMON]: [
new WeightedModifierType(modifierTypes.BERRY, 1)
].map(m => { m.setTier(ModifierTier.COMMON); return m; }),
@ -1038,7 +1040,7 @@ const wildModifierPool = {
].map(m => { m.setTier(ModifierTier.MASTER); return m; })
};
const trainerModifierPool = {
const trainerModifierPool: ModifierPool = {
[ModifierTier.COMMON]: [
new WeightedModifierType(modifierTypes.BERRY, 8),
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3)
@ -1062,7 +1064,7 @@ const trainerModifierPool = {
].map(m => { m.setTier(ModifierTier.MASTER); return m; })
};
const enemyBuffModifierPool = {
const enemyBuffModifierPool: ModifierPool = {
[ModifierTier.COMMON]: [
new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_BOOSTER, 10),
new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_REDUCTION, 10),
@ -1099,6 +1101,32 @@ const enemyBuffModifierPool = {
[ModifierTier.MASTER]: [ ].map(m => { m.setTier(ModifierTier.MASTER); return m; })
};
const dailyStarterModifierPool: ModifierPool = {
[ModifierTier.COMMON]: [
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 1),
new WeightedModifierType(modifierTypes.BERRY, 3),
].map(m => { m.setTier(ModifierTier.COMMON); return m; }),
[ModifierTier.GREAT]: [
new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 5),
].map(m => { m.setTier(ModifierTier.GREAT); return m; }),
[ModifierTier.ULTRA]: [
new WeightedModifierType(modifierTypes.REVIVER_SEED, 4),
new WeightedModifierType(modifierTypes.SOOTHE_BELL, 1),
new WeightedModifierType(modifierTypes.SOUL_DEW, 1),
new WeightedModifierType(modifierTypes.GOLDEN_PUNCH, 1),
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
[ModifierTier.ROGUE]: [
new WeightedModifierType(modifierTypes.GRIP_CLAW, 5),
new WeightedModifierType(modifierTypes.BATON, 2),
new WeightedModifierType(modifierTypes.FOCUS_BAND, 5),
new WeightedModifierType(modifierTypes.KINGS_ROCK, 3),
].map(m => { m.setTier(ModifierTier.ROGUE); return m; }),
[ModifierTier.MASTER]: [
new WeightedModifierType(modifierTypes.LEFTOVERS, 1),
new WeightedModifierType(modifierTypes.SHELL_BELL, 1),
].map(m => { m.setTier(ModifierTier.MASTER); return m; })
};
export function getModifierType(modifierTypeFunc: ModifierTypeFunc): ModifierType {
const modifierType = modifierTypeFunc();
if (!modifierType.id)
@ -1109,6 +1137,9 @@ export function getModifierType(modifierTypeFunc: ModifierTypeFunc): ModifierTyp
let modifierPoolThresholds = {};
let ignoredPoolIndexes = {};
let dailyStarterModifierPoolThresholds = {};
let ignoredDailyStarterPoolIndexes = {};
let enemyModifierPoolThresholds = {};
let enemyIgnoredPoolIndexes = {};
@ -1118,8 +1149,24 @@ let enemyBuffIgnoredPoolIndexes = {};
const tierWeights = [ 769 / 1024, 192 / 1024, 48 / 1024, 12 / 1024, 1 / 1024 ];
export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType) {
const player = !poolType;
const pool = player ? modifierPool : poolType === ModifierPoolType.WILD ? wildModifierPool : poolType === ModifierPoolType.TRAINER ? trainerModifierPool : enemyBuffModifierPool;
let pool: ModifierPool;
switch (poolType) {
case ModifierPoolType.PLAYER:
pool = modifierPool;
break;
case ModifierPoolType.WILD:
pool = wildModifierPool;
break;
case ModifierPoolType.TRAINER:
pool = trainerModifierPool;
break;
case ModifierPoolType.ENEMY_BUFF:
pool = enemyBuffModifierPool;
break;
case ModifierPoolType.DAILY_STARTER:
pool = dailyStarterModifierPool;
break;
}
const ignoredIndexes = {};
const modifierTableData = {};
const thresholds = Object.fromEntries(new Map(Object.keys(pool).slice(0, -1).map(t => {
@ -1130,7 +1177,7 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod
let i = 0;
pool[t].reduce((total: integer, modifierType: WeightedModifierType) => {
const weightedModifierType = modifierType as WeightedModifierType;
const existingModifiers = party[0].scene.findModifiers(m => (m.type.generatorId || m.type.id) === weightedModifierType.modifierType.id, player);
const existingModifiers = party[0].scene.findModifiers(m => (m.type.generatorId || m.type.id) === weightedModifierType.modifierType.id, poolType === ModifierPoolType.PLAYER);
const itemModifierType = weightedModifierType.modifierType instanceof ModifierTypeGenerator
? weightedModifierType.modifierType.generateType(party)
: weightedModifierType.modifierType;
@ -1168,15 +1215,24 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod
}
if (outputModifierData)
console.table(modifierTableData);
if (player) {
modifierPoolThresholds = thresholds;
ignoredPoolIndexes = ignoredIndexes;
} else if (poolType !== ModifierPoolType.ENEMY_BUFF) {
enemyModifierPoolThresholds = thresholds;
enemyIgnoredPoolIndexes = ignoredIndexes;
} else {
enemyBuffModifierPoolThresholds = thresholds;
enemyBuffIgnoredPoolIndexes = ignoredIndexes;
switch (poolType) {
case ModifierPoolType.PLAYER:
modifierPoolThresholds = thresholds;
ignoredPoolIndexes = ignoredIndexes;
break;
case ModifierPoolType.WILD:
case ModifierPoolType.TRAINER:
enemyModifierPoolThresholds = thresholds;
enemyIgnoredPoolIndexes = ignoredIndexes;
break;
case ModifierPoolType.ENEMY_BUFF:
enemyBuffModifierPoolThresholds = thresholds;
enemyBuffIgnoredPoolIndexes = ignoredIndexes;
break;
case ModifierPoolType.DAILY_STARTER:
dailyStarterModifierPoolThresholds = thresholds;
ignoredDailyStarterPoolIndexes = ignoredIndexes;
break;
}
}
@ -1184,9 +1240,7 @@ export function getModifierTypeFuncById(id: string): ModifierTypeFunc {
return modifierTypes[id];
}
export function getPlayerModifierTypeOptionsForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierTypeOption[] {
if (waveIndex % 10 === 0)
return modifierPool[ModifierTier.LUXURY].filter(m => !(m.weight instanceof Function) || m.weight(party)).map(m => new ModifierTypeOption(m.modifierType, 0));
export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemon[]): ModifierTypeOption[] {
const options: ModifierTypeOption[] = [];
const retryCount = Math.min(count * 5, 50);
new Array(count).fill(0).map(() => {
@ -1257,9 +1311,46 @@ export function getEnemyModifierTypesForWave(waveIndex: integer, count: integer,
return ret;
}
export function getDailyRunStarterModifiers(party: PlayerPokemon[]): Modifiers.PokemonHeldItemModifier[] {
const ret: Modifiers.PokemonHeldItemModifier[] = [];
for (let p of party) {
for (let m = 0; m < 3; m++) {
const tierValue = Utils.randSeedInt(64);
const tier = tierValue > 25 ? ModifierTier.COMMON : tierValue > 12 ? ModifierTier.GREAT : tierValue > 4 ? ModifierTier.ULTRA : tierValue ? ModifierTier.ROGUE : ModifierTier.MASTER;
const modifier = getNewModifierTypeOption(party, ModifierPoolType.DAILY_STARTER, tier).type.newModifier(p) as Modifiers.PokemonHeldItemModifier;
ret.push(modifier);
}
}
return ret;
}
function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier?: ModifierTier, upgradeCount?: integer): ModifierTypeOption {
const player = !poolType;
const pool = player ? modifierPool : poolType === ModifierPoolType.WILD ? wildModifierPool : poolType === ModifierPoolType.TRAINER ? trainerModifierPool : enemyBuffModifierPool;
let pool: ModifierPool;
let thresholds: object;
switch (poolType) {
case ModifierPoolType.PLAYER:
pool = modifierPool;
thresholds = modifierPoolThresholds;
break;
case ModifierPoolType.WILD:
pool = wildModifierPool;
thresholds = enemyModifierPoolThresholds;
break;
case ModifierPoolType.TRAINER:
pool = trainerModifierPool;
thresholds = enemyModifierPoolThresholds;
break;
case ModifierPoolType.ENEMY_BUFF:
pool = enemyBuffModifierPool;
thresholds = enemyBuffModifierPoolThresholds;
break;
case ModifierPoolType.DAILY_STARTER:
pool = dailyStarterModifierPool;
thresholds = dailyStarterModifierPoolThresholds;
break;
}
if (tier === undefined) {
const tierValue = Utils.randSeedInt(1024);
upgradeCount = 0;
@ -1283,7 +1374,6 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
}
}
const thresholds = player ? modifierPoolThresholds : pool !== enemyBuffModifierPool ? enemyModifierPoolThresholds : enemyBuffModifierPoolThresholds;
const tierThresholds = Object.keys(thresholds[tier]);
const totalWeight = parseInt(tierThresholds[tierThresholds.length - 1]);
const value = Utils.randSeedInt(totalWeight);
@ -1314,7 +1404,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
}
export function getDefaultModifierTypeForTier(tier: ModifierTier): ModifierType {
let modifierType: ModifierType | WeightedModifierType = modifierPool[tier || ModifierTier.COMMON][tier !== ModifierTier.LUXURY ? 0 : 2];
let modifierType: ModifierType | WeightedModifierType = modifierPool[tier || ModifierTier.COMMON][0];
if (modifierType instanceof WeightedModifierType)
modifierType = (modifierType as WeightedModifierType).modifierType;
return modifierType;

View File

@ -1260,17 +1260,14 @@ export class ExpBoosterModifier extends PersistentModifier {
}
apply(args: any[]): boolean {
console.log(this.boostMultiplier);
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (1 + (this.getStackCount() * this.boostMultiplier)));
return true;
}
getStackCount(): integer {
return this.boostMultiplier < 1 ? super.getStackCount() : 10;
}
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): integer {
return 99;
return this.boostMultiplier < 1 ? this.boostMultiplier < 0.6 ? 99 : 30 : 10;
}
}

View File

@ -1,8 +1,8 @@
import BattleScene, { STARTING_BIOME_OVERRIDE, bypassLogin, startingWave } from "./battle-scene";
import BattleScene, { bypassLogin, startingWave } from "./battle-scene";
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon";
import * as Utils from './utils';
import { Moves } from "./data/enums/moves";
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveCategory, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr } from "./data/move";
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr } from "./data/move";
import { Mode } from './ui/ui';
import { Command } from "./ui/command-ui-handler";
import { Stat } from "./data/pokemon-stat";
@ -19,7 +19,7 @@ import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } fr
import { biomeLinks } from "./data/biomes";
import { Biome } from "./data/enums/biome";
import { ModifierTier } from "./modifier/modifier-tier";
import { FusePokemonModifierType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, RememberMoveModifierType, TmModifierType, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptionsForWave, getPlayerShopModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type";
import { FusePokemonModifierType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, RememberMoveModifierType, TmModifierType, getDailyRunStarterModifiers, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptions, getPlayerShopModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { BattlerTagLapseType, EncoreTag, HideSpriteTag as HiddenTag, ProtectedTag, TrappedTag } from "./data/battler-tags";
import { BattlerTagType } from "./data/enums/battler-tag-type";
@ -53,7 +53,7 @@ import { Tutorial, handleTutorial } from "./tutorial";
import { TerrainType } from "./data/terrain";
import { OptionSelectConfig, OptionSelectItem } from "./ui/abstact-option-select-ui-handler";
import { SaveSlotUiMode } from "./ui/save-slot-select-ui-handler";
import { getDailyRunSeed, getDailyRunStarters } from "./data/daily-run";
import { fetchDailyRunSeed, getDailyRunStarters } from "./data/daily-run";
import { GameModes, gameModes } from "./game-mode";
export class LoginPhase extends Phase {
@ -137,6 +137,7 @@ export class TitlePhase extends Phase {
start(): void {
super.start();
this.scene.ui.clearText();
this.scene.ui.fadeIn(250);
this.scene.fadeOutBgm(0, false);
@ -169,12 +170,12 @@ export class TitlePhase extends Phase {
this.loadSaveSlot(slotId);
}
)
}/*,
},
{
label: 'Daily Run',
label: 'Daily Run (Beta)',
handler: () => this.initDailyRun(),
keepOpen: true
}*/);
});
const config: OptionSelectConfig = {
options: options,
noCancel: true
@ -205,35 +206,48 @@ export class TitlePhase extends Phase {
return this.end();
}
this.scene.sessionSlotId = slotId;
this.scene.setSeed(getDailyRunSeed(this.scene));
this.scene.gameMode = gameModes[GameModes.DAILY];
this.scene.money = this.scene.gameMode.getStartingMoney();
fetchDailyRunSeed().then(seed => {
this.scene.setSeed(seed);
this.scene.resetSeed(1);
const starters = getDailyRunStarters(this.scene);
this.scene.gameMode = gameModes[GameModes.DAILY];
this.scene.money = this.scene.gameMode.getStartingMoney();
const party = this.scene.getParty();
const loadPokemonAssets: Promise<void>[] = [];
for (let starter of starters) {
const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr);
const starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0));
const starterGender = starter.species.malePercent !== null
? !starterProps.female ? Gender.MALE : Gender.FEMALE
: Gender.GENDERLESS;
const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0);
const starterPokemon = this.scene.addPlayerPokemon(starter.species, this.scene.gameMode.getStartingLevel(), starterProps.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterIvs, starter.nature);
if (starter.moveset)
starterPokemon.tryPopulateMoveset(starter.moveset);
starterPokemon.setVisible(false);
party.push(starterPokemon);
loadPokemonAssets.push(starterPokemon.loadAssets());
}
Promise.all(loadPokemonAssets).then(() => {
this.scene.time.delayedCall(500, () => this.scene.playBgm());
this.scene.gameData.gameStats.dailyRunSessionsPlayed++;
this.scene.newBattle();
this.scene.sessionPlayTime = 0;
this.end();
const starters = getDailyRunStarters(this.scene, seed);
const startingLevel = this.scene.gameMode.getStartingLevel();
const party = this.scene.getParty();
const loadPokemonAssets: Promise<void>[] = [];
for (let starter of starters) {
const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr);
const starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0));
const starterGender = starter.species.malePercent !== null
? !starterProps.female ? Gender.MALE : Gender.FEMALE
: Gender.GENDERLESS;
const starterPokemon = this.scene.addPlayerPokemon(starter.species, startingLevel, starterProps.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, undefined, starter.nature);
starterPokemon.setVisible(false);
party.push(starterPokemon);
loadPokemonAssets.push(starterPokemon.loadAssets());
}
regenerateModifierPoolThresholds(party, ModifierPoolType.DAILY_STARTER);
const modifiers: Modifier[] = Array(3).fill(null).map(() => modifierTypes.EXP_SHARE().withIdFromFunc(modifierTypes.EXP_SHARE).newModifier())
.concat(Array(3).fill(null).map(() => modifierTypes.GOLDEN_EXP_CHARM().withIdFromFunc(modifierTypes.GOLDEN_EXP_CHARM).newModifier()))
.concat(getDailyRunStarterModifiers(party));
for (let m of modifiers)
this.scene.addModifier(m, true, false, false, true);
this.scene.updateModifiers(true, true);
Promise.all(loadPokemonAssets).then(() => {
this.scene.time.delayedCall(500, () => this.scene.playBgm());
this.scene.gameData.gameStats.dailyRunSessionsPlayed++;
this.scene.newArena(this.scene.gameMode.getStartingBiome(this.scene), true);
this.scene.newBattle();
this.scene.sessionPlayTime = 0;
this.end();
});
});
});
}
@ -242,12 +256,10 @@ export class TitlePhase extends Phase {
if (!this.loaded && !this.scene.gameMode.isDaily) {
this.scene.arena.preloadBgm();
this.scene.pushPhase(new SelectStarterPhase(this.scene));
this.scene.newArena(this.scene.gameMode.getStartingBiome(this.scene), true);
} else
this.scene.playBgm();
if (!this.loaded)
this.scene.newArena(this.scene.gameMode.getStartingBiome(this.scene), true);
this.scene.pushPhase(new EncounterPhase(this.scene, this.loaded));
if (this.loaded) {
@ -833,6 +845,8 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase {
}
doEncounter(): void {
this.scene.playBgm(undefined, true);
for (let pokemon of this.scene.getParty()) {
if (pokemon)
pokemon.resetBattleData();
@ -887,7 +901,8 @@ export class SelectBiomePhase extends BattlePhase {
this.end();
};
if (this.scene.gameMode.isClassic && this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex + 9))
if ((this.scene.gameMode.isClassic && this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex + 9))
|| (this.scene.gameMode.isDaily && this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)))
setNextBiome(Biome.END);
else if (this.scene.gameMode.hasRandomBiomes)
setNextBiome(this.generateNextBiome());
@ -949,8 +964,6 @@ export class SwitchBiomePhase extends BattlePhase {
this.scene.arenaPlayerTransition.setAlpha(0);
this.scene.arenaPlayerTransition.setVisible(true);
this.scene.time.delayedCall(1000, () => this.scene.playBgm());
this.scene.tweens.add({
targets: [ this.scene.arenaPlayer, this.scene.arenaBgTransition, this.scene.arenaPlayerTransition ],
duration: 1000,
@ -2917,7 +2930,11 @@ export class VictoryPhase extends PokemonPhase {
if (this.scene.gameMode.isEndless || !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) {
if (this.scene.currentBattle.waveIndex % 10)
this.scene.pushPhase(new SelectModifierPhase(this.scene));
else {
else if (this.scene.gameMode.isDaily) {
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_CHARM));
if (this.scene.currentBattle.waveIndex > 10 && !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex))
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_POKEBALL));
} else {
const superExpWave = !this.scene.gameMode.isEndless ? 20 : 10;
if (this.scene.currentBattle.waveIndex <= 750 && (this.scene.currentBattle.waveIndex <= 500 || (this.scene.currentBattle.waveIndex % 30) === superExpWave))
this.scene.pushPhase(new ModifierRewardPhase(this.scene, (this.scene.currentBattle.waveIndex % 30) !== superExpWave || this.scene.currentBattle.waveIndex > 250 ? modifierTypes.EXP_CHARM : modifierTypes.SUPER_EXP_CHARM));
@ -3076,12 +3093,15 @@ export class GameOverPhase extends BattlePhase {
start() {
super.start();
this.scene.gameData.clearSession(this.scene.sessionSlotId).then(() => {
(this.victory ? this.scene.gameData.tryClearSession : this.scene.gameData.deleteSession)(this.scene.sessionSlotId).then((success: boolean | [boolean, boolean]) => {
this.scene.time.delayedCall(1000, () => {
let firstClear = false;
if (this.victory) {
firstClear = this.scene.validateAchv(achvs.CLASSIC_VICTORY);
this.scene.gameData.gameStats.sessionsWon++;
if (this.victory && success[1]) {
if (this.scene.gameMode.isClassic) {
firstClear = this.scene.validateAchv(achvs.CLASSIC_VICTORY);
this.scene.gameData.gameStats.sessionsWon++;
} else if (this.scene.gameMode.isDaily && success[1])
this.scene.gameData.gameStats.dailyRunSessionsWon++;
}
this.scene.gameData.saveSystem();
const fadeDuration = this.victory ? 10000 : 5000;
@ -3090,7 +3110,7 @@ export class GameOverPhase extends BattlePhase {
this.scene.clearPhaseQueue();
this.scene.ui.clearText();
this.handleUnlocks();
if (this.victory && !firstClear)
if (this.victory && !firstClear && success[1])
this.scene.unshiftPhase(new GameOverModifierRewardPhase(this.scene, modifierTypes.VOUCHER_PREMIUM));
this.scene.reset();
this.scene.unshiftPhase(new TitlePhase(this.scene));
@ -3189,6 +3209,7 @@ export class ExpPhase extends PlayerPartyMemberPokemonPhase {
let exp = new Utils.NumberHolder(this.expValue);
this.scene.applyModifiers(ExpBoosterModifier, true, exp);
exp.value = Math.floor(exp.value);
console.log(this.expValue, exp.value);
this.scene.ui.showText(`${pokemon.name} gained\n${exp.value} EXP. Points!`, null, () => {
const lastLevel = pokemon.level;
let newLevel: integer;
@ -3916,7 +3937,7 @@ export class SelectModifierPhase extends BattlePhase {
}
getModifierTypeOptions(modifierCount: integer): ModifierTypeOption[] {
return getPlayerModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, modifierCount, this.scene.getParty());
return getPlayerModifierTypeOptions(modifierCount, this.scene.getParty());
}
addModifier(modifier: Modifier): Promise<void> {

View File

@ -615,7 +615,7 @@ export class GameData {
});
}
clearSession(slotId: integer): Promise<boolean> {
deleteSession(slotId: integer): Promise<boolean> {
return new Promise<boolean>(resolve => {
if (bypassLogin) {
localStorage.removeItem('sessionData');
@ -636,6 +636,27 @@ export class GameData {
});
}
tryClearSession(slotId: integer): Promise<[success: boolean, newClear: boolean]> {
return new Promise<[boolean, boolean]>(resolve => {
if (bypassLogin) {
localStorage.removeItem('sessionData');
return resolve([true, true]);
}
updateUserInfo().then(success => {
if (success !== null && !success)
return resolve([false, false]);
Utils.apiFetch(`savedata/clear?slot=${slotId}`).then(response => {
if (response.ok) {
loggedInUser.lastSessionSlot = -1;
return response.json();
}
resolve([false, false]);
}).then(jsonResponse => resolve([true, jsonResponse.success as boolean]));
});
});
}
parseSessionData(dataStr: string): SessionSaveData {
return JSON.parse(dataStr, (k: string, v: any) => {
/*const versions = [ scene.game.config.gameVersion, sessionData.gameVersion || '0.0.0' ];

View File

@ -90,7 +90,9 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
this.updateRerollCostText();
const typeOptions = args[1] as ModifierTypeOption[];
const shopTypeOptions = getPlayerShopModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, this.scene.getWaveMoneyAmount(1));
const shopTypeOptions = !this.scene.gameMode.hasNoShop
? getPlayerShopModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, this.scene.getWaveMoneyAmount(1))
: [];
const optionsYOffset = shopTypeOptions.length >= SHOP_OPTIONS_ROW_LIMIT ? -8 : -24;
for (let m = 0; m < typeOptions.length; m++) {