AgentSkillsCN

add-event-listener

注册并管理战斗事件监听器。适用于当宝可梦的技能、效果或道具需要对战斗事件作出响应时使用。

SKILL.md
--- frontmatter
name: add-event-listener
description: Register and manage battle event listeners. Use when abilities, effects, or items need to respond to battle events.

Adding Event Listeners in Pokestar Battle System

Event listeners are the core mechanism for abilities, effects, and held items to respond to battle events.

Registration Pattern

All battle components use registerListenerFunction to subscribe to events:

javascript
const listenerId = battle.registerListenerFunction({
  eventName: battleEventEnum.BEFORE_DAMAGE,
  callback: (args) => {
    // Your logic here
  },
  conditionCallback: getIsTargetPokemonCallback(target),
});

Always store the listenerId and unregister in the remove function:

javascript
abilityAdd({ battle, target }) {
  return {
    listenerId: this.registerListenerFunction({...}),
  };
},
abilityRemove({ battle, properties }) {
  battle.unregisterListener(properties.listenerId);
}

Class-Level vs Battle-Level Registration

Class-Level (Recommended for Abilities/Held Items)

Abilities and Held Items have this.registerListenerFunction which:

  • Automatically checks if the component is still active
  • Provides the component instance in callback args
javascript
// In an Ability
this.registerListenerFunction({
  battle,
  target,
  eventName: battleEventEnum.AFTER_DAMAGE_DEALT,
  callback: ({ abilityInstance }) => {
    // abilityInstance.data contains stored properties
  },
  conditionCallback: getIsSourcePokemonCallback(target),
});

Battle-Level (For Effects)

Effects use battle.registerListenerFunction directly:

javascript
// In an Effect
battle.registerListenerFunction({
  eventName: battleEventEnum.BEFORE_DAMAGE,
  callback: (args) => {
    // Effect logic
  },
  conditionCallback: getIsTargetPokemonCallback(target),
});

Common Battle Events

EventDescriptionKey Args
BATTLE_BEGINStart of battlebattle
TURN_BEGINStart of each turnbattle
TURN_ENDEnd of each turnbattle
BEFORE_MOVEBefore a move executessource, moveId, targets
AFTER_MOVEAfter a move executessource, moveId, targets
BEFORE_DAMAGEBefore damage is dealtdamage, target, source, type
AFTER_DAMAGE_DEALTAfter damage is dealtdamage, target, source, type
BEFORE_EFFECT_ADDBefore an effect is appliedeffectId, target, canAdd
AFTER_EFFECT_ADDAfter an effect is appliedeffectId, target
BEFORE_STATUS_APPLYBefore status condition appliedstatusId, target, canApply
AFTER_SKIP_TURNAfter skipping a turnsource
AFTER_FAINTAfter a Pokemon faintstarget, source

If an event isn't strongly typed, search for where it is emitted in BattlePokemon.js or Battle.js and add its type to battleEnums.js.

Condition Callbacks

Import from ../engine/eventConditions:

Common Condition Callbacks

CallbackDescription
getIsTargetPokemonCallback(pokemon)Only triggers when pokemon is the target
getIsSourcePokemonCallback(pokemon)Only triggers when pokemon is the source
getIsActivePokemonCallback(pokemon)Only triggers when pokemon is the active one
getIsInstanceOfType("move")Only triggers for move-related damage
getIsTargetSameTeamCallback(pokemon)Target is on same team
getIsTargetOpponentCallback(pokemon)Target is on opposing team
getIsSourceSameTeamCallback(pokemon)Source is on same team
getIsNotSourcePokemonCallback(pokemon)Source is NOT this pokemon
getIsWeatherCondition(weatherId)Current weather matches

Composing Multiple Conditions

javascript
conditionCallback: composeConditionCallbacks(
  getIsTargetPokemonCallback(target),
  getIsInstanceOfType("move"),
),

Modifying Event Arguments

RECOMMENDED: Return an object from the callback to modify event behavior:

javascript
callback: (args) => {
  return {
    damage: Math.floor(args.damage * 0.5), // reduce damange by 50%
    canAdd: false, // prevent something from being applied
  };
};

Common Gotchas

  1. Always Unregister: Store listenerId and call battle.unregisterListener(listenerId) in the remove function to prevent memory leaks.

  2. Source vs Target: Use the correct condition callback:

    • getIsSourcePokemonCallback - when this Pokemon is dealing damage/using moves
    • getIsTargetPokemonCallback - when this Pokemon is receiving damage/effects
  3. Return vs Mutate: When modifying arguments, ALWAYS return the new value instead of mutating. Mutating is an old, deprecated pattern.

  4. Instance Check: Class-level registerListenerFunction automatically checks if the component is still active before firing the callback.

References

  • references/pattern-*.md - Common event listener implementation patterns