[Move] Implement True Force Switch (Roar, Whirlwind, Circle Throw, Dragon Tail) (#4881)

* Force switch implemenation

* Fix conditionals checking `SwitchType`

Use `prependToPhase()` instead of `unshiftPhase()`

* Update comments slightly, replace `||` chain with `.includes()`

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
Ethan 2024-11-29 01:43:12 -05:00 committed by GitHub
parent 48dc5d0ee7
commit de0d97739e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 75 additions and 29 deletions

View File

@ -5967,50 +5967,97 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
* Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch * Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch
* If it did, the user of U-turn or Volt Switch will not be switched out. * If it did, the user of U-turn or Volt Switch will not be switched out.
*/ */
if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr) && if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr)
(move.id === Moves.U_TURN || move.id === Moves.VOLT_SWITCH || move.id === Moves.FLIP_TURN) && [ Moves.U_TURN, Moves.VOLT_SWITCH, Moves.FLIP_TURN ].includes(move.id)
) { ) {
if (this.hpDroppedBelowHalf(target)) { if (this.hpDroppedBelowHalf(target)) {
return false; return false;
} }
} }
// Switch out logic for the player's Pokemon
if (switchOutTarget.scene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) { if (switchOutTarget.scene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
return false; return false;
} }
if (switchOutTarget.hp > 0) { if (switchOutTarget.hp > 0) {
if (this.switchType === SwitchType.FORCE_SWITCH) {
switchOutTarget.leaveField(true);
const slotIndex = Utils.randIntRange(user.scene.currentBattle.getBattlerCount(), user.scene.getPlayerParty().length);
user.scene.prependToPhase(
new SwitchSummonPhase(
user.scene,
this.switchType,
switchOutTarget.getFieldIndex(),
slotIndex,
false,
true
),
MoveEndPhase
);
} else {
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
user.scene.prependToPhase(new SwitchPhase(user.scene, this.switchType, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase); user.scene.prependToPhase(
new SwitchPhase(
user.scene,
this.switchType,
switchOutTarget.getFieldIndex(),
true,
true
),
MoveEndPhase
);
return true; return true;
} }
}
return false; return false;
} else if (user.scene.currentBattle.battleType !== BattleType.WILD) { } else if (user.scene.currentBattle.battleType !== BattleType.WILD) { // Switch out logic for enemy trainers
// Switch out logic for trainer battles
if (switchOutTarget.scene.getEnemyParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) { if (switchOutTarget.scene.getEnemyParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
return false; return false;
} }
if (switchOutTarget.hp > 0) { if (switchOutTarget.hp > 0) {
// for opponent switching out if (this.switchType === SwitchType.FORCE_SWITCH) {
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); switchOutTarget.leaveField(true);
user.scene.prependToPhase(new SwitchSummonPhase(user.scene, this.switchType, switchOutTarget.getFieldIndex(), const slotIndex = Utils.randIntRange(user.scene.currentBattle.getBattlerCount(), user.scene.getEnemyParty().length);
(user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0), user.scene.prependToPhase(
false, false), MoveEndPhase); new SwitchSummonPhase(
} user.scene,
this.switchType,
switchOutTarget.getFieldIndex(),
slotIndex,
false,
false
),
MoveEndPhase
);
} else { } else {
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
user.scene.prependToPhase(
new SwitchSummonPhase(
user.scene,
this.switchType,
switchOutTarget.getFieldIndex(),
(user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0),
false,
false
),
MoveEndPhase
);
}
}
} else { // Switch out logic for wild pokemon
/** /**
* Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch * Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch
* If it did, the user of U-turn or Volt Switch will not be switched out. * If it did, the user of U-turn or Volt Switch will not be switched out.
*/ */
if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr) && if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr)
(move.id === Moves.U_TURN || move.id === Moves.VOLT_SWITCH) || move.id === Moves.FLIP_TURN) { && [ Moves.U_TURN, Moves.VOLT_SWITCH, Moves.FLIP_TURN ].includes(move.id)
) {
if (this.hpDroppedBelowHalf(target)) { if (this.hpDroppedBelowHalf(target)) {
return false; return false;
} }
} }
// Switch out logic for everything else (eg: WILD battles)
if (user.scene.currentBattle.waveIndex % 10 === 0) { if (user.scene.currentBattle.waveIndex % 10 === 0) {
return false; return false;
} }
@ -7777,11 +7824,10 @@ export function initMoves() {
.windMove(), .windMove(),
new AttackMove(Moves.WING_ATTACK, Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 35, -1, 0, 1), new AttackMove(Moves.WING_ATTACK, Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 35, -1, 0, 1),
new StatusMove(Moves.WHIRLWIND, Type.NORMAL, -1, 20, -1, -6, 1) new StatusMove(Moves.WHIRLWIND, Type.NORMAL, -1, 20, -1, -6, 1)
.attr(ForceSwitchOutAttr) .attr(ForceSwitchOutAttr, false, SwitchType.FORCE_SWITCH)
.ignoresSubstitute() .ignoresSubstitute()
.hidesTarget() .hidesTarget()
.windMove() .windMove(),
.partial(), // Should force random switches
new ChargingAttackMove(Moves.FLY, Type.FLYING, MoveCategory.PHYSICAL, 90, 95, 15, -1, 0, 1) new ChargingAttackMove(Moves.FLY, Type.FLYING, MoveCategory.PHYSICAL, 90, 95, 15, -1, 0, 1)
.chargeText(i18next.t("moveTriggers:flewUpHigh", { pokemonName: "{USER}" })) .chargeText(i18next.t("moveTriggers:flewUpHigh", { pokemonName: "{USER}" }))
.chargeAttr(SemiInvulnerableAttr, BattlerTagType.FLYING) .chargeAttr(SemiInvulnerableAttr, BattlerTagType.FLYING)
@ -7857,10 +7903,9 @@ export function initMoves() {
.soundBased() .soundBased()
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),
new StatusMove(Moves.ROAR, Type.NORMAL, -1, 20, -1, -6, 1) new StatusMove(Moves.ROAR, Type.NORMAL, -1, 20, -1, -6, 1)
.attr(ForceSwitchOutAttr) .attr(ForceSwitchOutAttr, false, SwitchType.FORCE_SWITCH)
.soundBased() .soundBased()
.hidesTarget() .hidesTarget(),
.partial(), // Should force random switching
new StatusMove(Moves.SING, Type.NORMAL, 55, 15, -1, 0, 1) new StatusMove(Moves.SING, Type.NORMAL, 55, 15, -1, 0, 1)
.attr(StatusEffectAttr, StatusEffect.SLEEP) .attr(StatusEffectAttr, StatusEffect.SLEEP)
.soundBased(), .soundBased(),
@ -9222,8 +9267,8 @@ export function initMoves() {
.attr(StatStageChangeAttr, [ Stat.ATK ], 1, true) .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true)
.attr(StatStageChangeAttr, [ Stat.SPD ], 2, true), .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true),
new AttackMove(Moves.CIRCLE_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5) new AttackMove(Moves.CIRCLE_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5)
.attr(ForceSwitchOutAttr) .attr(ForceSwitchOutAttr, false, SwitchType.FORCE_SWITCH)
.partial(), // Should force random switches .hidesTarget(),
new AttackMove(Moves.INCINERATE, Type.FIRE, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5) new AttackMove(Moves.INCINERATE, Type.FIRE, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5)
.target(MoveTarget.ALL_NEAR_ENEMIES) .target(MoveTarget.ALL_NEAR_ENEMIES)
.attr(RemoveHeldItemAttr, true), .attr(RemoveHeldItemAttr, true),
@ -9291,9 +9336,8 @@ export function initMoves() {
new AttackMove(Moves.FROST_BREATH, Type.ICE, MoveCategory.SPECIAL, 60, 90, 10, 100, 0, 5) new AttackMove(Moves.FROST_BREATH, Type.ICE, MoveCategory.SPECIAL, 60, 90, 10, 100, 0, 5)
.attr(CritOnlyAttr), .attr(CritOnlyAttr),
new AttackMove(Moves.DRAGON_TAIL, Type.DRAGON, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5) new AttackMove(Moves.DRAGON_TAIL, Type.DRAGON, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5)
.attr(ForceSwitchOutAttr) .attr(ForceSwitchOutAttr, false, SwitchType.FORCE_SWITCH)
.hidesTarget() .hidesTarget(),
.partial(), // Should force random switches
new SelfStatusMove(Moves.WORK_UP, Type.NORMAL, -1, 30, -1, 0, 5) new SelfStatusMove(Moves.WORK_UP, Type.NORMAL, -1, 30, -1, 0, 5)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, true), .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, true),
new AttackMove(Moves.ELECTROWEB, Type.ELECTRIC, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5) new AttackMove(Moves.ELECTROWEB, Type.ELECTRIC, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5)

View File

@ -10,5 +10,7 @@ export enum SwitchType {
/** 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 the returning Pokemon's Substitute to the switched in Pokemon */
SHED_TAIL SHED_TAIL,
/** Force switchout to a random party member */
FORCE_SWITCH,
} }