pokemon-io-core 0.0.81 → 0.0.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,14 @@ export interface BattlePlayerView {
12
12
  activeFighterId: string | null;
13
13
  activeIndex: number;
14
14
  }
15
- export type BattleStatus = "ongoing" | "finished";
15
+ export type BattleStatus = "ongoing" | "awaiting_forced_switch" | "finished";
16
+ export type ForcedSwitchReason = "roar";
17
+ export interface ForcedSwitchView {
18
+ targetPlayerId: string;
19
+ sourcePlayerId: string;
20
+ reason: ForcedSwitchReason;
21
+ turnNumber: number;
22
+ }
16
23
  export interface BattleView {
17
24
  roomId: string;
18
25
  status: BattleStatus;
@@ -30,4 +37,5 @@ export interface BattleView {
30
37
  player1: boolean;
31
38
  player2: boolean;
32
39
  };
40
+ forcedSwitch: ForcedSwitchView | null;
33
41
  }
@@ -49,6 +49,10 @@ export interface ClientToServerEvents {
49
49
  roomId: string;
50
50
  newIndex: number;
51
51
  }) => void;
52
+ "battle:chooseForcedSwitch": (payload: {
53
+ roomId: string;
54
+ newIndex: number;
55
+ }) => void;
52
56
  "battle:rematchSameLoadout": (payload: {
53
57
  roomId: string;
54
58
  }) => void;
@@ -31,9 +31,18 @@ export interface PlayerBattleState {
31
31
  activeIndex: number;
32
32
  inventory: EquippedItem[];
33
33
  }
34
+ export type ForcedSwitchState = null | {
35
+ targetPlayer: "player1" | "player2";
36
+ reason: {
37
+ kind: "move";
38
+ moveId: string;
39
+ actorFighterId: string;
40
+ };
41
+ };
34
42
  export interface BattleState {
35
43
  turnNumber: number;
36
44
  player1: PlayerBattleState;
37
45
  player2: PlayerBattleState;
38
46
  runtime: BattleRuntime;
47
+ forcedSwitch: ForcedSwitchState;
39
48
  }
@@ -42,6 +42,10 @@ export interface RuntimeBattleState extends BattleState {
42
42
  runtime: BattleRuntime;
43
43
  }
44
44
  export declare const createInitialBattleState: (config: BattleConfig) => BattleState;
45
+ export declare const applyForcedSwitchChoice: (state: BattleState, targetPlayer: "player1" | "player2", newIndex: number) => {
46
+ state: BattleState;
47
+ events: BattleEvent[];
48
+ };
45
49
  export declare const resolveTurn: (state: BattleState, actions: BattleActions) => {
46
50
  newState: BattleState;
47
51
  events: BattleEvent[];
@@ -115,6 +115,7 @@ export const createInitialBattleState = (config) => {
115
115
  player1,
116
116
  player2,
117
117
  runtime,
118
+ forcedSwitch: null,
118
119
  };
119
120
  return state;
120
121
  };
@@ -201,6 +202,49 @@ const computeDamage = (input) => {
201
202
  effectiveness: typeEffectiveness,
202
203
  };
203
204
  };
205
+ export const applyForcedSwitchChoice = (state, targetPlayer, newIndex) => {
206
+ const forced = state.forcedSwitch;
207
+ if (!forced)
208
+ return { state, events: [] };
209
+ if (forced.targetPlayer !== targetPlayer)
210
+ return { state, events: [] };
211
+ const self = targetPlayer === "player1" ? state.player1 : state.player2;
212
+ if (newIndex < 0 || newIndex >= self.fighterTeam.length)
213
+ return { state, events: [] };
214
+ const fromIndex = self.activeIndex;
215
+ if (newIndex === fromIndex)
216
+ return { state, events: [] };
217
+ const toFighter = self.fighterTeam[newIndex];
218
+ if (!toFighter || !toFighter.isAlive || toFighter.currentHp <= 0)
219
+ return { state, events: [] };
220
+ const fromFighter = self.fighterTeam[fromIndex];
221
+ const events = [
222
+ {
223
+ ...createBaseEvent(state.turnNumber, "fighter_switched", `El entrenador retira a ${fromFighter.fighterId} y manda a ${toFighter.fighterId}`),
224
+ fromFighterId: fromFighter.fighterId,
225
+ toFighterId: toFighter.fighterId,
226
+ },
227
+ {
228
+ ...createBaseEvent(state.turnNumber, "turn_end", `Termina el turno ${state.turnNumber}`),
229
+ },
230
+ ];
231
+ const switchedState = targetPlayer === "player1"
232
+ ? {
233
+ ...state,
234
+ forcedSwitch: null,
235
+ player1: { ...state.player1, activeIndex: newIndex },
236
+ }
237
+ : {
238
+ ...state,
239
+ forcedSwitch: null,
240
+ player2: { ...state.player2, activeIndex: newIndex },
241
+ };
242
+ const nextState = {
243
+ ...switchedState,
244
+ turnNumber: switchedState.turnNumber + 1,
245
+ };
246
+ return { state: nextState, events };
247
+ };
204
248
  let eventCounter = 0;
