pokerogue/src/data/moves/move-utils.ts
Sirz Benjie 48e911e03c
[Refactor] Remove circular deps 3 (#5959)
* Move game-mode to its own file

Reduces circular imports to 325

* Move battler-index to own file

Reduces circular deps to 314

* Move trainer-variant to own file

Reduces circ deps to 313

* Move enums in pokemon to their own file

* Move arena-tag-type to its own file

* Move pokemon-moves to its own file

* Move command to own file

* Move learnMoveType to own file

* Move form change item to own file

* Move battlerTagLapseType to own file

* Move anim enums to own shared file

* Move enums out of challenges

* Move species form change triggers to own file

Reduces circ imports to 291

* Update test importing pokemon move

* Replace move attribute imports with string names

* Untangle circular deps from game data

* Fix missing string call in switch summon phase

* Apply kev's suggestions from code review

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* Ensure ChargeMove's is method calls super

* Use InstanceType for proper narrowing

* Apply kev's suggestions from code review

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-06-09 16:24:13 -07:00

115 lines
3.8 KiB
TypeScript

import type Pokemon from "#app/field/pokemon";
import type { BattlerIndex } from "#enums/battler-index";
import type { MoveId } from "#enums/move-id";
import type { MoveTargetSet, UserMoveConditionFunc } from "./move";
import type Move from "./move";
import { NumberHolder, isNullOrUndefined } from "#app/utils/common";
import { MoveTarget } from "#enums/MoveTarget";
import { PokemonType } from "#enums/pokemon-type";
import { allMoves } from "#app/data/data-lists";
import { applyMoveAttrs } from "./apply-attrs";
import { BattlerTagType } from "#enums/battler-tag-type";
/**
* Return whether the move targets the field
*
* Examples include
* - Hazard moves like spikes
* - Weather moves like rain dance
* - User side moves like reflect and safeguard
*/
export function isFieldTargeted(move: Move): boolean {
switch (move.moveTarget) {
case MoveTarget.BOTH_SIDES:
case MoveTarget.USER_SIDE:
case MoveTarget.ENEMY_SIDE:
return true;
}
return false;
}
export function getMoveTargets(user: Pokemon, move: MoveId, replaceTarget?: MoveTarget): MoveTargetSet {
const variableTarget = new NumberHolder(0);
user.getOpponents(false).forEach(p => applyMoveAttrs("VariableTargetAttr", user, p, allMoves[move], variableTarget));
let moveTarget: MoveTarget | undefined;
if (allMoves[move].hasAttr("VariableTargetAttr")) {
moveTarget = variableTarget.value;
} else if (replaceTarget !== undefined) {
moveTarget = replaceTarget;
} else if (move) {
moveTarget = allMoves[move].moveTarget;
} else if (move === undefined) {
moveTarget = MoveTarget.NEAR_ENEMY;
}
const opponents = user.getOpponents(false);
let set: Pokemon[] = [];
let multiple = false;
const ally: Pokemon | undefined = user.getAlly();
switch (moveTarget) {
case MoveTarget.USER:
case MoveTarget.PARTY:
set = [user];
break;
case MoveTarget.NEAR_OTHER:
case MoveTarget.OTHER:
case MoveTarget.ALL_NEAR_OTHERS:
case MoveTarget.ALL_OTHERS:
set = !isNullOrUndefined(ally) ? opponents.concat([ally]) : opponents;
multiple = moveTarget === MoveTarget.ALL_NEAR_OTHERS || moveTarget === MoveTarget.ALL_OTHERS;
break;
case MoveTarget.NEAR_ENEMY:
case MoveTarget.ALL_NEAR_ENEMIES:
case MoveTarget.ALL_ENEMIES:
case MoveTarget.ENEMY_SIDE:
set = opponents;
multiple = moveTarget !== MoveTarget.NEAR_ENEMY;
break;
case MoveTarget.RANDOM_NEAR_ENEMY:
set = [opponents[user.randBattleSeedInt(opponents.length)]];
break;
case MoveTarget.ATTACKER:
return { targets: [-1 as BattlerIndex], multiple: false };
case MoveTarget.NEAR_ALLY:
case MoveTarget.ALLY:
set = !isNullOrUndefined(ally) ? [ally] : [];
break;
case MoveTarget.USER_OR_NEAR_ALLY:
case MoveTarget.USER_AND_ALLIES:
case MoveTarget.USER_SIDE:
set = !isNullOrUndefined(ally) ? [user, ally] : [user];
multiple = moveTarget !== MoveTarget.USER_OR_NEAR_ALLY;
break;
case MoveTarget.ALL:
case MoveTarget.BOTH_SIDES:
set = (!isNullOrUndefined(ally) ? [user, ally] : [user]).concat(opponents);
multiple = true;
break;
case MoveTarget.CURSE:
{
const extraTargets = !isNullOrUndefined(ally) ? [ally] : [];
set = user.getTypes(true).includes(PokemonType.GHOST) ? opponents.concat(extraTargets) : [user];
}
break;
}
return {
targets: set
.filter(p => p?.isActive(true))
.map(p => p.getBattlerIndex())
.filter(t => t !== undefined),
multiple,
};
}
export const frenzyMissFunc: UserMoveConditionFunc = (user: Pokemon, move: Move) => {
while (user.getMoveQueue().length && user.getMoveQueue()[0].move === move.id) {
user.getMoveQueue().shift();
}
user.removeTag(BattlerTagType.FRENZY); // FRENZY tag should be disrupted on miss/no effect
return true;
};