pokemon-io-core 0.0.98 → 0.0.99
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.
- package/dist/api/socketTypes.d.ts +10 -0
- package/dist/core/emotes.d.ts +13 -0
- package/dist/core/emotes.js +127 -0
- package/dist/core/events.d.ts +8 -2
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/engine/status/apply.js +3 -0
- package/dist/engine/status/endOfTurn.js +7 -0
- package/dist/engine/turn/resolveTurn.js +44 -25
- package/dist/skins/pokemon/items.js +0 -4
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BattleView } from "./battle.js";
|
|
2
2
|
import { RoomView } from "./room.js";
|
|
3
|
+
import { EmoteId } from "../core/emotes.js";
|
|
3
4
|
export interface ClientToServerEvents {
|
|
4
5
|
"room:create": (payload: {
|
|
5
6
|
nickname: string;
|
|
@@ -59,6 +60,10 @@ export interface ClientToServerEvents {
|
|
|
59
60
|
"battle:rematchChangeLoadout": (payload: {
|
|
60
61
|
roomId: string;
|
|
61
62
|
}) => void;
|
|
63
|
+
"battle:sendEmote": (payload: {
|
|
64
|
+
roomId: string;
|
|
65
|
+
emoteId: EmoteId;
|
|
66
|
+
}) => void;
|
|
62
67
|
ping: () => void;
|
|
63
68
|
}
|
|
64
69
|
export interface ServerToClientEvents {
|
|
@@ -73,5 +78,10 @@ export interface ServerToClientEvents {
|
|
|
73
78
|
message: string;
|
|
74
79
|
}) => void;
|
|
75
80
|
"battle:state": (payload: BattleView) => void;
|
|
81
|
+
"battle:emote": (payload: {
|
|
82
|
+
playerId: string;
|
|
83
|
+
emoteId: EmoteId;
|
|
84
|
+
timestamp: number;
|
|
85
|
+
}) => void;
|
|
76
86
|
pong: (msg: string) => void;
|
|
77
87
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type EmoteCategory = "emotional" | "strategic" | "social";
|
|
2
|
+
export type EmoteId = string;
|
|
3
|
+
export interface EmoteDefinition {
|
|
4
|
+
id: EmoteId;
|
|
5
|
+
category: EmoteCategory;
|
|
6
|
+
emoji: string;
|
|
7
|
+
label: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const EMOTES: EmoteDefinition[];
|
|
11
|
+
export declare const getEmoteById: (id: EmoteId) => EmoteDefinition | undefined;
|
|
12
|
+
export declare const getEmotesByCategory: (category: EmoteCategory) => EmoteDefinition[];
|
|
13
|
+
export declare const isValidEmoteId: (id: string) => id is EmoteId;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// src/core/emotes.ts
|
|
2
|
+
export const EMOTES = [
|
|
3
|
+
// Emotional (6)
|
|
4
|
+
{
|
|
5
|
+
id: "laugh",
|
|
6
|
+
category: "emotional",
|
|
7
|
+
emoji: "😂",
|
|
8
|
+
label: "Risa",
|
|
9
|
+
description: "¡Jaja!",
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: "scared",
|
|
13
|
+
category: "emotional",
|
|
14
|
+
emoji: "😱",
|
|
15
|
+
label: "Susto",
|
|
16
|
+
description: "¡Oh no!",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "cool",
|
|
20
|
+
category: "emotional",
|
|
21
|
+
emoji: "😎",
|
|
22
|
+
label: "Cool",
|
|
23
|
+
description: "Genial",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "angry",
|
|
27
|
+
category: "emotional",
|
|
28
|
+
emoji: "😡",
|
|
29
|
+
label: "Enfadado",
|
|
30
|
+
description: "¡Grr!",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "sad",
|
|
34
|
+
category: "emotional",
|
|
35
|
+
emoji: "😢",
|
|
36
|
+
label: "Triste",
|
|
37
|
+
description: "Auch...",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "thinking",
|
|
41
|
+
category: "emotional",
|
|
42
|
+
emoji: "🤔",
|
|
43
|
+
label: "Pensando",
|
|
44
|
+
description: "Hmm...",
|
|
45
|
+
},
|
|
46
|
+
// Strategic (6)
|
|
47
|
+
{
|
|
48
|
+
id: "attack",
|
|
49
|
+
category: "strategic",
|
|
50
|
+
emoji: "💥",
|
|
51
|
+
label: "Voy a atacar",
|
|
52
|
+
description: "¡Prepárate!",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "heal",
|
|
56
|
+
category: "strategic",
|
|
57
|
+
emoji: "💊",
|
|
58
|
+
label: "Voy a curarme",
|
|
59
|
+
description: "Necesito curarme",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "switch",
|
|
63
|
+
category: "strategic",
|
|
64
|
+
emoji: "🔄",
|
|
65
|
+
label: "Voy a cambiar",
|
|
66
|
+
description: "¡Cambio!",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: "special",
|
|
70
|
+
category: "strategic",
|
|
71
|
+
emoji: "⚡",
|
|
72
|
+
label: "Movimiento especial",
|
|
73
|
+
description: "¡Algo grande viene!",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: "defend",
|
|
77
|
+
category: "strategic",
|
|
78
|
+
emoji: "🛡️",
|
|
79
|
+
label: "Voy a defenderme",
|
|
80
|
+
description: "Me protejo",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: "plan",
|
|
84
|
+
category: "strategic",
|
|
85
|
+
emoji: "🎯",
|
|
86
|
+
label: "Tengo un plan",
|
|
87
|
+
description: "Sé lo que hago",
|
|
88
|
+
},
|
|
89
|
+
// Social (4)
|
|
90
|
+
{
|
|
91
|
+
id: "well_played",
|
|
92
|
+
category: "social",
|
|
93
|
+
emoji: "👍",
|
|
94
|
+
label: "Bien jugado",
|
|
95
|
+
description: "¡Buen movimiento!",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "thanks",
|
|
99
|
+
category: "social",
|
|
100
|
+
emoji: "🙏",
|
|
101
|
+
label: "Gracias",
|
|
102
|
+
description: "Gracias por la partida",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: "hello",
|
|
106
|
+
category: "social",
|
|
107
|
+
emoji: "👋",
|
|
108
|
+
label: "Hola",
|
|
109
|
+
description: "¡Hola!",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: "good_luck",
|
|
113
|
+
category: "social",
|
|
114
|
+
emoji: "🤝",
|
|
115
|
+
label: "Buena suerte",
|
|
116
|
+
description: "¡Suerte!",
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
export const getEmoteById = (id) => {
|
|
120
|
+
return EMOTES.find((emote) => emote.id === id);
|
|
121
|
+
};
|
|
122
|
+
export const getEmotesByCategory = (category) => {
|
|
123
|
+
return EMOTES.filter((emote) => emote.category === category);
|
|
124
|
+
};
|
|
125
|
+
export const isValidEmoteId = (id) => {
|
|
126
|
+
return EMOTES.some((emote) => emote.id === id);
|
|
127
|
+
};
|
package/dist/core/events.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FighterId, MoveId, ItemId, StatusId } from "./ids.js";
|
|
2
|
-
export type BattleEventKind = "turn_start" | "action_declared" | "action_skipped_hard_cc" | "move_miss" | "move_hit" | "item_used" | "status_cleared" | "damage" | "heal" | "fighter_switched" | "shield_applied" | "status_applied" | "status_refreshed" | "status_expired" | "fighter_fainted" | "turn_end" | "stats_changed" | "battle_end";
|
|
2
|
+
export type BattleEventKind = "turn_start" | "action_declared" | "action_skipped_hard_cc" | "move_miss" | "move_hit" | "item_used" | "status_cleared" | "damage" | "heal" | "fighter_switched" | "shield_applied" | "status_applied" | "status_refreshed" | "status_expired" | "status_tick" | "fighter_fainted" | "turn_end" | "stats_changed" | "battle_end";
|
|
3
3
|
export interface BaseBattleEvent {
|
|
4
4
|
id: string;
|
|
5
5
|
turnNumber: number;
|
|
@@ -79,6 +79,12 @@ export interface StatusExpiredEvent extends BaseBattleEvent {
|
|
|
79
79
|
targetId: FighterId;
|
|
80
80
|
statusId: StatusId;
|
|
81
81
|
}
|
|
82
|
+
export interface StatusTickEvent extends BaseBattleEvent {
|
|
83
|
+
kind: "status_tick";
|
|
84
|
+
targetId: FighterId;
|
|
85
|
+
statusId: StatusId;
|
|
86
|
+
stacks: number;
|
|
87
|
+
}
|
|
82
88
|
export interface FighterFaintedEvent extends BaseBattleEvent {
|
|
83
89
|
kind: "fighter_fainted";
|
|
84
90
|
fighterId: FighterId;
|
|
@@ -87,4 +93,4 @@ export interface BattleEndEvent extends BaseBattleEvent {
|
|
|
87
93
|
kind: "battle_end";
|
|
88
94
|
winner: "player1" | "player2" | "draw";
|
|
89
95
|
}
|
|
90
|
-
export type BattleEvent = ActionDeclaredEvent | MoveMissEvent | MoveHitEvent | ItemUsedEvent | DamageEvent | HealEvent | StatusAppliedEvent | StatusExpiredEvent | FighterFaintedEvent | FighterSwitchedEvent | BattleEndEvent | StatsChangedEvent | BaseBattleEvent;
|
|
96
|
+
export type BattleEvent = ActionDeclaredEvent | MoveMissEvent | MoveHitEvent | ItemUsedEvent | DamageEvent | HealEvent | StatusAppliedEvent | StatusExpiredEvent | StatusTickEvent | FighterFaintedEvent | FighterSwitchedEvent | BattleEndEvent | StatsChangedEvent | BaseBattleEvent;
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -37,10 +37,13 @@ export const applyStatusToFighter = (state, target, statusId) => {
|
|
|
37
37
|
duration,
|
|
38
38
|
prevStacks: existing?.stacks ?? 0,
|
|
39
39
|
});
|
|
40
|
+
const finalStacks = !existing ? 1 : existing.stacks === 1 ? 2 : existing.stacks;
|
|
40
41
|
events.push({
|
|
41
42
|
...createBaseEvent(state.turnNumber, "status_applied", `Se aplica ${statusId} a ${target.fighterId}`),
|
|
42
43
|
targetId: target.fighterId,
|
|
43
44
|
statusId,
|
|
45
|
+
stacks: finalStacks,
|
|
46
|
+
durationTurns: duration,
|
|
44
47
|
});
|
|
45
48
|
return {
|
|
46
49
|
updated: { ...target, statuses: newStatuses },
|
|
@@ -34,6 +34,13 @@ export const applyEndOfTurnStatuses = (state) => {
|
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
if (damageFromStatus > 0) {
|
|
37
|
+
// Emit status tick event for narration
|
|
38
|
+
events.push({
|
|
39
|
+
...createBaseEvent(state.turnNumber, "status_tick", `${updated.fighterId} sufre por ${st.statusId}`),
|
|
40
|
+
targetId: updated.fighterId,
|
|
41
|
+
statusId: st.statusId,
|
|
42
|
+
stacks: st.stacks,
|
|
43
|
+
});
|
|
37
44
|
const damageRes = applyDamageToFighter(state, updated, damageFromStatus, updated.fighterId, false);
|
|
38
45
|
updated = damageRes.updatedDefender;
|
|
39
46
|
events.push(...damageRes.events);
|
|
@@ -144,42 +144,61 @@ export const resolveTurn = (state, actions) => {
|
|
|
144
144
|
const { self } = getOpponentAndSelf(currentState, playerKey);
|
|
145
145
|
if (!self.isAlive || self.currentHp <= 0)
|
|
146
146
|
continue;
|
|
147
|
-
// ✅ Paralysis check (25% chance to skip)
|
|
147
|
+
// ✅ Paralysis check (25% chance to skip ALL actions, including items)
|
|
148
148
|
if (self.statuses.some((s) => s.statusId === "paralysis") &&
|
|
149
149
|
Math.random() < 0.25) {
|
|
150
150
|
events.push({
|
|
151
151
|
...createBaseEvent(currentState.turnNumber, "action_skipped_hard_cc", `${self.fighterId} está paralizado y no puede moverse`),
|
|
152
152
|
actorId: self.fighterId,
|
|
153
|
+
statusId: "paralysis",
|
|
153
154
|
});
|
|
154
155
|
continue;
|
|
155
156
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (self.volatileStatus?.kind === "charging") {
|
|
160
|
-
const newFighter = { ...self, volatileStatus: null };
|
|
161
|
-
currentState = {
|
|
162
|
-
...currentState,
|
|
163
|
-
[playerKey]: {
|
|
164
|
-
...currentState[playerKey],
|
|
165
|
-
fighterTeam: currentState[playerKey].fighterTeam.map((f) => f.fighterId === self.fighterId ? newFighter : f),
|
|
166
|
-
},
|
|
167
|
-
};
|
|
168
|
-
interruptionSuffix = " (Concentración perdida)";
|
|
169
|
-
}
|
|
170
|
-
events.push({
|
|
171
|
-
...createBaseEvent(currentState.turnNumber, "action_skipped_hard_cc", `${self.fighterId} no puede actuar por control total${interruptionSuffix}`),
|
|
172
|
-
actorId: self.fighterId,
|
|
173
|
-
});
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
if (action.kind === "use_move") {
|
|
177
|
-
const result = resolveDamageMove(currentState, playerKey, action);
|
|
157
|
+
// ✅ Items can be used regardless of hard CC (trainer action, not Pokémon action)
|
|
158
|
+
if (action.kind === "use_item") {
|
|
159
|
+
const result = resolveItemUse(currentState, playerKey, action);
|
|
178
160
|
currentState = result.state;
|
|
179
161
|
events.push(...result.events);
|
|
180
162
|
}
|
|
181
|
-
else if (action.kind === "
|
|
182
|
-
|
|
163
|
+
else if (action.kind === "use_move") {
|
|
164
|
+
// ✅ Moves are blocked by hard CC (sleep, freeze, flinch)
|
|
165
|
+
if (hasHardCc(currentState, self)) {
|
|
166
|
+
// Find the hard CC status
|
|
167
|
+
const ccStatus = self.statuses.find((s) => {
|
|
168
|
+
const def = currentState.runtime.statusesById[s.statusId];
|
|
169
|
+
return def?.kind === "hard_cc";
|
|
170
|
+
});
|
|
171
|
+
let interruptionSuffix = "";
|
|
172
|
+
// Si estaba cargando y es interrumpido, pierde la carga (Fly/Dig fallan)
|
|
173
|
+
if (self.volatileStatus?.kind === "charging") {
|
|
174
|
+
const newFighter = { ...self, volatileStatus: null };
|
|
175
|
+
currentState = {
|
|
176
|
+
...currentState,
|
|
177
|
+
[playerKey]: {
|
|
178
|
+
...currentState[playerKey],
|
|
179
|
+
fighterTeam: currentState[playerKey].fighterTeam.map((f) => f.fighterId === self.fighterId ? newFighter : f),
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
interruptionSuffix = " (Concentración perdida)";
|
|
183
|
+
}
|
|
184
|
+
// Status-specific messages
|
|
185
|
+
const statusMessages = {
|
|
186
|
+
sleep: `${self.fighterId} está dormido y no puede moverse`,
|
|
187
|
+
freeze: `${self.fighterId} está congelado y no puede actuar`,
|
|
188
|
+
flinch: `${self.fighterId} se acobarda y no puede atacar`,
|
|
189
|
+
};
|
|
190
|
+
const statusId = ccStatus?.statusId || "unknown";
|
|
191
|
+
const message = statusMessages[statusId] ||
|
|
192
|
+
`${self.fighterId} no puede actuar por control total`;
|
|
193
|
+
events.push({
|
|
194
|
+
...createBaseEvent(currentState.turnNumber, "action_skipped_hard_cc", `${message}${interruptionSuffix}`),
|
|
195
|
+
actorId: self.fighterId,
|
|
196
|
+
statusId: statusId,
|
|
197
|
+
});
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
// Execute the move
|
|
201
|
+
const result = resolveDamageMove(currentState, playerKey, action);
|
|
183
202
|
currentState = result.state;
|
|
184
203
|
events.push(...result.events);
|
|
185
204
|
}
|
|
@@ -43,7 +43,6 @@ export const POKEMON_ITEMS = [
|
|
|
43
43
|
maxUses: 2,
|
|
44
44
|
effects: [
|
|
45
45
|
{ kind: "damage", flatAmount: 15 },
|
|
46
|
-
{ kind: "apply_status", statusId: "burn" },
|
|
47
46
|
],
|
|
48
47
|
image: "shotgun",
|
|
49
48
|
},
|
|
@@ -55,7 +54,6 @@ export const POKEMON_ITEMS = [
|
|
|
55
54
|
maxUses: 4,
|
|
56
55
|
effects: [
|
|
57
56
|
{ kind: "damage", flatAmount: 8 },
|
|
58
|
-
{ kind: "apply_status", statusId: "burn" },
|
|
59
57
|
],
|
|
60
58
|
image: "pistol",
|
|
61
59
|
},
|
|
@@ -67,7 +65,6 @@ export const POKEMON_ITEMS = [
|
|
|
67
65
|
maxUses: 3,
|
|
68
66
|
effects: [
|
|
69
67
|
{ kind: "damage", flatAmount: 10 },
|
|
70
|
-
{ kind: "apply_status", statusId: "burn" },
|
|
71
68
|
],
|
|
72
69
|
image: "riffle",
|
|
73
70
|
},
|
|
@@ -79,7 +76,6 @@ export const POKEMON_ITEMS = [
|
|
|
79
76
|
maxUses: 1,
|
|
80
77
|
effects: [
|
|
81
78
|
{ kind: "damage", flatAmount: 30 },
|
|
82
|
-
{ kind: "apply_status", statusId: "burn" },
|
|
83
79
|
],
|
|
84
80
|
image: "sniper",
|
|
85
81
|
},
|