Skip to content
Draft
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
99e1571
[AI-Rework] Add starter code for move scoring functions (#356)
innerthunder Jan 26, 2025
a6fd730
Merge branch 'beta' into ai-rework
innerthunder Jan 26, 2025
dcb5294
Merge branch 'beta' into ai-rework
innerthunder Jan 31, 2025
8683c7a
Merge branch 'beta' into ai-rework
innerthunder Feb 4, 2025
ab30a8e
[AI-Rework] Rewrite enemy command selection (#403)
innerthunder Feb 6, 2025
d750b99
[AI-Rework] Add move-property-based scoring (#442)
innerthunder Feb 9, 2025
aaed8bf
Merge branch 'beta' into ai-rework
innerthunder Feb 9, 2025
62bcdc7
Prettier
innerthunder Feb 9, 2025
e3f3e56
Merge branch 'beta' into ai-rework
innerthunder Mar 5, 2025
7922965
Fix remaining conflicts from latest merge
innerthunder Mar 6, 2025
7c6dd9a
[AI-Rework] Add Move Attribute scoring parameters under `MoveAttrOpti…
innerthunder Mar 10, 2025
7155e44
Merge branch 'beta' into ai-rework
innerthunder Mar 13, 2025
28f65ee
Resolve remaining merge conflicts
innerthunder Mar 13, 2025
9246262
Merge branch 'beta' into ai-rework
innerthunder May 2, 2025
7f6b1bc
Resolve leftover conflicts from last merge
innerthunder May 2, 2025
6e2538e
Merge branch 'beta' into ai-rework
innerthunder May 5, 2025
19def79
Resolve remaining conflicts from last merge
innerthunder May 5, 2025
515eb12
[Addendum][AI-Rework][Test] Add matchers + Improve test utils (#1125)
innerthunder May 6, 2025
ffc11ff
[AI-Rework] Add effect scores for ability-changing effects (#834)
innerthunder May 9, 2025
56c5493
[AI-Rework] Add effect scores for arena-tag-based moves (#1158)
innerthunder May 14, 2025
899189d
Merge branch 'beta' into ai/beta-update-1
innerthunder May 15, 2025
700193e
Fix typecheck and linter issues
innerthunder May 15, 2025
dadd6c9
[AI-Rework] Rewrite `MoveCondition` + update Condition Score logic (#…
innerthunder May 21, 2025
7364400
Merge branch 'beta' into ai-rework
innerthunder May 23, 2025
006c3ce
Resolve remaining conflicts from last merge
innerthunder May 23, 2025
c1095d5
[AI-Rework] Remove `appliesScoreOnKO` option (#1222)
innerthunder May 27, 2025
30099ae
[AI-Rework] Add caching for invariant scoring calculations (#1258)
innerthunder Jun 21, 2025
1e06496
Merge branch 'beta' into ai-rework
innerthunder Jun 30, 2025
ede5db1
Run linter
innerthunder Jun 30, 2025
f95af98
[AI-Rework] Re-implement Instant Tera commands (#1283)
innerthunder Jul 17, 2025
6fafb86
Resolve merge issues
DayKev Jul 19, 2025
e5421d8
Remove unnecessary optional chaining
DayKev Jul 19, 2025
d7a7750
Apply review suggestions
innerthunder Jul 25, 2025
bbbb984
[AI-Rework] Add effect scores for moves with `BattlerTag` effects (#1…
innerthunder Jul 28, 2025
556a779
[AI-Rework] Add Effect Scores for Arena/Battler-tag-modifying effects…
innerthunder Aug 4, 2025
651c183
Merge branch 'beta' into ai-rework
innerthunder Aug 4, 2025
b2f357d
Merge branch 'ai-rework' of https://github.com/Despair-Games/poketern…
innerthunder Aug 4, 2025
0ea4e60
Resolve merge issues
innerthunder Aug 4, 2025
17b1c37
Fix flaky Rapid Spin tests
innerthunder Aug 4, 2025
b8fe6d0
ai-rework: Add Effect Scores for Status Effect inflicting moves (#1312)
innerthunder Aug 14, 2025
367886d
Simplify Wake Up Slap condition
innerthunder Aug 14, 2025
34e12f9
ai-rework: Add Effect Scores for misc. status-effect-related attribut…
innerthunder Aug 20, 2025
ebc2e1a
ai-rework: Add Effect Scores for healing effects (#1330)
innerthunder Aug 20, 2025
5c07d48
Merge branch 'beta' into ai-rework
innerthunder Aug 20, 2025
810d6c9
Fix linter issues
innerthunder Aug 20, 2025
783e2ee
Merge branch 'beta' into ai-rework
innerthunder Aug 21, 2025
b669649
Run linters
innerthunder Aug 21, 2025
b50943f
ai-rework: Rewrite Ally Target scoring logic (#1331)
innerthunder Aug 23, 2025
4814b46
ai-rework: Add scoring for `StatStageChangeAttr` + subclasses (#1337)
innerthunder Aug 28, 2025
9e94fca
Merge branch 'beta' into ai-rework
innerthunder Aug 28, 2025
cf0d2d4
Fix leftover conflict in `overrides-helper`
innerthunder Aug 28, 2025
42b9785
Reduce Focus Energy test flakiness
innerthunder Aug 28, 2025
d9069b8
ai-rework: Move ability- and move-revealing test utils to `FieldHelpe…
innerthunder Aug 29, 2025
5eafff2
Add AI paths to `create-test` script
innerthunder Aug 29, 2025
eba3d5f
ai-rework: Add Effect Scores for misc. stat-modifying attributes (#1340)
innerthunder Aug 31, 2025
14cdeb9
ai-rework: Rework multi-target move scoring logic (#1344)
innerthunder Aug 31, 2025
816c015
ai-rework: Add Effect Scores for self-damaging move effects (#1346)
innerthunder Sep 5, 2025
c35fef1
ai-rework: Add Effect Scores for Terrain- and Weather-changing effect…
innerthunder Sep 14, 2025
ad39b43
ai-rework: Update Effect Score calculation for charge moves (#1358)
innerthunder Sep 14, 2025
204dfd8
Merge branch 'beta' into ai-rework
innerthunder Sep 16, 2025
c80f075
Merge branch 'beta' into ai-rework
innerthunder Sep 16, 2025
5015ca4
Replace `isNil` references
innerthunder Sep 16, 2025
2bedbae
ai-rework: Add Effect Scores for type-modifying moves (#1362)
innerthunder Sep 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default tseslint.config(
"no-extra-semi": "error", // Disallows unnecessary semicolons for TypeScript-specific syntax
"import-x/extensions": ["error", "never", { json: "always" }], // Enforces no extension for imports unless json
"no-relative-import-paths/no-relative-import-paths": [
// Enforces absolute paths only (for example, converts "./data/moves/move-attrs/call-move-attr" to "#app/data/moves/move-attrs/call-move-attr")
// Enforces absolute paths only (for example, converts "./data/moves/move-attrs/call-move-attr" to "#moves/move-attrs/call-move-attr")
Comment thread
innerthunder marked this conversation as resolved.
Outdated
"error",
{ rootDir: "src", prefix: "#app" },
],
Expand Down
10 changes: 9 additions & 1 deletion src/@types/move-condition-func.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import type { Pokemon } from "#field/pokemon";
import type { Move } from "#moves/move";

export type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
/**
* A function representing a {@linkcode Move | Move's} condition.
* @param user - The {@linkcode Pokemon} using the move
* @param target - The {@linkcode Pokemon} targeted by the move
* @param move - The {@linkcode Move} being used
* @param simulated - (Optional) If `true`, suppresses changes to game state
* @returns `true` if the condition is satisfied in the given battle state
*/
export type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move, simulated?: boolean) => boolean;
8 changes: 8 additions & 0 deletions src/@types/move-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { MoveId } from "#enums/move-id";

/**
* A function to filter moves based on their properties.
* @param moveId - The {@linkcode MoveId} of the move to evaluate
* @returns `true` if the filter applies to the given move
*/
export type MoveFilter = (moveId: MoveId) => boolean;
25 changes: 25 additions & 0 deletions src/@types/pokemon-score-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-enable @typescript-eslint/no-unused-vars */
// -- end tsdoc imports --

import type { MoveId } from "#enums/move-id";

/**
* Caches invariant data for the Enemy AI to reuse
* during its score calculations for turn action selection against
* a "target" {@linkcode Pokemon}. This is stored within a "source"
* Pokemon's {@linkcode PokemonTurnData}, and is thus reset at the start of each turn.
*/
export interface PokemonScoreData {
/**
* A score for grading the source Pokemon's matchup against
* the target Pokemon.
* @see {@linkcode EnemyPokemon.getMatchupScore}
*/
matchupScore?: number;
/**
* The expected values of the source Pokemon's Attack Scores
* against the target Pokemon for each of the source's moves.
* @see {@linkcode Pokemon.getExpectedAttackScore}
*/
expectedAttackScores: Map<MoveId, number>;
}
6 changes: 6 additions & 0 deletions src/@types/pokemon-turn-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { TurnCommand } from "#app/turn-command-manager";
import type { TypeDamageMultiplier } from "#data/type";
import type { MoveId } from "#enums/move-id";
import type { AttackMoveResult } from "#types/attack-move-result";
import type { PokemonScoreData } from "#types/pokemon-score-data";

export interface PokemonTurnData {
turnCommand?: TurnCommand;
Expand All @@ -26,4 +27,9 @@ export interface PokemonTurnData {
switchedInThisTurn: boolean;
failedRunAway: boolean;
joinedRound: boolean;
/**
* Contains invariant scoring data for this Pokemon
* against all Pokemon on the field, mapped by their id.
*/
scoreData: Map<number, PokemonScoreData>;
}
5 changes: 5 additions & 0 deletions src/@types/pokemon-wave-data.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { AbilityId } from "#enums/ability-id";
import type { BerryType } from "#enums/berry-type";
import type { MoveId } from "#enums/move-id";

/** Container for Pokemon-specific data that resets at the end of each wave. */
export interface PokemonWaveData {
Expand All @@ -16,4 +17,8 @@ export interface PokemonWaveData {
* without applying them.
*/
abilitiesRevealed: AbilityId[];
/**
* The moves revealed from this Pokemon
*/
revealedMoves: Set<MoveId>;
}
3 changes: 1 addition & 2 deletions src/@types/protect-condition-func.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { MoveId } from "#enums/move-id";
import type { Arena } from "#field/arena";

/**
* A function to check if a move with the given {@linkcode MoveId}
* can have its effects negated by a {@linkcode ConditionalProtectTag | conditional protection}
* field effect (e.g. Wide Guard).
*/
export type ProtectConditionFunc = (arena: Arena, moveId: MoveId) => boolean;
export type ProtectConditionFunc = (moveId: MoveId) => boolean;
27 changes: 27 additions & 0 deletions src/constants/ability-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,30 @@ export const IGNORING_ABILITIES = Object.freeze([AbilityId.MOLD_BREAKER, Ability
* @see {@linkcode SuppressWeatherEffectAbAttr}
*/
export const WEATHER_SUPPRESSING_ABILITIES = Object.freeze([AbilityId.CLOUD_NINE, AbilityId.AIR_LOCK]);

/** Abilities perceived by the Enemy AI to have high value */
export const HIGH_VALUE_ABILITIES: Readonly<AbilityId[]> = Object.freeze([
AbilityId.WONDER_GUARD,
AbilityId.DESOLATE_LAND,
AbilityId.PRIMORDIAL_SEA,
AbilityId.HUGE_POWER,
AbilityId.PURE_POWER,
AbilityId.CONTRARY,
]);

/** Abilities perceived by the Enemy AI to have a detrimental effect on the source */
export const DETRIMENTAL_ABILITIES: Readonly<AbilityId[]> = Object.freeze([
AbilityId.TRUANT,
AbilityId.WIMP_OUT,
AbilityId.EMERGENCY_EXIT,
AbilityId.DEFEATIST,
AbilityId.KLUTZ,
AbilityId.SLOW_START,
]);

/** Abilities that grant an immunity to Electric-type moves */
export const ELECTRIC_IMMUNE_ABILITIES: Readonly<AbilityId[]> = Object.freeze([
AbilityId.VOLT_ABSORB,
AbilityId.LIGHTNING_ROD,
AbilityId.MOTOR_DRIVE,
]);
72 changes: 72 additions & 0 deletions src/constants/ai-constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* eslint-enable @typescript-eslint/no-unused-vars */
// -- end tsdoc imports --

/** The {@link Pokemon.getAttackScore | Attack Score} granted to moves that KO an opponent. */
export const KO_ATTACK_SCORE = 4;

/**
* The damage (in terms of % maximum HP) a projected attack must deal
* to a target to reach the next scoring "tier". An attack projected to deal
* damage below this threshold will receive an Attack Score of either 0 or 1.
* On the other hand, attacks projected to deal damage above this threshold
* will receive an Attack Score of either 1 or 2.
* @see {@linkcode Pokemon.getAttackScore}
* @see {@linkcode Pokemon.getExpectedAttackScore}
*/
export const ATTACK_SCORE_HP_THRESHOLD = 40;

/**
* A relatively major bonus to a move attribute's {@link MoveAttr.getEffectScore | Effect Score}.
* Used when a move with the attribute gains a decisive advantage in
* a given battle state.
*/
export const MAJOR_EFFECT_SCORE_BONUS = 2;

/**
* A relatively minor bonus to a move attribute's {@link MoveAttr.getEffectScore | Effect Score}.
* Used when a move with the attribute gains a slight advantage in
* a given battle state.
*/
export const MINOR_EFFECT_SCORE_BONUS = 1;

/**
* A relatively major penalty to a move's score. Used when a move has
* a significant drawback or is likely to fail from an unresolvable condition
* in a given battle state.
*/
export const MAJOR_EFFECT_SCORE_PENALTY = -MAJOR_EFFECT_SCORE_BONUS;

/**
* A relatively minor penalty to a move's score. Used when a move has
* a potential drawback or has a chance of failing from an unresolvable condition
* in a given battle state.
*/
export const MINOR_EFFECT_SCORE_PENALTY = -MINOR_EFFECT_SCORE_BONUS;

/**
* A weakly enforced upper limit for move attributes' {@link MoveAttr.getEffectScore | Effect Scores}.
* Attributes generally shouldn't give enough of a bonus to supersede
* attacks that can KO opponents.
*/
export const SOFT_EFFECT_SCORE_LIMIT = KO_ATTACK_SCORE - 1;

/**
* The score penalty granted to moves that effectively do nothing, e.g.
* - Attacking into an opponent with a known immunity via typing, Ability, etc.
* - Using a move that is known to fail before selection.
* - Using Skill Swap on an enemy with a bad ability (e.g. Truant)
*/
export const BAD_MOVE_PENALTY = -5;

/**
* The score penalty granted by default when targeting an ally in a double battle.
* This should prevent the AI from deciding to attack their ally, for example.
* Certain move attributes may {@link MoveAttrOptions.overridesAllyTargetPenalty | override this penalty}.
*/
export const ALLY_TARGET_PENALTY = -20;

/**
* The score penalty granted when targeting a Pokemon that is {@link https://bulbapedia.bulbagarden.net/wiki/Commander_(Ability) | Commanding}
* its ally. The AI should always expect this move to miss.
*/
export const COMMANDING_TARGET_PENALTY = -20;
21 changes: 21 additions & 0 deletions src/constants/move-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,24 @@ export const SACRIFICIAL_MOVES = Object.freeze([
MoveId.HEALING_WISH,
MoveId.LUNAR_DANCE,
]);

/** An array containing the {@linkcode MoveId | MoveIds} for all variations of the move Protect */
export const PROTECT_MOVES = Object.freeze([
MoveId.PROTECT,
MoveId.DETECT,
MoveId.ENDURE,
MoveId.SPIKY_SHIELD,
MoveId.KINGS_SHIELD,
MoveId.BANEFUL_BUNKER,
MoveId.OBSTRUCT,
MoveId.BURNING_BULWARK,
MoveId.SILK_TRAP,
]);

/** An array containing the {@linkcode MoveId | MoveIds} for all Status moves that set hazards */
export const HAZARD_STATUS_MOVES = Object.freeze([
MoveId.SPIKES,
MoveId.TOXIC_SPIKES,
MoveId.STEALTH_ROCK,
MoveId.STICKY_WEB,
]);
2 changes: 1 addition & 1 deletion src/data/abilities/ab-attrs/add-second-strike-ab-attr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr {
hitCount.value += 1;
}

if (multiplier?.value && pokemon.turnData.hitsLeft === 1) {
if (multiplier?.value && pokemon.turnData?.hitsLeft === 1) {
Comment thread
DayKev marked this conversation as resolved.
Outdated
multiplier.value = this.damageMultiplier;
}
return true;
Expand Down
40 changes: 22 additions & 18 deletions src/data/abilities/ab-attrs/field-prevent-explosion-like-ab-attr.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { AbAttr } from "#abilities/ab-attr";
import { getPokemonNameWithAffix } from "#app/messages";
import { AbAttrFlag } from "#enums/ab-attr-flag";
import type { Pokemon } from "#field/pokemon";
import type { BooleanHolder } from "#utils/common-utils";
import type { Move } from "#moves/move";
import { type BooleanHolder, isNil } from "#utils/common-utils";
import i18next from "i18next";

/**
Expand All @@ -12,29 +14,26 @@ import i18next from "i18next";
* - Damp
*/
export class FieldPreventExplosionLikeAbAttr extends AbAttr {
private moveUser: string;
private moveName: string;

constructor(showAbility: boolean = true, showAbilityInstant: boolean = false) {
super(showAbility, showAbilityInstant);
constructor() {
super();
this._flags.add(AbAttrFlag.FIELD_PREVENT_EXPLOSION_LIKE);
}

/**
* Applies the effects of the AbAttr when it is called in {@linkcode failIfDampCondition}
* @param cancelled a {@linkcode BooleanHolder} that determines if the move should fail
* @param args[0] contains the name of the move's user
* @param args[1] contains the move's name if present
* @param pokemon - (Unused) The {@linkcode Pokemon} with this ability
* @param simulated - (Unused) If `true`, suppresses changes to game state
* @param cancelled - A {@linkcode BooleanHolder} that determines if the move should fail
* @param attacker - The {@linkcode Pokemon} using the move
* @param move - The {@linkcode Move} being used
*/
public override apply(
_pokemon: Pokemon,
_simulated: boolean,
cancelled: BooleanHolder,
moveUser: string,
moveName: string,
_attacker: Pokemon,
Comment thread
innerthunder marked this conversation as resolved.
Outdated
_move?: Move,
): boolean {
this.moveUser = moveUser;
this.moveName = moveName;
cancelled.value = true;
return true;
}
Expand All @@ -44,11 +43,16 @@ export class FieldPreventExplosionLikeAbAttr extends AbAttr {
* Returns an ability activation message in cases where Damp prevents the usage of a move
* @returns the appropriate trigger message or null
*/
_getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null {
if (this.moveUser && this.moveName) {
const message = i18next.t("moveTriggers:cannotUseMove", { pokemonName: this.moveUser, moveName: this.moveName });
return message;
public override getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ...args: any[]): string | null {
/** @todo Remove casting when `args`' typing is changed to `unknown[]` */
const [attacker, move]: [Pokemon, Move] = args as [Pokemon, Move];
Comment thread
innerthunder marked this conversation as resolved.
Outdated
if (isNil(move)) {
return null;
}
return null;

return i18next.t("moveTriggers:cannotUseMove", {
pokemonName: getPokemonNameWithAffix(attacker),
moveName: move.name,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {

public override apply(pokemon: Pokemon, simulated: boolean, attacker?: Pokemon, move?: Move): boolean {
if (move && attacker && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
//If the mon didn't die to indirect damage
// If the mon didn't die to indirect damage
const cancelled = new BooleanHolder(false);
globalScene
.getField(true)
Expand All @@ -41,8 +41,7 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
p,
simulated,
cancelled,
getPokemonNameWithAffix(attacker),
move.name,
attacker,
),
);

Expand Down
4 changes: 2 additions & 2 deletions src/data/arena-tags/conditional-protect-tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export abstract class ConditionalProtectTag extends ArenaTag {
* @returns `true` if this tag protected against the attack; `false` otherwise
*/
override apply(
arena: Arena,
_arena: Arena,
simulated: boolean,
isProtected: BooleanHolder,
attacker: Pokemon,
Expand All @@ -70,7 +70,7 @@ export abstract class ConditionalProtectTag extends ArenaTag {
): boolean {
if (
(this.side === ArenaTagSide.PLAYER) === defender.isPlayer()
&& this.protectConditionFunc(arena, moveId)
&& this.protectConditionFunc(moveId)
&& (this.ignoresBypass || !allMoves.get(moveId).checkFlag(MoveFlags.IGNORE_PROTECT, attacker, defender))
) {
if (!isProtected.value) {
Expand Down
2 changes: 1 addition & 1 deletion src/data/arena-tags/crafty-shield-tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type { ProtectConditionFunc } from "#types/protect-condition-func";
* @returns `true` if the incoming move is a Status move, is not a hazard, and does not target all
* Pokemon or sides of the field.
*/
const CraftyShieldConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
export const CraftyShieldConditionFunc: ProtectConditionFunc = (moveId: MoveId) => {
const move = allMoves.get(moveId);
return (
move.category === MoveCategory.STATUS
Expand Down
2 changes: 1 addition & 1 deletion src/data/arena-tags/mat-block-tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import i18next from "i18next";
* @param moveId {@linkcode MoveId} The move to check against this condition.
* @returns `true` if the incoming move is not a Status move.
*/
const MatBlockConditionFunc: ProtectConditionFunc = (_arena, moveId): boolean => {
export const MatBlockConditionFunc: ProtectConditionFunc = (moveId: MoveId): boolean => {
const move = allMoves.get(moveId);
return move.category !== MoveCategory.STATUS;
};
Expand Down
2 changes: 1 addition & 1 deletion src/data/arena-tags/quick-guard-tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { ProtectConditionFunc } from "#types/protect-condition-func";
* @returns `true` if the incoming move's priority is greater than 0.
* This includes moves with modified priorities from abilities (e.g. Prankster)
*/
const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
export const QuickGuardConditionFunc: ProtectConditionFunc = (moveId: MoveId) => {
const move = allMoves.get(moveId);
const effectPhase = globalScene.phaseManager.getCurrentPhase();

Expand Down
2 changes: 1 addition & 1 deletion src/data/arena-tags/wide-guard-tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { ProtectConditionFunc } from "#types/protect-condition-func";
* @param moveId {@linkcode MoveId} The move to check against this condition
* @returns `true` if the incoming move is multi-targeted (even if it's only used against one Pokemon).
*/
const WideGuardConditionFunc: ProtectConditionFunc = (_arena, moveId): boolean => {
export const WideGuardConditionFunc: ProtectConditionFunc = (moveId: MoveId): boolean => {
const move = allMoves.get(moveId);

switch (move.moveTarget) {
Expand Down
2 changes: 1 addition & 1 deletion src/data/init/init-abilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@ export function initAbilities() {
.bypassFaint()
.partial(), // Meteor form should protect against status effects and yawn
new Ability(AbilityId.STAKEOUT, 7)
.attr(MovePowerBoostAbAttr, (_user, target, _move) => !!target?.turnData.switchedInThisTurn, 2),
.attr(MovePowerBoostAbAttr, (_user, target, _move) => !!target?.turnData?.switchedInThisTurn, 2),
Comment thread
DayKev marked this conversation as resolved.
Outdated
new Ability(AbilityId.WATER_BUBBLE, 7)
.attr(ReceivedTypeDamageMultiplierAbAttr, ElementalType.FIRE, 0.5)
.attr(MoveTypePowerBoostAbAttr, ElementalType.WATER, 2)
Expand Down
Loading