mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-04-19 16:18:32 +01:00
[Documentation] Add comments to MoveEffectPhase
(+ some style changes) (#3392)
* Document MoveEffectPhase * Edits based on Temp's suggestions
This commit is contained in:
parent
7f5e873457
commit
2e823b1914
138
src/phases.ts
138
src/phases.ts
@ -2957,9 +2957,11 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex, targets: BattlerIndex[], move: PokemonMove) {
|
constructor(scene: BattleScene, battlerIndex: BattlerIndex, targets: BattlerIndex[], move: PokemonMove) {
|
||||||
super(scene, battlerIndex);
|
super(scene, battlerIndex);
|
||||||
this.move = move;
|
this.move = move;
|
||||||
// In double battles, if the right Pokemon selects a spread move and the left Pokemon dies
|
/**
|
||||||
// with no party members available to switch in, then the right Pokemon takes the index
|
* In double battles, if the right Pokemon selects a spread move and the left Pokemon dies
|
||||||
// of the left Pokemon and gets hit unless this is checked.
|
* with no party members available to switch in, then the right Pokemon takes the index
|
||||||
|
* of the left Pokemon and gets hit unless this is checked.
|
||||||
|
*/
|
||||||
if (targets.includes(battlerIndex) && this.move.getMove().moveTarget === MoveTarget.ALL_NEAR_OTHERS) {
|
if (targets.includes(battlerIndex) && this.move.getMove().moveTarget === MoveTarget.ALL_NEAR_OTHERS) {
|
||||||
const i = targets.indexOf(battlerIndex);
|
const i = targets.indexOf(battlerIndex);
|
||||||
targets.splice(i, i + 1);
|
targets.splice(i, i + 1);
|
||||||
@ -2970,40 +2972,72 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
start() {
|
start() {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
|
/** The Pokemon using this phase's invoked move */
|
||||||
const user = this.getUserPokemon();
|
const user = this.getUserPokemon();
|
||||||
|
/** All Pokemon targeted by this phase's invoked move */
|
||||||
const targets = this.getTargets();
|
const targets = this.getTargets();
|
||||||
|
|
||||||
|
/** If the user was somehow removed from the field, end this phase */
|
||||||
if (!user?.isOnField()) {
|
if (!user?.isOnField()) {
|
||||||
return super.end();
|
return super.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does an effect from this move override other effects on this turn?
|
||||||
|
* e.g. Charging moves (Fly, etc.) on their first turn of use.
|
||||||
|
*/
|
||||||
const overridden = new Utils.BooleanHolder(false);
|
const overridden = new Utils.BooleanHolder(false);
|
||||||
|
/** The {@linkcode Move} object from {@linkcode allMoves} invoked by this phase */
|
||||||
const move = this.move.getMove();
|
const move = this.move.getMove();
|
||||||
|
|
||||||
// Assume single target for override
|
// Assume single target for override
|
||||||
applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget() ?? null, move, overridden, this.move.virtual).then(() => {
|
applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget() ?? null, move, overridden, this.move.virtual).then(() => {
|
||||||
|
// If other effects were overriden, stop this phase before they can be applied
|
||||||
if (overridden.value) {
|
if (overridden.value) {
|
||||||
return this.end();
|
return this.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
user.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
|
user.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this phase is for the first hit of the invoked move,
|
||||||
|
* resolve the move's total hit count. This block combines the
|
||||||
|
* effects of the move itself, Parental Bond, and Multi-Lens to do so.
|
||||||
|
*/
|
||||||
if (user.turnData.hitsLeft === undefined) {
|
if (user.turnData.hitsLeft === undefined) {
|
||||||
const hitCount = new Utils.IntegerHolder(1);
|
const hitCount = new Utils.IntegerHolder(1);
|
||||||
// Assume single target for multi hit
|
// Assume single target for multi hit
|
||||||
applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount);
|
applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount);
|
||||||
|
// If Parental Bond is applicable, double the hit count
|
||||||
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, targets.length, hitCount, new Utils.IntegerHolder(0));
|
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, targets.length, hitCount, new Utils.IntegerHolder(0));
|
||||||
|
// If Multi-Lens is applicable, multiply the hit count by 1 + the number of Multi-Lenses held by the user
|
||||||
if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) {
|
if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) {
|
||||||
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0));
|
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0));
|
||||||
}
|
}
|
||||||
user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value;
|
// Set the user's relevant turnData fields to reflect the final hit count
|
||||||
|
user.turnData.hitCount = hitCount.value;
|
||||||
|
user.turnData.hitsLeft = hitCount.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log to be entered into the user's move history once the move result is resolved.
|
||||||
|
* Note that `result` (a {@linkcode MoveResult}) logs whether the move was successfully
|
||||||
|
* used in the sense of it not failing or missing; it does not account for the move's
|
||||||
|
* effectiveness (which is logged as a {@linkcode HitResult}).
|
||||||
|
*/
|
||||||
const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual };
|
const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores results of hit checks of the invoked move against all targets, organized by battler index.
|
||||||
|
* @see {@linkcode hitCheck}
|
||||||
|
*/
|
||||||
const targetHitChecks = Object.fromEntries(targets.map(p => [p.getBattlerIndex(), this.hitCheck(p)]));
|
const targetHitChecks = Object.fromEntries(targets.map(p => [p.getBattlerIndex(), this.hitCheck(p)]));
|
||||||
const hasActiveTargets = targets.some(t => t.isActive(true));
|
const hasActiveTargets = targets.some(t => t.isActive(true));
|
||||||
|
/**
|
||||||
|
* If no targets are left for the move to hit (FAIL), or the invoked move is single-target
|
||||||
|
* (and not random target) and failed the hit check against its target (MISS), log the move
|
||||||
|
* as FAILed or MISSed (depending on the conditions above) and end this phase.
|
||||||
|
*/
|
||||||
if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) {
|
if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) {
|
||||||
this.stopMultiHit();
|
this.stopMultiHit();
|
||||||
if (hasActiveTargets) {
|
if (hasActiveTargets) {
|
||||||
@ -3018,6 +3052,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
return this.end();
|
return this.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** All move effect attributes are chained together in this array to be applied asynchronously. */
|
||||||
const applyAttrs: Promise<void>[] = [];
|
const applyAttrs: Promise<void>[] = [];
|
||||||
|
|
||||||
// Move animation only needs one target
|
// Move animation only needs one target
|
||||||
@ -3025,6 +3060,10 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
/** Has the move successfully hit a target (for damage) yet? */
|
/** Has the move successfully hit a target (for damage) yet? */
|
||||||
let hasHit: boolean = false;
|
let hasHit: boolean = false;
|
||||||
for (const target of targets) {
|
for (const target of targets) {
|
||||||
|
/**
|
||||||
|
* If the move missed a target, stop all future hits against that target
|
||||||
|
* and move on to the next target (if there is one).
|
||||||
|
*/
|
||||||
if (!targetHitChecks[target.getBattlerIndex()]) {
|
if (!targetHitChecks[target.getBattlerIndex()]) {
|
||||||
this.stopMultiHit(target);
|
this.stopMultiHit(target);
|
||||||
this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) }));
|
this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) }));
|
||||||
@ -3036,18 +3075,38 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Is the invoked move blocked by a protection effect on the target? */
|
||||||
const isProtected = !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target) && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType));
|
const isProtected = !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target) && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType));
|
||||||
|
|
||||||
|
/** Does this phase represent the invoked move's first strike? */
|
||||||
const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount);
|
const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount);
|
||||||
|
|
||||||
|
// Only log the move's result on the first strike
|
||||||
if (firstHit) {
|
if (firstHit) {
|
||||||
user.pushMoveHistory(moveHistoryEntry);
|
user.pushMoveHistory(moveHistoryEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since all fail/miss checks have applied, the move is considered successfully applied.
|
||||||
|
* It's worth noting that if the move has no effect or is protected against, it's move
|
||||||
|
* result is still logged as a SUCCESS.
|
||||||
|
*/
|
||||||
moveHistoryEntry.result = MoveResult.SUCCESS;
|
moveHistoryEntry.result = MoveResult.SUCCESS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the result of applying the invoked move to the target.
|
||||||
|
* If the target is protected, the result is always `NO_EFFECT`.
|
||||||
|
* Otherwise, the hit result is based on type effectiveness, immunities,
|
||||||
|
* and other factors that may negate the attack or status application.
|
||||||
|
*
|
||||||
|
* Internally, the call to {@linkcode Pokemon.apply} is where damage is calculated
|
||||||
|
* (for attack moves) and the target's HP is updated. However, this isn't
|
||||||
|
* made visible to the user until the resulting {@linkcode DamagePhase}
|
||||||
|
* is invoked.
|
||||||
|
*/
|
||||||
const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT;
|
const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT;
|
||||||
|
|
||||||
|
/** Does {@linkcode hitResult} indicate that damage was dealt to the target? */
|
||||||
const dealsDamage = [
|
const dealsDamage = [
|
||||||
HitResult.EFFECTIVE,
|
HitResult.EFFECTIVE,
|
||||||
HitResult.SUPER_EFFECTIVE,
|
HitResult.SUPER_EFFECTIVE,
|
||||||
@ -3055,28 +3114,53 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
HitResult.ONE_HIT_KO
|
HitResult.ONE_HIT_KO
|
||||||
].includes(hitResult);
|
].includes(hitResult);
|
||||||
|
|
||||||
|
/** Is this target the first one hit by the move on its current strike? */
|
||||||
const firstTarget = dealsDamage && !hasHit;
|
const firstTarget = dealsDamage && !hasHit;
|
||||||
if (firstTarget) {
|
if (firstTarget) {
|
||||||
hasHit = true;
|
hasHit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Does this phase represent the invoked move's last strike? */
|
||||||
const lastHit = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive());
|
const lastHit = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the user can change forms by using the invoked move,
|
||||||
|
* it only changes forms after the move's last hit
|
||||||
|
* (see Relic Song's interaction with Parental Bond when used by Meloetta).
|
||||||
|
*/
|
||||||
if (lastHit) {
|
if (lastHit) {
|
||||||
this.scene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger);
|
this.scene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Promise that applys *all* effects from the invoked move's MoveEffectAttrs.
|
||||||
|
* These are ordered by trigger type (see {@linkcode MoveEffectTrigger}), and each trigger
|
||||||
|
* type requires different conditions to be met with respect to the move's hit result.
|
||||||
|
*/
|
||||||
applyAttrs.push(new Promise(resolve => {
|
applyAttrs.push(new Promise(resolve => {
|
||||||
|
// Apply all effects with PRE_MOVE triggers (if the target isn't immune to the move)
|
||||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && hitResult !== HitResult.NO_EFFECT,
|
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && hitResult !== HitResult.NO_EFFECT,
|
||||||
user, target, move).then(() => {
|
user, target, move).then(() => {
|
||||||
|
// All other effects require the move to not have failed or have been cancelled to trigger
|
||||||
if (hitResult !== HitResult.FAIL) {
|
if (hitResult !== HitResult.FAIL) {
|
||||||
|
/** Are the move's effects tied to the first turn of a charge move? */
|
||||||
const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget() ?? null, move));
|
const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget() ?? null, move));
|
||||||
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present
|
/**
|
||||||
|
* If the invoked move's effects are meant to trigger during the move's "charge turn,"
|
||||||
|
* ignore all effects after this point.
|
||||||
|
* Otherwise, apply all self-targeted POST_APPLY effects.
|
||||||
|
*/
|
||||||
Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY
|
Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY
|
||||||
&& attr.selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, move)).then(() => {
|
&& attr.selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, move)).then(() => {
|
||||||
|
// All effects past this point require the move to have hit the target
|
||||||
if (hitResult !== HitResult.NO_EFFECT) {
|
if (hitResult !== HitResult.NO_EFFECT) {
|
||||||
|
// Apply all non-self-targeted POST_APPLY effects
|
||||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY
|
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY
|
||||||
&& !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, this.move.getMove()).then(() => {
|
&& !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, this.move.getMove()).then(() => {
|
||||||
|
/**
|
||||||
|
* If the move hit, and the target doesn't have Shield Dust,
|
||||||
|
* apply the chance to flinch the target gained from King's Rock
|
||||||
|
*/
|
||||||
if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr)) {
|
if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr)) {
|
||||||
const flinched = new Utils.BooleanHolder(false);
|
const flinched = new Utils.BooleanHolder(false);
|
||||||
user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
|
user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
|
||||||
@ -3084,15 +3168,23 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
|
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If the move was not protected against, apply all HIT effects
|
||||||
Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT
|
Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT
|
||||||
&& (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && (!attr.firstTargetOnly || firstTarget), user, target, this.move.getMove()).then(() => {
|
&& (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && (!attr.firstTargetOnly || firstTarget), user, target, this.move.getMove()).then(() => {
|
||||||
|
// Apply the target's post-defend ability effects (as long as the target is active or can otherwise apply them)
|
||||||
return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult).then(() => {
|
return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult).then(() => {
|
||||||
|
// If the invoked move is an enemy attack, apply the enemy's status effect-inflicting tags and tokens
|
||||||
target.lapseTag(BattlerTagType.BEAK_BLAST_CHARGING);
|
target.lapseTag(BattlerTagType.BEAK_BLAST_CHARGING);
|
||||||
if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) {
|
if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) {
|
||||||
user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target);
|
user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target);
|
||||||
}
|
}
|
||||||
})).then(() => {
|
})).then(() => {
|
||||||
|
// Apply the user's post-attack ability effects
|
||||||
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult).then(() => {
|
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult).then(() => {
|
||||||
|
/**
|
||||||
|
* If the invoked move is an attack, apply the user's chance to
|
||||||
|
* steal an item from the target granted by Grip Claw
|
||||||
|
*/
|
||||||
if (this.move.getMove() instanceof AttackMove) {
|
if (this.move.getMove() instanceof AttackMove) {
|
||||||
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target);
|
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target);
|
||||||
}
|
}
|
||||||
@ -3112,7 +3204,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// Trigger effect which should only apply one time on the last hit after all targeted effects have already applied
|
// Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved
|
||||||
const postTarget = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()) ?
|
const postTarget = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()) ?
|
||||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, user, null, move) :
|
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, user, null, move) :
|
||||||
null;
|
null;
|
||||||
@ -3125,6 +3217,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for all move effects to finish applying, then end this phase
|
||||||
Promise.allSettled(applyAttrs).then(() => this.end());
|
Promise.allSettled(applyAttrs).then(() => this.end());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -3134,6 +3227,13 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
const move = this.move.getMove();
|
const move = this.move.getMove();
|
||||||
move.type = move.defaultType;
|
move.type = move.defaultType;
|
||||||
const user = this.getUserPokemon();
|
const user = this.getUserPokemon();
|
||||||
|
/**
|
||||||
|
* If this phase isn't for the invoked move's last strike,
|
||||||
|
* unshift another MoveEffectPhase for the next strike.
|
||||||
|
* Otherwise, queue a message indicating the number of times the move has struck
|
||||||
|
* (if the move has struck more than once), then apply the heal from Shell Bell
|
||||||
|
* to the user.
|
||||||
|
*/
|
||||||
if (user) {
|
if (user) {
|
||||||
if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) {
|
if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) {
|
||||||
this.scene.unshiftPhase(this.getNewHitPhase());
|
this.scene.unshiftPhase(this.getNewHitPhase());
|
||||||
@ -3152,6 +3252,11 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
super.end();
|
super.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves whether this phase's invoked move hits or misses the given target
|
||||||
|
* @param target {@linkcode Pokemon} the Pokemon targeted by the invoked move
|
||||||
|
* @returns `true` if the move does not miss the target; `false` otherwise
|
||||||
|
*/
|
||||||
hitCheck(target: Pokemon): boolean {
|
hitCheck(target: Pokemon): boolean {
|
||||||
// Moves targeting the user and entry hazards can't miss
|
// Moves targeting the user and entry hazards can't miss
|
||||||
if ([MoveTarget.USER, MoveTarget.ENEMY_SIDE].includes(this.move.getMove().moveTarget)) {
|
if ([MoveTarget.USER, MoveTarget.ENEMY_SIDE].includes(this.move.getMove().moveTarget)) {
|
||||||
@ -3182,8 +3287,8 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hiddenTag = target.getTag(SemiInvulnerableTag);
|
const semiInvulnerableTag = target.getTag(SemiInvulnerableTag);
|
||||||
if (hiddenTag && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === hiddenTag.tagType)) {
|
if (semiInvulnerableTag && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3199,6 +3304,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct?
|
return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the {@linkcode Pokemon} using this phase's invoked move */
|
||||||
getUserPokemon(): Pokemon | undefined {
|
getUserPokemon(): Pokemon | undefined {
|
||||||
if (this.battlerIndex > BattlerIndex.ENEMY_2) {
|
if (this.battlerIndex > BattlerIndex.ENEMY_2) {
|
||||||
return this.scene.getPokemonById(this.battlerIndex) ?? undefined;
|
return this.scene.getPokemonById(this.battlerIndex) ?? undefined;
|
||||||
@ -3206,14 +3312,20 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex];
|
return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns an array of all {@linkcode Pokemon} targeted by this phase's invoked move */
|
||||||
getTargets(): Pokemon[] {
|
getTargets(): Pokemon[] {
|
||||||
return this.scene.getField(true).filter(p => this.targets.indexOf(p.getBattlerIndex()) > -1);
|
return this.scene.getField(true).filter(p => this.targets.indexOf(p.getBattlerIndex()) > -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the first target of this phase's invoked move */
|
||||||
getTarget(): Pokemon | undefined {
|
getTarget(): Pokemon | undefined {
|
||||||
return this.getTargets().find(() => true);
|
return this.getTargets()[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given {@linkcode Pokemon} from this phase's target list
|
||||||
|
* @param target {@linkcode Pokemon} the Pokemon to be removed
|
||||||
|
*/
|
||||||
removeTarget(target: Pokemon): void {
|
removeTarget(target: Pokemon): void {
|
||||||
const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex());
|
const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex());
|
||||||
if (targetIndex !== -1) {
|
if (targetIndex !== -1) {
|
||||||
@ -3221,6 +3333,11 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents subsequent strikes of this phase's invoked move from occurring
|
||||||
|
* @param target {@linkcode Pokemon} if defined, only stop subsequent
|
||||||
|
* strikes against this Pokemon
|
||||||
|
*/
|
||||||
stopMultiHit(target?: Pokemon): void {
|
stopMultiHit(target?: Pokemon): void {
|
||||||
/** If given a specific target, remove the target from subsequent strikes */
|
/** If given a specific target, remove the target from subsequent strikes */
|
||||||
if (target) {
|
if (target) {
|
||||||
@ -3236,6 +3353,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a new MoveEffectPhase with the same properties as this phase */
|
||||||
getNewHitPhase() {
|
getNewHitPhase() {
|
||||||
return new MoveEffectPhase(this.scene, this.battlerIndex, this.targets, this.move);
|
return new MoveEffectPhase(this.scene, this.battlerIndex, this.targets, this.move);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user