205
249
  const nextEventId = () => {
206
250
  eventCounter += 1;
@@ -658,7 +702,32 @@ const resolveDamageMove = (state, playerKey, action) => {
658
702
  // Todos los efectos (daño, aplicar estado, curas, etc.)
659
703
  const { actor: finalSelf, target: finalOpp, events: extraEvents, } = applyEffectsOnTarget(state, updatedSelf, updatedOpponent, move.typeId, isCritical, move.effects);
660
704
  events.push(...extraEvents);
661
- const newState = updateFightersInState(state, playerKey, finalSelf, finalOpp);
705
+ let newState = updateFightersInState(state, playerKey, finalSelf, finalOpp);
706
+ // --- FORCED SWITCH (Roar) ---
707
+ const hasForceSwitch = move.effects.some((e) => e.kind === "force_switch");
708
+ if (hasForceSwitch) {
709
+ const targetPlayer = playerKey === "player1" ? "player2" : "player1";
710
+ const targetSide = targetPlayer === "player1" ? newState.player1 : newState.player2;
711
+ const activeIdx = targetSide.activeIndex;
712
+ const hasBenchAlive = targetSide.fighterTeam.some((f, idx) => {
713
+ if (idx === activeIdx)
714
+ return false;
715
+ return f.isAlive && f.currentHp > 0;
716
+ });
717
+ if (hasBenchAlive) {
718
+ newState = {
719
+ ...newState,
720
+ forcedSwitch: {
721
+ targetPlayer,
722
+ reason: "roar",
723
+ },
724
+ };
725
+ events.push({
726
+ ...createBaseEvent(state.turnNumber, "action_declared", `¡El entrenador rival debe cambiar de luchador!`),
727
+ actorId: self.fighterId,
728
+ });
729
+ }
730
+ }
662
731
  return { state: newState, events };
663
732
  };
664
733
  // ------------------------------------------------------
@@ -931,6 +1000,12 @@ export const resolveTurn = (state, actions) => {
931
1000
  // switch_fighter u otros no implementados aún
932
1001
  continue;
933
1002
  }
1003
+ if (currentState.forcedSwitch) {
1004
+ return {
1005
+ newState: currentState,
1006
+ events,
1007
+ };
1008
+ }
934
1009
  const winnerAfterAction = checkWinner(currentState);
935
1010
  if (winnerAfterAction !== "none") {
936
1011
  events.push({
@@ -1,6 +1,6 @@
1
1
  import type { MoveId, StatusId, TypeId } from "./ids.js";
2
2
  export type EffectTarget = "self" | "enemy";
3
- export type EffectKind = "damage" | "heal" | "shield" | "modify_stats" | "apply_status" | "clear_status" | "modify_hit_chance" | "modify_crit_chance" | "modify_priority" | "modify_effective_speed";
3
+ export type EffectKind = "damage" | "heal" | "shield" | "modify_stats" | "apply_status" | "clear_status" | "modify_hit_chance" | "modify_crit_chance" | "modify_priority" | "modify_effective_speed" | "force_switch";
4
4
  export type TargetKind = EffectTarget;
5
5
  interface BaseEffect {
6
6
  target?: EffectTarget;
@@ -56,11 +56,19 @@ export interface ModifyPriorityEffect extends BaseEffect {
56
56
  kind: "modify_priority";
57
57
  delta: number;
58
58
  }
59
+ export interface ForceSwitchEffect extends BaseEffect {
60
+ kind: "force_switch";
61
+ /**
62
+ * Mensaje/razón opcional para debug/narración interna.
63
+ * Si no lo usas, lo puedes ignorar en el engine.
64
+ */
65
+ reason?: string;
66
+ }
59
67
  export interface ModifyEffectiveSpeedEffect extends BaseEffect {
60
68
  kind: "modify_effective_speed";
61
69
  multiplier: number;
62
70
  }
63
- export type EffectDefinition = DamageEffect | HealEffect | ShieldEffect | ModifyStatsEffect | ApplyStatusEffect | ClearStatusEffect | ModifyHitChanceEffect | ModifyCritChanceEffect | ModifyPriorityEffect | ModifyEffectiveSpeedEffect;
71
+ export type EffectDefinition = DamageEffect | HealEffect | ShieldEffect | ModifyStatsEffect | ApplyStatusEffect | ClearStatusEffect | ModifyHitChanceEffect | ModifyCritChanceEffect | ModifyPriorityEffect | ModifyEffectiveSpeedEffect | ForceSwitchEffect;
64
72
  export interface MoveDefinition {
65
73
  id: MoveId;
66
74
  name: string;
@@ -1,4 +1,18 @@
1
1
  export const POKEMON_MOVES = [
2
+ {
3
+ "id": "roar",
4
+ "name": "Rugido",
5
+ "typeId": "fire",
6
+ "accuracy": 100,
7
+ "maxPP": 3,
8
+ "priority": 0,
9
+ "effects": [
10
+ {
11
+ "kind": "force_switch",
12
+ "reason": "Roar"
13
+ },
14
+ ]
15
+ },
2
16
  {
3
17
  "id": "overheat",
4
18
  "name": "Sofoco Ígneo",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pokemon-io-core",
3
- "version": "0.0.81",
3
+ "version": "0.0.83",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",