Fix battle RNG varying when loading a game
parent
dbff672469
commit
366e3e5120
|
@ -67,6 +67,7 @@ export const STARTING_LEVEL_OVERRIDE = 0;
|
|||
export const STARTING_WAVE_OVERRIDE = 0;
|
||||
export const STARTING_BIOME_OVERRIDE = Biome.TOWN;
|
||||
export const STARTING_MONEY_OVERRIDE = 0;
|
||||
const DEBUG_RNG = false;
|
||||
|
||||
export const startingWave = STARTING_WAVE_OVERRIDE || 1;
|
||||
|
||||
|
@ -178,6 +179,10 @@ export default class BattleScene extends Phaser.Scene {
|
|||
|
||||
private blockInput: boolean;
|
||||
|
||||
public rngCounter: integer = 0;
|
||||
public rngSeedOverride: string = '';
|
||||
public rngOffset: integer = 0;
|
||||
|
||||
constructor() {
|
||||
super('battle');
|
||||
|
||||
|
@ -276,6 +281,20 @@ export default class BattleScene extends Phaser.Scene {
|
|||
this.load['cacheBuster'] = buildIdMatch[1];
|
||||
}
|
||||
|
||||
if (DEBUG_RNG) {
|
||||
const scene = this;
|
||||
const originalRealInRange = Phaser.Math.RND.realInRange;
|
||||
Phaser.Math.RND.realInRange = function (min: number, max: number): number {
|
||||
const ret = originalRealInRange.apply(this, [ min, max ]);
|
||||
const args = [ 'RNG', ++scene.rngCounter, ret / (max - min), `min: ${min} / max: ${max}` ];
|
||||
args.push(`seed: ${scene.rngSeedOverride || scene.waveSeed || scene.seed}`);
|
||||
if (scene.rngOffset)
|
||||
args.push(`offset: ${scene.rngOffset}`);
|
||||
console.log(...args);
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
// Load menu images
|
||||
this.loadAtlas('bg', 'ui');
|
||||
this.loadImage('command_fight_labels', 'ui');
|
||||
|
@ -805,10 +824,15 @@ export default class BattleScene extends Phaser.Scene {
|
|||
|
||||
setSeed(seed: string): void {
|
||||
this.seed = seed;
|
||||
this.rngCounter = 0;
|
||||
this.waveCycleOffset = this.getGeneratedWaveCycleOffset();
|
||||
this.offsetGym = this.gameMode.isClassic && this.getGeneratedOffsetGym();
|
||||
}
|
||||
|
||||
randBattleSeedInt(range: integer, min: integer = 0): integer {
|
||||
return this.currentBattle.randSeedInt(this, range, min);
|
||||
}
|
||||
|
||||
reset(clearScene?: boolean): void {
|
||||
this.gameMode = gameModes[GameModes.CLASSIC];
|
||||
|
||||
|
@ -843,7 +867,7 @@ export default class BattleScene extends Phaser.Scene {
|
|||
this.updateScoreText();
|
||||
this.scoreText.setVisible(false);
|
||||
|
||||
this.newArena(STARTING_BIOME_OVERRIDE || Biome.TOWN, true);
|
||||
this.newArena(STARTING_BIOME_OVERRIDE || Biome.TOWN);
|
||||
|
||||
this.arenaBgTransition.setPosition(0, 0);
|
||||
this.arenaPlayer.setPosition(300, 0);
|
||||
|
@ -851,6 +875,8 @@ export default class BattleScene extends Phaser.Scene {
|
|||
[ this.arenaEnemy, this.arenaNextEnemy ].forEach(a => a.setPosition(-280, 0));
|
||||
this.arenaNextEnemy.setVisible(false);
|
||||
|
||||
this.arena.init();
|
||||
|
||||
this.trainer.setTexture(`trainer_${this.gameData.gender === PlayerGender.FEMALE ? 'f' : 'm'}_back`);
|
||||
this.trainer.setPosition(406, 186);
|
||||
this.trainer.setVisible(true)
|
||||
|
@ -933,7 +959,9 @@ export default class BattleScene extends Phaser.Scene {
|
|||
|
||||
this.lastEnemyTrainer = lastBattle?.trainer ?? null;
|
||||
|
||||
this.currentBattle = new Battle(this.gameMode, newWaveIndex, newBattleType, newTrainer, newDouble);
|
||||
this.executeWithSeedOffset(() => {
|
||||
this.currentBattle = new Battle(this.gameMode, newWaveIndex, newBattleType, newTrainer, newDouble);
|
||||
}, newWaveIndex << 3, this.waveSeed);
|
||||
this.currentBattle.incrementTurn(this);
|
||||
|
||||
//this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6));
|
||||
|
@ -972,20 +1000,9 @@ export default class BattleScene extends Phaser.Scene {
|
|||
return this.currentBattle;
|
||||
}
|
||||
|
||||
newArena(biome: Biome, init?: boolean): Arena {
|
||||
newArena(biome: Biome): Arena {
|
||||
this.arena = new Arena(this, biome, Biome[biome].toLowerCase());
|
||||
|
||||
if (init) {
|
||||
const biomeKey = getBiomeKey(biome);
|
||||
|
||||
this.arenaPlayer.setBiome(biome);
|
||||
this.arenaPlayerTransition.setBiome(biome);
|
||||
this.arenaEnemy.setBiome(biome);
|
||||
this.arenaNextEnemy.setBiome(biome);
|
||||
this.arenaBg.setTexture(`${biomeKey}_bg`);
|
||||
this.arenaBgTransition.setTexture(`${biomeKey}_bg`);
|
||||
}
|
||||
|
||||
this.arenaBg.pipelineData = { terrainColorRatio: this.arena.getBgTerrainColorRatioForBiome() };
|
||||
|
||||
return this.arena;
|
||||
|
@ -1139,17 +1156,29 @@ export default class BattleScene extends Phaser.Scene {
|
|||
}
|
||||
|
||||
resetSeed(waveIndex?: integer): void {
|
||||
this.waveSeed = Utils.shiftCharCodes(this.seed, waveIndex || this.currentBattle?.waveIndex || 0);
|
||||
const wave = waveIndex || this.currentBattle?.waveIndex || 0;
|
||||
this.waveSeed = Utils.shiftCharCodes(this.seed, wave);
|
||||
Phaser.Math.RND.sow([ this.waveSeed ]);
|
||||
console.log('Wave Seed:', this.waveSeed, wave);
|
||||
this.rngCounter = 0;
|
||||
}
|
||||
|
||||
executeWithSeedOffset(func: Function, offset: integer, seedOverride?: string): void {
|
||||
if (!func)
|
||||
return;
|
||||
const tempRngCounter = this.rngCounter;
|
||||
const tempRngOffset = this.rngOffset;
|
||||
const tempRngSeedOverride = this.rngSeedOverride;
|
||||
const state = Phaser.Math.RND.state();
|
||||
Phaser.Math.RND.sow([ Utils.shiftCharCodes(seedOverride || this.seed, offset) ]);
|
||||
this.rngCounter = 0;
|
||||
this.rngOffset = offset;
|
||||
this.rngSeedOverride = seedOverride || '';
|
||||
func();
|
||||
Phaser.Math.RND.state(state);
|
||||
this.rngCounter = tempRngCounter;
|
||||
this.rngOffset = tempRngOffset;
|
||||
this.rngSeedOverride = tempRngSeedOverride;
|
||||
}
|
||||
|
||||
addFieldSprite(x: number, y: number, texture: string | Phaser.Textures.Texture, frame?: string | number, terrainColorRatio: number = 0): Phaser.GameObjects.Sprite {
|
||||
|
|
|
@ -59,6 +59,8 @@ export default class Battle {
|
|||
public battleSeed: string;
|
||||
private battleSeedState: string;
|
||||
|
||||
private rngCounter: integer = 0;
|
||||
|
||||
constructor(gameMode: GameMode, waveIndex: integer, battleType: BattleType, trainer: Trainer, double: boolean) {
|
||||
this.gameMode = gameMode;
|
||||
this.waveIndex = waveIndex;
|
||||
|
@ -191,16 +193,24 @@ export default class Battle {
|
|||
return null;
|
||||
}
|
||||
|
||||
randSeedInt(range: integer, min: integer = 0): integer {
|
||||
randSeedInt(scene: BattleScene, range: integer, min: integer = 0): integer {
|
||||
let ret: integer;
|
||||
const tempRngCounter = scene.rngCounter;
|
||||
const tempSeedOverride = scene.rngSeedOverride;
|
||||
const state = Phaser.Math.RND.state();
|
||||
if (this.battleSeedState)
|
||||
Phaser.Math.RND.state(this.battleSeedState);
|
||||
else
|
||||
else {
|
||||
Phaser.Math.RND.sow([ Utils.shiftCharCodes(this.battleSeed, this.turn << 6) ]);
|
||||
console.log('Battle Seed:', this.battleSeed);
|
||||
}
|
||||
scene.rngCounter = this.rngCounter++;
|
||||
scene.rngSeedOverride = this.battleSeed;
|
||||
ret = Utils.randSeedInt(range, min);
|
||||
this.battleSeedState = Phaser.Math.RND.state();
|
||||
Phaser.Math.RND.state(state);
|
||||
scene.rngCounter = tempRngCounter;
|
||||
scene.rngSeedOverride = tempSeedOverride;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,17 @@ export class Arena {
|
|||
this.updatePoolsForTimeOfDay();
|
||||
}
|
||||
|
||||
init() {
|
||||
const biomeKey = getBiomeKey(this.biomeType);
|
||||
|
||||
this.scene.arenaPlayer.setBiome(this.biomeType);
|
||||
this.scene.arenaPlayerTransition.setBiome(this.biomeType);
|
||||
this.scene.arenaEnemy.setBiome(this.biomeType);
|
||||
this.scene.arenaNextEnemy.setBiome(this.biomeType);
|
||||
this.scene.arenaBg.setTexture(`${biomeKey}_bg`);
|
||||
this.scene.arenaBgTransition.setTexture(`${biomeKey}_bg`);
|
||||
}
|
||||
|
||||
updatePoolsForTimeOfDay(): void {
|
||||
const timeOfDay = this.getTimeOfDay();
|
||||
if (timeOfDay !== this.lastTimeOfDay) {
|
||||
|
@ -664,28 +675,27 @@ export class ArenaBase extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
setBiome(biome: Biome, propValue?: integer): void {
|
||||
if (this.biome === biome)
|
||||
return;
|
||||
|
||||
const hasProps = getBiomeHasProps(biome);
|
||||
const biomeKey = getBiomeKey(biome);
|
||||
const baseKey = `${biomeKey}_${this.player ? 'a' : 'b'}`;
|
||||
|
||||
this.base.setTexture(baseKey);
|
||||
if (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 });
|
||||
this.scene.anims.create({
|
||||
key: baseKey,
|
||||
frames: baseFrameNames,
|
||||
frameRate: 12,
|
||||
repeat: -1
|
||||
});
|
||||
this.base.play(baseKey);
|
||||
} else
|
||||
this.base.stop();
|
||||
if (this.base.texture.frameTotal > 1) {
|
||||
const baseFrameNames = this.scene.anims.generateFrameNames(baseKey, { zeroPad: 4, suffix: ".png", start: 1, end: this.base.texture.frameTotal - 1 });
|
||||
this.scene.anims.create({
|
||||
key: baseKey,
|
||||
frames: baseFrameNames,
|
||||
frameRate: 12,
|
||||
repeat: -1
|
||||
});
|
||||
this.base.play(baseKey);
|
||||
} else
|
||||
this.base.stop();
|
||||
|
||||
this.add(this.base);
|
||||
this.add(this.base);
|
||||
}
|
||||
|
||||
if (!this.player) {
|
||||
(this.scene as BattleScene).executeWithSeedOffset(() => {
|
||||
|
@ -711,7 +721,7 @@ export class ArenaBase extends Phaser.GameObjects.Container {
|
|||
prop.setVisible(hasProps && !!(this.propValue & (1 << p)));
|
||||
this.add(prop);
|
||||
});
|
||||
}, (this.scene as BattleScene).currentBattle?.waveIndex || 0);
|
||||
}, (this.scene as BattleScene).currentBattle?.waveIndex || 0, (this.scene as BattleScene).waveSeed);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1092,7 +1092,7 @@ 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));
|
||||
isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !this.scene.currentBattle.randSeedInt(critChance));
|
||||
isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !this.scene.randBattleSeedInt(critChance));
|
||||
if (isCritical) {
|
||||
const blockCrit = new Utils.BooleanHolder(false);
|
||||
applyAbAttrs(BlockCritAbAttr, this, null, blockCrit);
|
||||
|
@ -1121,7 +1121,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
applyMoveAttrs(VariableDefAttr, source, this, move, targetDef);
|
||||
|
||||
if (!isTypeImmune) {
|
||||
damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier * ((this.scene.currentBattle.randSeedInt(15) + 85) / 100)) * criticalMultiplier;
|
||||
damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier * ((this.scene.randBattleSeedInt(15) + 85) / 100)) * criticalMultiplier;
|
||||
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) {
|
||||
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
|
||||
applyAbAttrs(BypassBurnDamageReductionAbAttr, this, burnDamageReductionCancelled);
|
||||
|
@ -1971,7 +1971,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
|
||||
randSeedInt(range: integer, min: integer = 0): integer {
|
||||
return this.scene.currentBattle
|
||||
? this.scene.currentBattle.randSeedInt(range, min)
|
||||
? this.scene.randBattleSeedInt(range, min)
|
||||
: Utils.randSeedInt(range, min);
|
||||
}
|
||||
|
||||
|
@ -2326,7 +2326,7 @@ export class EnemyPokemon extends Pokemon {
|
|||
}
|
||||
switch (this.aiType) {
|
||||
case AiType.RANDOM:
|
||||
const moveId = movePool[this.scene.currentBattle.randSeedInt(movePool.length)].moveId;
|
||||
const moveId = movePool[this.scene.randBattleSeedInt(movePool.length)].moveId;
|
||||
return { move: moveId, targets: this.getNextTargets(moveId) };
|
||||
case AiType.SMART_RANDOM:
|
||||
case AiType.SMART:
|
||||
|
@ -2373,7 +2373,7 @@ export class EnemyPokemon extends Pokemon {
|
|||
});
|
||||
let r = 0;
|
||||
if (this.aiType === AiType.SMART_RANDOM) {
|
||||
while (r < sortedMovePool.length - 1 && this.scene.currentBattle.randSeedInt(8) >= 5)
|
||||
while (r < sortedMovePool.length - 1 && this.scene.randBattleSeedInt(8) >= 5)
|
||||
r++;
|
||||
}
|
||||
console.log(movePool.map(m => m.getName()), moveScores, r, sortedMovePool.map(m => m.getName()));
|
||||
|
@ -2426,7 +2426,7 @@ export class EnemyPokemon extends Pokemon {
|
|||
return total;
|
||||
}, 0);
|
||||
|
||||
const randValue = this.scene.currentBattle.randSeedInt(totalWeight);
|
||||
const randValue = this.scene.randBattleSeedInt(totalWeight);
|
||||
let targetIndex: integer;
|
||||
|
||||
thresholds.every((t, i) => {
|
||||
|
|
|
@ -258,8 +258,9 @@ export class TitlePhase extends Phase {
|
|||
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.newArena(this.scene.gameMode.getStartingBiome(this.scene));
|
||||
this.scene.newBattle();
|
||||
this.scene.arena.init();
|
||||
this.scene.sessionPlayTime = 0;
|
||||
this.end();
|
||||
});
|
||||
|
@ -271,7 +272,7 @@ 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);
|
||||
this.scene.newArena(this.scene.gameMode.getStartingBiome(this.scene));
|
||||
} else
|
||||
this.scene.playBgm();
|
||||
|
||||
|
@ -435,6 +436,7 @@ export class SelectStarterPhase extends Phase {
|
|||
else
|
||||
this.scene.gameData.gameStats.endlessSessionsPlayed++;
|
||||
this.scene.newBattle();
|
||||
this.scene.arena.init();
|
||||
this.scene.sessionPlayTime = 0;
|
||||
this.end();
|
||||
});
|
||||
|
@ -497,7 +499,7 @@ export abstract class FieldPhase extends BattlePhase {
|
|||
const aSpeed = a?.getBattleStat(Stat.SPD) || 0;
|
||||
const bSpeed = b?.getBattleStat(Stat.SPD) || 0;
|
||||
|
||||
return aSpeed < bSpeed ? 1 : aSpeed > bSpeed ? -1 : !this.scene.currentBattle.randSeedInt(2) ? -1 : 1;
|
||||
return aSpeed < bSpeed ? 1 : aSpeed > bSpeed ? -1 : !this.scene.randBattleSeedInt(2) ? -1 : 1;
|
||||
});
|
||||
|
||||
const speedReversed = new Utils.BooleanHolder(false);
|
||||
|
@ -874,6 +876,7 @@ export class NextEncounterPhase extends EncounterPhase {
|
|||
pokemon.resetBattleData();
|
||||
}
|
||||
|
||||
this.scene.arenaNextEnemy.setBiome(this.scene.arena.biomeType);
|
||||
this.scene.arenaNextEnemy.setVisible(true);
|
||||
|
||||
const enemyField = this.scene.getEnemyField();
|
||||
|
@ -882,6 +885,7 @@ export class NextEncounterPhase extends EncounterPhase {
|
|||
x: '+=300',
|
||||
duration: 2000,
|
||||
onComplete: () => {
|
||||
this.scene.arenaEnemy.setBiome(this.scene.arena.biomeType);
|
||||
this.scene.arenaEnemy.setX(this.scene.arenaNextEnemy.x);
|
||||
this.scene.arenaEnemy.setAlpha(1);
|
||||
this.scene.arenaNextEnemy.setX(this.scene.arenaNextEnemy.x - 300);
|
||||
|
|
|
@ -577,12 +577,14 @@ export class GameData {
|
|||
scene.score = sessionData.score;
|
||||
scene.updateScoreText();
|
||||
|
||||
scene.newArena(sessionData.arena.biome);
|
||||
|
||||
const battleType = sessionData.battleType || 0;
|
||||
const trainerConfig = sessionData.trainer ? trainerConfigs[sessionData.trainer.trainerType] : null;
|
||||
const battle = scene.newBattle(sessionData.waveIndex, battleType, sessionData.trainer, battleType === BattleType.TRAINER ? trainerConfig?.doubleOnly || sessionData.trainer?.variant === TrainerVariant.DOUBLE : sessionData.enemyParty.length > 1);
|
||||
battle.enemyLevels = sessionData.enemyParty.map(p => p.level);
|
||||
|
||||
scene.newArena(sessionData.arena.biome, true);
|
||||
scene.arena.init();
|
||||
|
||||
sessionData.enemyParty.forEach((enemyData, e) => {
|
||||
const enemyPokemon = enemyData.toPokemon(scene, battleType, e, sessionData.trainer?.variant === TrainerVariant.DOUBLE) as EnemyPokemon;
|
||||
|
|
Loading…
Reference in New Issue