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() {
|
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 {
|
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);
|
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
||||||
|
|
||||||
if (switchOutTarget.hp > 0) {
|
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 true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -5,8 +5,25 @@
|
|||||||
export enum SwitchType {
|
export enum SwitchType {
|
||||||
/** Basic switchout where the Pokemon to switch in is selected */
|
/** Basic switchout where the Pokemon to switch in is selected */
|
||||||
SWITCH,
|
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 */
|
/** Transfers stat stages and other effects from the returning Pokemon to the switched in Pokemon */
|
||||||
BATON_PASS,
|
BATON_PASS,
|
||||||
/** Transfers the returning Pokemon's Substitute to the switched in Pokemon */
|
/** Transfers stat stages and other effects from the returning Pokemon to the switched in Pokemon
|
||||||
SHED_TAIL
|
*
|
||||||
|
* 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);
|
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 {
|
trySelectMove(moveIndex: integer, ignorePp?: boolean): boolean {
|
||||||
const move = this.getMoveset().length > moveIndex
|
const move = this.getMoveset().length > moveIndex
|
||||||
? this.getMoveset()[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 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 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 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.
|
* @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;
|
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
||||||
|
|
||||||
/** A base damage multiplier based on the source's level */
|
/** 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.
|
* - `result`: {@linkcode HitResult} indicates the attack's type effectiveness.
|
||||||
* - `damage`: `number` the attack's final damage output.
|
* - `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 damage = new Utils.NumberHolder(0);
|
||||||
const damageMin = new Utils.NumberHolder(0);
|
const damageMin = new Utils.NumberHolder(0);
|
||||||
const damageMax = new Utils.NumberHolder(0);
|
const damageMax = new Utils.NumberHolder(0);
|
||||||
@ -2496,7 +2505,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return {
|
return {
|
||||||
cancelled: cancelled.value,
|
cancelled: cancelled.value,
|
||||||
result: move.id === Moves.SHEER_COLD ? HitResult.IMMUNE : HitResult.NO_EFFECT,
|
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 {
|
return {
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
result: HitResult.EFFECTIVE,
|
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 {
|
return {
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
result: HitResult.ONE_HIT_KO,
|
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
|
* 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
|
* 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) */
|
/** 25% damage debuff on moves hitting more than one non-fainted target (regardless of immunities) */
|
||||||
const { targets, multiple } = getMoveTargets(source, move.id);
|
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 */
|
/** 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);
|
applyAbAttrs(MultCritAbAttr, source, null, simulated, criticalMultiplier);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2609,47 +2628,71 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
? 0.5
|
? 0.5
|
||||||
: 1;
|
: 1;
|
||||||
|
|
||||||
damage.value = Utils.toDmgValue(
|
damage.value = 1
|
||||||
baseDamage
|
|
||||||
* targetMultiplier
|
* targetMultiplier
|
||||||
* parentalBondMultiplier.value
|
* parentalBondMultiplier.value
|
||||||
* arenaAttackTypeMultiplier.value
|
* arenaAttackTypeMultiplier.value
|
||||||
* glaiveRushMultiplier.value
|
* glaiveRushMultiplier.value
|
||||||
* criticalMultiplier.value
|
|
||||||
* randomMultiplier
|
|
||||||
* stabMultiplier.value
|
* stabMultiplier.value
|
||||||
* typeMultiplier
|
* typeMultiplier
|
||||||
* burnMultiplier.value
|
* burnMultiplier.value
|
||||||
* screenMultiplier.value
|
* screenMultiplier.value
|
||||||
* hitsTagMultiplier.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 */
|
/** Doubles damage if the attacker has Tinted Lens and is using a resisted move */
|
||||||
if (!ignoreSourceAbility) {
|
if (!ignoreSourceAbility) {
|
||||||
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, simulated, damage);
|
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, simulated, damageMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Apply the enemy's Damage and Resistance tokens */
|
/** Apply the enemy's Damage and Resistance tokens */
|
||||||
if (!source.isPlayer()) {
|
if (!source.isPlayer()) {
|
||||||
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage);
|
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damageMultiplier);
|
||||||
}
|
}
|
||||||
if (!this.isPlayer()) {
|
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) */
|
/** Apply this Pokemon's post-calc defensive modifiers (e.g. Fur Coat) */
|
||||||
if (!ignoreAbility) {
|
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.
|
// This attribute may modify damage arbitrarily, so be careful about changing its order of application.
|
||||||
applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage);
|
applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage);
|
||||||
|
|
||||||
if (this.isFullHp() && !ignoreAbility) {
|
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)
|
// debug message for when damage is applied (i.e. not simulated)
|
||||||
if (!simulated) {
|
if (!simulated) {
|
||||||
console.log("damage", damage.value, move.name);
|
console.log("damage", damage.value, move.name);
|
||||||
@ -2667,10 +2710,54 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return {
|
return {
|
||||||
cancelled: cancelled.value,
|
cancelled: cancelled.value,
|
||||||
result: hitResult,
|
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
|
* Applies the results of a move to this pokemon
|
||||||
* @param source The {@linkcode Pokemon} using the move
|
* @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;
|
return (typeMultiplier === 0) ? HitResult.NO_EFFECT : HitResult.STATUS;
|
||||||
} else {
|
} else {
|
||||||
/** Determines whether the attack critically hits */
|
/** Determines whether the attack critically hits */
|
||||||
let isCritical: boolean;
|
let critical: CritResult = this.tryCriticalHit(source, move);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide);
|
const { cancelled, result, damage: dmg } = this.getAttackDamage(source, move, false, false, critical, false);
|
||||||
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 typeBoost = source.findTag(t => t instanceof TypeBoostTag && t.boostedType === source.getMoveType(move)) as TypeBoostTag;
|
const typeBoost = source.findTag(t => t instanceof TypeBoostTag && t.boostedType === source.getMoveType(move)) as TypeBoostTag;
|
||||||
if (typeBoost?.oneUse) {
|
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
|
* 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.
|
* 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 (damage > 0) {
|
||||||
if (source.isPlayer()) {
|
if (source.isPlayer()) {
|
||||||
@ -2762,7 +2832,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
source.turnData.currDamageDealt = damage;
|
source.turnData.currDamageDealt = damage;
|
||||||
this.turnData.damageTaken += damage;
|
this.turnData.damageTaken += damage;
|
||||||
this.battleData.hitCount++;
|
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);
|
this.turnData.attacksReceived.unshift(attackResult);
|
||||||
if (source.isPlayer() && !this.isPlayer()) {
|
if (source.isPlayer() && !this.isPlayer()) {
|
||||||
this.scene.applyModifiers(DamageMoneyRewardModifier, true, source, new Utils.NumberHolder(damage));
|
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"));
|
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 {
|
export default interface Pokemon {
|
||||||
scene: BattleScene
|
scene: BattleScene
|
||||||
}
|
}
|
||||||
@ -4590,7 +4669,7 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
return move.category !== MoveCategory.STATUS
|
return move.category !== MoveCategory.STATUS
|
||||||
&& moveTargets.some(p => {
|
&& moveTargets.some(p => {
|
||||||
const doesNotFail = move.applyConditions(this, p, move) || [Moves.SUCKER_PUNCH, Moves.UPPER_HAND, Moves.THUNDERCLAP].includes(move.id);
|
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);
|
}, this);
|
||||||
|
|
||||||
@ -4812,6 +4891,72 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
return 0;
|
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 {
|
damage(damage: integer, ignoreSegments: boolean = false, preventEndure: boolean = false, ignoreFaintPhase: boolean = false): integer {
|
||||||
if (this.isFainted()) {
|
if (this.isFainted()) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -5097,6 +5242,8 @@ export interface DamageCalculationResult {
|
|||||||
result: HitResult;
|
result: HitResult;
|
||||||
/** The damage dealt by the move */
|
/** The damage dealt by the move */
|
||||||
damage: number;
|
damage: number;
|
||||||
|
damageLow: number;
|
||||||
|
damageHigh: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1819,361 +1819,6 @@ export function setEvioliteOverride(v: string) {
|
|||||||
evioliteOverride = v;
|
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 = {
|
const modifierPool: ModifierPool = {
|
||||||
[ModifierTier.COMMON]: [
|
[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.showText(i18next.t("battle:switchQuestion", { pokemonName: this.useName ? getPokemonNameWithAffix(pokemon) : i18next.t("battle:pokemon") }), null, () => {
|
||||||
this.scene.ui.setMode(Mode.CONFIRM, () => {
|
this.scene.ui.setMode(Mode.CONFIRM, () => {
|
||||||
|
// Yes, I want to Pre-Switch
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
LoggerTools.isPreSwitch.value = true
|
LoggerTools.isPreSwitch.value = true
|
||||||
this.scene.tryRemovePhase(p => p instanceof PostSummonPhase && p.player && p.fieldIndex === this.fieldIndex);
|
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.scene.pokemonInfoContainer.hide()
|
||||||
this.end();
|
this.end();
|
||||||
}, () => {
|
}, () => {
|
||||||
|
// No, I want to leave my Pokémon as is
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
for (var i = 0; i < this.scene.getEnemyField().length; i++) {
|
for (var i = 0; i < this.scene.getEnemyField().length; i++) {
|
||||||
this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.toggleFlyout(false)
|
this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.toggleFlyout(false)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
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 { TrappedTag, EncoreTag } from "#app/data/battler-tags";
|
||||||
import { MoveTargetSet, getMoveTargets } from "#app/data/move";
|
import { MoveTargetSet, getMoveTargets } from "#app/data/move";
|
||||||
import { speciesStarters } from "#app/data/pokemon-species";
|
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 { Biome } from "#app/enums/biome";
|
||||||
import { Moves } from "#app/enums/moves";
|
import { Moves } from "#app/enums/moves";
|
||||||
import { PokeballType } from "#app/enums/pokeball";
|
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 { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { Command } from "#app/ui/command-ui-handler";
|
import { Command } from "#app/ui/command-ui-handler";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
@ -17,7 +17,9 @@ import { FieldPhase } from "./field-phase";
|
|||||||
import { SelectTargetPhase } from "./select-target-phase";
|
import { SelectTargetPhase } from "./select-target-phase";
|
||||||
import * as LoggerTools from "../logger";
|
import * as LoggerTools from "../logger";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
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 {
|
export class CommandPhase extends FieldPhase {
|
||||||
protected fieldIndex: integer;
|
protected fieldIndex: integer;
|
||||||
@ -39,6 +41,11 @@ export class CommandPhase extends FieldPhase {
|
|||||||
} else {
|
} else {
|
||||||
const allyCommand = this.scene.currentBattle.turnCommands[this.fieldIndex - 1];
|
const allyCommand = this.scene.currentBattle.turnCommands[this.fieldIndex - 1];
|
||||||
if (allyCommand?.command === Command.BALL || allyCommand?.command === Command.RUN) {
|
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 };
|
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: allyCommand?.command, skip: true };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,6 +59,7 @@ export class CommandPhase extends FieldPhase {
|
|||||||
|
|
||||||
const moveQueue = playerPokemon.getMoveQueue();
|
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]
|
while (moveQueue.length && moveQueue[0]
|
||||||
&& moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move)
|
&& 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?
|
|| !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) {
|
if (moveQueue.length) {
|
||||||
const queuedMove = moveQueue[0];
|
const queuedMove = moveQueue[0];
|
||||||
if (!queuedMove.move) {
|
if (!queuedMove.move) {
|
||||||
this.handleCommand(Command.FIGHT, -1, false);
|
// Struggle
|
||||||
|
this.handleCommand(Command.FIGHT, false, -1, false);
|
||||||
} else {
|
} else {
|
||||||
|
// Locate the queued move in our moveset
|
||||||
const moveIndex = playerPokemon.getMoveset().findIndex(m => m?.moveId === queuedMove.move);
|
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?
|
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 {
|
} 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);
|
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];
|
const playerPokemon = this.scene.getPlayerField()[this.fieldIndex];
|
||||||
let success: boolean;
|
let success: boolean;
|
||||||
|
|
||||||
|
if (!logCommand) {
|
||||||
|
LoggerTools.Actions[this.fieldIndex] = "%SKIP"
|
||||||
|
}
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case Command.FIGHT:
|
case Command.FIGHT:
|
||||||
let useStruggle = false;
|
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 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 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];
|
const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, moveId) : args[2];
|
||||||
|
let moveData: PokemonMove | undefined;
|
||||||
if (!moveId) {
|
if (!moveId) {
|
||||||
turnCommand.targets = [this.fieldIndex];
|
turnCommand.targets = [this.fieldIndex];
|
||||||
|
} else {
|
||||||
|
moveData = new PokemonMove(moveId, 0, 0, true);
|
||||||
}
|
}
|
||||||
console.log(moveTargets, getPokemonNameWithAffix(playerPokemon));
|
console.log(moveTargets, getPokemonNameWithAffix(playerPokemon));
|
||||||
if (moveTargets.targets.length > 1 && moveTargets.multiple) {
|
if (moveTargets.targets.length > 1 && moveTargets.multiple) {
|
||||||
this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex));
|
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) {
|
if (moveTargets.targets.length <= 1 || moveTargets.multiple) {
|
||||||
turnCommand.move!.targets = moveTargets.targets; //TODO: is the bang correct here?
|
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) {
|
} 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?
|
turnCommand.move!.targets = playerPokemon.getMoveQueue()[0].targets; //TODO: is the bang correct here?
|
||||||
} else {
|
} else {
|
||||||
this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex));
|
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;
|
this.scene.currentBattle.turnCommands[this.fieldIndex] = turnCommand;
|
||||||
success = true;
|
success = true;
|
||||||
@ -170,7 +193,9 @@ export class CommandPhase extends FieldPhase {
|
|||||||
} else {
|
} else {
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor };
|
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor };
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets;
|
this.scene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets;
|
||||||
|
LoggerTools.Actions[this.fieldIndex] = "Ball"
|
||||||
if (this.fieldIndex) {
|
if (this.fieldIndex) {
|
||||||
|
LoggerTools.Actions.shift()
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true;
|
this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true;
|
||||||
}
|
}
|
||||||
success = true;
|
success = true;
|
||||||
@ -180,10 +205,15 @@ export class CommandPhase extends FieldPhase {
|
|||||||
break;
|
break;
|
||||||
case Command.POKEMON:
|
case Command.POKEMON:
|
||||||
case Command.RUN:
|
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;
|
const isSwitch = command === Command.POKEMON;
|
||||||
|
/** Pulls data from the `BattleScene` */
|
||||||
const { currentBattle, arena } = this.scene;
|
const { currentBattle, arena } = this.scene;
|
||||||
|
/** Whether the player can flee from this Mystery Encounter */
|
||||||
const mysteryEncounterFleeAllowed = currentBattle.mysteryEncounter?.fleeAllowed;
|
const mysteryEncounterFleeAllowed = currentBattle.mysteryEncounter?.fleeAllowed;
|
||||||
if (!isSwitch && (arena.biomeType === Biome.END || (!isNullOrUndefined(mysteryEncounterFleeAllowed) && !mysteryEncounterFleeAllowed))) {
|
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.COMMAND, this.fieldIndex);
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
this.scene.ui.showText(i18next.t("battle:noEscapeForce"), null, () => {
|
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);
|
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||||
}, null, true);
|
}, null, true);
|
||||||
} else if (!isSwitch && (currentBattle.battleType === BattleType.TRAINER || currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE)) {
|
} 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.COMMAND, this.fieldIndex);
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
this.scene.ui.showText(i18next.t("battle:noEscapeTrainer"), null, () => {
|
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);
|
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||||
}, null, true);
|
}, null, true);
|
||||||
} else {
|
} 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;
|
const batonPass = isSwitch && args[0] as boolean;
|
||||||
|
/** Stores reasons why you cannot run away or switch your Trapped Pokémon. */
|
||||||
const trappedAbMessages: string[] = [];
|
const trappedAbMessages: string[] = [];
|
||||||
if (batonPass || !playerPokemon.isTrapped(trappedAbMessages)) {
|
if (batonPass || !playerPokemon.isTrapped(trappedAbMessages)) { // We are attempting to Baton Pass
|
||||||
currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
||||||
? { command: Command.POKEMON, cursor: cursor, args: args }
|
? { command: Command.POKEMON, cursor: cursor, args: args }
|
||||||
: { command: Command.RUN };
|
: { command: Command.RUN };
|
||||||
success = true;
|
success = true;
|
||||||
|
LoggerTools.Actions[this.fieldIndex] = isSwitch ? `Switch to ${this.scene.getParty()[cursor].name}` : "Run from battle"
|
||||||
if (!isSwitch && this.fieldIndex) {
|
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;
|
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) {
|
if (!isSwitch) {
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
}
|
}
|
||||||
@ -218,14 +261,16 @@ export class CommandPhase extends FieldPhase {
|
|||||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||||
}
|
}
|
||||||
}, null, true);
|
}, null, true);
|
||||||
} else {
|
} else { // We are not attempting to Baton Pass
|
||||||
const trapTag = playerPokemon.getTag(TrappedTag);
|
const trapTag = playerPokemon.getTag(TrappedTag);
|
||||||
|
|
||||||
// trapTag should be defined at this point, but just in case...
|
// trapTag should be defined at this point, but just in case...
|
||||||
if (!trapTag) {
|
if (!trapTag) {
|
||||||
|
// If the Pokémon is not trapped, break from this block and set the action
|
||||||
currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
||||||
? { command: Command.POKEMON, cursor: cursor, args: args }
|
? { command: Command.POKEMON, cursor: cursor, args: args }
|
||||||
: { command: Command.RUN };
|
: { command: Command.RUN };
|
||||||
|
LoggerTools.Actions[this.fieldIndex] = isSwitch ? `Switch to ${this.scene.getParty()[cursor].name}` : "Run from battle"
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +305,9 @@ export class CommandPhase extends FieldPhase {
|
|||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
if (this.fieldIndex) {
|
if (this.fieldIndex) {
|
||||||
|
LoggerTools.Actions[0] = ""
|
||||||
this.scene.unshiftPhase(new CommandPhase(this.scene, 0));
|
this.scene.unshiftPhase(new CommandPhase(this.scene, 0));
|
||||||
|
LoggerTools.Actions[1] = ""
|
||||||
this.scene.unshiftPhase(new CommandPhase(this.scene, 1));
|
this.scene.unshiftPhase(new CommandPhase(this.scene, 1));
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
@ -281,11 +328,39 @@ export class CommandPhase extends FieldPhase {
|
|||||||
return false;
|
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;
|
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 {
|
getFieldIndex(): integer {
|
||||||
return this.fieldIndex;
|
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.
|
* push a phase that prompts the player to summon a Pokemon from their party.
|
||||||
*/
|
*/
|
||||||
LoggerTools.isFaintSwitch.value = true;
|
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 {
|
} else {
|
||||||
this.scene.unshiftPhase(new VictoryPhase(this.scene, this.battlerIndex));
|
this.scene.unshiftPhase(new VictoryPhase(this.scene, this.battlerIndex));
|
||||||
if ([BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.scene.currentBattle.battleType)) {
|
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;
|
const hasReservePartyMember = !!this.scene.getEnemyParty().filter(p => p.isActive() && !p.isOnField() && p.trainerSlot === (pokemon as EnemyPokemon).trainerSlot).length;
|
||||||
if (hasReservePartyMember) {
|
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();
|
const playerField = this.scene.getPlayerField();
|
||||||
playerField.forEach((pokemon, i) => {
|
playerField.forEach((pokemon, i) => {
|
||||||
if (!pokemon.isAllowedInBattle() && legalPlayerPartyPokemon.length > 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 rerollCount: integer;
|
||||||
private modifierTiers?: ModifierTier[];
|
private modifierTiers?: ModifierTier[];
|
||||||
private customModifierSettings?: CustomModifierSettings;
|
private customModifierSettings?: CustomModifierSettings;
|
||||||
private modifierPredictions: ModifierTypeOption[][] = [];
|
private modifierPredictions?: ModifierTypeOption[][] = [];
|
||||||
private predictionCost: integer = 0;
|
private predictionCost: integer = 0;
|
||||||
private costTiers: integer[] = [];
|
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);
|
super(scene);
|
||||||
|
|
||||||
this.rerollCount = rerollCount;
|
this.rerollCount = rerollCount;
|
||||||
this.modifierTiers = modifierTiers;
|
this.modifierTiers = modifierTiers;
|
||||||
this.customModifierSettings = customModifierSettings;
|
this.customModifierSettings = customModifierSettings;
|
||||||
this.modifierPredictions = []
|
|
||||||
if (modifierPredictions != undefined) {
|
|
||||||
this.modifierPredictions = modifierPredictions;
|
this.modifierPredictions = modifierPredictions;
|
||||||
}
|
this.predictionCost = predictionCost
|
||||||
this.predictionCost = 0
|
|
||||||
this.costTiers = []
|
this.costTiers = []
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +43,7 @@ export class SelectModifierPhase extends BattlePhase {
|
|||||||
this.scene.applyModifiers(ExtraModifierModifier, true, modifierCount);
|
this.scene.applyModifiers(ExtraModifierModifier, true, modifierCount);
|
||||||
}
|
}
|
||||||
if (modifierOverride) {
|
if (modifierOverride) {
|
||||||
//modifierCount.value = modifierOverride
|
modifierCount.value = modifierOverride
|
||||||
}
|
}
|
||||||
const typeOptions: ModifierTypeOption[] = this.getModifierTypeOptions(modifierCount.value);
|
const typeOptions: ModifierTypeOption[] = this.getModifierTypeOptions(modifierCount.value);
|
||||||
typeOptions.forEach((option, idx) => {
|
typeOptions.forEach((option, idx) => {
|
||||||
@ -63,9 +60,11 @@ export class SelectModifierPhase extends BattlePhase {
|
|||||||
//console.log(option.type.name)
|
//console.log(option.type.name)
|
||||||
})
|
})
|
||||||
//console.log("====================")
|
//console.log("====================")
|
||||||
|
if (this.modifierPredictions != undefined) {
|
||||||
this.modifierPredictions[rerollOverride] = typeOptions
|
this.modifierPredictions[rerollOverride] = typeOptions
|
||||||
this.costTiers.push(this.predictionCost)
|
this.costTiers.push(this.predictionCost)
|
||||||
this.predictionCost += this.getRerollCost(typeOptions, false, rerollOverride)
|
this.predictionCost += this.getRerollCost(typeOptions, false, rerollOverride)
|
||||||
|
}
|
||||||
//Phaser.Math.RND.state(STATE) // Restore RNG state like nothing happened
|
//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?
|
return !cost!;// TODO: is the bang correct?
|
||||||
};
|
};
|
||||||
if (this.rerollCount == 0) {
|
if (this.rerollCount == 0) {
|
||||||
if (true) {
|
if (this.modifierPredictions == undefined) {
|
||||||
|
// Do nothing
|
||||||
|
} else if (true) {
|
||||||
this.modifierPredictions.forEach((mp, r) => {
|
this.modifierPredictions.forEach((mp, r) => {
|
||||||
// costTiers
|
// costTiers
|
||||||
console.log("Rerolls: " + r + (this.costTiers[r] != 0 ? " - ₽" + this.costTiers[r] : ""))
|
console.log("Rerolls: " + r + (this.costTiers[r] != 0 ? " - ₽" + this.costTiers[r] : ""))
|
||||||
@ -305,73 +306,8 @@ export class SelectModifierPhase extends BattlePhase {
|
|||||||
if (m.eviolite) {
|
if (m.eviolite) {
|
||||||
console.log(" With Eviolite unlocked: " + m.eviolite.name)
|
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));
|
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 { BattlerIndex } from "#app/battle";
|
||||||
import { Command } from "#app/ui/command-ui-handler";
|
import { Command } from "#app/ui/command-ui-handler";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
import { CommandPhase } from "./command-phase";
|
import { CommandPhase, targIDs } from "./command-phase";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import * as LoggerTools from "../logger";
|
import * as LoggerTools from "../logger";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
@ -32,9 +32,11 @@ export class SelectTargetPhase extends PokemonPhase {
|
|||||||
// Cancel this action
|
// Cancel this action
|
||||||
if (targets.length < 1) {
|
if (targets.length < 1) {
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex] = null;
|
this.scene.currentBattle.turnCommands[this.fieldIndex] = null;
|
||||||
|
LoggerTools.Actions[this.fieldIndex] = "";
|
||||||
this.scene.unshiftPhase(new CommandPhase(this.scene, this.fieldIndex));
|
this.scene.unshiftPhase(new CommandPhase(this.scene, this.fieldIndex));
|
||||||
} else {
|
} else {
|
||||||
turnCommand!.targets = targets; //TODO: is the bang correct here?
|
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) {
|
if (turnCommand?.command === Command.BALL && this.fieldIndex) {
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; //TODO: is the bang correct here?
|
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();
|
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) => {
|
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 (this.isModal) {console.error("Forced Switch Detected")}
|
||||||
if (slotIndex >= this.scene.currentBattle.getBattlerCount() && slotIndex < 6) {
|
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) {
|
switch (this.switchType) {
|
||||||
case SwitchType.SWITCH:
|
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)}`);
|
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Switch ${LoggerTools.playerPokeName(this.scene, fieldIndex)} to ${LoggerTools.playerPokeName(this.scene, slotIndex)}`);
|
||||||
break;
|
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)}`);
|
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Baton-Pass ${LoggerTools.playerPokeName(this.scene, fieldIndex)} to ${LoggerTools.playerPokeName(this.scene, slotIndex)}`);
|
||||||
break;
|
break;
|
||||||
case SwitchType.SHED_TAIL:
|
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;
|
break;
|
||||||
case SwitchType.PRE_SWITCH:
|
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)}`);
|
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:
|
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)}`);
|
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Pre-Switch & Baton-Pass ${LoggerTools.playerPokeName(this.scene, fieldIndex)} to ${LoggerTools.playerPokeName(this.scene, slotIndex)}`);
|
||||||
break;
|
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));
|
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();
|
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));
|
(this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(pokemon.id));
|
||||||
const substitute = pokemon.getTag(SubstituteTag);
|
const substitute = pokemon.getTag(SubstituteTag);
|
||||||
if (substitute) {
|
if (substitute) {
|
||||||
@ -107,7 +107,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
const switchedInPokemon = party[this.slotIndex];
|
const switchedInPokemon = party[this.slotIndex];
|
||||||
this.lastPokemon = this.getPokemon();
|
this.lastPokemon = this.getPokemon();
|
||||||
applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon);
|
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));
|
(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)) {
|
if (!this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id)) {
|
||||||
const batonPassModifier = this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier
|
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.
|
* 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.
|
* 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);
|
const substitute = this.lastPokemon.getTag(SubstituteTag);
|
||||||
if (substitute) {
|
if (substitute) {
|
||||||
switchedInPokemon.x += this.lastPokemon.getSubstituteOffset()[0];
|
switchedInPokemon.x += this.lastPokemon.getSubstituteOffset()[0];
|
||||||
@ -176,7 +176,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
pokemon.battleSummonData.turnCount--;
|
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);
|
pokemon.transferSummon(this.lastPokemon);
|
||||||
} else if (this.switchType === SwitchType.SHED_TAIL && pokemon) {
|
} else if (this.switchType === SwitchType.SHED_TAIL && pokemon) {
|
||||||
const subTag = this.lastPokemon.getTag(SubstituteTag);
|
const subTag = this.lastPokemon.getTag(SubstituteTag);
|
||||||
|
@ -33,26 +33,8 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
console.log(turnCommand.targets, turnCommand.move!.targets)
|
console.log(turnCommand.targets, turnCommand.move!.targets)
|
||||||
if (turnCommand.args && turnCommand.args[1] && turnCommand.args[1].isContinuing != undefined) {
|
if (turnCommand.args && turnCommand.args[1] && turnCommand.args[1].isContinuing != undefined) {
|
||||||
console.log(mv.getName(), targets)
|
console.log(mv.getName(), targets)
|
||||||
} else {
|
} else if (LoggerTools.Actions[pokemon.getBattlerIndex()].substring(0, 5) == "[???]") {
|
||||||
LoggerTools.Actions[pokemon.getBattlerIndex()] = mv.getName()
|
LoggerTools.Actions[pokemon.getBattlerIndex()] = mv.getName() + LoggerTools.Actions[pokemon.getBattlerIndex()].substring(5)
|
||||||
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])
|
|
||||||
}
|
|
||||||
console.log(mv.getName(), targets)
|
console.log(mv.getName(), targets)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,6 +197,7 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
break;
|
break;
|
||||||
case Command.POKEMON:
|
case Command.POKEMON:
|
||||||
const switchType = turnCommand.args?.[0] ? SwitchType.BATON_PASS : SwitchType.SWITCH;
|
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()));
|
this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, switchType, pokemon.getFieldIndex(), turnCommand.cursor!, true, pokemon.isPlayer()));
|
||||||
break;
|
break;
|
||||||
case Command.RUN:
|
case Command.RUN:
|
||||||
@ -257,10 +240,12 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
if (LoggerTools.Actions.length > 1 && !this.scene.currentBattle.double) {
|
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
|
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
|
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
|
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(" & "))
|
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));
|
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.LOCK_CAPSULE));
|
||||||
}
|
}
|
||||||
if (this.scene.currentBattle.waveIndex % 10) {
|
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) {
|
} else if (this.scene.gameMode.isDaily) {
|
||||||
LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "")
|
LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "")
|
||||||
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_CHARM));
|
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]);
|
vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]);
|
||||||
|
|
||||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
||||||
commandPhase.handleCommand(Command.RUN, 0);
|
commandPhase.handleCommand(Command.RUN, false, 0);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
||||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
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]);
|
vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]);
|
||||||
|
|
||||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
||||||
commandPhase.handleCommand(Command.RUN, 0);
|
commandPhase.handleCommand(Command.RUN, false, 0);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
||||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
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]);
|
vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]);
|
||||||
|
|
||||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
||||||
commandPhase.handleCommand(Command.RUN, 0);
|
commandPhase.handleCommand(Command.RUN, false, 0);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
||||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
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]);
|
vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]);
|
||||||
|
|
||||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
||||||
commandPhase.handleCommand(Command.RUN, 0);
|
commandPhase.handleCommand(Command.RUN, false, 0);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
||||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
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.scene.ui.setMode(Mode.FIGHT, (this.game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||||
});
|
});
|
||||||
this.game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
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) {
|
if (targetIndex !== null) {
|
||||||
|
@ -76,7 +76,7 @@ export default class BallUiHandler extends UiHandler {
|
|||||||
success = true;
|
success = true;
|
||||||
if (button === Button.ACTION && this.cursor < pokeballTypeCount) {
|
if (button === Button.ACTION && this.cursor < pokeballTypeCount) {
|
||||||
if (this.scene.pokeballCounts[this.cursor]) {
|
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.COMMAND, commandPhase.getFieldIndex());
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
success = true;
|
success = true;
|
||||||
|
@ -1075,7 +1075,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
this.currentEffectiveness = effectiveness;
|
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);
|
this.effectivenessContainer.setVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ export default class CommandUiHandler extends UiHandler {
|
|||||||
break;
|
break;
|
||||||
// Run
|
// Run
|
||||||
case Command.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;
|
success = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
|
|||||||
|
|
||||||
if (button === Button.CANCEL || button === Button.ACTION) {
|
if (button === Button.CANCEL || button === Button.ACTION) {
|
||||||
if (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;
|
success = true;
|
||||||
} else {
|
} else {
|
||||||
ui.playError();
|
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
|
* Gets multiplier text for a pokemon's move against a specific opponent.
|
||||||
* Returns undefined if it's a status move
|
* 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 {
|
private getEffectivenessText(pokemon: Pokemon, opponent: Pokemon, pokemonMove: PokemonMove): string | undefined {
|
||||||
const effectiveness = opponent.getMoveEffectiveness(pokemon, pokemonMove.getMove(), !opponent.battleData?.abilityRevealed);
|
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 undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.calcDamage(pokemon as PlayerPokemon, opponent, pokemonMove)
|
var calc = this.calcDamage(pokemon as PlayerPokemon, opponent, pokemonMove);
|
||||||
|
if (calc != "") {
|
||||||
return `${effectiveness}x`;
|
if (this.scene.typeHints) return `${effectiveness}x - ${calc}`;
|
||||||
|
return calc;
|
||||||
|
}
|
||||||
|
if (this.scene.typeHints) return `${effectiveness}x`;
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
displayMoves() {
|
displayMoves() {
|
||||||
@ -379,36 +389,32 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
calcDamage(user: PlayerPokemon, target: Pokemon, move: PokemonMove) {
|
calcDamage(user: PlayerPokemon, target: Pokemon, move: PokemonMove) {
|
||||||
var dmgHigh = 0
|
|
||||||
var dmgLow = 0
|
|
||||||
var crit = target.tryCriticalHit(user, move.getMove(), true)
|
var crit = target.tryCriticalHit(user, move.getMove(), true)
|
||||||
var out = target.getAttackDamage(user, move.getMove(), false, false, crit, true)
|
var out = target.getAttackDamage(user, move.getMove(), false, false, crit, true)
|
||||||
//console.log(out)
|
//console.log(out)
|
||||||
|
var dmgHigh = out.damageHigh
|
||||||
|
var dmgLow = out.damageLow
|
||||||
var minHits = 1
|
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)
|
var mh = move.getMove().getAttrs(MoveData.MultiHitAttr)
|
||||||
for (var i = 0; i < mh.length; i++) {
|
for (var i = 0; i < mh.length; i++) {
|
||||||
var mh2 = mh[i] as MoveData.MultiHitAttr
|
var mh2 = mh[i] as MoveData.MultiHitAttr
|
||||||
switch (mh2.multiHitType) {
|
switch (mh2.multiHitType) {
|
||||||
case MoveData.MultiHitType._2:
|
case MoveData.MultiHitType._2:
|
||||||
minHits = 2;
|
minHits = 2;
|
||||||
//maxHits = 2;
|
|
||||||
case MoveData.MultiHitType._2_TO_5:
|
case MoveData.MultiHitType._2_TO_5:
|
||||||
minHits = 2;
|
minHits = 2;
|
||||||
//maxHits = 5;
|
maxHits = 5;
|
||||||
case MoveData.MultiHitType._3:
|
case MoveData.MultiHitType._3:
|
||||||
minHits = 3;
|
minHits = 3;
|
||||||
//maxHits = 3;
|
|
||||||
case MoveData.MultiHitType._10:
|
case MoveData.MultiHitType._10:
|
||||||
minHits = 10;
|
minHits = 10;
|
||||||
//maxHits = 10;
|
|
||||||
case MoveData.MultiHitType.BEAT_UP:
|
case MoveData.MultiHitType.BEAT_UP:
|
||||||
const party = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
|
const party = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
|
||||||
// No status means the ally pokemon can contribute to Beat Up
|
// No status means the ally pokemon can contribute to Beat Up
|
||||||
minHits = party.reduce((total, pokemon) => {
|
minHits = party.reduce((total, pokemon) => {
|
||||||
return total + (pokemon.id === user.id ? 1 : pokemon?.status && pokemon.status.effect !== StatusEffect.NONE ? 0 : 1);
|
return total + (pokemon.id === user.id ? 1 : pokemon?.status && pokemon.status.effect !== StatusEffect.NONE ? 0 : 1);
|
||||||
}, 0);
|
}, 0);
|
||||||
//maxHits = minHits
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (maxHits == -1) {
|
if (maxHits == -1) {
|
||||||
@ -417,45 +423,43 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
|
|||||||
var h = user.getHeldItems()
|
var h = user.getHeldItems()
|
||||||
for (var i = 0; i < h.length; i++) {
|
for (var i = 0; i < h.length; i++) {
|
||||||
if (h[i].type instanceof PokemonMultiHitModifierType) {
|
if (h[i].type instanceof PokemonMultiHitModifierType) {
|
||||||
minHits += h[i].getStackCount()
|
minHits *= h[i].getStackCount()
|
||||||
maxHits += h[i].getStackCount()
|
maxHits *= h[i].getStackCount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (false) {
|
||||||
dmgLow = dmgLow * minHits
|
dmgLow = dmgLow * minHits
|
||||||
dmgHigh = dmgHigh * maxHits
|
dmgHigh = dmgHigh * maxHits
|
||||||
|
}
|
||||||
var qSuffix = ""
|
var qSuffix = ""
|
||||||
if (target.isBoss()) {
|
if (target.isBoss()) {
|
||||||
var bossSegs = (target as EnemyPokemon).bossSegments
|
var shieldsBrokenLow = (target as EnemyPokemon).calculateBossClearedShields(dmgLow)
|
||||||
//dmgLow /= bossSegs
|
var shieldsBrokenHigh = (target as EnemyPokemon).calculateBossClearedShields(dmgHigh)
|
||||||
//dmgHigh /= bossSegs
|
qSuffix = ` (${shieldsBrokenLow}-${shieldsBrokenHigh})`
|
||||||
//qSuffix = "?"
|
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 dmgLowP = Math.round((dmgLow)/target.getMaxHp() * 100)
|
||||||
var dmgHighP = Math.round((dmgHigh)/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 = ""
|
var koText = ""
|
||||||
if (Math.floor(dmgLow) >= target.hp) {
|
if (Math.floor(dmgLow) >= target.hp) {
|
||||||
koText = " (KO)"
|
koText = " KO"
|
||||||
} else if (Math.ceil(dmgHigh) >= target.hp) {
|
} else if (Math.ceil(dmgHigh) >= target.hp) {
|
||||||
var percentChance = Utils.rangemap(target.hp, dmgLow, dmgHigh, 0, 1)
|
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)
|
//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) {
|
if (target.getMoveEffectiveness(user, move.getMove(), false, true) == undefined) {
|
||||||
return "---"
|
return ""
|
||||||
}
|
}
|
||||||
if (this.scene.damageDisplay == "Percent")
|
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")
|
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)" : "")
|
//return target.getMoveEffectiveness(user, move.getMove(), false, true) + "x" + ((Math.floor(dmgLow) >= target.hp) ? " (KO)" : "")
|
||||||
if (this.scene.damageDisplay == "Off")
|
return "";
|
||||||
return target.getMoveEffectiveness(user, move.getMove(), false, true) + "x" + ((Math.floor(dmgLow) >= target.hp) ? " (KO)" : "")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -461,7 +461,7 @@ export default class PartyUiHandler extends MessageUiHandler {
|
|||||||
this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger, false, true);
|
this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger, false, true);
|
||||||
}
|
}
|
||||||
} else if (this.cursor) {
|
} 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) {
|
if (this.partyUiMode !== PartyUiMode.MODIFIER && this.partyUiMode !== PartyUiMode.TM_MODIFIER && this.partyUiMode !== PartyUiMode.MOVE_MODIFIER) {
|
||||||
|
Loading…
Reference in New Issue
Block a user