pokemon-io-core 0.0.11 → 0.0.13
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 +46 -0
- package/dist/api/socketTypes.js +1 -0
- package/dist/core/engine.js +146 -17
- package/dist/core/moves.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/skins/pokemon/index.d.ts +1 -0
- package/dist/skins/pokemon/index.js +1 -0
- package/dist/skins/pokemon/items.js +6 -31
- package/dist/skins/pokemon/moves.js +36 -16
- package/dist/skins/pokemon/statuses.d.ts +2 -0
- package/dist/skins/pokemon/statuses.js +21 -0
- package/package.json +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { BattleView } from "./battle";
|
|
2
|
+
import { RoomView } from "./room";
|
|
3
|
+
export interface ClientToServerEvents {
|
|
4
|
+
"room:create": (payload: {
|
|
5
|
+
nickname: string;
|
|
6
|
+
}) => void;
|
|
7
|
+
"room:join": (payload: {
|
|
8
|
+
roomId: string;
|
|
9
|
+
nickname: string;
|
|
10
|
+
}) => void;
|
|
11
|
+
"room:leave": (payload: {
|
|
12
|
+
roomId: string;
|
|
13
|
+
}) => void;
|
|
14
|
+
"room:setLoadout": (payload: {
|
|
15
|
+
roomId: string;
|
|
16
|
+
fighterId: string;
|
|
17
|
+
moveIds: string[];
|
|
18
|
+
itemIds: string[];
|
|
19
|
+
amuletId?: string | null;
|
|
20
|
+
}) => void;
|
|
21
|
+
"room:startBattleRequest": (payload: {
|
|
22
|
+
roomId: string;
|
|
23
|
+
}) => void;
|
|
24
|
+
"battle:useMove": (payload: {
|
|
25
|
+
roomId: string;
|
|
26
|
+
moveIndex: number;
|
|
27
|
+
}) => void;
|
|
28
|
+
"battle:useItem": (payload: {
|
|
29
|
+
roomId: string;
|
|
30
|
+
itemIndex: number;
|
|
31
|
+
}) => void;
|
|
32
|
+
"ping": () => void;
|
|
33
|
+
}
|
|
34
|
+
export interface ServerToClientEvents {
|
|
35
|
+
"connection:state": (payload: {
|
|
36
|
+
isConnected: boolean;
|
|
37
|
+
socketId: string | null;
|
|
38
|
+
}) => void;
|
|
39
|
+
"room:state": (payload: RoomView) => void;
|
|
40
|
+
"room:error": (payload: {
|
|
41
|
+
code: string;
|
|
42
|
+
message: string;
|
|
43
|
+
}) => void;
|
|
44
|
+
"battle:state": (payload: BattleView) => void;
|
|
45
|
+
"pong": (msg: string) => void;
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/core/engine.js
CHANGED
|
@@ -175,6 +175,89 @@ const applyDamageToFighter = (state, defender, amount, actorId, isCritical) => {
|
|
|
175
175
|
}
|
|
176
176
|
return { updatedDefender, events };
|
|
177
177
|
};
|
|
178
|
+
const applyStatusToFighter = (state, target, statusId) => {
|
|
179
|
+
const def = state.runtime.statusesById[statusId];
|
|
180
|
+
if (!def)
|
|
181
|
+
return { updated: target, events: [] };
|
|
182
|
+
const events = [];
|
|
183
|
+
const existing = target.statuses.find((s) => s.statusId === statusId);
|
|
184
|
+
const duration = Math.floor(randomInRange(def.minDurationTurns, def.maxDurationTurns + 1)) ||
|
|
185
|
+
def.minDurationTurns;
|
|
186
|
+
let newStatuses = [...target.statuses];
|
|
187
|
+
if (!existing) {
|
|
188
|
+
newStatuses.push({
|
|
189
|
+
statusId,
|
|
190
|
+
stacks: 1,
|
|
191
|
+
remainingTurns: duration,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
else if (existing.stacks === 1) {
|
|
195
|
+
newStatuses = newStatuses.map((s) => s.statusId === statusId
|
|
196
|
+
? { ...s, stacks: 2, remainingTurns: duration }
|
|
197
|
+
: s);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
newStatuses = newStatuses.map((s) => s.statusId === statusId ? { ...s, remainingTurns: duration } : s);
|
|
201
|
+
}
|
|
202
|
+
events.push({
|
|
203
|
+
...createBaseEvent(state.turnNumber, "status_applied", `Se aplica ${statusId} a ${target.fighterId}`),
|
|
204
|
+
targetId: target.fighterId,
|
|
205
|
+
statusId,
|
|
206
|
+
});
|
|
207
|
+
return {
|
|
208
|
+
updated: { ...target, statuses: newStatuses },
|
|
209
|
+
events,
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
const applyHealToFighter = (state, target, amount, sourceId) => {
|
|
213
|
+
const newHp = Math.min(target.maxHp, target.currentHp + amount);
|
|
214
|
+
const healed = newHp - target.currentHp;
|
|
215
|
+
const updated = {
|
|
216
|
+
...target,
|
|
217
|
+
currentHp: newHp,
|
|
218
|
+
isAlive: newHp > 0,
|
|
219
|
+
};
|
|
220
|
+
const events = [];
|
|
221
|
+
events.push({
|
|
222
|
+
...createBaseEvent(state.turnNumber, "heal", `${sourceId} cura ${healed} a ${target.fighterId}`),
|
|
223
|
+
actorId: sourceId,
|
|
224
|
+
targetId: target.fighterId,
|
|
225
|
+
amount: healed,
|
|
226
|
+
});
|
|
227
|
+
return { updated, events };
|
|
228
|
+
};
|
|
229
|
+
const applyEffectsOnTarget = (state, actor, target, effects) => {
|
|
230
|
+
let currentActor = actor;
|
|
231
|
+
let currentTarget = target;
|
|
232
|
+
const events = [];
|
|
233
|
+
for (const eff of effects) {
|
|
234
|
+
switch (eff.kind) {
|
|
235
|
+
case "heal": {
|
|
236
|
+
const res = applyHealToFighter(state, currentActor, eff.amount, actor.fighterId);
|
|
237
|
+
currentActor = res.updated;
|
|
238
|
+
events.push(...res.events);
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
case "apply_status": {
|
|
242
|
+
const res = applyStatusToFighter(state, currentTarget, eff.statusId);
|
|
243
|
+
currentTarget = res.updated;
|
|
244
|
+
events.push(...res.events);
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
case "damage": {
|
|
248
|
+
if (typeof eff.amount === "number") {
|
|
249
|
+
const res = applyDamageToFighter(state, currentTarget, eff.amount, actor.fighterId, false);
|
|
250
|
+
currentTarget = res.updatedDefender;
|
|
251
|
+
events.push(...res.events);
|
|
252
|
+
}
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
default:
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return { actor: currentActor, target: currentTarget, events };
|
|
260
|
+
};
|
|
178
261
|
const getMovePriorityAndSpeed = (state, playerKey, action) => {
|
|
179
262
|
const { self } = getOpponentAndSelf(state, playerKey);
|
|
180
263
|
if (action.kind === "no_action") {
|
|
@@ -200,43 +283,51 @@ const getMovePriorityAndSpeed = (state, playerKey, action) => {
|
|
|
200
283
|
};
|
|
201
284
|
const resolveDamageMove = (state, playerKey, action) => {
|
|
202
285
|
if (action.kind !== "use_move") {
|
|
203
|
-
// por ahora ignoramos otros tipos aquí
|
|
204
286
|
return { state, events: [] };
|
|
205
287
|
}
|
|
206
288
|
const { self, opponent } = getOpponentAndSelf(state, playerKey);
|
|
207
289
|
const events = [];
|
|
208
290
|
const moveSlot = self.moves[action.moveIndex];
|
|
209
291
|
if (!moveSlot) {
|
|
210
|
-
// movimiento vacío → no hace nada
|
|
211
292
|
return { state, events };
|
|
212
293
|
}
|
|
213
294
|
const move = state.runtime.movesById[moveSlot.moveId];
|
|
214
295
|
if (!move) {
|
|
215
296
|
return { state, events };
|
|
216
297
|
}
|
|
217
|
-
// Sin PP → no hace nada
|
|
298
|
+
// Sin PP → no hace nada
|
|
218
299
|
if (moveSlot.currentPP <= 0) {
|
|
219
300
|
return { state, events };
|
|
220
301
|
}
|
|
221
|
-
//
|
|
222
|
-
if (move.kind === "
|
|
223
|
-
|
|
224
|
-
|
|
302
|
+
// Movimiento solo de curación → no calculamos daño base
|
|
303
|
+
if (move.kind === "heal") {
|
|
304
|
+
events.push({
|
|
305
|
+
...createBaseEvent(state.turnNumber, "action_declared", `${self.fighterId} usa ${move.name}`),
|
|
306
|
+
actorId: self.fighterId,
|
|
307
|
+
});
|
|
308
|
+
const updatedMoves = self.moves.map((m, idx) => idx === action.moveIndex
|
|
309
|
+
? { ...m, currentPP: Math.max(0, m.currentPP - 1) }
|
|
310
|
+
: m);
|
|
311
|
+
let updatedSelf = { ...self, moves: updatedMoves };
|
|
312
|
+
let updatedOpponent = { ...opponent };
|
|
313
|
+
const { actor: finalSelf, target: finalOpp, events: extraEvents, } = applyEffectsOnTarget(state, updatedSelf, updatedOpponent, move.effects);
|
|
314
|
+
events.push(...extraEvents);
|
|
315
|
+
const newState = updateFightersInState(state, playerKey, finalSelf, finalOpp);
|
|
316
|
+
return { state: newState, events };
|
|
225
317
|
}
|
|
226
|
-
//
|
|
318
|
+
// Movimiento de daño/hybrid
|
|
227
319
|
events.push({
|
|
228
320
|
...createBaseEvent(state.turnNumber, "action_declared", `${self.fighterId} usa ${move.name}`),
|
|
229
321
|
actorId: self.fighterId,
|
|
230
322
|
});
|
|
231
|
-
// Comprobar accuracy
|
|
232
323
|
const accuracy = move.accuracy ?? 100;
|
|
233
324
|
const hitRoll = randomInRange(0, 100);
|
|
234
325
|
const hit = hitRoll < accuracy;
|
|
235
|
-
// Reducir PP
|
|
236
326
|
const updatedMoves = self.moves.map((m, idx) => idx === action.moveIndex
|
|
237
327
|
? { ...m, currentPP: Math.max(0, m.currentPP - 1) }
|
|
238
328
|
: m);
|
|
239
329
|
let updatedSelf = { ...self, moves: updatedMoves };
|
|
330
|
+
let updatedOpponent = { ...opponent };
|
|
240
331
|
if (!hit) {
|
|
241
332
|
events.push({
|
|
242
333
|
...createBaseEvent(state.turnNumber, "move_miss", `${self.fighterId} falla ${move.name}`),
|
|
@@ -244,16 +335,16 @@ const resolveDamageMove = (state, playerKey, action) => {
|
|
|
244
335
|
moveId: move.id,
|
|
245
336
|
targetId: opponent.fighterId,
|
|
246
337
|
});
|
|
247
|
-
const newState = updateFightersInState(state, playerKey, updatedSelf,
|
|
338
|
+
const newState = updateFightersInState(state, playerKey, updatedSelf, updatedOpponent);
|
|
248
339
|
return { state: newState, events };
|
|
249
340
|
}
|
|
250
|
-
// Crítico
|
|
341
|
+
// Crítico y daño base
|
|
251
342
|
const critChance = computeCritChance(state.runtime.rules, updatedSelf.effectiveStats.crit);
|
|
252
343
|
const isCritical = chance(critChance);
|
|
253
344
|
const { damage, effectiveness } = computeDamage({
|
|
254
345
|
state,
|
|
255
346
|
attacker: updatedSelf,
|
|
256
|
-
defender:
|
|
347
|
+
defender: updatedOpponent,
|
|
257
348
|
move,
|
|
258
349
|
isCritical,
|
|
259
350
|
});
|
|
@@ -265,9 +356,42 @@ const resolveDamageMove = (state, playerKey, action) => {
|
|
|
265
356
|
isCritical,
|
|
266
357
|
effectiveness,
|
|
267
358
|
});
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
359
|
+
const damageRes = applyDamageToFighter(state, updatedOpponent, damage, updatedSelf.fighterId, isCritical);
|
|
360
|
+
updatedOpponent = damageRes.updatedDefender;
|
|
361
|
+
events.push(...damageRes.events);
|
|
362
|
+
// Efectos secundarios del movimiento (quemado, buffs, daño extra fijo, etc.)
|
|
363
|
+
const { actor: finalSelf, target: finalOpp, events: extraEvents, } = applyEffectsOnTarget(state, updatedSelf, updatedOpponent, move.effects);
|
|
364
|
+
events.push(...extraEvents);
|
|
365
|
+
const newState = updateFightersInState(state, playerKey, finalSelf, finalOpp);
|
|
366
|
+
return { state: newState, events };
|
|
367
|
+
};
|
|
368
|
+
const resolveItemUse = (state, playerKey, action) => {
|
|
369
|
+
if (action.kind !== "use_item") {
|
|
370
|
+
return { state, events: [] };
|
|
371
|
+
}
|
|
372
|
+
const { self, opponent } = getOpponentAndSelf(state, playerKey);
|
|
373
|
+
const events = [];
|
|
374
|
+
const itemSlot = self.items[action.itemIndex];
|
|
375
|
+
if (!itemSlot || itemSlot.usesRemaining <= 0) {
|
|
376
|
+
return { state, events };
|
|
377
|
+
}
|
|
378
|
+
const itemDef = state.runtime.itemsById[itemSlot.itemId];
|
|
379
|
+
if (!itemDef) {
|
|
380
|
+
return { state, events };
|
|
381
|
+
}
|
|
382
|
+
// Reducir usos
|
|
383
|
+
const updatedItems = self.items.map((it, idx) => idx === action.itemIndex
|
|
384
|
+
? { ...it, usesRemaining: Math.max(0, it.usesRemaining - 1) }
|
|
385
|
+
: it);
|
|
386
|
+
let updatedSelf = { ...self, items: updatedItems };
|
|
387
|
+
let updatedOpponent = { ...opponent };
|
|
388
|
+
events.push({
|
|
389
|
+
...createBaseEvent(state.turnNumber, "action_declared", `${self.fighterId} usa objeto ${itemDef.name}`),
|
|
390
|
+
actorId: self.fighterId,
|
|
391
|
+
});
|
|
392
|
+
const { actor: finalSelf, target: finalOpp, events: extraEvents, } = applyEffectsOnTarget(state, updatedSelf, updatedOpponent, itemDef.effects);
|
|
393
|
+
events.push(...extraEvents);
|
|
394
|
+
const newState = updateFightersInState(state, playerKey, finalSelf, finalOpp);
|
|
271
395
|
return { state: newState, events };
|
|
272
396
|
};
|
|
273
397
|
const updateFightersInState = (state, actingPlayerKey, updatedSelf, updatedOpponent) => {
|
|
@@ -433,8 +557,13 @@ export const resolveTurn = (state, actions) => {
|
|
|
433
557
|
currentState = result.state;
|
|
434
558
|
events.push(...result.events);
|
|
435
559
|
}
|
|
560
|
+
else if (action.kind === "use_item") {
|
|
561
|
+
const result = resolveItemUse(currentState, playerKey, action);
|
|
562
|
+
currentState = result.state;
|
|
563
|
+
events.push(...result.events);
|
|
564
|
+
}
|
|
436
565
|
else {
|
|
437
|
-
//
|
|
566
|
+
// switch_fighter y otros aún no implementados
|
|
438
567
|
continue;
|
|
439
568
|
}
|
|
440
569
|
// Comprobar victoria inmediata tras cada acción
|
package/dist/core/moves.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type TargetKind = "self" | "enemy";
|
|
|
4
4
|
export type EffectKind = "damage" | "heal" | "shield" | "modify_stats" | "apply_status" | "modify_hit_chance" | "modify_crit_chance" | "modify_priority" | "modify_effective_speed";
|
|
5
5
|
export interface DamageEffect {
|
|
6
6
|
kind: "damage";
|
|
7
|
+
amount: number;
|
|
7
8
|
powerMultiplier?: number;
|
|
8
9
|
flatBonus?: number;
|
|
9
10
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,51 +1,26 @@
|
|
|
1
|
-
// Para MVP: 4 objetos simples
|
|
2
1
|
export const POKEMON_ITEMS = [
|
|
3
2
|
{
|
|
4
3
|
id: "potion",
|
|
5
4
|
name: "Poción",
|
|
6
5
|
category: "heal_shield",
|
|
7
6
|
maxUses: 2,
|
|
8
|
-
effects: [
|
|
9
|
-
{
|
|
10
|
-
kind: "heal", // tu EffectDefinition debe tener esto
|
|
11
|
-
amount: 30
|
|
12
|
-
}
|
|
13
|
-
]
|
|
7
|
+
effects: [{ kind: "heal", amount: 30 }]
|
|
14
8
|
},
|
|
15
9
|
{
|
|
16
10
|
id: "super_potion",
|
|
17
11
|
name: "Super Poción",
|
|
18
12
|
category: "heal_shield",
|
|
19
13
|
maxUses: 1,
|
|
20
|
-
effects: [
|
|
21
|
-
{
|
|
22
|
-
kind: "heal",
|
|
23
|
-
amount: 60
|
|
24
|
-
}
|
|
25
|
-
]
|
|
14
|
+
effects: [{ kind: "heal", amount: 60 }]
|
|
26
15
|
},
|
|
27
16
|
{
|
|
28
|
-
id: "
|
|
29
|
-
name: "Bomba",
|
|
17
|
+
id: "burn_bomb",
|
|
18
|
+
name: "Bomba de Fuego",
|
|
30
19
|
category: "damage",
|
|
31
20
|
maxUses: 1,
|
|
32
21
|
effects: [
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
powerMultiplier: 1.5
|
|
36
|
-
}
|
|
37
|
-
]
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
id: "guard_charm",
|
|
41
|
-
name: "Amuleto de Guardia",
|
|
42
|
-
category: "status_buff",
|
|
43
|
-
maxUses: 1,
|
|
44
|
-
effects: [
|
|
45
|
-
{
|
|
46
|
-
kind: "apply_status",
|
|
47
|
-
statusId: "guard"
|
|
48
|
-
}
|
|
22
|
+
{ kind: "damage", amount: 20 },
|
|
23
|
+
{ kind: "apply_status", statusId: "burn" }
|
|
49
24
|
]
|
|
50
25
|
}
|
|
51
26
|
];
|
|
@@ -12,9 +12,10 @@ export const POKEMON_MOVES = [
|
|
|
12
12
|
target: "enemy",
|
|
13
13
|
effects: [
|
|
14
14
|
{
|
|
15
|
-
kind: "damage"
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
kind: "damage",
|
|
16
|
+
amount: 20,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
18
19
|
},
|
|
19
20
|
{
|
|
20
21
|
id: "ember",
|
|
@@ -28,9 +29,10 @@ export const POKEMON_MOVES = [
|
|
|
28
29
|
target: "enemy",
|
|
29
30
|
effects: [
|
|
30
31
|
{
|
|
31
|
-
kind: "
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
kind: "apply_status",
|
|
33
|
+
statusId: "burn",
|
|
34
|
+
},
|
|
35
|
+
],
|
|
34
36
|
},
|
|
35
37
|
{
|
|
36
38
|
id: "water_gun",
|
|
@@ -44,9 +46,10 @@ export const POKEMON_MOVES = [
|
|
|
44
46
|
target: "enemy",
|
|
45
47
|
effects: [
|
|
46
48
|
{
|
|
47
|
-
kind: "damage"
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
kind: "damage",
|
|
50
|
+
amount: 20,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
50
53
|
},
|
|
51
54
|
{
|
|
52
55
|
id: "vine_whip",
|
|
@@ -60,9 +63,10 @@ export const POKEMON_MOVES = [
|
|
|
60
63
|
target: "enemy",
|
|
61
64
|
effects: [
|
|
62
65
|
{
|
|
63
|
-
kind: "damage"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
kind: "damage",
|
|
67
|
+
amount: 20,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
66
70
|
},
|
|
67
71
|
{
|
|
68
72
|
id: "thunder_shock",
|
|
@@ -76,8 +80,24 @@ export const POKEMON_MOVES = [
|
|
|
76
80
|
target: "enemy",
|
|
77
81
|
effects: [
|
|
78
82
|
{
|
|
79
|
-
kind: "damage"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
kind: "damage",
|
|
84
|
+
amount: 20,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: "fire_fang",
|
|
90
|
+
name: "Colmillo Ígneo",
|
|
91
|
+
typeId: "fire",
|
|
92
|
+
kind: "damage",
|
|
93
|
+
basePower: 65,
|
|
94
|
+
accuracy: 95,
|
|
95
|
+
maxPP: 15,
|
|
96
|
+
priority: 0,
|
|
97
|
+
target: "enemy",
|
|
98
|
+
effects: [
|
|
99
|
+
{ kind: "damage", amount: 10 },
|
|
100
|
+
{ kind: "apply_status", statusId: "burn" },
|
|
101
|
+
],
|
|
102
|
+
},
|
|
83
103
|
];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const POKEMON_STATUSES = [
|
|
2
|
+
{
|
|
3
|
+
id: "burn",
|
|
4
|
+
name: "Quemado",
|
|
5
|
+
kind: "soft",
|
|
6
|
+
minDurationTurns: 2,
|
|
7
|
+
maxDurationTurns: 4,
|
|
8
|
+
effectsPerStack: [
|
|
9
|
+
// DoT ligero
|
|
10
|
+
{ kind: "damage", amount: 10 }
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: "stun",
|
|
15
|
+
name: "Aturdido",
|
|
16
|
+
kind: "hard_cc",
|
|
17
|
+
minDurationTurns: 1,
|
|
18
|
+
maxDurationTurns: 1,
|
|
19
|
+
effectsPerStack: []
|
|
20
|
+
}
|
|
21
|
+
];
|