diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 07fd42761c9..50e8e90efc2 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1,6 +1,6 @@ import Phaser from "phaser"; import UI from "./ui/ui"; -import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase } from "./phases"; +import { NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, TurnInitPhase, ReturnPhase, LevelCapPhase, ShowTrainerPhase, LoginPhase, MovePhase, TitlePhase, SwitchPhase, runShinyCheck } from "./phases"; import Pokemon, { PlayerPokemon, EnemyPokemon } from "./field/pokemon"; import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species"; import { Constructor } from "#app/utils"; @@ -16,7 +16,7 @@ import { TextStyle, addTextObject, getTextColor } from "./ui/text"; import { allMoves } from "./data/move"; import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getPartyLuckValue } from "./modifier/modifier-type"; import AbilityBar from "./ui/ability-bar"; -import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, PostBattleInitAbAttr, applyAbAttrs, applyPostBattleInitAbAttrs } from "./data/ability"; +import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, PostBattleInitAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyPostBattleInitAbAttrs } from "./data/ability"; import { allAbilities } from "./data/ability"; import Battle, { BattleType, FixedBattleConfig } from "./battle"; import { GameMode, GameModes, getGameMode } from "./game-mode"; @@ -130,6 +130,8 @@ export default class BattleScene extends SceneBase { public doBiomePanels: boolean = false; public disableDailyShinies: boolean = true; // Disables shiny luck in Daily Runs to prevent affecting RNG public quickloadDisplayMode: string = "Dailies"; + public tempWaveSeed: string; + public tempRngCounter: integer = 0; /** * Determines the condition for a notification should be shown for Candy Upgrades * - 0 = 'Off' @@ -1055,6 +1057,230 @@ export default class BattleScene extends SceneBase { } } + generatePokemonForBattle(battle: Battle) { + var totalBst = 0; + battle.enemyLevels.forEach((level, e) => { + if (true) { + if (battle.battleType === BattleType.TRAINER) { + battle.enemyParty[e] = battle.trainer.genPartyMember(e); + } else { + LoggerTools.rarityslot[0] = e + const enemySpecies = this.randomSpecies(battle.waveIndex, level, true); + battle.enemyParty[e] = this.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!this.getEncounterBossSegments(battle.waveIndex, level, enemySpecies)); + if (this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { + battle.enemyParty[e].ivs = new Array(6).fill(31); + } + this.getParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => { + applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, battle.enemyParty[e]); + }); + } + } + const enemyPokemon = battle.enemyParty[e]; + + if (enemyPokemon.species.speciesId === Species.ETERNATUS) { + if (this.gameMode.isClassic && (battle.battleSpec === BattleSpec.FINAL_BOSS || this.gameMode.isWaveFinal(battle.waveIndex))) { + if (battle.battleSpec !== BattleSpec.FINAL_BOSS) { + enemyPokemon.formIndex = 1; + } + enemyPokemon.setBoss(); + } else if (!(battle.waveIndex % 1000)) { + enemyPokemon.formIndex = 1; + } + } + + totalBst += enemyPokemon.getSpeciesForm().baseTotal; + + console.log(enemyPokemon.name); + }); + } + peekBattleContents(waveIndex: integer) { + // This function is in progress + return; + + const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave; + const originalWave = ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1); + const newWaveIndex = waveIndex; + const arenaBiome = this.arena.biomeType + this.emulateReset() + let newDouble: boolean; + let newBattleType: BattleType; + let newTrainer: Trainer; + let battleConfig: FixedBattleConfig = null; + let battleType: BattleType; + let trainerData: TrainerData; + let double: boolean; + const playerField = this.getPlayerField(); + + this.arena.biomeType = Biome.END + + if (this.gameMode.isFixedBattle(newWaveIndex) && trainerData === undefined) { + battleConfig = this.gameMode.getFixedBattle(newWaveIndex); + newDouble = battleConfig.double; + newBattleType = battleConfig.battleType; + this.executeWithSeedOffset(() => newTrainer = battleConfig.getTrainer(this), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8); + if (newTrainer) { + this.field.add(newTrainer); + } + } else { + if (!this.gameMode.hasTrainers) { + newBattleType = BattleType.WILD; + console.log("(wild battles only)") + } else if (battleType === undefined) { + newBattleType = this.gameMode.isWaveTrainer(newWaveIndex, this.arena) ? BattleType.TRAINER : BattleType.WILD; + console.log(this.gameMode.isWaveTrainer(newWaveIndex, this.arena) ? "Trainer Battle" : "Wild battle") + } else { + newBattleType = battleType; + console.log(battleType == BattleType.WILD ? "Wild (manually set)" : (battleType == BattleType.TRAINER ? "Trainer (manually set)" : "Clear (manually set)")) + } + + if (newBattleType === BattleType.TRAINER) { + const trainerType = this.arena.randomTrainerType(newWaveIndex); + let doubleTrainer = false; + if (trainerConfigs[trainerType].doubleOnly) { + doubleTrainer = true; + } else if (trainerConfigs[trainerType].hasDouble) { + const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8); + this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); + playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance)); + doubleTrainer = !Utils.randSeedInt(doubleChance.value); + // Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance + if (trainerConfigs[trainerType].trainerTypeDouble && !(trainerType === TrainerType.TATE || trainerType === TrainerType.LIZA)) { + doubleTrainer = false; + } + } + newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, doubleTrainer ? TrainerVariant.DOUBLE : Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT); + this.field.add(newTrainer); + } + } + + if (double === undefined && newWaveIndex > 1) { + if (newBattleType === BattleType.WILD && !this.gameMode.isWaveFinal(newWaveIndex)) { + const doubleChance = new Utils.IntegerHolder(newWaveIndex % 10 === 0 ? 32 : 8); + this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); + playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance)); + console.log(Math.floor((1/doubleChance.value) * 100) + "% chance of a Double Battle") + newDouble = !Utils.randSeedInt(doubleChance.value); + console.log("Double Battle: " + (newDouble ? "Yes" : "No")) + } else if (newBattleType === BattleType.TRAINER) { + //console.log("[Can't determine double trainer battles yet]") + newDouble = newTrainer.variant === TrainerVariant.DOUBLE; + console.log("Trainer Double Battle: " + (newDouble ? "Yes" : "No")) + } + } else if (!battleConfig) { + newDouble = !!double; + console.log("Double Battle: " + (newDouble ? "Yes" : "No") + " (manually set)") + } + + if (Overrides.BATTLE_TYPE_OVERRIDE === "double") { + newDouble = true; + console.log("Double Battle: Yes (Player override)") + } + /* Override battles into single only if not fighting with trainers */ + if (newBattleType !== BattleType.TRAINER && Overrides.BATTLE_TYPE_OVERRIDE === "single") { + newDouble = false; + console.log("Double Battle: No (Player override)") + } + + const lastBattle = this.currentBattle; + + if (lastBattle?.double && !newDouble) { + this.tryRemovePhase(p => p instanceof SwitchPhase); + } + + const maxExpLevel = this.getMaxExpLevel(); + + this.lastEnemyTrainer = lastBattle?.trainer ?? null; + + var simBattle = undefined + this.executeWithSeedOffset(() => { + simBattle = new Battle(this.gameMode, newWaveIndex, newBattleType, newTrainer, newDouble); + }, newWaveIndex << 3, this.waveSeed); + simBattle.incrementTurn(this); + + //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) { + let isNewBiome = !(lastBattle.waveIndex % 10) || ((this.gameMode.hasShortBiomes || this.gameMode.isDaily) && (lastBattle.waveIndex % 50) === 49); + if (!isNewBiome && this.gameMode.hasShortBiomes && (lastBattle.waveIndex % 10) < 9) { + let w = lastBattle.waveIndex - ((lastBattle.waveIndex % 10) - 1); + let biomeWaves = 1; + while (w < lastBattle.waveIndex) { + let wasNewBiome = false; + this.executeWithSeedOffset(() => { + wasNewBiome = !Utils.randSeedInt(6 - biomeWaves); + }, w << 4); + if (wasNewBiome) { + biomeWaves = 1; + } else { + biomeWaves++; + } + w++; + } + + this.executeWithSeedOffset(() => { + isNewBiome = !Utils.randSeedInt(6 - biomeWaves); + }, lastBattle.waveIndex << 4); + if (isNewBiome) console.log("This wave attempted to switch biomes early") + } + const resetArenaState = isNewBiome || simBattle.battleType === BattleType.TRAINER || simBattle.battleSpec === BattleSpec.FINAL_BOSS; + //this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy()); + //this.trySpreadPokerus(); + if (!isNewBiome && (newWaveIndex % 10) === 5) { + this.arena.updatePoolsForTimeOfDay(); + } + if (resetArenaState) { + this.arena.resetArenaEffects(); + playerField.forEach((_, p) => { + //this.unshiftPhase(new ReturnPhase(this, p)) + console.log("Pushed new ReturnPhase for " + this.getParty()[p].name) + }); + + for (const pokemon of this.getParty()) { + // Only trigger form change when Eiscue is in Noice form + // Hardcoded Eiscue for now in case it is fused with another pokemon + if (pokemon.species.speciesId === Species.EISCUE && pokemon.hasAbility(Abilities.ICE_FACE) && pokemon.formIndex === 1) { + //this.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger); + console.log("Triggered form change for an Eiscue") + } + + pokemon.resetBattleData(); + applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon); + } + + //this.unshiftPhase(new ShowTrainerPhase(this)); + console.log("Inserted new ShowTrainerPhase to front of queue") + } + + for (const pokemon of this.getParty()) { + //this.triggerPokemonFormChange(pokemon, SpeciesFormChangeTimeOfDayTrigger); + } + + if (!this.gameMode.hasRandomBiomes && !isNewBiome) { + //this.pushPhase(new NextEncounterPhase(this)); + console.log("Pushed new NextEncounterPhase") + } else { + //this.pushPhase(new SelectBiomePhase(this)); + console.log("Pushed new SelectBiomePhase") + //this.pushPhase(new NewBiomeEncounterPhase(this)) + console.log("Pushed new NewBiomeEncounterPhase"); + + const newMaxExpLevel = this.getMaxExpLevel(); + if (newMaxExpLevel > maxExpLevel) { + //this.pushPhase(new LevelCapPhase(this)); + console.log("Attempted to announce level cap change (Lv. " + newMaxExpLevel + ")") + } + } + } + + this.generatePokemonForBattle(simBattle) + + console.log("Generated battle", simBattle) + + this.arena.biomeType = arenaBiome + + this.restoreSeed() + } + newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle { const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave; const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1); @@ -1396,6 +1622,25 @@ export default class BattleScene extends SceneBase { }); } + emulateReset(waveIndex?: integer) { + const wave = waveIndex || this.currentBattle?.waveIndex || 0; + this.tempWaveSeed = this.waveSeed; + this.tempRngCounter = this.rngCounter; + this.waveSeed = Utils.shiftCharCodes(this.seed, wave); + Phaser.Math.RND.sow([ this.waveSeed ]); + console.log("Temporarily reset wave RNG"); + this.rngCounter = 0; + //this.setScoreText("RNG: 0") + } + restoreSeed(waveIndex?: integer) { + const wave = waveIndex || this.currentBattle?.waveIndex || 0; + this.waveSeed = this.tempWaveSeed; + Phaser.Math.RND.sow([ this.waveSeed ]); + console.log("Restored wave RNG"); + this.rngCounter = this.tempRngCounter; + //this.setScoreText("RNG: 0") + } + resetSeed(waveIndex?: integer): void { const wave = waveIndex || this.currentBattle?.waveIndex || 0; this.waveSeed = Utils.shiftCharCodes(this.seed, wave); diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 395afd44711..00b4409e67b 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -2042,14 +2042,18 @@ export function getModifierTypeFuncById(id: string): ModifierTypeFunc { return modifierTypes[id]; } -export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemon[], modifierTiers?: ModifierTier[], scene?: BattleScene, shutUpBro?: boolean): ModifierTypeOption[] { +export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemon[], modifierTiers?: ModifierTier[], scene?: BattleScene, shutUpBro?: boolean, generateAltTiers?: boolean, shinyCheckOnly?: boolean): ModifierTypeOption[] { const options: ModifierTypeOption[] = []; const retryCount = Math.min(count * 5, 50); new Array(count).fill(0).map((_, i) => { - let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, modifierTiers?.length > i ? modifierTiers[i] : undefined, undefined, undefined, scene, shutUpBro); + let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, modifierTiers?.length > i ? modifierTiers[i] : undefined, undefined, undefined, scene, shutUpBro, generateAltTiers, shinyCheckOnly); let r = 0; + const aT = candidate.alternates while (options.length && ++r < retryCount && options.filter(o => o.type.name === candidate.type.name || o.type.group === candidate.type.group).length) { - candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, candidate.type.tier, candidate.upgradeCount, undefined, scene, shutUpBro); + candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, candidate.type.tier, candidate.upgradeCount, undefined, scene, shutUpBro, generateAltTiers, shinyCheckOnly); + } + if (candidate.alternates == undefined) { + candidate.alternates = aT } options.push(candidate); }); @@ -2105,10 +2109,14 @@ export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers: const retryCount = 50; let candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier, undefined, undefined, scene); let r = 0; + const aT = candidate.alternates let matchingModifier: Modifiers.PersistentModifier; while (++r < retryCount && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate.type.id)) && matchingModifier.getMaxStackCount(scene) < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)) { candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier, undefined, undefined, scene); } + if (candidate.alternates == undefined) { + candidate.alternates = aT + } const modifier = candidate.type.newModifier() as Modifiers.EnemyPersistentModifier; modifier.stackCount = tierStackCount; @@ -2138,7 +2146,7 @@ export function getDailyRunStarterModifiers(party: PlayerPokemon[], scene?: Batt return ret; } -function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier?: ModifierTier, upgradeCount?: integer, retryCount: integer = 0, scene?: BattleScene, shutUpBro?: boolean): ModifierTypeOption { +function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier?: ModifierTier, upgradeCount?: integer, retryCount: integer = 0, scene?: BattleScene, shutUpBro?: boolean, generateAltTiers?: boolean, shinyCheckOnly?: boolean): ModifierTypeOption { const player = !poolType; const pool = getModifierPoolForType(poolType); let thresholds: object; @@ -2159,7 +2167,49 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, thresholds = dailyStarterModifierPoolThresholds; break; } + var alternateTiers = [] if (tier === undefined) { + if (generateAltTiers) { + for (var luck = 0; luck <= 14; luck++) { + var state = Phaser.Math.RND.state() + var tierValueTemp = Utils.randSeedInt(1024); + var upgradeCountTemp = 0; + var tierTemp = undefined; + if (upgradeCount) { + upgradeCountTemp = upgradeCount; + } + if (player && tierValueTemp) { + var partyLuckValue = luck + const upgradeOddsTemp = Math.floor(128 / ((partyLuckValue + 4) / 4)); + let upgraded = false; + do { + upgraded = Utils.randSeedInt(upgradeOddsTemp) < 4; + if (upgraded) { + upgradeCountTemp++; + } + } while (upgraded); + } + tierTemp = tierValueTemp > 255 ? ModifierTier.COMMON : tierValueTemp > 60 ? ModifierTier.GREAT : tierValueTemp > 12 ? ModifierTier.ULTRA : tierValueTemp ? ModifierTier.ROGUE : ModifierTier.MASTER; + // Does this actually do anything? + if (!upgradeCountTemp) { + upgradeCountTemp = Math.min(upgradeCountTemp, ModifierTier.MASTER - tierTemp); + } + tierTemp += upgradeCountTemp; + while (tierTemp && (!modifierPool.hasOwnProperty(tierTemp) || !modifierPool[tierTemp].length)) { + tierTemp--; + if (upgradeCountTemp) { + upgradeCountTemp--; + } + } + alternateTiers[luck] = tierTemp + Phaser.Math.RND.state(state) + } + if (shinyCheckOnly) { + var O = new ModifierTypeOption(undefined, 0, 0); + O.alternates = alternateTiers; + return O; + } + } const tierValue = Utils.randSeedInt(1024); if (!upgradeCount) { upgradeCount = 0; @@ -2167,7 +2217,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, if (player && tierValue) { var partyLuckValue = getPartyLuckValue(party); if (scene) { - if (scene.gameMode.modeId == GameModes.DAILY && scene.disableDailyShinies && false) { + if (scene.gameMode.modeId == GameModes.DAILY && scene.disableDailyShinies) { partyLuckValue = 0 } } @@ -2197,10 +2247,31 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, if (tier < ModifierTier.MASTER) { var partyShinyCount = party.filter(p => p.isShiny() && !p.isFainted() && (!this.scene.disableDailyShinies || p.species.luckOverride != 0)).length; if (scene) { - if (scene.gameMode.modeId == GameModes.DAILY && scene.disableDailyShinies && false) { + if (scene.gameMode.modeId == GameModes.DAILY && scene.disableDailyShinies) { partyShinyCount = 0 } } + if (generateAltTiers) { + for (var luck = 0; luck <= 14; luck++) { + var state = Phaser.Math.RND.state() + var upgradeOddsTemp = Math.floor(32 / ((luck + 2) / 2)); + var upgradeCountTemp = 0; + while (modifierPool.hasOwnProperty(tier + upgradeCountTemp + 1) && modifierPool[tier + upgradeCountTemp + 1].length) { + if (!Utils.randSeedInt(upgradeOddsTemp)) { + upgradeCountTemp++; + } else { + break; + } + } + alternateTiers[luck] = tier + upgradeCountTemp + Phaser.Math.RND.state(state) + } + if (shinyCheckOnly) { + var O = new ModifierTypeOption(undefined, 0, 0); + O.alternates = alternateTiers; + return O; + } + } const upgradeOdds = Math.floor(32 / ((partyShinyCount + 2) / 2)); while (modifierPool.hasOwnProperty(tier + upgradeCount + 1) && modifierPool[tier + upgradeCount + 1].length) { if (!Utils.randSeedInt(upgradeOdds)) { @@ -2216,6 +2287,10 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier--; } + if (shinyCheckOnly) { + return new ModifierTypeOption(undefined, 0, 0); + } + const tierThresholds = Object.keys(thresholds[tier]); const totalWeight = parseInt(tierThresholds[tierThresholds.length - 1]); const value = Utils.randSeedInt(totalWeight); @@ -2242,13 +2317,21 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, if (player) { if (!shutUpBro) console.log(ModifierTier[tier], upgradeCount); } - return getNewModifierTypeOption(party, poolType, tier, upgradeCount, ++retryCount, scene, shutUpBro); + return getNewModifierTypeOption(party, poolType, tier, upgradeCount, ++retryCount, scene, shutUpBro, generateAltTiers); } } if (!shutUpBro) console.log(modifierType, !player ? "(enemy)" : ""); - return new ModifierTypeOption(modifierType as ModifierType, upgradeCount); + var Option = new ModifierTypeOption(modifierType as ModifierType, upgradeCount); + if (alternateTiers.length > 0) { + //console.log(Option.type.name, alternateTiers) + Option.alternates = alternateTiers + } + if (!generateAltTiers) { + //Option.alternates = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] + } + return Option; } export function getDefaultModifierTypeForTier(tier: ModifierTier): ModifierType { @@ -2263,6 +2346,7 @@ export class ModifierTypeOption { public type: ModifierType; public upgradeCount: integer; public cost: integer; + public alternates?: integer[]; constructor(type: ModifierType, upgradeCount: integer, cost: number = 0) { this.type = type; diff --git a/src/phases.ts b/src/phases.ts index e928a4d7fd0..bec0d6a473d 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -3803,6 +3803,8 @@ export class TurnEndPhase extends FieldPhase { this.scene.arena.trySetTerrain(TerrainType.NONE, false); } + this.scene.peekBattleContents(50) + this.end(); } } @@ -6728,6 +6730,60 @@ export class AttemptRunPhase extends PokemonPhase { //#region 71 SelectModifierPhase +const tierNames = [ + "Poké", + "Great", + "Ultra", + "Rogue", + "Master" +] +/** + * This function rolls for modifiers with a certain luck value, checking to see if shiny luck would affect your results. + * @param scene + * @param predictionCost + * @param rerollOverride + * @param modifierOverride + * @returns + */ +export function shinyCheckStep(scene: BattleScene, predictionCost: Utils.IntegerHolder, rerollOverride: integer, modifierOverride?: integer) { + var modifierPredictions = [] + const party = scene.getParty(); + regenerateModifierPoolThresholds(party, ModifierPoolType.PLAYER, rerollOverride); + const modifierCount = new Utils.IntegerHolder(3); + scene.applyModifiers(ExtraModifierModifier, true, modifierCount); + if (modifierOverride) { + //modifierCount.value = modifierOverride + } + var isOk = true; + const typeOptions: ModifierTypeOption[] = getPlayerModifierTypeOptions(modifierCount.value, scene.getParty(), undefined, scene, true, true); + typeOptions.forEach((option, idx) => { + if (option.alternates && option.alternates.length > 0) { + for (var i = 0; i < option.alternates.length; i++) { + if (option.alternates[i] > option.type.tier) { + isOk = false // Shiny Luck affects this wave in some way + } + } + } + }) + modifierPredictions.push(typeOptions) + predictionCost.value += (Math.min(Math.ceil(scene.currentBattle.waveIndex / 10) * 250 * Math.pow(2, rerollOverride), Number.MAX_SAFE_INTEGER)) + return isOk; +} +/** + * Simulates modifier rolls for as many rerolls as you can afford, checking to see if shiny luck will alter your results. + * @param scene The current `BattleScene`. + * @returns `true` if no changes were detected, `false` otherwise + */ +export function runShinyCheck(scene: BattleScene, wv?: integer) { + scene.resetSeed(wv); + const predictionCost = new Utils.IntegerHolder(0) + var isOk = true; + for (var i = 0; i < 14 && isOk; i++) { + isOk = isOk && shinyCheckStep(scene, predictionCost, i) + } + scene.resetSeed(wv); + return isOk +} export class SelectModifierPhase extends BattlePhase { private rerollCount: integer; private modifierTiers: ModifierTier[]; @@ -6759,7 +6815,7 @@ export class SelectModifierPhase extends BattlePhase { if (modifierOverride) { //modifierCount.value = modifierOverride } - const typeOptions: ModifierTypeOption[] = this.getModifierTypeOptions(modifierCount.value, true); + const typeOptions: ModifierTypeOption[] = this.getModifierTypeOptions(modifierCount.value, true, true); typeOptions.forEach((option, idx) => { //console.log(option.type.name) }) @@ -6995,6 +7051,16 @@ export class SelectModifierPhase extends BattlePhase { console.log("Rerolls: " + r) mp.forEach((m, i) => { console.log(" " + m.type.name) + if (m.alternates) { + for (var j = 0, currentTier = m.type.tier; j < m.alternates.length; j++) { + if (m.alternates[j] > currentTier) { + currentTier = m.alternates[j] + console.log(" At " + j + " luck: " + tierNames[currentTier] + "-tier item") + } + } + } else { + console.log(" No alt-luck data") + } }) }) } @@ -7026,8 +7092,8 @@ export class SelectModifierPhase extends BattlePhase { return ModifierPoolType.PLAYER; } - getModifierTypeOptions(modifierCount: integer, shutUpBro?: boolean): ModifierTypeOption[] { - return getPlayerModifierTypeOptions(modifierCount, this.scene.getParty(), this.scene.lockModifierTiers ? this.modifierTiers : undefined, this.scene, shutUpBro); + getModifierTypeOptions(modifierCount: integer, shutUpBro?: boolean, calcAllLuck?: boolean): ModifierTypeOption[] { + return getPlayerModifierTypeOptions(modifierCount, this.scene.getParty(), this.scene.lockModifierTiers ? this.modifierTiers : undefined, this.scene, shutUpBro, calcAllLuck); } addModifier(modifier: Modifier): Promise {