Overhaul random logic and implement battle seed

This commit is contained in:
Flashfyre 2024-01-02 21:31:59 -05:00
parent 6558de01c4
commit e05bf78481
9 changed files with 121 additions and 96 deletions

View File

@ -260,7 +260,7 @@ export abstract class FieldPhase extends BattlePhase {
const aSpeed = a?.getBattleStat(Stat.SPD) || 0; const aSpeed = a?.getBattleStat(Stat.SPD) || 0;
const bSpeed = b?.getBattleStat(Stat.SPD) || 0; const bSpeed = b?.getBattleStat(Stat.SPD) || 0;
return aSpeed < bSpeed ? 1 : aSpeed > bSpeed ? -1 : !Utils.randInt(2) ? -1 : 1; return aSpeed < bSpeed ? 1 : aSpeed > bSpeed ? -1 : !this.scene.currentBattle.randSeedInt(2) ? -1 : 1;
}); });
const speedReversed = new Utils.BooleanHolder(false); const speedReversed = new Utils.BooleanHolder(false);
@ -1692,7 +1692,7 @@ export class MovePhase extends BattlePhase {
switch (this.pokemon.status.effect) { switch (this.pokemon.status.effect) {
case StatusEffect.PARALYSIS: case StatusEffect.PARALYSIS:
if (Utils.randInt(4) === 0) { if (!this.pokemon.randSeedInt(4)) {
activated = true; activated = true;
this.cancelled = true; this.cancelled = true;
} }
@ -1704,7 +1704,7 @@ export class MovePhase extends BattlePhase {
this.cancelled = activated; this.cancelled = activated;
break; break;
case StatusEffect.FREEZE: case StatusEffect.FREEZE:
healed = Utils.randInt(5) === 0; healed = !this.pokemon.randSeedInt(5);
activated = !healed; activated = !healed;
this.cancelled = activated; this.cancelled = activated;
break; break;
@ -1919,7 +1919,7 @@ export class MoveEffectPhase extends PokemonPhase {
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, userAccuracyLevel); applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, userAccuracyLevel);
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this.getUserPokemon(), null, targetEvasionLevel); applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this.getUserPokemon(), null, targetEvasionLevel);
this.scene.applyModifiers(TempBattleStatBoosterModifier, this.player, TempBattleStat.ACC, userAccuracyLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, this.player, TempBattleStat.ACC, userAccuracyLevel);
const rand = Utils.randInt(100, 1); const rand = this.getUserPokemon().randSeedInt(100, 1);
let accuracyMultiplier = 1; let accuracyMultiplier = 1;
if (userAccuracyLevel.value !== targetEvasionLevel.value) { if (userAccuracyLevel.value !== targetEvasionLevel.value) {
accuracyMultiplier = userAccuracyLevel.value > targetEvasionLevel.value accuracyMultiplier = userAccuracyLevel.value > targetEvasionLevel.value
@ -2020,9 +2020,8 @@ export class StatChangePhase extends PokemonPhase {
constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer) { constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer) {
super(scene, battlerIndex); super(scene, battlerIndex);
const allStats = Utils.getEnumValues(BattleStat);
this.selfTarget = selfTarget; this.selfTarget = selfTarget;
this.stats = stats.map(s => s !== BattleStat.RAND ? s : allStats[Utils.randInt(BattleStat.SPD + 1)]); this.stats = stats;
this.levels = levels; this.levels = levels;
} }
@ -2032,7 +2031,8 @@ export class StatChangePhase extends PokemonPhase {
if (pokemon.isFainted()) if (pokemon.isFainted())
return this.end(); return this.end();
const filteredStats = this.stats.filter(stat => { const allStats = Utils.getEnumValues(BattleStat);
const filteredStats = this.stats.map(s => s !== BattleStat.RAND ? s : allStats[pokemon.randSeedInt(BattleStat.SPD + 1)]).filter(stat => {
const cancelled = new Utils.BooleanHolder(false); const cancelled = new Utils.BooleanHolder(false);
if (!this.selfTarget && this.levels < 0) if (!this.selfTarget && this.levels < 0)
@ -3046,7 +3046,7 @@ export class AttemptCapturePhase extends PokemonPhase {
shakeCounter.stop(); shakeCounter.stop();
this.failCatch(shakeCount); this.failCatch(shakeCount);
} else if (shakeCount++ < 3) { } else if (shakeCount++ < 3) {
if (Utils.randInt(65536) < y) if (pokemon.randSeedInt(65536) < y)
this.scene.playSound('pb_move'); this.scene.playSound('pb_move');
else { else {
shakeCounter.stop(); shakeCounter.stop();
@ -3190,7 +3190,7 @@ export class AttemptRunPhase extends PokemonPhase {
const escapeChance = new Utils.IntegerHolder((((playerPokemon.getStat(Stat.SPD) * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256); const escapeChance = new Utils.IntegerHolder((((playerPokemon.getStat(Stat.SPD) * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256);
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, escapeChance); applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, escapeChance);
if (Utils.randInt(256) < escapeChance.value) { if (playerPokemon.randSeedInt(256) < escapeChance.value) {
this.scene.playSound('flee'); this.scene.playSound('flee');
this.scene.queueMessage('You got away safely!', null, true, 500); this.scene.queueMessage('You got away safely!', null, true, 500);

View File

@ -48,6 +48,8 @@ export default class Battle {
public playerParticipantIds: Set<integer> = new Set<integer>(); public playerParticipantIds: Set<integer> = new Set<integer>();
public escapeAttempts: integer = 0; public escapeAttempts: integer = 0;
public lastMove: Moves; public lastMove: Moves;
public battleSeed: string;
private battleSeedState: string;
constructor(waveIndex: integer, battleType: BattleType, trainer: Trainer, double: boolean) { constructor(waveIndex: integer, battleType: BattleType, trainer: Trainer, double: boolean) {
this.waveIndex = waveIndex; this.waveIndex = waveIndex;
@ -61,6 +63,8 @@ export default class Battle {
this.double = double; this.double = double;
this.turn = 0; this.turn = 0;
this.started = false; this.started = false;
this.battleSeed = Utils.randomString(16, true);
this.battleSeedState = null;
} }
private getLevelForWave(): integer { private getLevelForWave(): integer {
@ -86,6 +90,7 @@ export default class Battle {
incrementTurn(scene: BattleScene): void { incrementTurn(scene: BattleScene): void {
this.turn++; this.turn++;
this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ])); this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [ bt, null ]));
this.battleSeedState = null;
} }
addParticipant(playerPokemon: PlayerPokemon): void { addParticipant(playerPokemon: PlayerPokemon): void {
@ -120,6 +125,19 @@ export default class Battle {
return null; return null;
} }
randSeedInt(range: integer, min: integer = 0): integer {
let ret: integer;
const state = Phaser.Math.RND.state();
if (this.battleSeedState)
Phaser.Math.RND.state(this.battleSeedState);
else
Phaser.Math.RND.sow([ Utils.shiftCharCodes(this.battleSeed, this.turn << 6) ]);
ret = Utils.randSeedInt(range, min);
this.battleSeedState = Phaser.Math.RND.state();
Phaser.Math.RND.state(state);
return ret;
}
} }
export class FixedBattle extends Battle { export class FixedBattle extends Battle {

View File

@ -347,8 +347,8 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
} }
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && Utils.randInt(100) < this.chance && !pokemon.status) { if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[Utils.randInt(this.effects.length)]; const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, attacker.getBattlerIndex(), effect)); pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, attacker.getBattlerIndex(), effect));
} }
@ -370,7 +370,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
} }
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && Utils.randInt(100) < this.chance) if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance)
return attacker.addTag(this.tagType, this.turnCount, move.moveId, pokemon.id); return attacker.addTag(this.tagType, this.turnCount, move.moveId, pokemon.id);
return false; return false;
@ -490,7 +490,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, defender, move.getMove()))) { if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, defender, move.getMove()))) {
const heldItems = this.getTargetHeldItems(defender).filter(i => i.getTransferrable(false)); const heldItems = this.getTargetHeldItems(defender).filter(i => i.getTransferrable(false));
if (heldItems.length) { if (heldItems.length) {
const stolenItem = heldItems[Utils.randInt(heldItems.length)]; const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false, false).then(success => { pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false, false).then(success => {
if (success) if (success)
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` stole\n${defender.name}'s ${stolenItem.type.name}!`)); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` stole\n${defender.name}'s ${stolenItem.type.name}!`));
@ -523,7 +523,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move.getMove()))) { if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move.getMove()))) {
const heldItems = this.getTargetHeldItems(attacker).filter(i => i.getTransferrable(false)); const heldItems = this.getTargetHeldItems(attacker).filter(i => i.getTransferrable(false));
if (heldItems.length) { if (heldItems.length) {
const stolenItem = heldItems[Utils.randInt(heldItems.length)]; const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false, false).then(success => { pokemon.scene.tryTransferHeldItemModifier(stolenItem, pokemon, false, false).then(success => {
if (success) if (success)
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` stole\n${attacker.name}'s ${stolenItem.type.name}!`)); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` stole\n${attacker.name}'s ${stolenItem.type.name}!`));

View File

@ -202,10 +202,10 @@ export class ConfusedTag extends BattlerTag {
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nconfused!')); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nconfused!'));
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION)); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION));
if (Utils.randInt(2)) { if (pokemon.randSeedInt(2)) {
const atk = pokemon.getBattleStat(Stat.ATK); const atk = pokemon.getBattleStat(Stat.ATK);
const def = pokemon.getBattleStat(Stat.DEF); const def = pokemon.getBattleStat(Stat.DEF);
const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (Utils.randInt(15, 85) / 100)); const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100));
pokemon.scene.queueMessage('It hurt itself in its\nconfusion!'); pokemon.scene.queueMessage('It hurt itself in its\nconfusion!');
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex())); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex()));
pokemon.damage(damage); pokemon.damage(damage);
@ -250,7 +250,7 @@ export class InfatuatedTag extends BattlerTag {
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is in love\nwith ${pokemon.scene.getPokemonById(this.sourceId).name}!`)); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is in love\nwith ${pokemon.scene.getPokemonById(this.sourceId).name}!`));
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT)); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT));
if (Utils.randInt(2)) { if (pokemon.randSeedInt(2)) {
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nimmobilized by love!')); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nimmobilized by love!'));
(pokemon.scene.getCurrentPhase() as MovePhase).cancel(); (pokemon.scene.getCurrentPhase() as MovePhase).cancel();
} }
@ -347,7 +347,7 @@ export class FrenzyTag extends BattlerTag {
onRemove(pokemon: Pokemon): void { onRemove(pokemon: Pokemon): void {
super.onRemove(pokemon); super.onRemove(pokemon);
pokemon.addTag(BattlerTagType.CONFUSED, Utils.randIntRange(1, 4) + 1); pokemon.addTag(BattlerTagType.CONFUSED, pokemon.randSeedIntRange(2, 4));
} }
} }

