mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-25 08:49:32 +02:00
Big updates
Reworked system for logging the player's actions Updated function that calculates damage to use the new functions for base and applied damage calcs, and made new functions for checking critical hits and calculating how much damage you'll do to a Boss Damage calculations now show even if you have type hints off (the multiplier won't show), as long as you have the calc itself enabled Removed luck tracking, as it is no longer necessary Left eviolite logging in there, but removed eviolite checks Players no longer have Eviolite force-disabled
This commit is contained in:
parent
0f904a807c
commit
36a97b9510
@ -5259,7 +5259,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
||||
}
|
||||
|
||||
isBatonPass() {
|
||||
return this.switchType === SwitchType.BATON_PASS;
|
||||
return this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.MID_TURN_BATON_PASS;
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
@ -5277,7 +5277,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
||||
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
||||
|
||||
if (switchOutTarget.hp > 0) {
|
||||
user.scene.prependToPhase(new SwitchPhase(user.scene, this.switchType, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase);
|
||||
user.scene.prependToPhase(new SwitchPhase(user.scene, this.switchType == SwitchType.SHED_TAIL ? SwitchType.SHED_TAIL : (this.switchType == SwitchType.BATON_PASS ? SwitchType.MID_TURN_BATON_PASS : SwitchType.MID_TURN_SWITCH), switchOutTarget.getFieldIndex(), true, true), MoveEndPhase);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -5,8 +5,25 @@
|
||||
export enum SwitchType {
|
||||
/** Basic switchout where the Pokemon to switch in is selected */
|
||||
SWITCH,
|
||||
/** Basic switchout where the Pokemon to switch in is selected
|
||||
*
|
||||
* This type is called outside of CommandPhase, and needs its own separate action log
|
||||
*/
|
||||
MID_TURN_SWITCH,
|
||||
/** Transfers stat stages and other effects from the returning Pokemon to the switched in Pokemon */
|
||||
BATON_PASS,
|
||||
/** Transfers the returning Pokemon's Substitute to the switched in Pokemon */
|
||||
SHED_TAIL
|
||||
/** Transfers stat stages and other effects from the returning Pokemon to the switched in Pokemon
|
||||
*
|
||||
* This type is called outside of CommandPhase, and needs its own separate action log
|
||||
*/
|
||||
MID_TURN_BATON_PASS,
|
||||
/** Transfers the returning Pokemon's Substitute to the switched in Pokemon
|
||||
*
|
||||
* This type is called outside of CommandPhase, and needs its own separate action log
|
||||
*/
|
||||
SHED_TAIL,
|
||||
/** Basic switchout, but occurring outside of battle */
|
||||
PRE_SWITCH,
|
||||
/** Basic switchout, but occurring outside of battle */
|
||||
PRE_BATON_PASS,
|
||||
}
|
||||
|
@ -2169,6 +2169,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
this.scene.triggerPokemonFormChange(this, SpeciesFormChangeMoveLearnedTrigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates selecting a move.
|
||||
* @param moveIndex The move's position in the Pokémon's move pool.
|
||||
* @param ignorePp If `true`, having 0 PP will not make the move be considered invalid.
|
||||
* @returns Whether the player can successfully choose this move.
|
||||
*/
|
||||
trySelectMove(moveIndex: integer, ignorePp?: boolean): boolean {
|
||||
const move = this.getMoveset().length > moveIndex
|
||||
? this.getMoveset()[moveIndex]
|
||||
@ -2406,9 +2412,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* @param ignoreSourceAbility if `true`, ignore's the attacking Pokemon's ability effects (defaults to `false`).
|
||||
* @param isCritical if `true`, calculates effective stats as if the hit were critical (defaults to `false`).
|
||||
* @param simulated if `true`, suppresses changes to game state during calculation (defaults to `true`).
|
||||
* @param isMin if `true`, returns the minimum damage (random power returns lowest possible, crits fail unless guaranteed) (defaults to `false`, overrides `isMax`).
|
||||
* @param isMax if `true`, returns the maximum damage (random power returns highest possible, crits guaranteed unless immune) (defaults to `false`).
|
||||
* @returns The move's base damage against this Pokemon when used by the source Pokemon.
|
||||
*/
|
||||
getBaseDamage(source: Pokemon, move: Move, moveCategory: MoveCategory, ignoreAbility: boolean = false, ignoreSourceAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true): number {
|
||||
getBaseDamage(source: Pokemon, move: Move, moveCategory: MoveCategory, ignoreAbility: boolean = false, ignoreSourceAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true, isMin: boolean = false, isMax: boolean = false): number {
|
||||
if (isMin) isMax = false; // If attempting return both minimum and maximum at once, return minimum
|
||||
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
||||
|
||||
/** A base damage multiplier based on the source's level */
|
||||
@ -2458,7 +2467,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* - `result`: {@linkcode HitResult} indicates the attack's type effectiveness.
|
||||
* - `damage`: `number` the attack's final damage output.
|
||||
*/
|
||||
getAttackDamage(source: Pokemon, move: Move, ignoreAbility: boolean = false, ignoreSourceAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true): DamageCalculationResult {
|
||||
getAttackDamage(source: Pokemon, move: Move, ignoreAbility: boolean = false, ignoreSourceAbility: boolean = false, isCritical: CritResult, simulated: boolean = true): DamageCalculationResult {
|
||||
const damage = new Utils.NumberHolder(0);
|
||||
const damageMin = new Utils.NumberHolder(0);
|
||||
const damageMax = new Utils.NumberHolder(0);
|
||||
@ -2496,7 +2505,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return {
|
||||
cancelled: cancelled.value,
|
||||
result: move.id === Moves.SHEER_COLD ? HitResult.IMMUNE : HitResult.NO_EFFECT,
|
||||
damage: 0
|
||||
damage: 0,
|
||||
damageHigh: 0,
|
||||
damageLow: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@ -2507,7 +2518,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return {
|
||||
cancelled: false,
|
||||
result: HitResult.EFFECTIVE,
|
||||
damage: fixedDamage.value
|
||||
damage: fixedDamage.value,
|
||||
damageHigh: fixedDamage.value,
|
||||
damageLow: fixedDamage.value,
|
||||
};
|
||||
}
|
||||
|
||||
@ -2518,7 +2531,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return {
|
||||
cancelled: false,
|
||||
result: HitResult.ONE_HIT_KO,
|
||||
damage: this.hp
|
||||
damage: this.hp,
|
||||
damageHigh: this.hp,
|
||||
damageLow: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@ -2526,7 +2541,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* The attack's base damage, as determined by the source's level, move power
|
||||
* and Attack stat as well as this Pokemon's Defense stat
|
||||
*/
|
||||
const baseDamage = this.getBaseDamage(source, move, moveCategory, ignoreAbility, ignoreSourceAbility, isCritical, simulated);
|
||||
const baseDamage = this.getBaseDamage(source, move, moveCategory, ignoreAbility, ignoreSourceAbility, isCritical.isCrit, simulated);
|
||||
/** The minimum possible base damage, assuming the move always fails to crit, unless some effect guarantees a Critical Hit */
|
||||
const baseDamageMin = this.getBaseDamage(source, move, moveCategory, ignoreAbility, ignoreSourceAbility, isCritical.alwaysCrit, simulated, true);
|
||||
/** The maximum possible base damage, assuming the move always crits, unless some effect makes the opponent immune to Critical Hits */
|
||||
const baseDamageMax = this.getBaseDamage(source, move, moveCategory, ignoreAbility, ignoreSourceAbility, isCritical.canCrit, simulated, false, true);
|
||||
|
||||
/** 25% damage debuff on moves hitting more than one non-fainted target (regardless of immunities) */
|
||||
const { targets, multiple } = getMoveTargets(source, move.id);
|
||||
@ -2546,7 +2565,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
/** The damage multiplier when the given move critically hits */
|
||||
const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1);
|
||||
const criticalMultiplier = new Utils.NumberHolder(1.5);
|
||||
applyAbAttrs(MultCritAbAttr, source, null, simulated, criticalMultiplier);
|
||||
|
||||
/**
|
||||
@ -2609,47 +2628,71 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
? 0.5
|
||||
: 1;
|
||||
|
||||
damage.value = Utils.toDmgValue(
|
||||
baseDamage
|
||||
damage.value = 1
|
||||
* targetMultiplier
|
||||
* parentalBondMultiplier.value
|
||||
* arenaAttackTypeMultiplier.value
|
||||
* glaiveRushMultiplier.value
|
||||
* criticalMultiplier.value
|
||||
* randomMultiplier
|
||||
* stabMultiplier.value
|
||||
* typeMultiplier
|
||||
* burnMultiplier.value
|
||||
* screenMultiplier.value
|
||||
* hitsTagMultiplier.value
|
||||
* mistyTerrainMultiplier
|
||||
);
|
||||
* mistyTerrainMultiplier;
|
||||
|
||||
damageMin.value = damage.value
|
||||
damageMax.value = damage.value
|
||||
|
||||
// Base Damage
|
||||
damage.value *= baseDamage
|
||||
damageMin.value *= baseDamageMin
|
||||
damageMax.value *= baseDamageMax
|
||||
|
||||
// Critical Hit
|
||||
damage.value *= isCritical.isCrit ? criticalMultiplier.value : 1 // Applies crit multiplier if the attack was a Critical Hit
|
||||
damageMin.value *= isCritical.alwaysCrit ? criticalMultiplier.value : 1 // Applies crit multiplier to minimum damage if the attack is guaranteed to crit
|
||||
damageMax.value *= isCritical.canCrit ? criticalMultiplier.value : 1 // Applies crit multiplier to minimum damage if it is possible for the move to crit
|
||||
|
||||
// Random Damage
|
||||
damage.value *= randomMultiplier // Random value between 85% and 100%, or 100% if Simulated
|
||||
damageMin.value *= 0.85 // Lowest possible roll
|
||||
damageMax.value *= 1 // Highest possible roll
|
||||
|
||||
damage.value = Utils.toDmgValue(damage.value)
|
||||
damageMin.value = Utils.toDmgValue(damageMin.value)
|
||||
damageMax.value = Utils.toDmgValue(damageMax.value)
|
||||
|
||||
var damageMultiplier = new Utils.NumberHolder(1); // General multiplier, applied to all three damage calcs (uses a NumberHolder so we don't apply abilities 3 times)
|
||||
|
||||
/** Doubles damage if the attacker has Tinted Lens and is using a resisted move */
|
||||
if (!ignoreSourceAbility) {
|
||||
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, simulated, damage);
|
||||
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, simulated, damageMultiplier);
|
||||
}
|
||||
|
||||
/** Apply the enemy's Damage and Resistance tokens */
|
||||
if (!source.isPlayer()) {
|
||||
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage);
|
||||
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damageMultiplier);
|
||||
}
|
||||
if (!this.isPlayer()) {
|
||||
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage);
|
||||
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damageMultiplier);
|
||||
}
|
||||
|
||||
/** Apply this Pokemon's post-calc defensive modifiers (e.g. Fur Coat) */
|
||||
if (!ignoreAbility) {
|
||||
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, simulated, damage);
|
||||
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, simulated, damageMultiplier);
|
||||
}
|
||||
|
||||
// This attribute may modify damage arbitrarily, so be careful about changing its order of application.
|
||||
applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage);
|
||||
|
||||
if (this.isFullHp() && !ignoreAbility) {
|
||||
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage);
|
||||
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, simulated, damageMultiplier);
|
||||
}
|
||||
|
||||
damage.value *= damageMultiplier.value
|
||||
damageMin.value *= damageMultiplier.value
|
||||
damageMax.value *= damageMultiplier.value
|
||||
|
||||
// debug message for when damage is applied (i.e. not simulated)
|
||||
if (!simulated) {
|
||||
console.log("damage", damage.value, move.name);
|
||||
@ -2667,10 +2710,54 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return {
|
||||
cancelled: cancelled.value,
|
||||
result: hitResult,
|
||||
damage: damage.value
|
||||
damage: damage.value,
|
||||
damageLow: damageMin.value,
|
||||
damageHigh: damageMax.value,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to set a move as a Critical Hit.
|
||||
* @param source The attacking Pokémon.
|
||||
* @param move The move that `source` is using against this Pokémon.
|
||||
* @param simulated Suppresses changes to game state during the calculation (Defaults to `false`).
|
||||
* @returns Whether the move was a Critical Hit, whether it could be at all, and whether it was guaranteed to be one.
|
||||
*/
|
||||
tryCriticalHit(source: Pokemon, move: Move, simulated: boolean = false): CritResult {
|
||||
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||
|
||||
let isCritical: boolean;
|
||||
let canCrit = true;
|
||||
let alwaysCrit = false;
|
||||
|
||||
// Calculates whether the move was a Critical Hit or not
|
||||
const critOnly = new Utils.BooleanHolder(false);
|
||||
const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT);
|
||||
applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly);
|
||||
applyAbAttrs(ConditionalCritAbAttr, source, null, false, critOnly, this, move);
|
||||
if (critOnly.value || critAlways) {
|
||||
isCritical = true;
|
||||
alwaysCrit = true;
|
||||
} else {
|
||||
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))];
|
||||
isCritical = simulated ? false : (critChance === 1 || !this.scene.randBattleSeedInt(critChance));
|
||||
}
|
||||
|
||||
// Determines if this Pokémon (the target) is immune to Critical Hits, and if so, set isCritical to false
|
||||
const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide);
|
||||
const blockCrit = new Utils.BooleanHolder(false);
|
||||
applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit);
|
||||
if (noCritTag || blockCrit.value || Overrides.NEVER_CRIT_OVERRIDE) {
|
||||
isCritical = false;
|
||||
canCrit = false;
|
||||
}
|
||||
return {
|
||||
isCrit: isCritical,
|
||||
canCrit: canCrit,
|
||||
alwaysCrit: alwaysCrit
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the results of a move to this pokemon
|
||||
* @param source The {@linkcode Pokemon} using the move
|
||||
@ -2689,26 +2776,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return (typeMultiplier === 0) ? HitResult.NO_EFFECT : HitResult.STATUS;
|
||||
} else {
|
||||
/** Determines whether the attack critically hits */
|
||||
let isCritical: boolean;
|
||||
const critOnly = new Utils.BooleanHolder(false);
|
||||
const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT);
|
||||
applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly);
|
||||
applyAbAttrs(ConditionalCritAbAttr, source, null, false, critOnly, this, move);
|
||||
if (critOnly.value || critAlways) {
|
||||
isCritical = true;
|
||||
} else {
|
||||
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))];
|
||||
isCritical = critChance === 1 || !this.scene.randBattleSeedInt(critChance);
|
||||
}
|
||||
let critical: CritResult = this.tryCriticalHit(source, move);
|
||||
|
||||
const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide);
|
||||
const blockCrit = new Utils.BooleanHolder(false);
|
||||
applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit);
|
||||
if (noCritTag || blockCrit.value || Overrides.NEVER_CRIT_OVERRIDE) {
|
||||
isCritical = false;
|
||||
}
|
||||
|
||||
const { cancelled, result, damage: dmg } = this.getAttackDamage(source, move, false, false, isCritical, false);
|
||||
const { cancelled, result, damage: dmg } = this.getAttackDamage(source, move, false, false, critical, false);
|
||||
|
||||
const typeBoost = source.findTag(t => t instanceof TypeBoostTag && t.boostedType === source.getMoveType(move)) as TypeBoostTag;
|
||||
if (typeBoost?.oneUse) {
|
||||
@ -2749,7 +2819,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* We explicitly require to ignore the faint phase here, as we want to show the messages
|
||||
* about the critical hit and the super effective/not very effective messages before the faint phase.
|
||||
*/
|
||||
const damage = this.damageAndUpdate(isBlockedBySubstitute ? 0 : dmg, result as DamageResult, isCritical, isOneHitKo, isOneHitKo, true);
|
||||
const damage = this.damageAndUpdate(isBlockedBySubstitute ? 0 : dmg, result as DamageResult, critical.isCrit, isOneHitKo, isOneHitKo, true);
|
||||
|
||||
if (damage > 0) {
|
||||
if (source.isPlayer()) {
|
||||
@ -2762,7 +2832,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
source.turnData.currDamageDealt = damage;
|
||||
this.turnData.damageTaken += damage;
|
||||
this.battleData.hitCount++;
|
||||
const attackResult = { move: move.id, result: result as DamageResult, damage: damage, critical: isCritical, sourceId: source.id, sourceBattlerIndex: source.getBattlerIndex() };
|
||||
const attackResult = { move: move.id, result: result as DamageResult, damage: damage, critical: critical.isCrit, sourceId: source.id, sourceBattlerIndex: source.getBattlerIndex() };
|
||||
this.turnData.attacksReceived.unshift(attackResult);
|
||||
if (source.isPlayer() && !this.isPlayer()) {
|
||||
this.scene.applyModifiers(DamageMoneyRewardModifier, true, source, new Utils.NumberHolder(damage));
|
||||
@ -2770,7 +2840,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
}
|
||||
|
||||
if (isCritical) {
|
||||
if (critical.isCrit) {
|
||||
this.scene.queueMessage(i18next.t("battle:hitResultCriticalHit"));
|
||||
}
|
||||
|
||||
@ -3937,6 +4007,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
}
|
||||
|
||||
export interface CritResult {
|
||||
/** Whether the move was a Critical Hit */
|
||||
isCrit: boolean;
|
||||
/** Whether the opponent can receive Critical Hits */
|
||||
canCrit: boolean;
|
||||
/** Whether the target is guaranteed to land a Critical Hit with their move */
|
||||
alwaysCrit: boolean
|
||||
}
|
||||
|
||||
export default interface Pokemon {
|
||||
scene: BattleScene
|
||||
}
|
||||
@ -4590,7 +4669,7 @@ export class EnemyPokemon extends Pokemon {
|
||||
return move.category !== MoveCategory.STATUS
|
||||
&& moveTargets.some(p => {
|
||||
const doesNotFail = move.applyConditions(this, p, move) || [Moves.SUCKER_PUNCH, Moves.UPPER_HAND, Moves.THUNDERCLAP].includes(move.id);
|
||||
return doesNotFail && p.getAttackDamage(this, move, !p.battleData.abilityRevealed, false, isCritical).damage >= p.hp;
|
||||
return doesNotFail && p.getAttackDamage(this, move, !p.battleData.abilityRevealed, false, {isCrit: isCritical, canCrit: true, alwaysCrit: isCritical}).damage >= p.hp;
|
||||
});
|
||||
}, this);
|
||||
|
||||
@ -4812,6 +4891,72 @@ export class EnemyPokemon extends Pokemon {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates how much damage an attack will actually do to a Boss Pokémon.
|
||||
*
|
||||
* Returns the normal damage if this Pokémon is not a Boss Pokémon.
|
||||
* @param damage The damage dealt to the Pokémon
|
||||
* @returns The actual amount of damage the Pokémon receieves
|
||||
*/
|
||||
calculateBossDamage(damage: integer): integer {
|
||||
if (this.isBoss()) {
|
||||
const segmentSize = this.getMaxHp() / this.bossSegments;
|
||||
for (let s = this.bossSegmentIndex; s > 0; s--) {
|
||||
const hpThreshold = segmentSize * s;
|
||||
const roundedHpThreshold = Math.round(hpThreshold);
|
||||
if (this.hp >= roundedHpThreshold) {
|
||||
if (this.hp - damage <= roundedHpThreshold) {
|
||||
const hpRemainder = this.hp - roundedHpThreshold;
|
||||
let segmentsBypassed = 0;
|
||||
while (segmentsBypassed < this.bossSegmentIndex && this.canBypassBossSegments(segmentsBypassed + 1) && (damage - hpRemainder) >= Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1))) {
|
||||
segmentsBypassed++;
|
||||
//console.log('damage', damage, 'segment', segmentsBypassed + 1, 'segment size', segmentSize, 'damage needed', Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1)));
|
||||
}
|
||||
|
||||
damage = Utils.toDmgValue(this.hp - hpThreshold + segmentSize * segmentsBypassed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates how many shields will be broken by an attack.
|
||||
*
|
||||
* Returns 0 if this Pokémon is not a Boss Pokémon.
|
||||
* @param damage
|
||||
* @returns
|
||||
*/
|
||||
calculateBossClearedShields(damage: integer): integer {
|
||||
let clearedBossSegmentIndex = this.isBoss()
|
||||
? this.bossSegmentIndex + 1
|
||||
: 0;
|
||||
if (this.isBoss()) {
|
||||
const segmentSize = this.getMaxHp() / this.bossSegments;
|
||||
for (let s = this.bossSegmentIndex; s > 0; s--) {
|
||||
const hpThreshold = segmentSize * s;
|
||||
const roundedHpThreshold = Math.round(hpThreshold);
|
||||
if (this.hp >= roundedHpThreshold) {
|
||||
if (this.hp - damage <= roundedHpThreshold) {
|
||||
const hpRemainder = this.hp - roundedHpThreshold;
|
||||
let segmentsBypassed = 0;
|
||||
while (segmentsBypassed < this.bossSegmentIndex && this.canBypassBossSegments(segmentsBypassed + 1) && (damage - hpRemainder) >= Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1))) {
|
||||
segmentsBypassed++;
|
||||
//console.log('damage', damage, 'segment', segmentsBypassed + 1, 'segment size', segmentSize, 'damage needed', Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1)));
|
||||
}
|
||||
|
||||
damage = Utils.toDmgValue(this.hp - hpThreshold + segmentSize * segmentsBypassed);
|
||||
clearedBossSegmentIndex - (s - segmentsBypassed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return clearedBossSegmentIndex;
|
||||
}
|
||||
|
||||
damage(damage: integer, ignoreSegments: boolean = false, preventEndure: boolean = false, ignoreFaintPhase: boolean = false): integer {
|
||||
if (this.isFainted()) {
|
||||
return 0;
|
||||
@ -5097,6 +5242,8 @@ export interface DamageCalculationResult {
|
||||
result: HitResult;
|
||||
/** The damage dealt by the move */
|
||||
damage: number;
|
||||
damageLow: number;
|
||||
damageHigh: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1819,361 +1819,6 @@ export function setEvioliteOverride(v: string) {
|
||||
evioliteOverride = v;
|
||||
}
|
||||
|
||||
export function calculateItemConditions(party: Pokemon[], log?: boolean, showAll?: boolean) {
|
||||
let total_common = 0
|
||||
let total_great = 0
|
||||
let total_ultra = 0
|
||||
let total_rogue = 0
|
||||
let total_master = 0
|
||||
let items: string[][] = [[], [], [], [], []]
|
||||
if (!hasMaximumBalls(party, PokeballType.POKEBALL)) {
|
||||
items[0].push(`Poké Ball (6)`)
|
||||
total_common += 6
|
||||
}
|
||||
items[0].push(`Rare Candy (2)`)
|
||||
total_common += 2
|
||||
var potion = Math.min(party.filter(p => (p.getInverseHp() >= 10 || p.getHpRatio() <= 0.875) && !p.isFainted()).length, 3)
|
||||
if (potion > 0) {
|
||||
items[0].push(`Potion (${potion * 3})`)
|
||||
total_common += potion * 3
|
||||
}
|
||||
var superpotion = Math.min(party.filter(p => (p.getInverseHp() >= 25 || p.getHpRatio() <= 0.75) && !p.isFainted()).length, 3)
|
||||
if (superpotion > 0) {
|
||||
items[0].push(`Super Potion (${superpotion})`)
|
||||
total_common += superpotion
|
||||
}
|
||||
var ether = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m?.ppUsed && (m.getMovePp() - m.ppUsed) <= 5 && m.ppUsed >= Math.floor(m.getMovePp() / 2)).length).length, 3)
|
||||
if (ether > 0) {
|
||||
items[0].push(`Ether (${ether * 3})`)
|
||||
items[0].push(`Max Ether (${ether})`)
|
||||
total_common += ether * 4
|
||||
}
|
||||
let lure = skipInLastClassicWaveOrDefault(2)(party)
|
||||
if (lure > 0) {
|
||||
items[0].push(`Lure (${lure})`)
|
||||
total_common += lure;
|
||||
}
|
||||
if (showAll) {
|
||||
items[0].push(`X Attack (0.66)`)
|
||||
items[0].push(`X Defense (0.66)`)
|
||||
items[0].push(`X Sp. Atk (0.66)`)
|
||||
items[0].push(`X Sp. Def (0.66)`)
|
||||
items[0].push(`X Speed (0.66)`)
|
||||
items[0].push(`X Accuracy (0.66)`)
|
||||
} else {
|
||||
items[0].push(`Any X Item (4, 6 kinds)`)
|
||||
}
|
||||
items[0].push(`Berry (2)`)
|
||||
items[0].push(`Common TM (2)`)
|
||||
total_common += 8 // X item = 4, berry = 2, common TM = 2
|
||||
|
||||
|
||||
|
||||
if (!hasMaximumBalls(party, PokeballType.GREAT_BALL)) {
|
||||
items[1].push(`Great Ball (6)`)
|
||||
total_great += 6
|
||||
}
|
||||
items[1].push(`PP Up (2)`)
|
||||
total_great += 2
|
||||
let statusPartyCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => {
|
||||
if (i instanceof Modifiers.TurnStatusEffectModifier) {
|
||||
return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status?.effect;
|
||||
}
|
||||
return false;
|
||||
})).length, 3)
|
||||
if (statusPartyCount > 0) {
|
||||
items[1].push(`Full Heal (${statusPartyCount * 3})`)
|
||||
total_great += statusPartyCount * 3
|
||||
}
|
||||
let reviveCount = Math.min(party.filter(p => p.isFainted()).length, 3);
|
||||
if (reviveCount > 0) {
|
||||
items[1].push(`Revive (${reviveCount * 9})`)
|
||||
items[1].push(`Max Revive (${reviveCount * 3})`)
|
||||
total_great += reviveCount * 12
|
||||
}
|
||||
if (party.filter(p => p.isFainted()).length >= Math.ceil(party.length / 2)) {
|
||||
items[1].push(`Sacred Ash (1)`)
|
||||
total_great++
|
||||
}
|
||||
let hyperpotion = Math.min(party.filter(p => (p.getInverseHp() >= 100 || p.getHpRatio() <= 0.625) && !p.isFainted()).length, 3)
|
||||
if (hyperpotion > 0) {
|
||||
items[1].push(`Hyper Potion (${hyperpotion * 3})`)
|
||||
total_great += hyperpotion * 3
|
||||
}
|
||||
let maxpotion = Math.min(party.filter(p => (p.getInverseHp() >= 150 || p.getHpRatio() <= 0.5) && !p.isFainted()).length, 3)
|
||||
if (maxpotion > 0) {
|
||||
items[1].push(`Max Potion (${maxpotion})`)
|
||||
total_great += maxpotion
|
||||
}
|
||||
let fullrestore = Math.floor((Math.min(party.filter(p => (p.getInverseHp() >= 150 || p.getHpRatio() <= 0.5) && !p.isFainted()).length, 3) + statusPartyCount) / 2)
|
||||
if (fullrestore > 0) {
|
||||
items[1].push(`Full Restore (${fullrestore})`)
|
||||
total_great += fullrestore
|
||||
}
|
||||
let elexir = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m?.ppUsed && (m.getMovePp() - m.ppUsed) <= 5 && m.ppUsed >= Math.floor(m.getMovePp() / 2)).length).length, 3)
|
||||
if (elexir) {
|
||||
items[1].push(`Elexir (${elexir * 3})`)
|
||||
items[1].push(`Max Elexir (${elexir})`)
|
||||
total_great += elexir * 4
|
||||
}
|
||||
items[1].push("Dire Hit (4)")
|
||||
total_great += 4
|
||||
let superlure = skipInLastClassicWaveOrDefault(4)(party)
|
||||
if (superlure > 0) {
|
||||
items[1].push(`Super Lure (4)`)
|
||||
items[1].push(`Nugget (5)`)
|
||||
total_great += 9
|
||||
}
|
||||
let evo = Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15), 8)
|
||||
if (evo > 0) {
|
||||
items[1].push(`Evolution Item (${evo})`)
|
||||
total_great += evo
|
||||
}
|
||||
if (party[0].scene.gameMode.isClassic && party[0].scene.currentBattle.waveIndex < 180) {
|
||||
if (!party[0].scene.getModifiers(Modifiers.MapModifier).length) {
|
||||
console.log(`Map (1)`)
|
||||
} else {
|
||||
console.log(`Map (1, results in a retry as it's already owned)`)
|
||||
}
|
||||
total_great++
|
||||
}
|
||||
items[1].push(`Rare TM (2)`)
|
||||
total_great += 3
|
||||
if (party.find(p => p.getLearnableLevelMoves().length)) {
|
||||
// Memory Mushroom
|
||||
let highestLev = party.map(p => p.level).reduce((highestLevel: integer, level: integer) => Math.max(highestLevel, level), 1)
|
||||
let memoryshroom = Math.min(Math.ceil(highestLev / 20), 4)
|
||||
if (memoryshroom > 0) {
|
||||
items[1].push(`Memory Mushroom (${memoryshroom})`)
|
||||
total_great += memoryshroom
|
||||
}
|
||||
}
|
||||
if (showAll) {
|
||||
items[1].push(`${i18next.t(`modifierType:BaseStatBoosterItem.${BaseStatBoosterModifierTypeGenerator.items[Stat.HP]}`)} (0.5)`)
|
||||
items[1].push(`${i18next.t(`modifierType:BaseStatBoosterItem.${BaseStatBoosterModifierTypeGenerator.items[Stat.ATK]}`)} (0.5)`)
|
||||
items[1].push(`${i18next.t(`modifierType:BaseStatBoosterItem.${BaseStatBoosterModifierTypeGenerator.items[Stat.DEF]}`)} (0.5)`)
|
||||
items[1].push(`${i18next.t(`modifierType:BaseStatBoosterItem.${BaseStatBoosterModifierTypeGenerator.items[Stat.SPATK]}`)} (0.5)`)
|
||||
items[1].push(`${i18next.t(`modifierType:BaseStatBoosterItem.${BaseStatBoosterModifierTypeGenerator.items[Stat.SPDEF]}`)} (0.5)`)
|
||||
items[1].push(`${i18next.t(`modifierType:BaseStatBoosterItem.${BaseStatBoosterModifierTypeGenerator.items[Stat.SPD]}`)} (0.5)`)
|
||||
} else {
|
||||
items[1].push(`Any Vitamin (3, 6 kinds)`)
|
||||
}
|
||||
total_great += 3
|
||||
if (party[0].scene.getModifiers(Modifiers.TerastallizeAccessModifier).length) {
|
||||
if (showAll) {
|
||||
const teratypes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
const randomchance1 = 1/3 * 1/64
|
||||
const randomchance2 = 1/3 * 63/64 * 1/18
|
||||
const teamTypes = party.map(p => p.getTypes(false, false, true)).flat()
|
||||
teratypes.forEach((v, i) => {
|
||||
if (i == Type.STELLAR) {
|
||||
teratypes[i] += randomchance1
|
||||
} else {
|
||||
teratypes[i] += randomchance2
|
||||
}
|
||||
})
|
||||
teamTypes.forEach(v => {
|
||||
teratypes[v] += 2/3 * 1/teamTypes.length
|
||||
})
|
||||
items[1].push(`Any Tera Shard (1, 19 kinds)`)
|
||||
teratypes.forEach((v, i) => {
|
||||
items[1].push(` ${i18next.t(`pokemonInfo:Type.${Type[i]}`)}: ${Math.round(v*1000)/10}%`)
|
||||
})
|
||||
} else {
|
||||
items[1].push(`Any Tera Shard (1, 19 kinds)`)
|
||||
}
|
||||
total_great++;
|
||||
}
|
||||
if (party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1) {
|
||||
items[1].push(`DNA Splicer (4)`)
|
||||
total_great += 4
|
||||
}
|
||||
if (!party[0].scene.gameMode.isDaily ) {
|
||||
items[1].push("Voucher (1, or 0 if reroll)")
|
||||
total_great += 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!hasMaximumBalls(party, PokeballType.ULTRA_BALL)) {
|
||||
items[2].push(`Ultra Ball (15)`)
|
||||
total_ultra += 15
|
||||
}
|
||||
if (superlure) {
|
||||
items[2].push(`Max Lure (4)`)
|
||||
items[2].push(`Big Nugget (12)`)
|
||||
total_ultra += 16
|
||||
}
|
||||
items[2].push(`PP Max (3)`)
|
||||
items[2].push(`Mint (4)`)
|
||||
total_ultra += 7
|
||||
let evoRare = Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15) * 4, 32)
|
||||
if (evoRare) {
|
||||
items[2].push(`Rare Evolution Item (${evoRare})`)
|
||||
total_ultra += evoRare
|
||||
}
|
||||
if (superlure) {
|
||||
items[2].push(`Amulet Coin (3)`)
|
||||
total_ultra += 3
|
||||
}
|
||||
if (!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.unlocks[Unlockables.EVIOLITE]) {
|
||||
if (party.some(p => ((p.getSpeciesForm(true).speciesId in pokemonEvolutions) || (p.isFusion() && (p.getFusionSpeciesForm(true).speciesId in pokemonEvolutions))) && !p.getHeldItems().some(i => i instanceof Modifiers.EvolutionStatBoosterModifier))) {
|
||||
items[2].push(`Eviolite (10)`)
|
||||
total_ultra += 10
|
||||
}
|
||||
}
|
||||
items[2].push(`Species Stat Booster (12, retries if incompatible)`)
|
||||
total_ultra += 12
|
||||
const checkedSpecies = [ Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD ]
|
||||
const checkedAbilitiesT = [Abilities.QUICK_FEET, Abilities.GUTS, Abilities.MARVEL_SCALE, Abilities.TOXIC_BOOST, Abilities.POISON_HEAL, Abilities.MAGIC_GUARD];
|
||||
const checkedAbilitiesF = [Abilities.QUICK_FEET, Abilities.GUTS, Abilities.MARVEL_SCALE, Abilities.FLARE_BOOST, Abilities.MAGIC_GUARD];
|
||||
const checkedAbilitiesW = [Abilities.WEAK_ARMOR, Abilities.CONTRARY, Abilities.MOODY, Abilities.ANGER_SHELL, Abilities.COMPETITIVE, Abilities.DEFIANT];
|
||||
const checkedMoves = [Moves.FACADE, Moves.TRICK, Moves.FLING, Moves.SWITCHEROO, Moves.PSYCHO_SHIFT];
|
||||
const weightMultiplier = party.filter(
|
||||
p => !p.getHeldItems().some(i => i instanceof Modifiers.ResetNegativeStatStageModifier && i.stackCount >= i.getMaxHeldItemCount(p)) &&
|
||||
(checkedAbilitiesW.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && selfStatLowerMoves.includes(m.moveId)))).length;
|
||||
if (party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.SpeciesCritBoosterModifier) && (checkedSpecies.includes(p.getSpeciesForm(true).speciesId) || (p.isFusion() && checkedSpecies.includes(p.getFusionSpeciesForm(true).speciesId))))) {
|
||||
items[2].push(`Leek (12)`)
|
||||
total_ultra += 12
|
||||
}
|
||||
if (party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier) && (checkedAbilitiesT.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && checkedMoves.includes(m.moveId))))) {
|
||||
items[2].push(`Toxic Orb (10)`)
|
||||
total_ultra += 10
|
||||
}
|
||||
if (party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier) && (checkedAbilitiesF.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && checkedMoves.includes(m.moveId))))) {
|
||||
items[2].push(`Flame Orb (10)`)
|
||||
total_ultra += 10
|
||||
}
|
||||
let whiteherb = 0 * (weightMultiplier ? 2 : 1) + (weightMultiplier ? weightMultiplier * 0 : 0)
|
||||
if (whiteherb) {
|
||||
items[2].push(`White Herb (${whiteherb})`)
|
||||
total_ultra += whiteherb
|
||||
}
|
||||
if (superlure) {
|
||||
items[2].push(`Wide Lens (5)`)
|
||||
total_ultra += 5
|
||||
}
|
||||
items[2].push(`Reviver Seed (4)`)
|
||||
items[2].push(`Attack Type Booster (9)`)
|
||||
items[2].push(`Epic TM (11)`)
|
||||
items[2].push(`Rarer Candy (4)`)
|
||||
if (superlure) {
|
||||
items[2].push(`Golden Punch (2)`)
|
||||
items[2].push(`IV Scanner (4)`)
|
||||
items[2].push(`EXP Charm (8)`)
|
||||
items[2].push(`EXP Share (10)`)
|
||||
items[2].push(`EXP Balance (3)`)
|
||||
total_ultra += 27
|
||||
}
|
||||
let teraorb = Math.min(Math.max(Math.floor(party[0].scene.currentBattle.waveIndex / 50) * 2, 1), 4)
|
||||
if (teraorb) {
|
||||
items[2].push(`Tera Orb (${teraorb})`)
|
||||
total_ultra += teraorb
|
||||
}
|
||||
items[2].push(`Quick Claw (3)`)
|
||||
items[2].push(`Wide Lens (4)`)
|
||||
total_ultra += 35
|
||||
|
||||
|
||||
|
||||
if (!hasMaximumBalls(party, PokeballType.ROGUE_BALL)) {
|
||||
items[3].push(`Rogue Ball (16)`)
|
||||
total_rogue += 16
|
||||
}
|
||||
if (superlure) {
|
||||
items[3].push(`Relic Gold (2)`)
|
||||
total_rogue += 2
|
||||
}
|
||||
items[3].push(`Leftovers (3)`)
|
||||
items[3].push(`Shell Bell (3)`)
|
||||
items[3].push(`Berry Pouch (4)`)
|
||||
items[3].push(`Grip Claw (5)`)
|
||||
items[3].push(`Scope Lens (4)`)
|
||||
items[3].push(`Baton (2)`)
|
||||
items[3].push(`Soul Dew (7)`)
|
||||
items[3].push(`Soothe Bell (4)`)
|
||||
let abilitycharm = skipInClassicAfterWave(189, 6)(party);
|
||||
if (abilitycharm) {
|
||||
items[3].push(`Ability Charm (${abilitycharm})`)
|
||||
total_rogue += abilitycharm
|
||||
}
|
||||
items[3].push(`Focus Band (5)`)
|
||||
items[3].push(`King's Rock (3)`)
|
||||
total_rogue += 40
|
||||
if (superlure) {
|
||||
items[3].push(`Lock Capsule (3)`)
|
||||
items[3].push(`Super EXP Charm (8)`)
|
||||
total_rogue += 11
|
||||
}
|
||||
let formchanger = Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6
|
||||
let megabraclet = Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9
|
||||
let dynamaxband = Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9
|
||||
if (formchanger) {
|
||||
items[3].push(`Form Change Item (${formchanger}, retries if incompatible)`)
|
||||
total_rogue += formchanger
|
||||
}
|
||||
if (megabraclet) {
|
||||
items[3].push(`Mega Bracelet (${megabraclet}, retries if already owned)`)
|
||||
total_rogue += megabraclet
|
||||
}
|
||||
if (dynamaxband) {
|
||||
items[3].push(`Dynamax Band (${dynamaxband}, retries if already owned)`)
|
||||
total_rogue += dynamaxband
|
||||
}
|
||||
if (!party[0].scene.gameMode.isDaily) {
|
||||
items[3].push(`Voucher Plus (3 - number of rerolls)`)
|
||||
total_rogue += 3
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!hasMaximumBalls(party, PokeballType.MASTER_BALL)) {
|
||||
items[4].push(`Master Ball (24)`)
|
||||
total_master += 24
|
||||
}
|
||||
items[4].push(`Shiny Charm (14)`)
|
||||
total_master += 14
|
||||
items[4].push(`Healing Charm (18)`)
|
||||
total_master += 18
|
||||
items[4].push(`Multi Lens (18)`)
|
||||
total_master += 18
|
||||
if (!party[0].scene.gameMode.isDaily && !party[0].scene.gameMode.isEndless && !party[0].scene.gameMode.isSplicedOnly) {
|
||||
items[4].push(`Voucher Premium (5, -2 per reroll)`)
|
||||
total_master += 3
|
||||
}
|
||||
if (!party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1) {
|
||||
items[4].push(`DNA Splicer (24)`)
|
||||
total_master += 24
|
||||
}
|
||||
if ((!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE])) {
|
||||
items[4].push(`Mini Black Hole (1)`)
|
||||
total_master += 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
items[0].sort()
|
||||
items[1].sort()
|
||||
items[2].sort()
|
||||
items[3].sort()
|
||||
items[4].sort()
|
||||
if (!log)
|
||||
return items;
|
||||
let itemlabels = [
|
||||
`Poké (${items[0].length}, weight ${total_common})`,
|
||||
`Great (${items[1].length}, weight ${total_great})`,
|
||||
`Ultra (${items[2].length}, weight ${total_ultra})`,
|
||||
`Rogue (${items[3].length}, weight ${total_rogue})`,
|
||||
`Master (${items[4].length}, weight ${total_master})`
|
||||
]
|
||||
items.forEach((mi, idx) => {
|
||||
console.log(itemlabels[idx])
|
||||
mi.forEach(m => {
|
||||
console.log(" " + mi)
|
||||
})
|
||||
})
|
||||
return items;
|
||||
}
|
||||
|
||||
const modifierPool: ModifierPool = {
|
||||
[ModifierTier.COMMON]: [
|
||||
|
7825
src/old-phases.ts
7825
src/old-phases.ts
File diff suppressed because it is too large
Load Diff
@ -73,6 +73,7 @@ export class CheckSwitchPhase extends BattlePhase {
|
||||
|
||||
this.scene.ui.showText(i18next.t("battle:switchQuestion", { pokemonName: this.useName ? getPokemonNameWithAffix(pokemon) : i18next.t("battle:pokemon") }), null, () => {
|
||||
this.scene.ui.setMode(Mode.CONFIRM, () => {
|
||||
// Yes, I want to Pre-Switch
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
LoggerTools.isPreSwitch.value = true
|
||||
this.scene.tryRemovePhase(p => p instanceof PostSummonPhase && p.player && p.fieldIndex === this.fieldIndex);
|
||||
@ -89,6 +90,7 @@ export class CheckSwitchPhase extends BattlePhase {
|
||||
//this.scene.pokemonInfoContainer.hide()
|
||||
this.end();
|
||||
}, () => {
|
||||
// No, I want to leave my Pokémon as is
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
for (var i = 0; i < this.scene.getEnemyField().length; i++) {
|
||||
this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.toggleFlyout(false)
|
||||
|
@ -1,5 +1,5 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { TurnCommand, BattleType } from "#app/battle";
|
||||
import { TurnCommand, BattleType, BattlerIndex } from "#app/battle";
|
||||
import { TrappedTag, EncoreTag } from "#app/data/battler-tags";
|
||||
import { MoveTargetSet, getMoveTargets } from "#app/data/move";
|
||||
import { speciesStarters } from "#app/data/pokemon-species";
|
||||
@ -8,7 +8,7 @@ import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||
import { Biome } from "#app/enums/biome";
|
||||
import { Moves } from "#app/enums/moves";
|
||||
import { PokeballType } from "#app/enums/pokeball";
|
||||
import { FieldPosition, PlayerPokemon } from "#app/field/pokemon";
|
||||
import { FieldPosition, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { Command } from "#app/ui/command-ui-handler";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
@ -17,7 +17,9 @@ import { FieldPhase } from "./field-phase";
|
||||
import { SelectTargetPhase } from "./select-target-phase";
|
||||
import * as LoggerTools from "../logger";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import { isNullOrUndefined } from "#app/utils";
|
||||
import { getEnumKeys, isNullOrUndefined } from "#app/utils";
|
||||
|
||||
export const targIDs: string[] = ["Self", "Self", "Ally", "L", "R", "Self", "Ally"]
|
||||
|
||||
export class CommandPhase extends FieldPhase {
|
||||
protected fieldIndex: integer;
|
||||
@ -39,6 +41,11 @@ export class CommandPhase extends FieldPhase {
|
||||
} else {
|
||||
const allyCommand = this.scene.currentBattle.turnCommands[this.fieldIndex - 1];
|
||||
if (allyCommand?.command === Command.BALL || allyCommand?.command === Command.RUN) {
|
||||
if (this.fieldIndex == 0) {
|
||||
LoggerTools.Actions[1] = ""; // Remove the second Pokémon's action, as we will not be attacking this turn
|
||||
} else {
|
||||
LoggerTools.Actions[0] = "" // Remove the first Pokémon's action, as their turn is now being skipped]
|
||||
}
|
||||
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: allyCommand?.command, skip: true };
|
||||
}
|
||||
}
|
||||
@ -52,6 +59,7 @@ export class CommandPhase extends FieldPhase {
|
||||
|
||||
const moveQueue = playerPokemon.getMoveQueue();
|
||||
|
||||
// Remove queued moves the Pokemon no longer has access to or can't use this turn
|
||||
while (moveQueue.length && moveQueue[0]
|
||||
&& moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move)
|
||||
|| !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct?
|
||||
@ -61,12 +69,16 @@ export class CommandPhase extends FieldPhase {
|
||||
if (moveQueue.length) {
|
||||
const queuedMove = moveQueue[0];
|
||||
if (!queuedMove.move) {
|
||||
this.handleCommand(Command.FIGHT, -1, false);
|
||||
// Struggle
|
||||
this.handleCommand(Command.FIGHT, false, -1, false);
|
||||
} else {
|
||||
// Locate the queued move in our moveset
|
||||
const moveIndex = playerPokemon.getMoveset().findIndex(m => m?.moveId === queuedMove.move);
|
||||
if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) { // TODO: is the bang correct?
|
||||
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 });
|
||||
// Use the queued move
|
||||
this.handleCommand(Command.FIGHT, false, moveIndex, queuedMove.ignorePP, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 });
|
||||
} else {
|
||||
// The move is no longer in our moveset or is unuseable; allow the player to choose an action
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}
|
||||
}
|
||||
@ -80,10 +92,14 @@ export class CommandPhase extends FieldPhase {
|
||||
}
|
||||
}
|
||||
|
||||
handleCommand(command: Command, cursor: integer, ...args: any[]): boolean {
|
||||
handleCommand(command: Command, logCommand: boolean = true, cursor: integer, ...args: any[]): boolean {
|
||||
const playerPokemon = this.scene.getPlayerField()[this.fieldIndex];
|
||||
let success: boolean;
|
||||
|
||||
if (!logCommand) {
|
||||
LoggerTools.Actions[this.fieldIndex] = "%SKIP"
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case Command.FIGHT:
|
||||
let useStruggle = false;
|
||||
@ -93,19 +109,26 @@ export class CommandPhase extends FieldPhase {
|
||||
const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor]!.moveId : Moves.NONE : Moves.STRUGGLE; // TODO: is the bang correct?
|
||||
const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args };
|
||||
const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, moveId) : args[2];
|
||||
let moveData: PokemonMove | undefined;
|
||||
if (!moveId) {
|
||||
turnCommand.targets = [this.fieldIndex];
|
||||
} else {
|
||||
moveData = new PokemonMove(moveId, 0, 0, true);
|
||||
}
|
||||
console.log(moveTargets, getPokemonNameWithAffix(playerPokemon));
|
||||
if (moveTargets.targets.length > 1 && moveTargets.multiple) {
|
||||
this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex));
|
||||
// No need to log the move, as SelectTargetPhase will call another CommandPhase with the correct data
|
||||
}
|
||||
if (moveTargets.targets.length <= 1 || moveTargets.multiple) {
|
||||
turnCommand.move!.targets = moveTargets.targets; //TODO: is the bang correct here?
|
||||
LoggerTools.Actions[this.fieldIndex] = (moveData ? moveData!.getName() : "[???]") + " " + this.formatTargets([], this.fieldIndex)
|
||||
} else if (playerPokemon.getTag(BattlerTagType.CHARGING) && playerPokemon.getMoveQueue().length >= 1) {
|
||||
// A charging move will be executed this turn, so we do not need to log ourselves using it (we already selected the move last turn)
|
||||
turnCommand.move!.targets = playerPokemon.getMoveQueue()[0].targets; //TODO: is the bang correct here?
|
||||
} else {
|
||||
this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex));
|
||||
// No need to log the move, as SelectTargetPhase will call another CommandPhase with the correct data
|
||||
}
|
||||
this.scene.currentBattle.turnCommands[this.fieldIndex] = turnCommand;
|
||||
success = true;
|
||||
@ -170,7 +193,9 @@ export class CommandPhase extends FieldPhase {
|
||||
} else {
|
||||
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor };
|
||||
this.scene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets;
|
||||
LoggerTools.Actions[this.fieldIndex] = "Ball"
|
||||
if (this.fieldIndex) {
|
||||
LoggerTools.Actions.shift()
|
||||
this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true;
|
||||
}
|
||||
success = true;
|
||||
@ -180,10 +205,15 @@ export class CommandPhase extends FieldPhase {
|
||||
break;
|
||||
case Command.POKEMON:
|
||||
case Command.RUN:
|
||||
// We are attempting to switch Pokémon or run from battle
|
||||
/** Are we attempting to switch (`true`) or run away (`false`)? */
|
||||
const isSwitch = command === Command.POKEMON;
|
||||
/** Pulls data from the `BattleScene` */
|
||||
const { currentBattle, arena } = this.scene;
|
||||
/** Whether the player can flee from this Mystery Encounter */
|
||||
const mysteryEncounterFleeAllowed = currentBattle.mysteryEncounter?.fleeAllowed;
|
||||
if (!isSwitch && (arena.biomeType === Biome.END || (!isNullOrUndefined(mysteryEncounterFleeAllowed) && !mysteryEncounterFleeAllowed))) {
|
||||
// We are attempting to run away, and we are either in ???, or in a Mystery Encounter we're not allowed to flee from
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.ui.showText(i18next.t("battle:noEscapeForce"), null, () => {
|
||||
@ -191,6 +221,7 @@ export class CommandPhase extends FieldPhase {
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}, null, true);
|
||||
} else if (!isSwitch && (currentBattle.battleType === BattleType.TRAINER || currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE)) {
|
||||
// We are attempting to run away from a Trainer Battle
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
this.scene.ui.showText(i18next.t("battle:noEscapeTrainer"), null, () => {
|
||||
@ -198,17 +229,29 @@ export class CommandPhase extends FieldPhase {
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}, null, true);
|
||||
} else {
|
||||
// We are attempting to switch, or are attempting to run away and the game isn't preventing it
|
||||
/**
|
||||
* Whether we are attempting to Baton Pass.
|
||||
*
|
||||
* If we are running away, not switching, this value is `false`. */
|
||||
const batonPass = isSwitch && args[0] as boolean;
|
||||
/** Stores reasons why you cannot run away or switch your Trapped Pokémon. */
|
||||
const trappedAbMessages: string[] = [];
|
||||
if (batonPass || !playerPokemon.isTrapped(trappedAbMessages)) {
|
||||
if (batonPass || !playerPokemon.isTrapped(trappedAbMessages)) { // We are attempting to Baton Pass
|
||||
currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
||||
? { command: Command.POKEMON, cursor: cursor, args: args }
|
||||
: { command: Command.RUN };
|
||||
success = true;
|
||||
LoggerTools.Actions[this.fieldIndex] = isSwitch ? `Switch to ${this.scene.getParty()[cursor].name}` : "Run from battle"
|
||||
if (!isSwitch && this.fieldIndex) {
|
||||
// If we're trying to run away, and we are in the second field slot, skip the first Pokémon's turn
|
||||
currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true;
|
||||
}
|
||||
} else if (trappedAbMessages.length > 0) {
|
||||
} else if (trappedAbMessages.length > 0) { // We are attempting to Baton Pass, but we were trapped during the turn and can't do so anymore
|
||||
// This is actually genius wow
|
||||
// If batonPass was true, playerPokemon.isTrapped() will run
|
||||
// If this function also returns true, the top block is skipped, and this block runs
|
||||
// If batonPass is false, playerPokemon.isTrapped() will not run, which means trappedAbMessages.length is 0, and this block is skipped, too
|
||||
if (!isSwitch) {
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
}
|
||||
@ -218,14 +261,16 @@ export class CommandPhase extends FieldPhase {
|
||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||
}
|
||||
}, null, true);
|
||||
} else {
|
||||
} else { // We are not attempting to Baton Pass
|
||||
const trapTag = playerPokemon.getTag(TrappedTag);
|
||||
|
||||
// trapTag should be defined at this point, but just in case...
|
||||
if (!trapTag) {
|
||||
// If the Pokémon is not trapped, break from this block and set the action
|
||||
currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
||||
? { command: Command.POKEMON, cursor: cursor, args: args }
|
||||
: { command: Command.RUN };
|
||||
LoggerTools.Actions[this.fieldIndex] = isSwitch ? `Switch to ${this.scene.getParty()[cursor].name}` : "Run from battle"
|
||||
break;
|
||||
}
|
||||
|
||||
@ -260,7 +305,9 @@ export class CommandPhase extends FieldPhase {
|
||||
|
||||
cancel() {
|
||||
if (this.fieldIndex) {
|
||||
LoggerTools.Actions[0] = ""
|
||||
this.scene.unshiftPhase(new CommandPhase(this.scene, 0));
|
||||
LoggerTools.Actions[1] = ""
|
||||
this.scene.unshiftPhase(new CommandPhase(this.scene, 1));
|
||||
this.end();
|
||||
}
|
||||
@ -281,11 +328,39 @@ export class CommandPhase extends FieldPhase {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.handleCommand(Command.FIGHT, moveIndex, false);
|
||||
// Select the forced move (it is not logged since the player cannot make a choice here)
|
||||
this.handleCommand(Command.FIGHT, false, moveIndex, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an attack's targets.
|
||||
* @param indices The `BattlerIndex` of all targets
|
||||
* @param fieldIndex The `BattlerIndex` of the user
|
||||
*/
|
||||
formatTargets(indices: BattlerIndex[], fieldIndex: BattlerIndex) {
|
||||
var output: string[] = [];
|
||||
for (var i = 0; i < indices.length; i++) {
|
||||
var selection = "";
|
||||
if (fieldIndex < 2) {
|
||||
// Player
|
||||
selection = targIDs[indices[i] + 1];
|
||||
} else {
|
||||
// Enemy
|
||||
selection = targIDs[indices[i] + 3];
|
||||
}
|
||||
// If this Pokémon is on the right side of the field, flip the terms 'self' and 'ally'
|
||||
if (selection == "Self" && fieldIndex % 2 == 1) {
|
||||
selection = "Ally"
|
||||
}
|
||||
if (selection == "Ally" && fieldIndex % 2 == 1) {
|
||||
selection = "Self"
|
||||
}
|
||||
}
|
||||
return "→ " + output.join(", ");
|
||||
}
|
||||
|
||||
getFieldIndex(): integer {
|
||||
return this.fieldIndex;
|
||||
}
|
||||
|
@ -109,14 +109,14 @@ export class FaintPhase extends PokemonPhase {
|
||||
* push a phase that prompts the player to summon a Pokemon from their party.
|
||||
*/
|
||||
LoggerTools.isFaintSwitch.value = true;
|
||||
this.scene.pushPhase(new SwitchPhase(this.scene, SwitchType.SWITCH, this.fieldIndex, true, false));
|
||||
this.scene.pushPhase(new SwitchPhase(this.scene, SwitchType.MID_TURN_SWITCH, this.fieldIndex, true, false));
|
||||
}
|
||||
} else {
|
||||
this.scene.unshiftPhase(new VictoryPhase(this.scene, this.battlerIndex));
|
||||
if ([BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.scene.currentBattle.battleType)) {
|
||||
const hasReservePartyMember = !!this.scene.getEnemyParty().filter(p => p.isActive() && !p.isOnField() && p.trainerSlot === (pokemon as EnemyPokemon).trainerSlot).length;
|
||||
if (hasReservePartyMember) {
|
||||
this.scene.pushPhase(new SwitchSummonPhase(this.scene, SwitchType.SWITCH, this.fieldIndex, -1, false, false));
|
||||
this.scene.pushPhase(new SwitchSummonPhase(this.scene, SwitchType.MID_TURN_SWITCH, this.fieldIndex, -1, false, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,7 +242,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase {
|
||||
const playerField = this.scene.getPlayerField();
|
||||
playerField.forEach((pokemon, i) => {
|
||||
if (!pokemon.isAllowedInBattle() && legalPlayerPartyPokemon.length > i) {
|
||||
this.scene.unshiftPhase(new SwitchPhase(this.scene, SwitchType.SWITCH, i, true, false));
|
||||
this.scene.unshiftPhase(new SwitchPhase(this.scene, SwitchType.MID_TURN_SWITCH, i, true, false));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -17,21 +17,18 @@ export class SelectModifierPhase extends BattlePhase {
|
||||
private rerollCount: integer;
|
||||
private modifierTiers?: ModifierTier[];
|
||||
private customModifierSettings?: CustomModifierSettings;
|
||||
private modifierPredictions: ModifierTypeOption[][] = [];
|
||||
private modifierPredictions?: ModifierTypeOption[][] = [];
|
||||
private predictionCost: integer = 0;
|
||||
private costTiers: integer[] = [];
|
||||
|
||||
constructor(scene: BattleScene, rerollCount: integer = 0, modifierTiers?: ModifierTier[], customModifierSettings?: CustomModifierSettings) {
|
||||
constructor(scene: BattleScene, rerollCount: integer = 0, modifierTiers?: ModifierTier[], customModifierSettings?: CustomModifierSettings, predictionCost: integer = 0, modifierPredictions?: ModifierTypeOption[][]) {
|
||||
super(scene);
|
||||
|
||||
this.rerollCount = rerollCount;
|
||||
this.modifierTiers = modifierTiers;
|
||||
this.customModifierSettings = customModifierSettings;
|
||||
this.modifierPredictions = []
|
||||
if (modifierPredictions != undefined) {
|
||||
this.modifierPredictions = modifierPredictions;
|
||||
}
|
||||
this.predictionCost = 0
|
||||
this.predictionCost = predictionCost
|
||||
this.costTiers = []
|
||||
}
|
||||
|
||||
@ -46,7 +43,7 @@ export class SelectModifierPhase extends BattlePhase {
|
||||
this.scene.applyModifiers(ExtraModifierModifier, true, modifierCount);
|
||||
}
|
||||
if (modifierOverride) {
|
||||
//modifierCount.value = modifierOverride
|
||||
modifierCount.value = modifierOverride
|
||||
}
|
||||
const typeOptions: ModifierTypeOption[] = this.getModifierTypeOptions(modifierCount.value);
|
||||
typeOptions.forEach((option, idx) => {
|
||||
@ -63,9 +60,11 @@ export class SelectModifierPhase extends BattlePhase {
|
||||
//console.log(option.type.name)
|
||||
})
|
||||
//console.log("====================")
|
||||
if (this.modifierPredictions != undefined) {
|
||||
this.modifierPredictions[rerollOverride] = typeOptions
|
||||
this.costTiers.push(this.predictionCost)
|
||||
this.predictionCost += this.getRerollCost(typeOptions, false, rerollOverride)
|
||||
}
|
||||
//Phaser.Math.RND.state(STATE) // Restore RNG state like nothing happened
|
||||
}
|
||||
|
||||
@ -296,7 +295,9 @@ export class SelectModifierPhase extends BattlePhase {
|
||||
return !cost!;// TODO: is the bang correct?
|
||||
};
|
||||
if (this.rerollCount == 0) {
|
||||
if (true) {
|
||||
if (this.modifierPredictions == undefined) {
|
||||
// Do nothing
|
||||
} else if (true) {
|
||||
this.modifierPredictions.forEach((mp, r) => {
|
||||
// costTiers
|
||||
console.log("Rerolls: " + r + (this.costTiers[r] != 0 ? " - ₽" + this.costTiers[r] : ""))
|
||||
@ -305,73 +306,8 @@ export class SelectModifierPhase extends BattlePhase {
|
||||
if (m.eviolite) {
|
||||
console.log(" With Eviolite unlocked: " + m.eviolite.name)
|
||||
}
|
||||
if (m.alternates) {
|
||||
//console.log(m.alternates)
|
||||
let showedLuckFlag = false
|
||||
for (var j = 0, currentTier = m.type!.tier; j < m.alternates.length; j++) {
|
||||
if (m.alternates[j] > currentTier) {
|
||||
currentTier = m.alternates[j]
|
||||
if (m.advancedAlternates) {
|
||||
if (!showedLuckFlag) {
|
||||
showedLuckFlag = true
|
||||
console.log(" Your luck: " + getPartyLuckValue(party) + " (" + getLuckString(getPartyLuckValue(party)) + ")")
|
||||
}
|
||||
console.log(" At " + j + " luck (" + getLuckString(j) + "): " + m.advancedAlternates[j])
|
||||
} else {
|
||||
if (!showedLuckFlag) {
|
||||
showedLuckFlag = true
|
||||
console.log(" Your luck: " + getPartyLuckValue(party) + " (" + getLuckString(getPartyLuckValue(party)) + ")")
|
||||
}
|
||||
console.log(" At " + j + " luck (" + getLuckString(j) + "): " + LoggerTools.tierNames[currentTier] + "-tier item")// (failed to generate item)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//console.log(" No alt-luck data")
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
let modifierList: string[] = []
|
||||
this.modifierPredictions.forEach((mp, r) => {
|
||||
//console.log("Rerolls: " + r)
|
||||
mp.forEach((m, i) => {
|
||||
modifierList.push(m.type!.name + (r > 0 ? " (x" + r + ")" : ""))
|
||||
//console.log(" " + m.type!.name)
|
||||
if (m.eviolite) {
|
||||
modifierList.push(m.type!.name + (r > 0 ? " (x" + r + " with eviolite unlocked)" : " (With eviolite unlocked)"))
|
||||
//console.log(" With Eviolite unlocked: " + m.eviolite.name)
|
||||
}
|
||||
if (m.alternates) {
|
||||
//console.log(m.alternates)
|
||||
let showedLuckFlag = false
|
||||
for (var j = 0, currentTier = m.type!.tier; j < m.alternates.length; j++) {
|
||||
if (m.alternates[j] > currentTier) {
|
||||
currentTier = m.alternates[j]
|
||||
if (m.advancedAlternates) {
|
||||
if (!showedLuckFlag) {
|
||||
showedLuckFlag = true
|
||||
console.log(" Your luck: " + getPartyLuckValue(party) + " (" + getLuckString(getPartyLuckValue(party)) + ")")
|
||||
}
|
||||
console.log(" At " + j + " luck (" + getLuckString(j) + "): " + m.advancedAlternates[j])
|
||||
} else {
|
||||
if (!showedLuckFlag) {
|
||||
showedLuckFlag = true
|
||||
console.log(" Your luck: " + getPartyLuckValue(party) + " (" + getLuckString(getPartyLuckValue(party)) + ")")
|
||||
}
|
||||
console.log(" At " + j + " luck (" + getLuckString(j) + "): " + LoggerTools.tierNames[currentTier] + "-tier item (failed to generate item)")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//console.log(" No alt-luck data")
|
||||
}
|
||||
})
|
||||
})
|
||||
modifierList.sort()
|
||||
modifierList.forEach(v => {
|
||||
console.log(v)
|
||||
})
|
||||
}
|
||||
}
|
||||
this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers));
|
||||
|
@ -2,7 +2,7 @@ import BattleScene from "#app/battle-scene";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { Command } from "#app/ui/command-ui-handler";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { CommandPhase } from "./command-phase";
|
||||
import { CommandPhase, targIDs } from "./command-phase";
|
||||
import { PokemonPhase } from "./pokemon-phase";
|
||||
import * as LoggerTools from "../logger";
|
||||
import i18next from "#app/plugins/i18n";
|
||||
@ -32,9 +32,11 @@ export class SelectTargetPhase extends PokemonPhase {
|
||||
// Cancel this action
|
||||
if (targets.length < 1) {
|
||||
this.scene.currentBattle.turnCommands[this.fieldIndex] = null;
|
||||
LoggerTools.Actions[this.fieldIndex] = "";
|
||||
this.scene.unshiftPhase(new CommandPhase(this.scene, this.fieldIndex));
|
||||
} else {
|
||||
turnCommand!.targets = targets; //TODO: is the bang correct here?
|
||||
LoggerTools.Actions[this.fieldIndex] += " " + this.formatTargets(targets, this.fieldIndex)
|
||||
}
|
||||
if (turnCommand?.command === Command.BALL && this.fieldIndex) {
|
||||
this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; //TODO: is the bang correct here?
|
||||
@ -42,4 +44,31 @@ export class SelectTargetPhase extends PokemonPhase {
|
||||
this.end();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an attack's targets.
|
||||
* @param indices The `BattlerIndex` of all targets
|
||||
* @param fieldIndex The `BattlerIndex` of the user
|
||||
*/
|
||||
formatTargets(indices: BattlerIndex[], fieldIndex: BattlerIndex) {
|
||||
var output: string[] = [];
|
||||
for (var i = 0; i < indices.length; i++) {
|
||||
var selection = "";
|
||||
if (fieldIndex < 2) {
|
||||
// Player
|
||||
selection = targIDs[indices[i] + 1];
|
||||
} else {
|
||||
// Enemy
|
||||
selection = targIDs[indices[i] + 3];
|
||||
}
|
||||
// If this Pokémon is on the right side of the field, flip the terms 'self' and 'ally'
|
||||
if (selection == "Self" && fieldIndex % 2 == 1) {
|
||||
selection = "Ally"
|
||||
}
|
||||
if (selection == "Ally" && fieldIndex % 2 == 1) {
|
||||
selection = "Self"
|
||||
}
|
||||
}
|
||||
return "→ " + output.join(", ");
|
||||
}
|
||||
}
|
||||
|
@ -69,16 +69,20 @@ export class SwitchPhase extends BattlePhase {
|
||||
this.scene.ui.setMode(Mode.PARTY, this.isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH, fieldIndex, (slotIndex: integer, option: PartyOption) => {
|
||||
if (this.isModal) {console.error("Forced Switch Detected")}
|
||||
if (slotIndex >= this.scene.currentBattle.getBattlerCount() && slotIndex < 6) {
|
||||
const switchType = (option === PartyOption.PASS_BATON) ? SwitchType.BATON_PASS : this.switchType;
|
||||
const switchType = (option === PartyOption.PASS_BATON) ? (this.switchType == SwitchType.PRE_SWITCH ? SwitchType.PRE_BATON_PASS : SwitchType.BATON_PASS) : this.switchType;
|
||||
switch (this.switchType) {
|
||||
case SwitchType.SWITCH:
|
||||
case SwitchType.BATON_PASS:
|
||||
// These actions have already been logged by the player; no need to set them
|
||||
break;
|
||||
case SwitchType.MID_TURN_SWITCH:
|
||||
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Switch ${LoggerTools.playerPokeName(this.scene, fieldIndex)} to ${LoggerTools.playerPokeName(this.scene, slotIndex)}`);
|
||||
break;
|
||||
case SwitchType.BATON_PASS:
|
||||
case SwitchType.MID_TURN_BATON_PASS:
|
||||
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Baton-Pass ${LoggerTools.playerPokeName(this.scene, fieldIndex)} to ${LoggerTools.playerPokeName(this.scene, slotIndex)}`);
|
||||
break;
|
||||
case SwitchType.SHED_TAIL:
|
||||
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Switch to ${LoggerTools.playerPokeName(this.scene, slotIndex)}`)
|
||||
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Shed Tail to ${LoggerTools.playerPokeName(this.scene, slotIndex)}`)
|
||||
break;
|
||||
case SwitchType.PRE_SWITCH:
|
||||
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Pre-Switch ${LoggerTools.playerPokeName(this.scene, fieldIndex)} to ${LoggerTools.playerPokeName(this.scene, slotIndex)}`);
|
||||
@ -86,9 +90,6 @@ export class SwitchPhase extends BattlePhase {
|
||||
case SwitchType.PRE_BATON_PASS:
|
||||
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Pre-Switch & Baton-Pass ${LoggerTools.playerPokeName(this.scene, fieldIndex)} to ${LoggerTools.playerPokeName(this.scene, slotIndex)}`);
|
||||
break;
|
||||
default:
|
||||
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Switch ${LoggerTools.playerPokeName(this.scene, fieldIndex)} to ${LoggerTools.playerPokeName(this.scene, slotIndex)}`);
|
||||
break;
|
||||
}
|
||||
this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, switchType, fieldIndex, slotIndex, this.doReturn));
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
||||
|
||||
const pokemon = this.getPokemon();
|
||||
|
||||
if (this.switchType === SwitchType.SWITCH) {
|
||||
if (this.switchType === SwitchType.SWITCH || this.switchType === SwitchType.MID_TURN_SWITCH) {
|
||||
(this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(pokemon.id));
|
||||
const substitute = pokemon.getTag(SubstituteTag);
|
||||
if (substitute) {
|
||||
@ -107,7 +107,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
||||
const switchedInPokemon = party[this.slotIndex];
|
||||
this.lastPokemon = this.getPokemon();
|
||||
applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon);
|
||||
if (this.switchType === SwitchType.BATON_PASS && switchedInPokemon) {
|
||||
if ((this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.MID_TURN_BATON_PASS) && switchedInPokemon) {
|
||||
(this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.transferTagsBySourceId(this.lastPokemon.id, switchedInPokemon.id));
|
||||
if (!this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id)) {
|
||||
const batonPassModifier = this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier
|
||||
@ -132,7 +132,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
||||
* If this switch is passing a Substitute, make the switched Pokemon match the returned Pokemon's state as it left.
|
||||
* Otherwise, clear any persisting tags on the returned Pokemon.
|
||||
*/
|
||||
if (this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.SHED_TAIL) {
|
||||
if (this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.MID_TURN_BATON_PASS || this.switchType === SwitchType.SHED_TAIL) {
|
||||
const substitute = this.lastPokemon.getTag(SubstituteTag);
|
||||
if (substitute) {
|
||||
switchedInPokemon.x += this.lastPokemon.getSubstituteOffset()[0];
|
||||
@ -176,7 +176,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
||||
pokemon.battleSummonData.turnCount--;
|
||||
}
|
||||
|
||||
if (this.switchType === SwitchType.BATON_PASS && pokemon) {
|
||||
if ((this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.MID_TURN_BATON_PASS) && pokemon) {
|
||||
pokemon.transferSummon(this.lastPokemon);
|
||||
} else if (this.switchType === SwitchType.SHED_TAIL && pokemon) {
|
||||
const subTag = this.lastPokemon.getTag(SubstituteTag);
|
||||
|
@ -33,26 +33,8 @@ export class TurnStartPhase extends FieldPhase {
|
||||
console.log(turnCommand.targets, turnCommand.move!.targets)
|
||||
if (turnCommand.args && turnCommand.args[1] && turnCommand.args[1].isContinuing != undefined) {
|
||||
console.log(mv.getName(), targets)
|
||||
} else {
|
||||
LoggerTools.Actions[pokemon.getBattlerIndex()] = mv.getName()
|
||||
if (this.scene.currentBattle.double) {
|
||||
var targIDs = ["Self", "Self", "Ally", "L", "R"]
|
||||
if (pokemon.getBattlerIndex() == 1) targIDs = ["Self", "Ally", "Self", "L", "R"]
|
||||
LoggerTools.Actions[pokemon.getBattlerIndex()] += " → " + targets.map(v => targIDs[v+1])
|
||||
} else {
|
||||
var targIDs = ["Self", "", "", "", ""]
|
||||
var myField = this.scene.getField()
|
||||
if (myField[0])
|
||||
targIDs[1] = myField[0].name
|
||||
if (myField[1])
|
||||
targIDs[2] = myField[1].name
|
||||
var eField = this.scene.getEnemyField()
|
||||
if (eField[0])
|
||||
targIDs[3] = eField[0].name
|
||||
if (eField[1])
|
||||
targIDs[4] = eField[1].name
|
||||
LoggerTools.Actions[pokemon.getBattlerIndex()] += " → " + targets.map(v => targIDs[v+1])
|
||||
}
|
||||
} else if (LoggerTools.Actions[pokemon.getBattlerIndex()].substring(0, 5) == "[???]") {
|
||||
LoggerTools.Actions[pokemon.getBattlerIndex()] = mv.getName() + LoggerTools.Actions[pokemon.getBattlerIndex()].substring(5)
|
||||
console.log(mv.getName(), targets)
|
||||
}
|
||||
}
|
||||
@ -215,6 +197,7 @@ export class TurnStartPhase extends FieldPhase {
|
||||
break;
|
||||
case Command.POKEMON:
|
||||
const switchType = turnCommand.args?.[0] ? SwitchType.BATON_PASS : SwitchType.SWITCH;
|
||||
LoggerTools.Actions.push(`${switchType == SwitchType.SWITCH ? "Switch" : "Baton-Pass"} to ${this.scene.getParty()[turnCommand.cursor!].name}`)
|
||||
this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, switchType, pokemon.getFieldIndex(), turnCommand.cursor!, true, pokemon.isPlayer()));
|
||||
break;
|
||||
case Command.RUN:
|
||||
@ -257,10 +240,12 @@ export class TurnStartPhase extends FieldPhase {
|
||||
if (LoggerTools.Actions.length > 1 && !this.scene.currentBattle.double) {
|
||||
LoggerTools.Actions.pop() // If this is a single battle, but we somehow have two actions, delete the second
|
||||
}
|
||||
if (LoggerTools.Actions.length > 1 && (LoggerTools.Actions[0] == "" || LoggerTools.Actions[0] == undefined || LoggerTools.Actions[0] == null))
|
||||
if (LoggerTools.Actions.length > 1 && (LoggerTools.Actions[0] == "" || LoggerTools.Actions[0] == "%SKIP" || LoggerTools.Actions[0] == undefined || LoggerTools.Actions[0] == null))
|
||||
LoggerTools.Actions.shift() // If the left slot isn't doing anything, delete its entry
|
||||
if (LoggerTools.Actions.length > 1 && (LoggerTools.Actions[1] == "" || LoggerTools.Actions[1] == undefined || LoggerTools.Actions[1] == null))
|
||||
if (LoggerTools.Actions.length > 1 && (LoggerTools.Actions[1] == "" || LoggerTools.Actions[0] == "%SKIP" || LoggerTools.Actions[1] == undefined || LoggerTools.Actions[1] == null))
|
||||
LoggerTools.Actions.pop() // If the right slot isn't doing anything, delete its entry
|
||||
|
||||
// Log the player's actions
|
||||
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, LoggerTools.Actions.join(" & "))
|
||||
|
||||
/**
|
||||
|
@ -48,7 +48,7 @@ export class VictoryPhase extends PokemonPhase {
|
||||
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.LOCK_CAPSULE));
|
||||
}
|
||||
if (this.scene.currentBattle.waveIndex % 10) {
|
||||
this.scene.pushPhase(new SelectModifierPhase(this.scene, undefined, undefined, undefined, undefined, this.getFixedBattleCustomModifiers()));
|
||||
this.scene.pushPhase(new SelectModifierPhase(this.scene, undefined, undefined, this.getFixedBattleCustomModifiers()));
|
||||
} else if (this.scene.gameMode.isDaily) {
|
||||
LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "")
|
||||
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_CHARM));
|
||||
|
@ -41,7 +41,7 @@ describe("Escape chance calculations", () => {
|
||||
vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]);
|
||||
|
||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
||||
commandPhase.handleCommand(Command.RUN, 0);
|
||||
commandPhase.handleCommand(Command.RUN, false, 0);
|
||||
|
||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
||||
@ -103,7 +103,7 @@ describe("Escape chance calculations", () => {
|
||||
vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]);
|
||||
|
||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
||||
commandPhase.handleCommand(Command.RUN, 0);
|
||||
commandPhase.handleCommand(Command.RUN, false, 0);
|
||||
|
||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
||||
@ -163,7 +163,7 @@ describe("Escape chance calculations", () => {
|
||||
vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]);
|
||||
|
||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
||||
commandPhase.handleCommand(Command.RUN, 0);
|
||||
commandPhase.handleCommand(Command.RUN, false, 0);
|
||||
|
||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
||||
@ -240,7 +240,7 @@ describe("Escape chance calculations", () => {
|
||||
vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]);
|
||||
|
||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
||||
commandPhase.handleCommand(Command.RUN, 0);
|
||||
commandPhase.handleCommand(Command.RUN, false, 0);
|
||||
|
||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
||||
|
@ -52,7 +52,7 @@ export class MoveHelper extends GameManagerHelper {
|
||||
this.game.scene.ui.setMode(Mode.FIGHT, (this.game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
this.game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
(this.game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
(this.game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, false, movePosition, false);
|
||||
});
|
||||
|
||||
if (targetIndex !== null) {
|
||||
|
@ -76,7 +76,7 @@ export default class BallUiHandler extends UiHandler {
|
||||
success = true;
|
||||
if (button === Button.ACTION && this.cursor < pokeballTypeCount) {
|
||||
if (this.scene.pokeballCounts[this.cursor]) {
|
||||
if (commandPhase.handleCommand(Command.BALL, this.cursor)) {
|
||||
if (commandPhase.handleCommand(Command.BALL, false, this.cursor)) {
|
||||
this.scene.ui.setMode(Mode.COMMAND, commandPhase.getFieldIndex());
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
success = true;
|
||||
|
@ -1075,7 +1075,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||
}
|
||||
this.currentEffectiveness = effectiveness;
|
||||
|
||||
if (!(this.scene as BattleScene).typeHints || effectiveness === undefined || this.flyoutMenu?.flyoutVisible) {
|
||||
if (effectiveness == "" || effectiveness === undefined || this.flyoutMenu?.flyoutVisible) {
|
||||
this.effectivenessContainer.setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ export default class CommandUiHandler extends UiHandler {
|
||||
break;
|
||||
// Run
|
||||
case Command.RUN:
|
||||
(this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.RUN, 0);
|
||||
(this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.RUN, false, 0);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
|
||||
|
||||
if (button === Button.CANCEL || button === Button.ACTION) {
|
||||
if (button === Button.ACTION) {
|
||||
if ((this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, cursor, false)) {
|
||||
if ((this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, true, cursor, false)) {
|
||||
success = true;
|
||||
} else {
|
||||
ui.playError();
|
||||
@ -288,8 +288,14 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets multiplier text for a pokemon's move against a specific opponent
|
||||
* Returns undefined if it's a status move
|
||||
* Gets multiplier text for a pokemon's move against a specific opponent.
|
||||
* Returns undefined if it's a status move.
|
||||
*
|
||||
* If Type Hints is enabled, shows the move's type effectiveness.
|
||||
*
|
||||
* If Damage Calculation is enabled, shows the move's expected damage range.
|
||||
*
|
||||
* If Type Hints and Damage Calculation are both off, the type effectiveness multiplier is hidden.
|
||||
*/
|
||||
private getEffectivenessText(pokemon: Pokemon, opponent: Pokemon, pokemonMove: PokemonMove): string | undefined {
|
||||
const effectiveness = opponent.getMoveEffectiveness(pokemon, pokemonMove.getMove(), !opponent.battleData?.abilityRevealed);
|
||||
@ -297,9 +303,13 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.calcDamage(pokemon as PlayerPokemon, opponent, pokemonMove)
|
||||
|
||||
return `${effectiveness}x`;
|
||||
var calc = this.calcDamage(pokemon as PlayerPokemon, opponent, pokemonMove);
|
||||
if (calc != "") {
|
||||
if (this.scene.typeHints) return `${effectiveness}x - ${calc}`;
|
||||
return calc;
|
||||
}
|
||||
if (this.scene.typeHints) return `${effectiveness}x`;
|
||||
return "";
|
||||
}
|
||||
|
||||
displayMoves() {
|
||||
@ -379,36 +389,32 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
|
||||
}
|
||||
|
||||
calcDamage(user: PlayerPokemon, target: Pokemon, move: PokemonMove) {
|
||||
var dmgHigh = 0
|
||||
var dmgLow = 0
|
||||
var crit = target.tryCriticalHit(user, move.getMove(), true)
|
||||
var out = target.getAttackDamage(user, move.getMove(), false, false, crit, true)
|
||||
//console.log(out)
|
||||
var dmgHigh = out.damageHigh
|
||||
var dmgLow = out.damageLow
|
||||
var minHits = 1
|
||||
var maxHits = -1
|
||||
var maxHits = -1 // If nothing changes this value, it is set to minHits
|
||||
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;
|
||||
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
|
||||
}
|
||||
}
|
||||
if (maxHits == -1) {
|
||||
@ -417,45 +423,43 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
|
||||
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()
|
||||
minHits *= h[i].getStackCount()
|
||||
maxHits *= h[i].getStackCount()
|
||||
}
|
||||
}
|
||||
if (false) {
|
||||
dmgLow = dmgLow * minHits
|
||||
dmgHigh = dmgHigh * maxHits
|
||||
}
|
||||
var qSuffix = ""
|
||||
if (target.isBoss()) {
|
||||
var bossSegs = (target as EnemyPokemon).bossSegments
|
||||
//dmgLow /= bossSegs
|
||||
//dmgHigh /= bossSegs
|
||||
//qSuffix = "?"
|
||||
var shieldsBrokenLow = (target as EnemyPokemon).calculateBossClearedShields(dmgLow)
|
||||
var shieldsBrokenHigh = (target as EnemyPokemon).calculateBossClearedShields(dmgHigh)
|
||||
qSuffix = ` (${shieldsBrokenLow}-${shieldsBrokenHigh})`
|
||||
if (shieldsBrokenLow == shieldsBrokenHigh) {
|
||||
qSuffix = ` (${shieldsBrokenLow})`
|
||||
}
|
||||
dmgLow = (target as EnemyPokemon).calculateBossDamage(dmgLow);
|
||||
dmgHigh = (target as EnemyPokemon).calculateBossDamage(dmgHigh);
|
||||
}
|
||||
var dmgLowP = Math.round((dmgLow)/target.getMaxHp() * 100)
|
||||
var dmgHighP = Math.round((dmgHigh)/target.getMaxHp() * 100)
|
||||
/*
|
||||
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)"
|
||||
koText = " KO"
|
||||
} else if (Math.ceil(dmgHigh) >= target.hp) {
|
||||
var percentChance = Utils.rangemap(target.hp, dmgLow, dmgHigh, 0, 1)
|
||||
koText = " (" + Math.round(percentChance * 100) + "% KO)"
|
||||
koText = " " + Math.round(percentChance * 100) + "% KO"
|
||||
}
|
||||
//console.log(target.getMoveEffectiveness(user, move.getMove(), false, true) + "x - " + ((dmgLowP == dmgHighP) ? (dmgLowP + "%" + qSuffix) : (dmgLowP + "%-" + dmgHighP + "%" + qSuffix)) + koText)
|
||||
if (target.getMoveEffectiveness(user, move.getMove(), false, true) == undefined) {
|
||||
return "---"
|
||||
return ""
|
||||
}
|
||||
if (this.scene.damageDisplay == "Percent")
|
||||
return target.getMoveEffectiveness(user, move.getMove(), false, true) + "x - " + (dmgLowP == dmgHighP ? dmgLowP + "%" + qSuffix : dmgLowP + "%-" + dmgHighP + "%" + qSuffix) + koText
|
||||
return (dmgLowP == dmgHighP ? dmgLowP + "%" + qSuffix : dmgLowP + "%-" + dmgHighP + "%" + qSuffix) + koText
|
||||
if (this.scene.damageDisplay == "Value")
|
||||
return target.getMoveEffectiveness(user, move.getMove(), false, true) + "x - " + (dmgLowP == dmgHighP ? dmgLowP + "%" + qSuffix : dmgLowP + "%-" + dmgHighP + "%" + qSuffix) + koText
|
||||
return (dmgLow == dmgHigh ? dmgLow + qSuffix : dmgLow + "-" + dmgHigh + qSuffix) + koText
|
||||
//return target.getMoveEffectiveness(user, move.getMove(), false, true) + "x" + ((Math.floor(dmgLow) >= target.hp) ? " (KO)" : "")
|
||||
if (this.scene.damageDisplay == "Off")
|
||||
return target.getMoveEffectiveness(user, move.getMove(), false, true) + "x" + ((Math.floor(dmgLow) >= target.hp) ? " (KO)" : "")
|
||||
return "";
|
||||
}
|
||||
}
|
@ -461,7 +461,7 @@ export default class PartyUiHandler extends MessageUiHandler {
|
||||
this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger, false, true);
|
||||
}
|
||||
} else if (this.cursor) {
|
||||
(this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.POKEMON, this.cursor, option === PartyOption.PASS_BATON);
|
||||
(this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.POKEMON, false, this.cursor, option === PartyOption.PASS_BATON);
|
||||
}
|
||||
}
|
||||
if (this.partyUiMode !== PartyUiMode.MODIFIER && this.partyUiMode !== PartyUiMode.TM_MODIFIER && this.partyUiMode !== PartyUiMode.MOVE_MODIFIER) {
|
||||
|
Loading…
Reference in New Issue
Block a user