Add redirection logic to Lightning Rod and Storm Drain

pull/16/head
Flashfyre 2024-03-11 20:55:41 -04:00
parent 60c3c0a008
commit 71e820f149
5 changed files with 58 additions and 12 deletions

View File

@ -711,13 +711,15 @@ export default class BattleScene extends Phaser.Scene {
return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1));
}
getField(): Pokemon[] {
getField(activeOnly: boolean = false): Pokemon[] {
const ret = new Array(4).fill(null);
const playerField = this.getPlayerField();
const enemyField = this.getEnemyField();
ret.splice(0, playerField.length, ...playerField);
ret.splice(2, enemyField.length, ...enemyField);
return ret;
return activeOnly
? ret.filter(p => p?.isActive())
: ret;
}
getPokemonById(pokemonId: integer): Pokemon {
@ -935,7 +937,7 @@ export default class BattleScene extends Phaser.Scene {
updateFieldScale(): Promise<void> {
return new Promise(resolve => {
const fieldScale = Math.floor(Math.pow(1 / this.getField().filter(p => p?.isActive())
const fieldScale = Math.floor(Math.pow(1 / this.getField(true)
.map(p => p.getSpriteScale())
.reduce((highestScale: number, scale: number) => highestScale = Math.max(scale, highestScale), 0), 0.7) * 40
) / 40;

View File

@ -8,7 +8,7 @@ import { Weather, WeatherType } from "./weather";
import { BattlerTag } from "./battler-tags";
import { BattlerTagType } from "./enums/battler-tag-type";
import { StatusEffect, getStatusEffectDescriptor } from "./status-effect";
import Move, { AttackMove, MoveCategory, MoveFlags, RecoilAttr, StatusMoveTypeImmunityAttr } from "./move";
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, StatusMoveTypeImmunityAttr, allMoves } from "./move";
import { ArenaTagType } from "./enums/arena-tag-type";
import { Stat } from "./pokemon-stat";
import { PokemonHeldItemModifier } from "../modifier/modifier";
@ -1062,6 +1062,39 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr {
}
}
export class RedirectMoveAbAttr extends AbAttr {
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.canRedirect(args[0] as Moves)) {
const target = args[1] as Utils.IntegerHolder;
const newTarget = pokemon.getBattlerIndex();
if (target.value !== newTarget) {
target.value = newTarget;
return true;
}
}
return false;
}
canRedirect(moveId: Moves): boolean {
const move = allMoves[moveId];
return !![ MoveTarget.NEAR_OTHER, MoveTarget.OTHER ].find(t => move.moveTarget === t);
}
}
export class RedirectTypeMoveAbAttr extends RedirectMoveAbAttr {
public type: Type;
constructor(type: Type) {
super();
this.type = type;
}
canRedirect(moveId: Moves): boolean {
return super.canRedirect(moveId) && allMoves[moveId].type === this.type;
}
}
export class ReduceStatusEffectDurationAbAttr extends AbAttr {
private statusEffect: StatusEffect;
@ -1681,6 +1714,7 @@ export function initAbilities() {
new Ability(Abilities.NATURAL_CURE, "Natural Cure", "All status conditions heal when the Pokémon switches out.", 3)
.attr(PreSwitchOutResetStatusAbAttr),
new Ability(Abilities.LIGHTNING_ROD, "Lightning Rod", "The Pokémon draws in all Electric-type moves. Instead of being hit by Electric-type moves, it boosts its Sp. Atk.", 3)
.attr(RedirectTypeMoveAbAttr, Type.ELECTRIC)
.attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPATK, 1),
new Ability(Abilities.SERENE_GRACE, "Serene Grace (N)", "Boosts the likelihood of additional effects occurring when attacking.", 3),
new Ability(Abilities.SWIFT_SWIM, "Swift Swim", "Boosts the Pokémon's Speed stat in rain.", 3)
@ -1831,6 +1865,7 @@ export function initAbilities() {
.attr(PostSummonAddBattlerTagAbAttr, BattlerTagType.SLOW_START, 5),
new Ability(Abilities.SCRAPPY, "Scrappy (N)", "The Pokémon can hit Ghost-type Pokémon with Normal- and Fighting-type moves.", 4),
new Ability(Abilities.STORM_DRAIN, "Storm Drain", "Draws in all Water-type moves. Instead of being hit by Water-type moves, it boosts its Sp. Atk.", 4)
.attr(RedirectTypeMoveAbAttr, Type.WATER)
.attr(TypeImmunityStatChangeAbAttr, Type.WATER, BattleStat.SPATK, 1),
new Ability(Abilities.ICE_BODY, "Ice Body", "The Pokémon gradually regains HP in a hailstorm.", 4)
.attr(PostWeatherLapseHealAbAttr, 1, WeatherType.HAIL),

View File

@ -101,7 +101,7 @@ export class Weather {
}
isEffectSuppressed(scene: BattleScene): boolean {
const field = scene.getField().filter(p => p);
const field = scene.getField(true);
for (let pokemon of field) {
const suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr;

View File

@ -2303,7 +2303,7 @@ export class EnemyPokemon extends Pokemon {
getNextTargets(moveId: Moves): BattlerIndex[] {
const moveTargets = getMoveTargets(this, moveId);
const targets = this.scene.getField().filter(p => p?.isActive(true) && moveTargets.targets.indexOf(p.getBattlerIndex()) > -1);
const targets = this.scene.getField(true).filter(p => moveTargets.targets.indexOf(p.getBattlerIndex()) > -1);
if (moveTargets.multiple)
return targets.map(p => p.getBattlerIndex());

View File

@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getWeatherDamageMessage, ge
import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
import { ArenaTagType } from "./data/enums/arena-tag-type";
import { Abilities, CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability";
import { Abilities, 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 } from "./data/ability";
import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./field/arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
@ -369,7 +369,7 @@ export abstract class FieldPhase extends BattlePhase {
}
executeForAll(func: PokemonFunc): void {
const field = this.scene.getField().filter(p => p?.summonData && p.isActive());
const field = this.scene.getField(true).filter(p => p.summonData);
field.forEach(pokemon => func(pokemon));
}
}
@ -1867,6 +1867,15 @@ export class MovePhase extends BattlePhase {
return this.end();
}
// Move redirection abilities (ie. Storm Drain) only support single target moves
const moveTarget = this.targets.length === 1
? new Utils.IntegerHolder(this.targets[0])
: null;
if (moveTarget) {
this.scene.getField(true).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, this.move.moveId, moveTarget));
this.targets[0] = moveTarget.value;
}
if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) {
if (this.pokemon.turnData.attacksReceived.length) {
const attacker = this.pokemon.turnData.attacksReceived.length ? this.pokemon.scene.getPokemonById(this.pokemon.turnData.attacksReceived[0].sourceId) : null;
@ -1880,8 +1889,8 @@ export class MovePhase extends BattlePhase {
}
}
const targets = this.scene.getField().filter(p => {
if (p?.isActive(true) && this.targets.indexOf(p.getBattlerIndex()) > -1) {
const targets = this.scene.getField(true).filter(p => {
if (this.targets.indexOf(p.getBattlerIndex()) > -1) {
const hiddenTag = p.getTag(HiddenTag);
if (hiddenTag && !this.move.getMove().getAttrs(HitsTagAttr).filter(hta => (hta as HitsTagAttr).tagType === hiddenTag.tagType).length)
return false;
@ -2197,7 +2206,7 @@ export class MoveEffectPhase extends PokemonPhase {
}
getTargets(): Pokemon[] {
return this.scene.getField().filter(p => p?.isActive(true) && this.targets.indexOf(p.getBattlerIndex()) > -1);
return this.scene.getField(true).filter(p => this.targets.indexOf(p.getBattlerIndex()) > -1);
}
getTarget(): Pokemon {
@ -2690,7 +2699,7 @@ export class FaintPhase extends PokemonPhase {
}
pokemon.lapseTags(BattlerTagLapseType.FAINT);
this.scene.getField().filter(p => p !== pokemon && p?.isActive(true)).forEach(p => p.removeTagsBySourceId(pokemon.id));
this.scene.getField(true).filter(p => p !== pokemon).forEach(p => p.removeTagsBySourceId(pokemon.id));
pokemon.faintCry(() => {
const friendshipDecrease = new Utils.IntegerHolder(10);