View File

@ -1409,7 +1409,7 @@ export class RandomLevelDamageAttr extends FixedDamageAttr {
} }
getDamage(user: Pokemon, target: Pokemon, move: Move): number { getDamage(user: Pokemon, target: Pokemon, move: Move): number {
return Math.max(Math.floor(user.level * (Utils.randIntRange(50, 150) * 0.01)), 1); return Math.max(Math.floor(user.level * (user.randSeedIntRange(50, 150) * 0.01)), 1);
} }
} }
@ -1584,7 +1584,7 @@ export class MultiHitAttr extends MoveAttr {
switch (this.multiHitType) { switch (this.multiHitType) {
case MultiHitType._2_TO_5: case MultiHitType._2_TO_5:
{ {
const rand = Utils.randInt(16); const rand = user.randSeedInt(16);
const hitValue = new Utils.IntegerHolder(rand); const hitValue = new Utils.IntegerHolder(rand);
applyAbAttrs(MaxMultiHitAbAttr, user, null, hitValue); applyAbAttrs(MaxMultiHitAbAttr, user, null, hitValue);
if (hitValue.value >= 10) if (hitValue.value >= 10)
@ -1609,7 +1609,7 @@ export class MultiHitAttr extends MoveAttr {
break; break;
case MultiHitType._1_TO_10: case MultiHitType._1_TO_10:
{ {
const rand = Utils.randInt(90); const rand = user.randSeedInt(90);
const hitValue = new Utils.IntegerHolder(rand); const hitValue = new Utils.IntegerHolder(rand);
applyAbAttrs(MaxMultiHitAbAttr, user, null, hitValue); applyAbAttrs(MaxMultiHitAbAttr, user, null, hitValue);
if (hitValue.value >= 81) if (hitValue.value >= 81)
@ -1658,7 +1658,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const statusCheck = move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance; const statusCheck = move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance;
if (statusCheck) { if (statusCheck) {
const pokemon = this.selfTarget ? user : target; const pokemon = this.selfTarget ? user : target;
if (pokemon.status) { if (pokemon.status) {
@ -1689,7 +1689,7 @@ export class StealHeldItemAttr extends MoveEffectAttr {
return new Promise<boolean>(resolve => { return new Promise<boolean>(resolve => {
const heldItems = this.getTargetHeldItems(target).filter(i => i.getTransferrable(false)); const heldItems = this.getTargetHeldItems(target).filter(i => i.getTransferrable(false));
if (heldItems.length) { if (heldItems.length) {
const stolenItem = heldItems[Utils.randInt(heldItems.length)]; const stolenItem = heldItems[user.randSeedInt(heldItems.length)];
user.scene.tryTransferHeldItemModifier(stolenItem, user, false, false).then(success => { user.scene.tryTransferHeldItemModifier(stolenItem, user, false, false).then(success => {
if (success) if (success)
user.scene.queueMessage(getPokemonMessage(user, ` stole\n${target.name}'s ${stolenItem.type.name}!`)); user.scene.queueMessage(getPokemonMessage(user, ` stole\n${target.name}'s ${stolenItem.type.name}!`));
@ -1933,7 +1933,7 @@ export class StatChangeAttr extends MoveEffectAttr {
if (!super.apply(user, target, move, args) || (this.condition && !this.condition(user, target, move))) if (!super.apply(user, target, move, args) || (this.condition && !this.condition(user, target, move)))
return false; return false;
if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) { if (move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance) {
const levels = this.getLevels(user); const levels = this.getLevels(user);
user.scene.unshiftPhase(new StatChangePhase(user.scene, (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, levels)); user.scene.unshiftPhase(new StatChangePhase(user.scene, (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, levels));
return true; return true;
@ -2321,7 +2321,7 @@ export class FrenzyAttr extends MoveEffectAttr {
if (!user.getMoveQueue().length) { if (!user.getMoveQueue().length) {
if (!user.getTag(BattlerTagType.FRENZY)) { if (!user.getTag(BattlerTagType.FRENZY)) {
const turnCount = Utils.randIntRange(3, 4); const turnCount = user.randSeedIntRange(2, 3);
new Array(turnCount).fill(null).map(() => user.getMoveQueue().push({ move: move.id, targets: [ target.getBattlerIndex() ], ignorePP: true })); new Array(turnCount).fill(null).map(() => user.getMoveQueue().push({ move: move.id, targets: [ target.getBattlerIndex() ], ignorePP: true }));
user.addTag(BattlerTagType.FRENZY, 1, move.id, user.id); user.addTag(BattlerTagType.FRENZY, 1, move.id, user.id);
} else { } else {
@ -2345,14 +2345,16 @@ export const frenzyMissFunc: UserMoveCondition = (user: Pokemon, move: Move) =>
export class AddBattlerTagAttr extends MoveEffectAttr { export class AddBattlerTagAttr extends MoveEffectAttr {
public tagType: BattlerTagType; public tagType: BattlerTagType;
public turnCount: integer; public turnCountMin: integer;
public turnCountMax: integer;
private failOnOverlap: boolean; private failOnOverlap: boolean;
constructor(tagType: BattlerTagType, selfTarget?: boolean, turnCount?: integer, failOnOverlap?: boolean) { constructor(tagType: BattlerTagType, selfTarget: boolean = false, failOnOverlap: boolean = false, turnCountMin: integer = 0, turnCountMax?: integer) {
super(selfTarget); super(selfTarget);
this.tagType = tagType; this.tagType = tagType;
this.turnCount = turnCount; this.turnCountMin = turnCountMin;
this.turnCountMax = turnCountMax !== undefined ? turnCountMax : turnCountMin;
this.failOnOverlap = !!failOnOverlap; this.failOnOverlap = !!failOnOverlap;
} }
@ -2361,8 +2363,8 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
return false; return false;
const chance = this.getTagChance(user, target, move); const chance = this.getTagChance(user, target, move);
if (chance < 0 || chance === 100 || Utils.randInt(100) < chance) { if (chance < 0 || chance === 100 || user.randSeedInt(100) < chance) {
(this.selfTarget ? user : target).addTag(this.tagType, this.turnCount, move.id, user.id); (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedInt(this.turnCountMax - this.turnCountMin, this.turnCountMin), move.id, user.id);
return true; return true;
} }
@ -2454,13 +2456,13 @@ export class FlinchAttr extends AddBattlerTagAttr {
export class ConfuseAttr extends AddBattlerTagAttr { export class ConfuseAttr extends AddBattlerTagAttr {
constructor(selfTarget?: boolean) { constructor(selfTarget?: boolean) {
super(BattlerTagType.CONFUSED, selfTarget, Utils.randIntRange(1, 4) + 1); super(BattlerTagType.CONFUSED, selfTarget, false, 2, 5);
} }
} }
export class TrapAttr extends AddBattlerTagAttr { export class TrapAttr extends AddBattlerTagAttr {
constructor(tagType: BattlerTagType) { constructor(tagType: BattlerTagType) {
super(tagType, false, Utils.randIntRange(2, 5) + 1); super(tagType, false, false, 3, 6);
} }
} }
@ -2472,12 +2474,12 @@ export class ProtectAttr extends AddBattlerTagAttr {
getCondition(): MoveCondition { getCondition(): MoveCondition {
return ((user, target, move): boolean => { return ((user, target, move): boolean => {
let timesUsed = 0; let timesUsed = 0;
const moveHistory = user.getLastXMoves(-1); const moveHistory = user.getLastXMoves();
let turnMove: TurnMove; let turnMove: TurnMove;
while (moveHistory.length && (turnMove = moveHistory.shift()).move === move.id && turnMove.result === MoveResult.SUCCESS) while (moveHistory.length && (turnMove = moveHistory.shift()).move === move.id && turnMove.result === MoveResult.SUCCESS)
timesUsed++; timesUsed++;
if (timesUsed) if (timesUsed)
return !Utils.randInt(Math.pow(2, timesUsed)); return !user.randSeedInt(Math.pow(2, timesUsed));
return true; return true;
}); });
} }
@ -2485,7 +2487,7 @@ export class ProtectAttr extends AddBattlerTagAttr {
export class IgnoreAccuracyAttr extends AddBattlerTagAttr { export class IgnoreAccuracyAttr extends AddBattlerTagAttr {
constructor() { constructor() {
super(BattlerTagType.IGNORE_ACCURACY, true, 1); super(BattlerTagType.IGNORE_ACCURACY, true, false, 1);
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@ -2500,14 +2502,14 @@ export class IgnoreAccuracyAttr extends AddBattlerTagAttr {
export class FaintCountdownAttr extends AddBattlerTagAttr { export class FaintCountdownAttr extends AddBattlerTagAttr {
constructor() { constructor() {
super(BattlerTagType.PERISH_SONG, false, 4, true); super(BattlerTagType.PERISH_SONG, false, true, 4);
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) if (!super.apply(user, target, move, args))
return false; return false;
user.scene.queueMessage(getPokemonMessage(target, `\nwill faint in ${this.turnCount - 1} turns.`)); user.scene.queueMessage(getPokemonMessage(target, `\nwill faint in ${this.turnCountMin - 1} turns.`));
return true; return true;
} }
@ -2548,7 +2550,7 @@ export class AddArenaTagAttr extends MoveEffectAttr {
if (!super.apply(user, target, move, args)) if (!super.apply(user, target, move, args))
return false; return false;
if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) { if (move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance) {
user.scene.arena.addTag(this.tagType, this.turnCount, move.id, user.id); user.scene.arena.addTag(this.tagType, this.turnCount, move.id, user.id);
return true; return true;
} }
@ -2689,7 +2691,7 @@ export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
const moveset = (!this.enemyMoveset ? user : target).getMoveset(); const moveset = (!this.enemyMoveset ? user : target).getMoveset();
const moves = moveset.filter(m => !m.getMove().hasFlag(MoveFlags.IGNORE_VIRTUAL)); const moves = moveset.filter(m => !m.getMove().hasFlag(MoveFlags.IGNORE_VIRTUAL));
if (moves.length) { if (moves.length) {
const move = moves[Utils.randInt(moves.length)]; const move = moves[user.randSeedInt(moves.length)];
const moveIndex = moveset.findIndex(m => m.moveId === move.moveId); const moveIndex = moveset.findIndex(m => m.moveId === move.moveId);
const moveTargets = getMoveTargets(user, move.moveId); const moveTargets = getMoveTargets(user, move.moveId);
if (!moveTargets.targets.length) if (!moveTargets.targets.length)
@ -2698,7 +2700,7 @@ export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
? moveTargets.targets ? moveTargets.targets
: moveTargets.targets.indexOf(target.getBattlerIndex()) > -1 : moveTargets.targets.indexOf(target.getBattlerIndex()) > -1
? [ target.getBattlerIndex() ] ? [ target.getBattlerIndex() ]
: [ moveTargets.targets[Utils.randInt(moveTargets.targets.length)] ]; : [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ];
user.getMoveQueue().push({ move: move.moveId, targets: targets, ignorePP: this.enemyMoveset }); user.getMoveQueue().push({ move: move.moveId, targets: targets, ignorePP: this.enemyMoveset });
user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, moveset[moveIndex], true)); user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, moveset[moveIndex], true));
return true; return true;
@ -2712,7 +2714,7 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
return new Promise(resolve => { return new Promise(resolve => {
const moveIds = Utils.getEnumValues(Moves).filter(m => !allMoves[m].hasFlag(MoveFlags.IGNORE_VIRTUAL)); const moveIds = Utils.getEnumValues(Moves).filter(m => !allMoves[m].hasFlag(MoveFlags.IGNORE_VIRTUAL));
const moveId = moveIds[Utils.randInt(moveIds.length)]; const moveId = moveIds[user.randSeedInt(moveIds.length)];
const moveTargets = getMoveTargets(user, moveId); const moveTargets = getMoveTargets(user, moveId);
if (!moveTargets.targets.length) { if (!moveTargets.targets.length) {
@ -2723,7 +2725,7 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr {
? moveTargets.targets ? moveTargets.targets
: moveTargets.targets.indexOf(target.getBattlerIndex()) > -1 : moveTargets.targets.indexOf(target.getBattlerIndex()) > -1
? [ target.getBattlerIndex() ] ? [ target.getBattlerIndex() ]
: [ moveTargets.targets[Utils.randInt(moveTargets.targets.length)] ]; : [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ];
user.getMoveQueue().push({ move: moveId, targets: targets, ignorePP: true }); user.getMoveQueue().push({ move: moveId, targets: targets, ignorePP: true });
user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, new PokemonMove(moveId, 0, 0, true), true)); user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, new PokemonMove(moveId, 0, 0, true), true));
initMoveAnim(moveId).then(() => { initMoveAnim(moveId).then(() => {
@ -2760,7 +2762,7 @@ export class CopyMoveAttr extends OverrideMoveEffectAttr {
? moveTargets.targets ? moveTargets.targets
: moveTargets.targets.indexOf(target.getBattlerIndex()) > -1 : moveTargets.targets.indexOf(target.getBattlerIndex()) > -1
? [ target.getBattlerIndex() ] ? [ target.getBattlerIndex() ]
: [ moveTargets.targets[Utils.randInt(moveTargets.targets.length)] ]; : [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ];
user.getMoveQueue().push({ move: lastMove, targets: targets, ignorePP: true }); user.getMoveQueue().push({ move: lastMove, targets: targets, ignorePP: true });
user.scene.unshiftPhase(new MovePhase(user.scene, user as PlayerPokemon, targets, new PokemonMove(lastMove, 0, 0, true), true)); user.scene.unshiftPhase(new MovePhase(user.scene, user as PlayerPokemon, targets, new PokemonMove(lastMove, 0, 0, true), true));
@ -2945,7 +2947,7 @@ export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet {
multiple = moveTarget !== MoveTarget.NEAR_ENEMY; multiple = moveTarget !== MoveTarget.NEAR_ENEMY;
break; break;
case MoveTarget.RANDOM_NEAR_ENEMY: case MoveTarget.RANDOM_NEAR_ENEMY:
set = [ opponents[Utils.randInt(opponents.length)] ]; set = [ opponents[user.randSeedInt(opponents.length)] ];
break; break;
case MoveTarget.ATTACKER: case MoveTarget.ATTACKER:
return { targets: [ -1 as BattlerIndex ], multiple: false }; return { targets: [ -1 as BattlerIndex ], multiple: false };
@ -3250,7 +3252,7 @@ export function initMoves() {
new StatusMove(Moves.REFLECT, "Reflect (N)", Type.PSYCHIC, -1, 20, 74, "A wondrous wall of light is put up to reduce damage from physical attacks for five turns.", -1, 0, 1) new StatusMove(Moves.REFLECT, "Reflect (N)", Type.PSYCHIC, -1, 20, 74, "A wondrous wall of light is put up to reduce damage from physical attacks for five turns.", -1, 0, 1)
.target(MoveTarget.USER_SIDE), .target(MoveTarget.USER_SIDE),
new SelfStatusMove(Moves.FOCUS_ENERGY, "Focus Energy", Type.NORMAL, -1, 30, -1, "The user takes a deep breath and focuses so that critical hits land more easily.", -1, 0, 1) new SelfStatusMove(Moves.FOCUS_ENERGY, "Focus Energy", Type.NORMAL, -1, 30, -1, "The user takes a deep breath and focuses so that critical hits land more easily.", -1, 0, 1)
.attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, true, undefined, true), .attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, true, true),
new AttackMove(Moves.BIDE, "Bide (N)", Type.NORMAL, MoveCategory.PHYSICAL, -1, -1, 10, -1, "The user endures attacks for two turns, then strikes back to cause double the damage taken.", -1, 1, 1) new AttackMove(Moves.BIDE, "Bide (N)", Type.NORMAL, MoveCategory.PHYSICAL, -1, -1, 10, -1, "The user endures attacks for two turns, then strikes back to cause double the damage taken.", -1, 1, 1)
.ignoresVirtual() .ignoresVirtual()
.target(MoveTarget.USER), .target(MoveTarget.USER),
@ -3397,7 +3399,7 @@ export function initMoves() {
new AttackMove(Moves.THIEF, "Thief", Type.DARK, MoveCategory.PHYSICAL, 60, 100, 25, 18, "The user attacks and steals the target's held item simultaneously. The user can't steal anything if it already holds an item.", -1, 0, 2) new AttackMove(Moves.THIEF, "Thief", Type.DARK, MoveCategory.PHYSICAL, 60, 100, 25, 18, "The user attacks and steals the target's held item simultaneously. The user can't steal anything if it already holds an item.", -1, 0, 2)
.attr(StealHeldItemAttr), .attr(StealHeldItemAttr),
new StatusMove(Moves.SPIDER_WEB, "Spider Web", Type.BUG, -1, 10, -1, "The user ensnares the target with thin, gooey silk so it can't flee from battle.", -1, 0, 2) new StatusMove(Moves.SPIDER_WEB, "Spider Web", Type.BUG, -1, 10, -1, "The user ensnares the target with thin, gooey silk so it can't flee from battle.", -1, 0, 2)
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, 1, true), .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1),
new StatusMove(Moves.MIND_READER, "Mind Reader", Type.NORMAL, -1, 5, -1, "The user senses the target's movements with its mind to ensure its next attack does not miss the target.", -1, 0, 2) new StatusMove(Moves.MIND_READER, "Mind Reader", Type.NORMAL, -1, 5, -1, "The user senses the target's movements with its mind to ensure its next attack does not miss the target.", -1, 0, 2)
.attr(IgnoreAccuracyAttr), .attr(IgnoreAccuracyAttr),
new StatusMove(Moves.NIGHTMARE, "Nightmare", Type.GHOST, 100, 15, -1, "A sleeping target sees a nightmare that inflicts some damage every turn.", -1, 0, 2) new StatusMove(Moves.NIGHTMARE, "Nightmare", Type.GHOST, 100, 15, -1, "A sleeping target sees a nightmare that inflicts some damage every turn.", -1, 0, 2)
@ -3501,7 +3503,7 @@ export function initMoves() {
new AttackMove(Moves.STEEL_WING, "Steel Wing", Type.STEEL, MoveCategory.PHYSICAL, 70, 90, 25, -1, "The target is hit with wings of steel. This may also raise the user's Defense stat.", 10, 0, 2) new AttackMove(Moves.STEEL_WING, "Steel Wing", Type.STEEL, MoveCategory.PHYSICAL, 70, 90, 25, -1, "The target is hit with wings of steel. This may also raise the user's Defense stat.", 10, 0, 2)
.attr(StatChangeAttr, BattleStat.DEF, 1, true), .attr(StatChangeAttr, BattleStat.DEF, 1, true),
new StatusMove(Moves.MEAN_LOOK, "Mean Look", Type.NORMAL, -1, 5, -1, "The user pins the target with a dark, arresting look. The target becomes unable to flee.", -1, 0, 2) new StatusMove(Moves.MEAN_LOOK, "Mean Look", Type.NORMAL, -1, 5, -1, "The user pins the target with a dark, arresting look. The target becomes unable to flee.", -1, 0, 2)
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, 1, true), .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1),
new StatusMove(Moves.ATTRACT, "Attract", Type.NORMAL, 100, 15, -1, "If it is the opposite gender of the user, the target becomes infatuated and less likely to attack.", -1, 0, 2) new StatusMove(Moves.ATTRACT, "Attract", Type.NORMAL, 100, 15, -1, "If it is the opposite gender of the user, the target becomes infatuated and less likely to attack.", -1, 0, 2)
.attr(AddBattlerTagAttr, BattlerTagType.INFATUATED) .attr(AddBattlerTagAttr, BattlerTagType.INFATUATED)
.condition((user, target, move) => user.isOppositeGender(target)), .condition((user, target, move) => user.isOppositeGender(target)),
@ -3538,7 +3540,7 @@ export function initMoves() {
.attr(ForceSwitchOutAttr, true, true) .attr(ForceSwitchOutAttr, true, true)
.hidesUser(), .hidesUser(),
new StatusMove(Moves.ENCORE, "Encore", Type.NORMAL, 100, 5, 122, "The user compels the target to keep using the move it encored for three turns.", -1, 0, 2) new StatusMove(Moves.ENCORE, "Encore", Type.NORMAL, 100, 5, 122, "The user compels the target to keep using the move it encored for three turns.", -1, 0, 2)
.attr(AddBattlerTagAttr, BattlerTagType.ENCORE, false, undefined, true) .attr(AddBattlerTagAttr, BattlerTagType.ENCORE, false, true)
.condition((user, target, move) => new EncoreTag(move.id, user.id).canAdd(target)), .condition((user, target, move) => new EncoreTag(move.id, user.id).canAdd(target)),
new AttackMove(Moves.PURSUIT, "Pursuit (N)", Type.DARK, MoveCategory.PHYSICAL, 40, 100, 20, -1, "The power of this attack move is doubled if it's used on a target that's switching out of battle.", -1, 0, 2), new AttackMove(Moves.PURSUIT, "Pursuit (N)", Type.DARK, MoveCategory.PHYSICAL, 40, 100, 20, -1, "The power of this attack move is doubled if it's used on a target that's switching out of battle.", -1, 0, 2),
new AttackMove(Moves.RAPID_SPIN, "Rapid Spin", Type.NORMAL, MoveCategory.PHYSICAL, 50, 100, 40, -1, "A spin attack that can also eliminate such moves as Bind, Wrap, and Leech Seed. This also raises the user's Speed stat.", 100, 0, 2) new AttackMove(Moves.RAPID_SPIN, "Rapid Spin", Type.NORMAL, MoveCategory.PHYSICAL, 50, 100, 40, -1, "A spin attack that can also eliminate such moves as Bind, Wrap, and Leech Seed. This also raises the user's Speed stat.", 100, 0, 2)
@ -3642,7 +3644,7 @@ export function initMoves() {
.attr(RandomMovesetMoveAttr, true) .attr(RandomMovesetMoveAttr, true)
.ignoresVirtual(), .ignoresVirtual(),
new SelfStatusMove(Moves.INGRAIN, "Ingrain", Type.GRASS, -1, 20, -1, "The user lays roots that restore its HP on every turn. Because it's rooted, it can't switch out.", -1, 0, 3) new SelfStatusMove(Moves.INGRAIN, "Ingrain", Type.GRASS, -1, 20, -1, "The user lays roots that restore its HP on every turn. Because it's rooted, it can't switch out.", -1, 0, 3)
.attr(AddBattlerTagAttr, BattlerTagType.INGRAIN, true, undefined, true), .attr(AddBattlerTagAttr, BattlerTagType.INGRAIN, true, true),
new AttackMove(Moves.SUPERPOWER, "Superpower", Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, "The user attacks the target with great power. However, this also lowers the user's Attack and Defense stats.", 100, 0, 3) new AttackMove(Moves.SUPERPOWER, "Superpower", Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, "The user attacks the target with great power. However, this also lowers the user's Attack and Defense stats.", 100, 0, 3)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], -1, true), .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], -1, true),
new SelfStatusMove(Moves.MAGIC_COAT, "Magic Coat (N)", Type.PSYCHIC, -1, 15, -1, "Moves like Leech Seed and moves that inflict status conditions are blocked by a barrier and reflected back to the user of those moves.", -1, 4, 3), new SelfStatusMove(Moves.MAGIC_COAT, "Magic Coat (N)", Type.PSYCHIC, -1, 15, -1, "Moves like Leech Seed and moves that inflict status conditions are blocked by a barrier and reflected back to the user of those moves.", -1, 4, 3),
@ -3650,7 +3652,7 @@ export function initMoves() {
new AttackMove(Moves.REVENGE, "Revenge (N)", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, "This attack move's power is doubled if the user has been hurt by the opponent in the same turn.", -1, -4, 3), new AttackMove(Moves.REVENGE, "Revenge (N)", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, "This attack move's power is doubled if the user has been hurt by the opponent in the same turn.", -1, -4, 3),
new AttackMove(Moves.BRICK_BREAK, "Brick Break (N)", Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 15, 58, "The user attacks with a swift chop. It can also break barriers, such as Light Screen and Reflect.", -1, 0, 3), new AttackMove(Moves.BRICK_BREAK, "Brick Break (N)", Type.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 15, 58, "The user attacks with a swift chop. It can also break barriers, such as Light Screen and Reflect.", -1, 0, 3),
new StatusMove(Moves.YAWN, "Yawn", Type.NORMAL, -1, 10, -1, "The user lets loose a huge yawn that lulls the target into falling asleep on the next turn.", -1, 0, 3) new StatusMove(Moves.YAWN, "Yawn", Type.NORMAL, -1, 10, -1, "The user lets loose a huge yawn that lulls the target into falling asleep on the next turn.", -1, 0, 3)
.attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, undefined, true) .attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true)
.condition((user, target, move) => !target.status), .condition((user, target, move) => !target.status),
new AttackMove(Moves.KNOCK_OFF, "Knock Off (N)", Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, "The user slaps down the target's held item, and that item can't be used in that battle. The move does more damage if the target has a held item.", -1, 0, 3), new AttackMove(Moves.KNOCK_OFF, "Knock Off (N)", Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, "The user slaps down the target's held item, and that item can't be used in that battle. The move does more damage if the target has a held item.", -1, 0, 3),
new AttackMove(Moves.ENDEAVOR, "Endeavor", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, "This attack move cuts down the target's HP to equal the user's HP.", -1, 0, 3) new AttackMove(Moves.ENDEAVOR, "Endeavor", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, "This attack move cuts down the target's HP to equal the user's HP.", -1, 0, 3)
@ -3782,7 +3784,7 @@ export function initMoves() {
new SelfStatusMove(Moves.IRON_DEFENSE, "Iron Defense", Type.STEEL, -1, 15, 104, "The user hardens its body's surface like iron, sharply raising its Defense stat.", -1, 0, 3) new SelfStatusMove(Moves.IRON_DEFENSE, "Iron Defense", Type.STEEL, -1, 15, 104, "The user hardens its body's surface like iron, sharply raising its Defense stat.", -1, 0, 3)
.attr(StatChangeAttr, BattleStat.DEF, 2, true), .attr(StatChangeAttr, BattleStat.DEF, 2, true),
new StatusMove(Moves.BLOCK, "Block", Type.NORMAL, -1, 5, -1, "The user blocks the target's way with arms spread wide to prevent escape.", -1, 0, 3) new StatusMove(Moves.BLOCK, "Block", Type.NORMAL, -1, 5, -1, "The user blocks the target's way with arms spread wide to prevent escape.", -1, 0, 3)
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, 1, true), .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1),
new StatusMove(Moves.HOWL, "Howl", Type.NORMAL, -1, 40, -1, "The user howls loudly to raise the spirit of itself and allies. This raises their Attack stats.", -1, 0, 3) new StatusMove(Moves.HOWL, "Howl", Type.NORMAL, -1, 40, -1, "The user howls loudly to raise the spirit of itself and allies. This raises their Attack stats.", -1, 0, 3)
.attr(StatChangeAttr, BattleStat.ATK, 1, true) .attr(StatChangeAttr, BattleStat.ATK, 1, true)
.soundBased() .soundBased()
@ -3833,7 +3835,7 @@ export function initMoves() {
.attr(StatChangeAttr, BattleStat.SPATK, -2, true), .attr(StatChangeAttr, BattleStat.SPATK, -2, true),
new SelfStatusMove(Moves.ROOST, "Roost", Type.FLYING, -1, 10, -1, "The user lands and rests its body. This move restores the user's HP by up to half of its max HP.", -1, 0, 4) new SelfStatusMove(Moves.ROOST, "Roost", Type.FLYING, -1, 10, -1, "The user lands and rests its body. This move restores the user's HP by up to half of its max HP.", -1, 0, 4)
.attr(HealAttr, 0.5) .attr(HealAttr, 0.5)
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, true, 1), .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, true, false, 1),
new StatusMove(Moves.GRAVITY, "Gravity", Type.PSYCHIC, -1, 5, -1, "This move enables Flying-type Pokémon or Pokémon with the Levitate Ability to be hit by Ground-type moves. Moves that involve flying can't be used.", -1, 0, 4) new StatusMove(Moves.GRAVITY, "Gravity", Type.PSYCHIC, -1, 5, -1, "This move enables Flying-type Pokémon or Pokémon with the Levitate Ability to be hit by Ground-type moves. Moves that involve flying can't be used.", -1, 0, 4)
.attr(AddArenaTagAttr, ArenaTagType.GRAVITY, 5) .attr(AddArenaTagAttr, ArenaTagType.GRAVITY, 5)
.target(MoveTarget.BOTH_SIDES), .target(MoveTarget.BOTH_SIDES),
@ -3884,7 +3886,7 @@ export function initMoves() {
new SelfStatusMove(Moves.POWER_TRICK, "Power Trick (N)", Type.PSYCHIC, -1, 10, -1, "The user employs its psychic power to switch its Attack stat with its Defense stat.", -1, 0, 4), new SelfStatusMove(Moves.POWER_TRICK, "Power Trick (N)", Type.PSYCHIC, -1, 10, -1, "The user employs its psychic power to switch its Attack stat with its Defense stat.", -1, 0, 4),
new StatusMove(Moves.GASTRO_ACID, "Gastro Acid (N)", Type.POISON, 100, 10, -1, "The user hurls up its stomach acids on the target. The fluid eliminates the effect of the target's Ability.", -1, 0, 4), new StatusMove(Moves.GASTRO_ACID, "Gastro Acid (N)", Type.POISON, 100, 10, -1, "The user hurls up its stomach acids on the target. The fluid eliminates the effect of the target's Ability.", -1, 0, 4),
new StatusMove(Moves.LUCKY_CHANT, "Lucky Chant (N)", Type.NORMAL, -1, 30, -1, "The user chants an incantation toward the sky, preventing opposing Pokémon from landing critical hits for five turns.", -1, 0, 4) new StatusMove(Moves.LUCKY_CHANT, "Lucky Chant (N)", Type.NORMAL, -1, 30, -1, "The user chants an incantation toward the sky, preventing opposing Pokémon from landing critical hits for five turns.", -1, 0, 4)
.attr(AddBattlerTagAttr, BattlerTagType.NO_CRIT, false, 5) .attr(AddBattlerTagAttr, BattlerTagType.NO_CRIT, false, false, 5)
.target(MoveTarget.USER_SIDE), .target(MoveTarget.USER_SIDE),
new StatusMove(Moves.ME_FIRST, "Me First (N)", Type.NORMAL, -1, 20, -1, "The user cuts ahead of the target to copy and use the target's intended move with greater power. This move fails if it isn't used first.", -1, 0, 4) new StatusMove(Moves.ME_FIRST, "Me First (N)", Type.NORMAL, -1, 20, -1, "The user cuts ahead of the target to copy and use the target's intended move with greater power. This move fails if it isn't used first.", -1, 0, 4)
.ignoresVirtual() .ignoresVirtual()
@ -3904,7 +3906,7 @@ export function initMoves() {
.target(MoveTarget.ENEMY_SIDE), .target(MoveTarget.ENEMY_SIDE),
new StatusMove(Moves.HEART_SWAP, "Heart Swap (N)", Type.PSYCHIC, -1, 10, -1, "The user employs its psychic power to switch stat changes with the target.", -1, 0, 4), new StatusMove(Moves.HEART_SWAP, "Heart Swap (N)", Type.PSYCHIC, -1, 10, -1, "The user employs its psychic power to switch stat changes with the target.", -1, 0, 4),
new SelfStatusMove(Moves.AQUA_RING, "Aqua Ring", Type.WATER, -1, 20, -1, "The user envelops itself in a veil made of water. It regains some HP every turn.", -1, 0, 4) new SelfStatusMove(Moves.AQUA_RING, "Aqua Ring", Type.WATER, -1, 20, -1, "The user envelops itself in a veil made of water. It regains some HP every turn.", -1, 0, 4)
.attr(AddBattlerTagAttr, BattlerTagType.AQUA_RING, true, undefined, true), .attr(AddBattlerTagAttr, BattlerTagType.AQUA_RING, true, true),
new SelfStatusMove(Moves.MAGNET_RISE, "Magnet Rise (N)", Type.ELECTRIC, -1, 10, -1, "The user levitates using electrically generated magnetism for five turns.", -1, 0, 4), new SelfStatusMove(Moves.MAGNET_RISE, "Magnet Rise (N)", Type.ELECTRIC, -1, 10, -1, "The user levitates using electrically generated magnetism for five turns.", -1, 0, 4),
new AttackMove(Moves.FLARE_BLITZ, "Flare Blitz", Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 15, 165, "The user cloaks itself in fire and charges the target. This also damages the user quite a lot. This attack may leave the target with a burn.", 10, 0, 4) new AttackMove(Moves.FLARE_BLITZ, "Flare Blitz", Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 15, 165, "The user cloaks itself in fire and charges the target. This also damages the user quite a lot. This attack may leave the target with a burn.", 10, 0, 4)
.attr(RecoilAttr) .attr(RecoilAttr)
@ -4110,7 +4112,7 @@ export function initMoves() {
.ignoresProtect() .ignoresProtect()
.target(MoveTarget.BOTH_SIDES), .target(MoveTarget.BOTH_SIDES),
new AttackMove(Moves.SMACK_DOWN, "Smack Down", Type.ROCK, MoveCategory.PHYSICAL, 50, 100, 15, -1, "The user throws a stone or similar projectile to attack the target. A flying Pokémon will fall to the ground when it's hit.", 100, 0, 5) new AttackMove(Moves.SMACK_DOWN, "Smack Down", Type.ROCK, MoveCategory.PHYSICAL, 50, 100, 15, -1, "The user throws a stone or similar projectile to attack the target. A flying Pokémon will fall to the ground when it's hit.", 100, 0, 5)
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, 5) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 5)
.makesContact(false), .makesContact(false),
new AttackMove(Moves.STORM_THROW, "Storm Throw", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, "The user strikes the target with a fierce blow. This attack always results in a critical hit.", -1, 0, 5) new AttackMove(Moves.STORM_THROW, "Storm Throw", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, "The user strikes the target with a fierce blow. This attack always results in a critical hit.", -1, 0, 5)
.attr(CritOnlyAttr), .attr(CritOnlyAttr),
@ -4402,10 +4404,10 @@ export function initMoves() {
new AttackMove(Moves.OBLIVION_WING, "Oblivion Wing", Type.FLYING, MoveCategory.SPECIAL, 80, 100, 10, -1, "The user absorbs its target's HP. The user's HP is restored by over half of the damage taken by the target.", -1, 0, 6) new AttackMove(Moves.OBLIVION_WING, "Oblivion Wing", Type.FLYING, MoveCategory.SPECIAL, 80, 100, 10, -1, "The user absorbs its target's HP. The user's HP is restored by over half of the damage taken by the target.", -1, 0, 6)
.attr(HitHealAttr, 0.75), .attr(HitHealAttr, 0.75),
new AttackMove(Moves.THOUSAND_ARROWS, "Thousand Arrows", Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, "This move also hits opposing Pokémon that are in the air. Those Pokémon are knocked down to the ground.", 100, 0, 6) new AttackMove(Moves.THOUSAND_ARROWS, "Thousand Arrows", Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, "This move also hits opposing Pokémon that are in the air. Those Pokémon are knocked down to the ground.", 100, 0, 6)
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, 5) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 5)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.THOUSAND_WAVES, "Thousand Waves", Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, "The user attacks with a wave that crawls along the ground. Those it hits can't flee from battle.", -1, 0, 6) new AttackMove(Moves.THOUSAND_WAVES, "Thousand Waves", Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, "The user attacks with a wave that crawls along the ground. Those it hits can't flee from battle.", -1, 0, 6)
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, 1, true) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.LANDS_WRATH, "Land's Wrath", Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, "The user gathers the energy of the land and focuses that power on opposing Pokémon to damage them.", -1, 0, 6) new AttackMove(Moves.LANDS_WRATH, "Land's Wrath", Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, "The user gathers the energy of the land and focuses that power on opposing Pokémon to damage them.", -1, 0, 6)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
@ -4608,7 +4610,7 @@ export function initMoves() {
new SelfStatusMove(Moves.STUFF_CHEEKS, "Stuff Cheeks (N)", Type.NORMAL, -1, 10, -1, "The user eats its held Berry, then sharply raises its Defense stat.", 100, 0, 8), new SelfStatusMove(Moves.STUFF_CHEEKS, "Stuff Cheeks (N)", Type.NORMAL, -1, 10, -1, "The user eats its held Berry, then sharply raises its Defense stat.", 100, 0, 8),
new SelfStatusMove(Moves.NO_RETREAT, "No Retreat", Type.FIGHTING, -1, 5, -1, "This move raises all the user's stats but prevents the user from switching out or fleeing.", 100, 0, 8) new SelfStatusMove(Moves.NO_RETREAT, "No Retreat", Type.FIGHTING, -1, 5, -1, "This move raises all the user's stats but prevents the user from switching out or fleeing.", 100, 0, 8)
.attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD ], 1, true)
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, true, 1, true), .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, true, true, 1),
new StatusMove(Moves.TAR_SHOT, "Tar Shot (N)", Type.ROCK, 100, 15, -1, "The user pours sticky tar over the target, lowering the target's Speed stat. The target becomes weaker to Fire-type moves.", 100, 0, 8), new StatusMove(Moves.TAR_SHOT, "Tar Shot (N)", Type.ROCK, 100, 15, -1, "The user pours sticky tar over the target, lowering the target's Speed stat. The target becomes weaker to Fire-type moves.", 100, 0, 8),
new StatusMove(Moves.MAGIC_POWDER, "Magic Powder (N)", Type.PSYCHIC, 100, 20, -1, "The user scatters a cloud of magic powder that changes the target to Psychic type.", -1, 0, 8) new StatusMove(Moves.MAGIC_POWDER, "Magic Powder (N)", Type.PSYCHIC, 100, 20, -1, "The user scatters a cloud of magic powder that changes the target to Psychic type.", -1, 0, 8)
.powderMove(), .powderMove(),
@ -4617,7 +4619,7 @@ export function initMoves() {
new StatusMove(Moves.TEATIME, "Teatime (N)", Type.NORMAL, -1, 10, -1, "The user has teatime with all the Pokémon in the battle. Each Pokémon eats its held Berry.", -1, 0, 8) new StatusMove(Moves.TEATIME, "Teatime (N)", Type.NORMAL, -1, 10, -1, "The user has teatime with all the Pokémon in the battle. Each Pokémon eats its held Berry.", -1, 0, 8)
.target(MoveTarget.ALL), .target(MoveTarget.ALL),
new StatusMove(Moves.OCTOLOCK, "Octolock (P)", Type.FIGHTING, 100, 15, -1, "The user locks the target in and prevents it from fleeing. This move also lowers the target's Defense and Sp. Def every turn.", -1, 0, 8) new StatusMove(Moves.OCTOLOCK, "Octolock (P)", Type.FIGHTING, 100, 15, -1, "The user locks the target in and prevents it from fleeing. This move also lowers the target's Defense and Sp. Def every turn.", -1, 0, 8)
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, 1, true), .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1),
new AttackMove(Moves.BOLT_BEAK, "Bolt Beak (N)", Type.ELECTRIC, MoveCategory.PHYSICAL, 85, 100, 10, -1, "The user stabs the target with its electrified beak. If the user attacks before the target, the power of this move is doubled.", -1, 0, 8), new AttackMove(Moves.BOLT_BEAK, "Bolt Beak (N)", Type.ELECTRIC, MoveCategory.PHYSICAL, 85, 100, 10, -1, "The user stabs the target with its electrified beak. If the user attacks before the target, the power of this move is doubled.", -1, 0, 8),
new AttackMove(Moves.FISHIOUS_REND, "Fishious Rend (N)", Type.WATER, MoveCategory.PHYSICAL, 85, 100, 10, -1, "The user rends the target with its hard gills. If the user attacks before the target, the power of this move is doubled.", -1, 0, 8) new AttackMove(Moves.FISHIOUS_REND, "Fishious Rend (N)", Type.WATER, MoveCategory.PHYSICAL, 85, 100, 10, -1, "The user rends the target with its hard gills. If the user attacks before the target, the power of this move is doubled.", -1, 0, 8)
.bitingMove(), .bitingMove(),
@ -4965,7 +4967,7 @@ export function initMoves() {
new AttackMove(Moves.HARD_PRESS, "Hard Press", Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, "The target is crushed with an arm, a claw, or the like to inflict damage. The more HP the target has left, the greater the move's power.", -1, 0, 9) new AttackMove(Moves.HARD_PRESS, "Hard Press", Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, "The target is crushed with an arm, a claw, or the like to inflict damage. The more HP the target has left, the greater the move's power.", -1, 0, 9)
.attr(OpponentHighHpPowerAttr), .attr(OpponentHighHpPowerAttr),
new StatusMove(Moves.DRAGON_CHEER, "Dragon Cheer (P)", Type.DRAGON, -1, 15, -1, "The user raises its allies' morale with a draconic cry so that their future attacks have a heightened chance of landing critical hits. This rouses Dragon types more.", 100, 0, 9) new StatusMove(Moves.DRAGON_CHEER, "Dragon Cheer (P)", Type.DRAGON, -1, 15, -1, "The user raises its allies' morale with a draconic cry so that their future attacks have a heightened chance of landing critical hits. This rouses Dragon types more.", 100, 0, 9)
.attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, false, undefined, true) .attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, false, true)
.target(MoveTarget.NEAR_ALLY), .target(MoveTarget.NEAR_ALLY),
new AttackMove(Moves.ALLURING_VOICE, "Alluring Voice (N)", Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, "The user attacks the target using its angelic voice. This also confuses the target if its stats have been boosted during the turn.", -1, 0, 9), new AttackMove(Moves.ALLURING_VOICE, "Alluring Voice (N)", Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, "The user attacks the target using its angelic voice. This also confuses the target if its stats have been boosted during the turn.", -1, 0, 9),
new AttackMove(Moves.TEMPER_FLARE, "Temper Flare (N)", Type.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, "Spurred by desperation, the user attacks the target. This move's power is doubled if the user's previous move failed.", -1, 0, 9), new AttackMove(Moves.TEMPER_FLARE, "Temper Flare (N)", Type.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, "Spurred by desperation, the user attacks the target. This move's power is doubled if the user's previous move failed.", -1, 0, 9),

View File

@ -16,14 +16,10 @@ export class Status {
public turnCount: integer; public turnCount: integer;
public cureTurn: integer; public cureTurn: integer;
constructor(effect: StatusEffect, turnCount?: integer, cureTurn?: integer) { constructor(effect: StatusEffect, turnCount: integer = 0, cureTurn?: integer) {
this.effect = effect; this.effect = effect;
this.turnCount = turnCount === undefined ? 0 : turnCount; this.turnCount = turnCount === undefined ? 0 : turnCount;
if (cureTurn === undefined) { this.cureTurn = cureTurn;
if (effect === StatusEffect.SLEEP)
this.cureTurn = Utils.randInt(3, 2);
} else
this.cureTurn = cureTurn;
} }
incrementTurn(): void { incrementTurn(): void {

View File

@ -565,7 +565,7 @@ export class SurviveDamageModifier extends PokemonHeldItemModifier {
const pokemon = args[0] as Pokemon; const pokemon = args[0] as Pokemon;
const surviveDamage = args[1] as Utils.BooleanHolder; const surviveDamage = args[1] as Utils.BooleanHolder;
if (!surviveDamage.value && Utils.randInt(10) < this.getStackCount()) { if (!surviveDamage.value && pokemon.randSeedInt(10) < this.getStackCount()) {
surviveDamage.value = true; surviveDamage.value = true;
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` hung on\nusing its ${this.type.name}!`)); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` hung on\nusing its ${this.type.name}!`));
@ -598,9 +598,10 @@ export class FlinchChanceModifier extends PokemonHeldItemModifier {
} }
apply(args: any[]): boolean { apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
const flinched = args[1] as Utils.BooleanHolder; const flinched = args[1] as Utils.BooleanHolder;
if (!flinched.value && Utils.randInt(10) < this.getStackCount()) { if (!flinched.value && pokemon.randSeedInt(10) < this.getStackCount()) {
flinched.value = true; flinched.value = true;
return true; return true;
} }
@ -741,7 +742,7 @@ export class BerryModifier extends PokemonHeldItemModifier {
} }
const preserve = new Utils.BooleanHolder(false); const preserve = new Utils.BooleanHolder(false);
pokemon.scene.applyModifiers(PreserveBerryModifier, pokemon.isPlayer(), preserve); pokemon.scene.applyModifiers(PreserveBerryModifier, pokemon.isPlayer(), pokemon, preserve);
getBerryEffectFunc(this.berryType)(pokemon); getBerryEffectFunc(this.berryType)(pokemon);
if (!preserve.value) if (!preserve.value)
@ -769,12 +770,12 @@ export class PreserveBerryModifier extends PersistentModifier {
} }
shouldApply(args: any[]): boolean { shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args[0] instanceof Utils.BooleanHolder; return super.shouldApply(args) && args[0] instanceof Pokemon && args[1] instanceof Utils.BooleanHolder;
} }
apply(args: any[]): boolean { apply(args: any[]): boolean {
if (!(args[0] as Utils.BooleanHolder).value) if (!(args[1] as Utils.BooleanHolder).value)
(args[0] as Utils.BooleanHolder).value = Utils.randInt(this.getMaxStackCount(null)) < this.getStackCount(); (args[1] as Utils.BooleanHolder).value = (args[0] as Pokemon).randSeedInt(this.getMaxStackCount(null)) < this.getStackCount();
return true; return true;
} }
@ -1396,7 +1397,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
if (!opponents.length) if (!opponents.length)
return false; return false;
const targetPokemon = opponents[Utils.randInt(opponents.length)]; const targetPokemon = opponents[pokemon.randSeedInt(opponents.length)];
const transferredItemCount = this.getTransferredItemCount(); const transferredItemCount = this.getTransferredItemCount();
if (!transferredItemCount) if (!transferredItemCount)
@ -1413,7 +1414,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
for (let i = 0; i < transferredItemCount; i++) { for (let i = 0; i < transferredItemCount; i++) {
if (!itemModifiers.length) if (!itemModifiers.length)
break; break;
const randItemIndex = Utils.randInt(itemModifiers.length); const randItemIndex = pokemon.randSeedInt(itemModifiers.length);
const randItem = itemModifiers[randItemIndex]; const randItem = itemModifiers[randItemIndex];
heldItemTransferPromises.push(pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, false, false, true).then(success => { heldItemTransferPromises.push(pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, false, false, true).then(success => {
if (success) { if (success) {

View File

@ -132,7 +132,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.fusionShiny = dataSource.fusionShiny; this.fusionShiny = dataSource.fusionShiny;
this.fusionGender = dataSource.fusionGender; this.fusionGender = dataSource.fusionGender;
} else { } else {
this.id = Utils.randSeedInt(4294967295); this.id = Utils.randSeedInt(4294967296);
this.ivs = ivs || [ this.ivs = ivs || [
Utils.binToDec(Utils.decToBin(this.id).substring(0, 5)), Utils.binToDec(Utils.decToBin(this.id).substring(0, 5)),
Utils.binToDec(Utils.decToBin(this.id).substring(5, 10)), Utils.binToDec(Utils.decToBin(this.id).substring(5, 10)),
@ -956,7 +956,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (source.getTag(BattlerTagType.CRIT_BOOST)) if (source.getTag(BattlerTagType.CRIT_BOOST))
critLevel.value += 2; critLevel.value += 2;
const critChance = Math.ceil(16 / Math.pow(2, critLevel.value)); const critChance = Math.ceil(16 / Math.pow(2, critLevel.value));
isCritical = !source.getTag(BattlerTagType.NO_CRIT) && !(this.getAbility().hasAttr(BlockCritAbAttr)) && (critChance === 1 || !Utils.randInt(critChance)); isCritical = !source.getTag(BattlerTagType.NO_CRIT) && !(this.getAbility().hasAttr(BlockCritAbAttr)) && (critChance === 1 || !this.scene.currentBattle.randSeedInt(critChance));
} }
const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this); const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this);
const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source); const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source);
@ -968,7 +968,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier); applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier);
if (!isTypeImmune) { if (!isTypeImmune) {
damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * weatherTypeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier; damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * weatherTypeMultiplier * ((this.scene.currentBattle.randSeedInt(15) + 85) / 100)) * criticalMultiplier;
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)
damage.value = Math.floor(damage.value / 2); damage.value = Math.floor(damage.value / 2);
move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => { move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
@ -1087,14 +1087,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return this.species.speciesId === Species.ETERNATUS && this.formIndex === 1; return this.species.speciesId === Species.ETERNATUS && this.formIndex === 1;
} }
addTag(tagType: BattlerTagType, turnCount?: integer, sourceMove?: Moves, sourceId?: integer): boolean { addTag(tagType: BattlerTagType, turnCount: integer = 0, sourceMove?: Moves, sourceId?: integer): boolean {
const existingTag = this.getTag(tagType); const existingTag = this.getTag(tagType);
if (existingTag) { if (existingTag) {
existingTag.onOverlap(this); existingTag.onOverlap(this);
return false; return false;
} }
const newTag = getBattlerTag(tagType, turnCount || 0, sourceMove, sourceId); const newTag = getBattlerTag(tagType, turnCount, sourceMove, sourceId);
const cancelled = new Utils.BooleanHolder(false); const cancelled = new Utils.BooleanHolder(false);
applyPreApplyBattlerTagAbAttrs(PreApplyBattlerTagAbAttr, this, newTag, cancelled); applyPreApplyBattlerTagAbAttrs(PreApplyBattlerTagAbAttr, this, newTag, cancelled);
@ -1387,10 +1387,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (cancelled.value) if (cancelled.value)
return false; return false;
if (effect === StatusEffect.SLEEP) let cureTurn: integer;
this.setFrameRate(4);
this.status = new Status(effect); if (effect === StatusEffect.SLEEP) {
cureTurn = this.randSeedIntRange(2, 4);
this.setFrameRate(4);
}
this.status = new Status(effect, 0, cureTurn);
return true; return true;
} }
@ -1693,6 +1697,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
fusionCanvas.remove(); fusionCanvas.remove();
} }
randSeedInt(range: integer, min: integer = 0): integer {
return this.scene.currentBattle
? this.scene.currentBattle.randSeedInt(range, min)
: Utils.randSeedInt(range, min);
}
randSeedIntRange(min: integer, max: integer): integer {
return this.randSeedInt((max - min) + 1, min);
}
destroy(): void { destroy(): void {
this.battleInfo.destroy(); this.battleInfo.destroy();
super.destroy(); super.destroy();
@ -1958,7 +1972,7 @@ export class EnemyPokemon extends Pokemon {
} }
switch (this.aiType) { switch (this.aiType) {
case AiType.RANDOM: case AiType.RANDOM:
const moveId = movePool[Utils.randInt(movePool.length)].moveId; const moveId = movePool[this.scene.currentBattle.randSeedInt(movePool.length)].moveId;
return { move: moveId, targets: this.getNextTargets(moveId) }; return { move: moveId, targets: this.getNextTargets(moveId) };
case AiType.SMART_RANDOM: case AiType.SMART_RANDOM:
case AiType.SMART: case AiType.SMART:
@ -1990,7 +2004,7 @@ export class EnemyPokemon extends Pokemon {
}); });
let r = 0; let r = 0;
if (this.aiType === AiType.SMART_RANDOM) { if (this.aiType === AiType.SMART_RANDOM) {
while (r < sortedMovePool.length - 1 && Utils.randInt(8) >= 5) while (r < sortedMovePool.length - 1 && this.scene.currentBattle.randSeedInt(8) >= 5)
r++; r++;
} }
console.log(movePool.map(m => m.getName()), moveScores, r, sortedMovePool.map(m => m.getName())); console.log(movePool.map(m => m.getName()), moveScores, r, sortedMovePool.map(m => m.getName()));
@ -2043,7 +2057,7 @@ export class EnemyPokemon extends Pokemon {
return total; return total;
}, 0); }, 0);
const randValue = Utils.randInt(totalWeight); const randValue = this.scene.currentBattle.randSeedInt(totalWeight);
let targetIndex: integer; let targetIndex: integer;
thresholds.every((t, i) => { thresholds.every((t, i) => {

View File

@ -2,12 +2,12 @@ export function toReadableString(str: string): string {
return str.replace(/\_/g, ' ').split(' ').map(s => `${s.slice(0, 1)}${s.slice(1).toLowerCase()}`).join(' '); return str.replace(/\_/g, ' ').split(' ').map(s => `${s.slice(0, 1)}${s.slice(1).toLowerCase()}`).join(' ');
} }
export function randomString(length: integer) { export function randomString(length: integer, seeded: boolean = false) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = ''; let result = '';
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length); const randomIndex = seeded ? randSeedInt(characters.length) : Math.floor(Math.random() * characters.length);
result += characters[randomIndex]; result += characters[randomIndex];
} }
@ -56,17 +56,13 @@ export function padInt(value: integer, length: integer, padWith?: string): strin
return valueStr; return valueStr;
} }
export function randInt(range: integer, min?: integer): integer { export function randInt(range: integer, min: integer = 0): integer {
if (!min)
min = 0;
if (range === 1) if (range === 1)
return min; return min;
return Math.floor(Math.random() * range) + min; return Math.floor(Math.random() * range) + min;
} }
export function randSeedInt(range: integer, min?: integer): integer { export function randSeedInt(range: integer, min: integer = 0): integer {
if (!min)
min = 0;
if (range === 1) if (range === 1)
return min; return min;
return Phaser.Math.RND.integerInRange(min, (range - 1) + min); return Phaser.Math.RND.integerInRange(min, (range - 1) + min);
@ -162,9 +158,7 @@ export function apiFetch(path: string): Promise<Response> {
}); });
} }
export function apiPost(path: string, data?: any, contentType?: string): Promise<Response> { export function apiPost(path: string, data?: any, contentType: string = 'application/json'): Promise<Response> {
if (!contentType)
contentType = 'application/json';
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const headers = { const headers = {
'Accept': contentType, 'Accept': contentType,