mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-26 17:29:30 +02:00
Bugfix Variety Pack
Adds RNG logging for moves, which I apparently missed Moves that deal fixed damage in a range (psywave does this I think) will now show the correct range of possible damage values rather than mauling RNG Shell Side Arm no longer destroys RNG when showing damage calc
This commit is contained in:
parent
b0e0c24181
commit
1cf2be3757
@ -1054,12 +1054,12 @@ export class FixedDamageAttr extends MoveAttr {
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
(args[0] as Utils.IntegerHolder).value = this.getDamage(user, target, move);
|
||||
(args[0] as Utils.IntegerHolder).value = this.getDamage(user, target, move, args[1], args[2]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getDamage(user: Pokemon, target: Pokemon, move: Move): integer {
|
||||
getDamage(user: Pokemon, target: Pokemon, move: Move, extra1?: any, extra2?: any): integer {
|
||||
return this.damage;
|
||||
}
|
||||
}
|
||||
@ -1153,8 +1153,14 @@ export class RandomLevelDamageAttr extends FixedDamageAttr {
|
||||
super(0);
|
||||
}
|
||||
|
||||
getDamage(user: Pokemon, target: Pokemon, move: Move): number {
|
||||
return Math.max(Math.floor(user.level * (user.randSeedIntRange(50, 150) * 0.01)), 1);
|
||||
getDamage(user: Pokemon, target: Pokemon, move: Move, isLow?: boolean, isHigh?: boolean): number {
|
||||
if (isLow) {
|
||||
return Math.max(Math.floor(user.level * (50 * 0.01)), 1);
|
||||
}
|
||||
if (isHigh) {
|
||||
return Math.max(Math.floor(user.level * (150 * 0.01)), 1);
|
||||
}
|
||||
return Math.max(Math.floor(user.level * (user.randSeedIntRange(50, 150, "Random damage") * 0.01)), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1762,7 +1768,7 @@ export class MultiHitAttr extends MoveAttr {
|
||||
switch (this.multiHitType) {
|
||||
case MultiHitType._2_TO_5:
|
||||
{
|
||||
const rand = user.randSeedInt(16);
|
||||
const rand = user.randSeedInt(16, undefined, "Random number of hits for a 2-to-5-hits move");
|
||||
const hitValue = new Utils.IntegerHolder(rand);
|
||||
applyAbAttrs(MaxMultiHitAbAttr, user, null, hitValue);
|
||||
if (hitValue.value >= 10) {
|
||||
@ -1826,7 +1832,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true);
|
||||
const statusCheck = moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance;
|
||||
const statusCheck = moveChance < 0 || moveChance === 100 || user.randSeedInt(100, undefined, "Chance to apply " + Utils.getEnumKeys(StatusEffect)[this.effect]) < moveChance;
|
||||
if (statusCheck) {
|
||||
const pokemon = this.selfTarget ? user : target;
|
||||
if (pokemon.status) {
|
||||
@ -1915,6 +1921,7 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
const rand = Phaser.Math.RND.realInRange(0, 1);
|
||||
console.log("Phaser.Math.RND.realInRange(0, 1)", rand)
|
||||
if (rand >= this.chance) {
|
||||
return resolve(false);
|
||||
}
|
||||
@ -1923,10 +1930,13 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
|
||||
const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD;
|
||||
const highestItemTier = heldItems.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier, highestTier), 0);
|
||||
const tierHeldItems = heldItems.filter(m => m.type.getOrInferTier(poolType) === highestItemTier);
|
||||
const stolenItem = tierHeldItems[user.randSeedInt(tierHeldItems.length)];
|
||||
const stolenItem = tierHeldItems[user.randSeedInt(tierHeldItems.length, undefined, "Selecting an item to steal")];
|
||||
user.scene.tryTransferHeldItemModifier(stolenItem, user, false).then(success => {
|
||||
if (success) {
|
||||
user.scene.queueMessage(getPokemonMessage(user, ` stole\n${target.name}'s ${stolenItem.type.name}!`));
|
||||
console.log(`Stole ${stolenItem.type.name}`)
|
||||
} else {
|
||||
console.log(`Attempted to steal ${stolenItem.type.name} but the thief can't hold any more`)
|
||||
}
|
||||
resolve(success);
|
||||
});
|
||||
@ -1998,7 +2008,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
|
||||
}
|
||||
|
||||
if (heldItems.length) {
|
||||
const removedItem = heldItems[user.randSeedInt(heldItems.length)];
|
||||
const removedItem = heldItems[user.randSeedInt(heldItems.length, undefined, "Selecting item to remove")];
|
||||
|
||||
// Decrease item amount and update icon
|
||||
!--removedItem.stackCount;
|
||||
@ -2056,7 +2066,7 @@ export class EatBerryAttr extends MoveEffectAttr {
|
||||
if (heldBerries.length <= 0) {
|
||||
return false;
|
||||
}
|
||||
this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)];
|
||||
this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length, undefined, "Selecting a berry to eat")];
|
||||
const preserve = new Utils.BooleanHolder(false);
|
||||
target.scene.applyModifiers(PreserveBerryModifier, target.isPlayer(), target, preserve); // check for berry pouch preservation
|
||||
if (!preserve.value) {
|
||||
@ -2114,7 +2124,7 @@ export class StealEatBerryAttr extends EatBerryAttr {
|
||||
return false;
|
||||
}
|
||||
// if the target has berries, pick a random berry and steal it
|
||||
this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)];
|
||||
this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length, undefined, "Selecting a berry to steal")];
|
||||
const message = i18next.t("battle:stealEatBerry", {pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name});
|
||||
user.scene.queueMessage(message);
|
||||
this.reduceBerryModifier(target);
|
||||
@ -2471,7 +2481,7 @@ export class StatChangeAttr extends MoveEffectAttr {
|
||||
}
|
||||
|
||||
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true);
|
||||
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) {
|
||||
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100, undefined, "Chance to apply status condition") < moveChance) {
|
||||
const levels = this.getLevels(user);
|
||||
user.scene.unshiftPhase(new StatChangePhase(user.scene, (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, levels, this.showMessage));
|
||||
return true;
|
||||
@ -3612,7 +3622,10 @@ export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr {
|
||||
if (atkRatio > specialRatio) {
|
||||
category.value = MoveCategory.PHYSICAL;
|
||||
return true;
|
||||
} else if (atkRatio === specialRatio && user.randSeedInt(2) === 0) {
|
||||
} else if (atkRatio === specialRatio && args[1] == "SIM") {
|
||||
category.value = MoveCategory.PHYSICAL;
|
||||
return true;
|
||||
} else if (atkRatio === specialRatio && user.randSeedInt(2, undefined, "Randomly selecting an attack type for Shell Side Arm") === 0) {
|
||||
category.value = MoveCategory.PHYSICAL;
|
||||
return true;
|
||||
}
|
||||
@ -4080,7 +4093,7 @@ export class FrenzyAttr extends MoveEffectAttr {
|
||||
}
|
||||
|
||||
if (!user.getTag(BattlerTagType.FRENZY) && !user.getMoveQueue().length) {
|
||||
const turnCount = user.randSeedIntRange(1, 2);
|
||||
const turnCount = user.randSeedIntRange(1, 2, "Frenzy targeting");
|
||||
new Array(turnCount).fill(null).map(() => user.getMoveQueue().push({ move: move.id, targets: [ target.getBattlerIndex() ], ignorePP: true }));
|
||||
user.addTag(BattlerTagType.FRENZY, turnCount, move.id, user.id);
|
||||
} else {
|
||||
@ -4122,8 +4135,8 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
|
||||
}
|
||||
|
||||
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true);
|
||||
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) {
|
||||
return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedInt(this.turnCountMax - this.turnCountMin, this.turnCountMin), move.id, user.id);
|
||||
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100, undefined, "Chance to apply battler tag") < moveChance) {
|
||||
return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedInt(this.turnCountMax - this.turnCountMin, this.turnCountMin, "Duration of effect"), move.id, user.id);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -4302,7 +4315,7 @@ export class ProtectAttr extends AddBattlerTagAttr {
|
||||
timesUsed++;
|
||||
}
|
||||
if (timesUsed) {
|
||||
return !user.randSeedInt(Math.pow(3, timesUsed));
|
||||
return !user.randSeedInt(Math.pow(3, timesUsed), undefined, "Chance for Protect-like move to fail");
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@ -4417,7 +4430,7 @@ export class AddArenaTagAttr extends MoveEffectAttr {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance) {
|
||||
if (move.chance < 0 || move.chance === 100 || user.randSeedInt(100, undefined, "Chance to add arena tag") < move.chance) {
|
||||
user.scene.arena.addTag(this.tagType, this.turnCount, move.id, user.id, (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY);
|
||||
return true;
|
||||
}
|
||||
@ -4492,7 +4505,7 @@ export class AddArenaTrapTagHitAttr extends AddArenaTagAttr {
|
||||
const moveChance = this.getMoveChance(user,target,move,this.selfTarget, true);
|
||||
const side = (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||
const tag = user.scene.arena.getTagOnSide(this.tagType, side) as ArenaTrapTag;
|
||||
if ((moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance)) {
|
||||
if ((moveChance < 0 || moveChance === 100 || user.randSeedInt(100, undefined, "Chance to apply trap") < moveChance)) {
|
||||
user.scene.arena.addTag(this.tagType, 0, move.id, user.id, side);
|
||||
if (!tag) {
|
||||
return true;
|
||||
@ -4646,7 +4659,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
|
||||
&& user.scene.getEnemyParty().findIndex(p => p.isFainted() && !p.isBoss()) > -1) {
|
||||
// Selects a random fainted pokemon
|
||||
const faintedPokemon = user.scene.getEnemyParty().filter(p => p.isFainted() && !p.isBoss());
|
||||
const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length)];
|
||||
const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length, undefined, "Randomly selecting a Pokemon to revive")];
|
||||
const slotIndex = user.scene.getEnemyParty().findIndex(p => pokemon.id === p.id);
|
||||
pokemon.resetStatus();
|
||||
pokemon.heal(Math.min(Math.max(Math.ceil(Math.floor(0.5 * pokemon.getMaxHp())), 1), pokemon.getMaxHp()));
|
||||
@ -4953,7 +4966,7 @@ export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
|
||||
const moveset = (!this.enemyMoveset ? user : target).getMoveset();
|
||||
const moves = moveset.filter(m => !m.getMove().hasFlag(MoveFlags.IGNORE_VIRTUAL));
|
||||
if (moves.length) {
|
||||
const move = moves[user.randSeedInt(moves.length)];
|
||||
const move = moves[user.randSeedInt(moves.length, undefined, "Randomly selecting a known move")];
|
||||
const moveIndex = moveset.findIndex(m => m.moveId === move.moveId);
|
||||
const moveTargets = getMoveTargets(user, move.moveId);
|
||||
if (!moveTargets.targets.length) {
|
||||
@ -4971,7 +4984,7 @@ export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
|
||||
}
|
||||
default: {
|
||||
moveTargets.targets.splice(moveTargets.targets.indexOf(user.getAlly().getBattlerIndex()));
|
||||
selectTargets = [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ];
|
||||
selectTargets = [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length, undefined, "Randomly selecting a target")] ];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -4989,7 +5002,7 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
||||
return new Promise(resolve => {
|
||||
const moveIds = Utils.getEnumValues(Moves).filter(m => !allMoves[m].hasFlag(MoveFlags.IGNORE_VIRTUAL) && !allMoves[m].name.endsWith(" (N)"));
|
||||
const moveId = moveIds[user.randSeedInt(moveIds.length)];
|
||||
const moveId = moveIds[user.randSeedInt(moveIds.length, undefined, "Randomly selecting any valid move")];
|
||||
|
||||
const moveTargets = getMoveTargets(user, moveId);
|
||||
if (!moveTargets.targets.length) {
|
||||
@ -5000,7 +5013,7 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr {
|
||||
? moveTargets.targets
|
||||
: moveTargets.targets.indexOf(target.getBattlerIndex()) > -1
|
||||
? [ target.getBattlerIndex() ]
|
||||
: [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ];
|
||||
: [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length, undefined, "Randomly selecting a target")] ];
|
||||
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));
|
||||
initMoveAnim(user.scene, moveId).then(() => {
|
||||
@ -5183,7 +5196,7 @@ export class CopyMoveAttr extends OverrideMoveEffectAttr {
|
||||
? moveTargets.targets
|
||||
: moveTargets.targets.indexOf(target.getBattlerIndex()) > -1
|
||||
? [ target.getBattlerIndex() ]
|
||||
: [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length)] ];
|
||||
: [ moveTargets.targets[user.randSeedInt(moveTargets.targets.length, undefined, "Randomly selecting a target for the copied move")] ];
|
||||
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));
|
||||
@ -5818,7 +5831,7 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr {
|
||||
if (!validTypes.length) {
|
||||
return false;
|
||||
}
|
||||
const type = validTypes[user.randSeedInt(validTypes.length)];
|
||||
const type = validTypes[user.randSeedInt(validTypes.length, undefined, "Randomly selecting a type for Conversion2 that resists Type." + Utils.getEnumKeys(Type)[moveData.type])];
|
||||
user.summonData.types = [ type ];
|
||||
user.scene.queueMessage(i18next.t("battle:transformedIntoType", {pokemonName: getPokemonNameWithAffix(user), type: Utils.toReadableString(Type[type])}));
|
||||
user.updateInfo();
|
||||
@ -5871,7 +5884,7 @@ export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet {
|
||||
multiple = moveTarget !== MoveTarget.NEAR_ENEMY;
|
||||
break;
|
||||
case MoveTarget.RANDOM_NEAR_ENEMY:
|
||||
set = [ opponents[user.randSeedInt(opponents.length)] ];
|
||||
set = [ opponents[user.randSeedInt(opponents.length, undefined, "Randomly selecting an opponent to attack")] ];
|
||||
break;
|
||||
case MoveTarget.ATTACKER:
|
||||
return { targets: [ -1 as BattlerIndex ], multiple: false };
|
||||
|
@ -135,10 +135,13 @@ function findBest(scene: BattleScene, pokemon: EnemyPokemon, override?: boolean)
|
||||
var offset = 0
|
||||
scene.getModifiers(BypassSpeedChanceModifier, true).forEach(m => {
|
||||
//console.log(m, m.getPokemon(this.scene), pokemon)
|
||||
if (m.getPokemon(scene).isActive()) {
|
||||
console.log(m.getPokemon(scene).name + " has a Quick Claw")
|
||||
offset++
|
||||
}
|
||||
var p = m.getPokemon(scene)
|
||||
scene.getField().forEach((p2, idx) => {
|
||||
if (p == p2) {
|
||||
console.log(m.getPokemon(scene).name + " (Position: " + (idx + 1) + ") has a Quick Claw")
|
||||
offset++
|
||||
}
|
||||
})
|
||||
})
|
||||
scene.currentBattle.multiInt(scene, rolls, offset + 3, 65536)
|
||||
//console.log(rolls)
|
||||
|
@ -362,11 +362,17 @@ export default class FightUiHandler extends UiHandler {
|
||||
damage2.value = Math.floor(damage2.value / 2);
|
||||
}
|
||||
|
||||
const fixedDamage = new Utils.IntegerHolder(0);
|
||||
MoveData.applyMoveAttrs(MoveData.FixedDamageAttr, user, target, move, fixedDamage);
|
||||
if (!isTypeImmune && fixedDamage.value) {
|
||||
damage1.value = fixedDamage.value;
|
||||
damage2.value = fixedDamage.value;
|
||||
const fixedDamage1 = new Utils.IntegerHolder(0);
|
||||
const fixedDamage2 = new Utils.IntegerHolder(0);
|
||||
MoveData.applyMoveAttrs(MoveData.FixedDamageAttr, user, target, move, fixedDamage1, true, false);
|
||||
MoveData.applyMoveAttrs(MoveData.FixedDamageAttr, user, target, move, fixedDamage2, false, true);
|
||||
if (!isTypeImmune && fixedDamage1.value) {
|
||||
damage1.value = fixedDamage1.value;
|
||||
isCritical = false;
|
||||
result = HitResult.EFFECTIVE;
|
||||
}
|
||||
if (!isTypeImmune && fixedDamage2.value) {
|
||||
damage2.value = fixedDamage2.value;
|
||||
isCritical = false;
|
||||
result = HitResult.EFFECTIVE;
|
||||
}
|
||||
@ -392,13 +398,18 @@ export default class FightUiHandler extends UiHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (!fixedDamage.value) {
|
||||
if (!fixedDamage1.value) {
|
||||
if (!user.isPlayer()) {
|
||||
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage1);
|
||||
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage2);
|
||||
}
|
||||
if (!target.isPlayer()) {
|
||||
} else {
|
||||
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fixedDamage2.value) {
|
||||
if (!user.isPlayer()) {
|
||||
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage2);
|
||||
} else {
|
||||
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage2);
|
||||
}
|
||||
}
|
||||
@ -870,334 +881,4 @@ export default class FightUiHandler extends UiHandler {
|
||||
}
|
||||
this.cursorObj = null;
|
||||
}
|
||||
}
|
||||
|
||||
export function simulateAttack(scene: BattleScene, user: Pokemon, target: Pokemon, move: Move) {
|
||||
let result: HitResult;
|
||||
const damage1 = new Utils.NumberHolder(0);
|
||||
const damage2 = new Utils.NumberHolder(0);
|
||||
const defendingSidePlayField = target.isPlayer() ? scene.getPlayerField() : scene.getEnemyField();
|
||||
|
||||
const variableCategory = new Utils.IntegerHolder(move.category);
|
||||
MoveData.applyMoveAttrs(MoveData.VariableMoveCategoryAttr, user, target, move, variableCategory);
|
||||
const moveCategory = variableCategory.value as MoveData.MoveCategory;
|
||||
|
||||
const typeChangeMovePowerMultiplier = new Utils.NumberHolder(1);
|
||||
MoveData.applyMoveAttrs(MoveData.VariableMoveTypeAttr, user, target, move);
|
||||
applyPreAttackAbAttrs(MoveTypeChangeAttr, user, target, move, typeChangeMovePowerMultiplier);
|
||||
const types = target.getTypes(true, true);
|
||||
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
const typeless = move.hasAttr(MoveData.TypelessAttr);
|
||||
const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveData.MoveCategory.STATUS || move.getAttrs(MoveData.StatusMoveTypeImmunityAttr).find(attr => types.includes(attr.immuneType)))
|
||||
? target.getAttackTypeEffectiveness(move.type, user, false, false)
|
||||
: 1);
|
||||
MoveData.applyMoveAttrs(MoveData.VariableMoveTypeMultiplierAttr, user, target, move, typeMultiplier);
|
||||
if (typeless) {
|
||||
typeMultiplier.value = 1;
|
||||
}
|
||||
if (types.find(t => move.isTypeImmune(user, target, t))) {
|
||||
typeMultiplier.value = 0;
|
||||
}
|
||||
|
||||
// Apply arena tags for conditional protection
|
||||
if (!move.checkFlag(MoveData.MoveFlags.IGNORE_PROTECT, user, target) && !move.isAllyTarget()) {
|
||||
const defendingSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||
scene.arena.applyTagsForSide(ArenaTagType.QUICK_GUARD, defendingSide, cancelled, this, move.priority);
|
||||
scene.arena.applyTagsForSide(ArenaTagType.WIDE_GUARD, defendingSide, cancelled, this, move.moveTarget);
|
||||
scene.arena.applyTagsForSide(ArenaTagType.MAT_BLOCK, defendingSide, cancelled, this, move.category);
|
||||
scene.arena.applyTagsForSide(ArenaTagType.CRAFTY_SHIELD, defendingSide, cancelled, this, move.category, move.moveTarget);
|
||||
}
|
||||
|
||||
switch (moveCategory) {
|
||||
case MoveData.MoveCategory.PHYSICAL:
|
||||
case MoveData.MoveCategory.SPECIAL:
|
||||
const isPhysical = moveCategory === MoveData.MoveCategory.PHYSICAL;
|
||||
const power = new Utils.NumberHolder(move.power);
|
||||
const sourceTeraType = user.getTeraType();
|
||||
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type && power.value < 60 && move.priority <= 0 && !move.hasAttr(MoveData.MultiHitAttr) && !scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === user.id)) {
|
||||
power.value = 60;
|
||||
}
|
||||
applyPreAttackAbAttrs(VariableMovePowerAbAttr, user, target, move, power);
|
||||
|
||||
if (user.getAlly()?.hasAbilityWithAttr(AllyMoveCategoryPowerBoostAbAttr)) {
|
||||
applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, user, target, move, power);
|
||||
}
|
||||
|
||||
const fieldAuras = new Set(
|
||||
scene.getField(true)
|
||||
.map((p) => p.getAbilityAttrs(FieldMoveTypePowerBoostAbAttr) as FieldMoveTypePowerBoostAbAttr[])
|
||||
.flat(),
|
||||
);
|
||||
for (const aura of fieldAuras) {
|
||||
// The only relevant values are `move` and the `power` holder
|
||||
aura.applyPreAttack(null, null, null, move, [power]);
|
||||
}
|
||||
|
||||
const alliedField: Pokemon[] = user instanceof PlayerPokemon ? scene.getPlayerField() : scene.getEnemyField();
|
||||
alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, user, move, power));
|
||||
|
||||
power.value *= typeChangeMovePowerMultiplier.value;
|
||||
|
||||
if (!typeless) {
|
||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, user, target, move, cancelled, typeMultiplier);
|
||||
MoveData.applyMoveAttrs(MoveData.NeutralDamageAgainstFlyingTypeMultiplierAttr, user, target, move, typeMultiplier);
|
||||
}
|
||||
if (!cancelled.value) {
|
||||
applyPreDefendAbAttrs(MoveImmunityAbAttr, user, target, move, cancelled, typeMultiplier);
|
||||
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, user, move, cancelled, typeMultiplier));
|
||||
}
|
||||
|
||||
if (cancelled.value) {
|
||||
//user.stopMultiHit(target);
|
||||
result = HitResult.NO_EFFECT;
|
||||
} else {
|
||||
const typeBoost = user.findTag(t => t instanceof TypeBoostTag && t.boostedType === move.type) as TypeBoostTag;
|
||||
if (typeBoost) {
|
||||
power.value *= typeBoost.boostValue;
|
||||
if (typeBoost.oneUse) {
|
||||
//user.removeTag(typeBoost.tagType);
|
||||
}
|
||||
}
|
||||
const arenaAttackTypeMultiplier = new Utils.NumberHolder(scene.arena.getAttackTypeMultiplier(move.type, user.isGrounded()));
|
||||
MoveData.applyMoveAttrs(MoveData.IgnoreWeatherTypeDebuffAttr, user, target, move, arenaAttackTypeMultiplier);
|
||||
if (scene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() && move.type === Type.GROUND && move.moveTarget === MoveData.MoveTarget.ALL_NEAR_OTHERS) {
|
||||
power.value /= 2;
|
||||
}
|
||||
|
||||
MoveData.applyMoveAttrs(MoveData.VariablePowerAttr, user, target, move, power);
|
||||
|
||||
scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, new Utils.IntegerHolder(0), power);
|
||||
if (!typeless) {
|
||||
scene.arena.applyTags(WeakenMoveTypeTag, move.type, power);
|
||||
scene.applyModifiers(AttackTypeBoosterModifier, user.isPlayer(), user, move.type, power);
|
||||
}
|
||||
if (user.getTag(HelpingHandTag)) {
|
||||
power.value *= 1.5;
|
||||
}
|
||||
let isCritical: boolean = true;
|
||||
const critOnly = new Utils.BooleanHolder(false);
|
||||
const critAlways = user.getTag(BattlerTagType.ALWAYS_CRIT);
|
||||
MoveData.applyMoveAttrs(MoveData.CritOnlyAttr, user, target, move, critOnly);
|
||||
applyAbAttrs(ConditionalCritAbAttr, user, null, critOnly, target, move);
|
||||
if (isCritical) {
|
||||
const blockCrit = new Utils.BooleanHolder(false);
|
||||
applyAbAttrs(BlockCritAbAttr, target, null, blockCrit);
|
||||
if (blockCrit.value) {
|
||||
isCritical = false;
|
||||
}
|
||||
}
|
||||
const sourceAtk = new Utils.IntegerHolder(user.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, target, null, false));
|
||||
const targetDef = new Utils.IntegerHolder(target.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, user, move, false));
|
||||
const sourceAtkCrit = new Utils.IntegerHolder(user.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, target, null, isCritical));
|
||||
const targetDefCrit = new Utils.IntegerHolder(target.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, user, move, isCritical));
|
||||
const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1);
|
||||
applyAbAttrs(MultCritAbAttr, user, null, criticalMultiplier);
|
||||
const screenMultiplier = new Utils.NumberHolder(1);
|
||||
if (!isCritical) {
|
||||
scene.arena.applyTagsForSide(WeakenMoveScreenTag, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, move.category, scene.currentBattle.double, screenMultiplier);
|
||||
}
|
||||
const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier.value) === 0;
|
||||
const sourceTypes = user.getTypes();
|
||||
const matchesSourceType = sourceTypes[0] === move.type || (sourceTypes.length > 1 && sourceTypes[1] === move.type);
|
||||
const stabMultiplier = new Utils.NumberHolder(1);
|
||||
if (sourceTeraType === Type.UNKNOWN && matchesSourceType) {
|
||||
stabMultiplier.value += 0.5;
|
||||
} else if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type) {
|
||||
stabMultiplier.value += 0.5;
|
||||
}
|
||||
|
||||
applyAbAttrs(StabBoostAbAttr, user, null, stabMultiplier);
|
||||
|
||||
if (sourceTeraType !== Type.UNKNOWN && matchesSourceType) {
|
||||
stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25);
|
||||
}
|
||||
|
||||
MoveData.applyMoveAttrs(MoveData.VariableAtkAttr, user, target, move, sourceAtk);
|
||||
MoveData.applyMoveAttrs(MoveData.VariableDefAttr, user, target, move, targetDef);
|
||||
MoveData.applyMoveAttrs(MoveData.VariableAtkAttr, user, target, move, sourceAtkCrit);
|
||||
MoveData.applyMoveAttrs(MoveData.VariableDefAttr, user, target, move, targetDefCrit);
|
||||
|
||||
const effectPhase = scene.getCurrentPhase();
|
||||
let numTargets = 1;
|
||||
if (effectPhase instanceof MoveEffectPhase) {
|
||||
numTargets = effectPhase.getTargets().length;
|
||||
}
|
||||
const twoStrikeMultiplier = new Utils.NumberHolder(1);
|
||||
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, target, move, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier);
|
||||
|
||||
if (!isTypeImmune) {
|
||||
damage1.value = Math.ceil(((((2 * user.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * twoStrikeMultiplier.value * 0.85); // low roll
|
||||
damage2.value = Math.ceil(((((2 * user.level / 5 + 2) * power.value * sourceAtkCrit.value / targetDefCrit.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * twoStrikeMultiplier.value * criticalMultiplier.value); // high roll crit
|
||||
if (isPhysical && user.status && user.status.effect === StatusEffect.BURN) {
|
||||
if (!move.hasAttr(MoveData.BypassBurnDamageReductionAttr)) {
|
||||
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
|
||||
applyAbAttrs(BypassBurnDamageReductionAbAttr, user, burnDamageReductionCancelled);
|
||||
if (!burnDamageReductionCancelled.value) {
|
||||
damage1.value = Math.floor(damage1.value / 2);
|
||||
damage2.value = Math.floor(damage2.value / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyPreAttackAbAttrs(DamageBoostAbAttr, user, target, move, damage1);
|
||||
applyPreAttackAbAttrs(DamageBoostAbAttr, user, target, move, damage2);
|
||||
|
||||
/**
|
||||
* For each {@link HitsTagAttr} the move has, doubles the damage of the move if:
|
||||
* The target has a {@link BattlerTagType} that this move interacts with
|
||||
* AND
|
||||
* The move doubles damage when used against that tag
|
||||
*/
|
||||
move.getAttrs(MoveData.HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
|
||||
if (target.getTag(hta.tagType)) {
|
||||
damage1.value *= 2;
|
||||
damage2.value *= 2;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (scene.arena.terrain?.terrainType === TerrainType.MISTY && target.isGrounded() && move.type === Type.DRAGON) {
|
||||
damage1.value = Math.floor(damage1.value / 2);
|
||||
damage2.value = Math.floor(damage2.value / 2);
|
||||
}
|
||||
|
||||
const fixedDamage = new Utils.IntegerHolder(0);
|
||||
MoveData.applyMoveAttrs(MoveData.FixedDamageAttr, user, target, move, fixedDamage);
|
||||
if (!isTypeImmune && fixedDamage.value) {
|
||||
damage1.value = fixedDamage.value;
|
||||
damage2.value = fixedDamage.value;
|
||||
isCritical = false;
|
||||
result = HitResult.EFFECTIVE;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
if (!typeMultiplier.value) {
|
||||
result = move.id === Moves.SHEER_COLD ? HitResult.IMMUNE : HitResult.NO_EFFECT;
|
||||
} else {
|
||||
const oneHitKo = new Utils.BooleanHolder(false);
|
||||
MoveData.applyMoveAttrs(MoveData.OneHitKOAttr, user, target, move, oneHitKo);
|
||||
if (oneHitKo.value) {
|
||||
result = HitResult.ONE_HIT_KO;
|
||||
isCritical = false;
|
||||
damage1.value = target.hp;
|
||||
damage2.value = target.hp;
|
||||
} else if (typeMultiplier.value >= 2) {
|
||||
result = HitResult.SUPER_EFFECTIVE;
|
||||
} else if (typeMultiplier.value >= 1) {
|
||||
result = HitResult.EFFECTIVE;
|
||||
} else {
|
||||
result = HitResult.NOT_VERY_EFFECTIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fixedDamage.value) {
|
||||
if (!user.isPlayer()) {
|
||||
scene.applyModifiers(EnemyDamageBoosterModifier, false, damage1);
|
||||
scene.applyModifiers(EnemyDamageBoosterModifier, false, damage2);
|
||||
}
|
||||
if (!target.isPlayer()) {
|
||||
scene.applyModifiers(EnemyDamageReducerModifier, false, damage1);
|
||||
scene.applyModifiers(EnemyDamageReducerModifier, false, damage2);
|
||||
}
|
||||
}
|
||||
|
||||
MoveData.applyMoveAttrs(MoveData.ModifiedDamageAttr, user, target, move, damage1);
|
||||
MoveData.applyMoveAttrs(MoveData.ModifiedDamageAttr, user, target, move, damage2);
|
||||
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, user, target, move, cancelled, damage1);
|
||||
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, user, target, move, cancelled, damage2);
|
||||
const destinyTag = target.getTag(BattlerTagType.DESTINY_BOND);
|
||||
|
||||
const oneHitKo = result === HitResult.ONE_HIT_KO;
|
||||
if (damage1.value) {
|
||||
if (target.getHpRatio() === 1) {
|
||||
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, target, user, move, cancelled, damage1);
|
||||
}
|
||||
}
|
||||
if (damage2.value) {
|
||||
if (target.getHpRatio() === 1) {
|
||||
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, target, user, move, cancelled, damage2);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MoveData.MoveCategory.STATUS:
|
||||
if (!typeless) {
|
||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, target, user, move, cancelled, typeMultiplier);
|
||||
}
|
||||
if (!cancelled.value) {
|
||||
applyPreDefendAbAttrs(MoveImmunityAbAttr, target, user, move, cancelled, typeMultiplier);
|
||||
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, user, move, cancelled, typeMultiplier));
|
||||
}
|
||||
if (!typeMultiplier.value) {
|
||||
return -1
|
||||
}
|
||||
result = cancelled.value || !typeMultiplier.value ? HitResult.NO_EFFECT : HitResult.STATUS;
|
||||
break;
|
||||
}
|
||||
return [damage1.value, damage2.value]
|
||||
}
|
||||
|
||||
export function calcDamage(scene: BattleScene, user: PlayerPokemon, target: Pokemon, move: PokemonMove) {
|
||||
var dmgHigh = 0
|
||||
var dmgLow = 0
|
||||
// dmgLow = (((2*user.level/5 + 2) * power * myAtk / theirDef)/50 + 2) * 0.85 * modifiers
|
||||
// dmgHigh = (((2*user.level/5 + 2) * power * myAtkC / theirDefC)/50 + 2) * 1.5 * modifiers
|
||||
var out = this.simulateAttack(scene, user, target, move.getMove())
|
||||
var minHits = 1
|
||||
var maxHits = 1
|
||||
var mh = move.getMove().getAttrs(MoveData.MultiHitAttr)
|
||||
for (var i = 0; i < mh.length; i++) {
|
||||
var mh2 = mh[i] as MoveData.MultiHitAttr
|
||||
switch (mh2.multiHitType) {
|
||||
case MoveData.MultiHitType._2:
|
||||
minHits = 2;
|
||||
maxHits = 2;
|
||||
case MoveData.MultiHitType._2_TO_5:
|
||||
minHits = 2;
|
||||
maxHits = 5;
|
||||
case MoveData.MultiHitType._3:
|
||||
minHits = 3;
|
||||
maxHits = 3;
|
||||
case MoveData.MultiHitType._10:
|
||||
minHits = 10;
|
||||
maxHits = 10;
|
||||
case MoveData.MultiHitType.BEAT_UP:
|
||||
const party = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
|
||||
// No status means the ally pokemon can contribute to Beat Up
|
||||
minHits = party.reduce((total, pokemon) => {
|
||||
return total + (pokemon.id === user.id ? 1 : pokemon?.status && pokemon.status.effect !== StatusEffect.NONE ? 0 : 1);
|
||||
}, 0);
|
||||
maxHits = minHits
|
||||
}
|
||||
}
|
||||
var h = user.getHeldItems()
|
||||
for (var i = 0; i < h.length; i++) {
|
||||
if (h[i].type instanceof PokemonMultiHitModifierType) {
|
||||
minHits += h[i].getStackCount()
|
||||
maxHits += h[i].getStackCount()
|
||||
}
|
||||
}
|
||||
dmgLow = out[0] * minHits
|
||||
dmgHigh = out[1] * maxHits
|
||||
/*
|
||||
if (user.hasAbility(Abilities.PARENTAL_BOND)) {
|
||||
// Second hit deals 0.25x damage
|
||||
dmgLow *= 1.25
|
||||
dmgHigh *= 1.25
|
||||
}
|
||||
*/
|
||||
var koText = ""
|
||||
if (Math.floor(dmgLow) >= target.hp) {
|
||||
koText = " (KO)"
|
||||
} else if (Math.ceil(dmgHigh) >= target.hp) {
|
||||
var percentChance = (target.hp - dmgLow + 1) / (dmgHigh - dmgLow + 1)
|
||||
koText = " (" + Math.round(percentChance * 100) + "% KO)"
|
||||
}
|
||||
return (Math.round(dmgLow) == Math.round(dmgHigh) ? Math.round(dmgLow).toString() : Math.round(dmgLow) + "-" + Math.round(dmgHigh)) + koText
|
||||
dmgLow = Math.round((dmgLow)/target.getBattleStat(Stat.HP)*100)
|
||||
dmgHigh = Math.round((dmgHigh)/target.getBattleStat(Stat.HP)*100)
|
||||
return (dmgLow == dmgHigh ? dmgLow + "%" : dmgLow + "%-" + dmgHigh + "%") + koText
|
||||
return "???"
|
||||
}
|
Loading…
Reference in New Issue
Block a user