Implement Snipe Shot, and Propeller Tail/Stalwart (#661)

* Implemented Snipe Shot and Stalwart/Propeller Tail

* Remove Testing Overrides

I don't know why these got pushed, they are in the gitignore file.

* Snipe Shot also has a high crit rate

* Add Comment

* Add TsDoc documentation to BypassRedirectAttr

* Add ability pop-up for when Propeller Tail/Stalwart proc.

* Fix Formatting

* Tab align comment
pull/720/head
Athebyne 2024-05-10 11:40:21 -04:00 committed by GitHub
parent 593ac38267
commit 2ab335a3c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 29 additions and 12 deletions

View File

@ -2377,6 +2377,8 @@ export class RedirectTypeMoveAbAttr extends RedirectMoveAbAttr {
} }
} }
export class BlockRedirectAbAttr extends AbAttr { }
export class ReduceStatusEffectDurationAbAttr extends AbAttr { export class ReduceStatusEffectDurationAbAttr extends AbAttr {
private statusEffect: StatusEffect; private statusEffect: StatusEffect;
@ -3465,7 +3467,7 @@ export function initAbilities() {
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.SPD, -1, false, true) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.SPD, -1, false, true)
.bypassFaint(), .bypassFaint(),
new Ability(Abilities.PROPELLER_TAIL, 8) new Ability(Abilities.PROPELLER_TAIL, 8)
.unimplemented(), .attr(BlockRedirectAbAttr),
new Ability(Abilities.MIRROR_ARMOR, 8) new Ability(Abilities.MIRROR_ARMOR, 8)
.ignorable() .ignorable()
.unimplemented(), .unimplemented(),
@ -3475,7 +3477,7 @@ export function initAbilities() {
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
.unimplemented(), .unimplemented(),
new Ability(Abilities.STALWART, 8) new Ability(Abilities.STALWART, 8)
.unimplemented(), .attr(BlockRedirectAbAttr),
new Ability(Abilities.STEAM_ENGINE, 8) new Ability(Abilities.STEAM_ENGINE, 8)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => (move.type === Type.FIRE || move.type === Type.WATER) && move.category !== MoveCategory.STATUS, BattleStat.SPD, 6), .attr(PostDefendStatChangeAbAttr, (target, user, move) => (move.type === Type.FIRE || move.type === Type.WATER) && move.category !== MoveCategory.STATUS, BattleStat.SPD, 6),
new Ability(Abilities.PUNK_ROCK, 8) new Ability(Abilities.PUNK_ROCK, 8)

View File

@ -2653,6 +2653,11 @@ const crashDamageFunc = (user: Pokemon, move: Move) => {
}; };
export class TypelessAttr extends MoveAttr { } export class TypelessAttr extends MoveAttr { }
/**
* Attribute used for moves which ignore redirection effects, and always target their original target, i.e. Snipe Shot
* Bypasses Storm Drain, Follow Me, Ally Switch, and the like.
*/
export class BypassRedirectAttr extends MoveAttr { }
export class DisableMoveAttr extends MoveEffectAttr { export class DisableMoveAttr extends MoveEffectAttr {
constructor() { constructor() {
@ -6172,7 +6177,8 @@ export function initMoves() {
.attr(DiscourageFrequentUseAttr) .attr(DiscourageFrequentUseAttr)
.ignoresVirtual(), .ignoresVirtual(),
new AttackMove(Moves.SNIPE_SHOT, Type.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 8) new AttackMove(Moves.SNIPE_SHOT, Type.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 8)
.partial(), .attr(HighCritAttr)
.attr(BypassRedirectAttr),
new AttackMove(Moves.JAW_LOCK, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8) new AttackMove(Moves.JAW_LOCK, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8)
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1)
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, true, false, 1) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, true, false, 1)

View File

@ -2,7 +2,7 @@ import BattleScene, { AnySound, bypassLogin, startingWave } from "./battle-scene
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon"; import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon";
import * as Utils from './utils'; import * as Utils from './utils';
import { Moves } from "./data/enums/moves"; import { Moves } from "./data/enums/moves";
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr, NoEffectAttr, FixedDamageAttr, PostVictoryStatChangeAttr, OneHitKOAccuracyAttr, ForceSwitchOutAttr, VariableTargetAttr } from "./data/move"; import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr, NoEffectAttr, BypassRedirectAttr ,FixedDamageAttr, PostVictoryStatChangeAttr, OneHitKOAccuracyAttr, ForceSwitchOutAttr, VariableTargetAttr } from "./data/move";
import { Mode } from './ui/ui'; import { Mode } from './ui/ui';
import { Command } from "./ui/command-ui-handler"; import { Command } from "./ui/command-ui-handler";
import { Stat } from "./data/pokemon-stat"; import { Stat } from "./data/pokemon-stat";
@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, get
import { TempBattleStat } from "./data/temp-battle-stat"; import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag"; import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
import { ArenaTagType } from "./data/enums/arena-tag-type"; import { ArenaTagType } from "./data/enums/arena-tag-type";
import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr } from "./data/ability"; import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr } from "./data/ability";
import { Unlockables, getUnlockableName } from "./system/unlockables"; import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./field/arena"; import { getBiomeKey } from "./field/arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle"; import { BattleType, BattlerIndex, TurnCommand } from "./battle";
@ -2211,13 +2211,22 @@ export class MovePhase extends BattlePhase {
} }
// Move redirection abilities (ie. Storm Drain) only support single target moves // Move redirection abilities (ie. Storm Drain) only support single target moves
const moveTarget = this.targets.length === 1 const moveTarget = this.targets.length === 1
? new Utils.IntegerHolder(this.targets[0]) ? new Utils.IntegerHolder(this.targets[0])
: null; : null;
if (moveTarget) { if (moveTarget) {
var oldTarget = moveTarget.value;
this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, this.move.moveId, moveTarget)); this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, this.move.moveId, moveTarget));
this.targets[0] = moveTarget.value; //Check if this move is immune to being redirected, and restore its target to the intended target if it is.
if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) || this.move.getMove().getAttrs(BypassRedirectAttr).length)) {
//If an ability prevented this move from being redirected, display its ability pop up.
if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) && !this.move.getMove().getAttrs(BypassRedirectAttr).length) && oldTarget != moveTarget.value) {
this.scene.unshiftPhase(new ShowAbilityPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr)));
} }
moveTarget.value = oldTarget;
}
this.targets[0] = moveTarget.value;
}
if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) { if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) {
if (this.pokemon.turnData.attacksReceived.length) { if (this.pokemon.turnData.attacksReceived.length) {