pokemon-io-core 0.0.71 → 0.0.73
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/room.d.ts +3 -4
- package/dist/api/socketTypes.d.ts +5 -0
- package/dist/api/socketTypes.js +1 -0
- package/dist/core/engine.d.ts +0 -32
- package/dist/core/engine.js +64 -101
- package/dist/core/events.d.ts +6 -7
- package/dist/skins/CombatSkin.d.ts +1 -16
- package/dist/skins/cliches/clicheSkin.js +4 -16
- package/dist/skins/pokemon/pokemonSkin.js +1 -13
- package/package.json +1 -1
package/dist/api/room.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
export interface PlayerLoadoutView {
|
|
2
|
-
fighterIds: string[];
|
|
3
|
-
movesByFighterId: Record<string, string[]>;
|
|
4
|
-
itemIds: string[];
|
|
5
|
-
amuletId: string | null;
|
|
6
2
|
fighterId: string | null;
|
|
7
3
|
moveIds: string[];
|
|
4
|
+
itemIds: string[];
|
|
5
|
+
amuletId: string | null;
|
|
8
6
|
}
|
|
9
7
|
export interface PlayerView {
|
|
10
8
|
id: string;
|
|
@@ -19,6 +17,7 @@ export interface RoomView {
|
|
|
19
17
|
id: string;
|
|
20
18
|
ownerId: string;
|
|
21
19
|
status: RoomStatusView;
|
|
20
|
+
fightersPerPlayer: number;
|
|
22
21
|
players: PlayerView[];
|
|
23
22
|
skinId: string;
|
|
24
23
|
stageId: string;
|
|
@@ -32,6 +32,7 @@ export interface ClientToServerEvents {
|
|
|
32
32
|
roomId: string;
|
|
33
33
|
skinId: string;
|
|
34
34
|
stageId: string;
|
|
35
|
+
fightersPerPlayer: number;
|
|
35
36
|
}) => void;
|
|
36
37
|
"room:list": () => void;
|
|
37
38
|
"battle:useMove": (payload: {
|
|
@@ -42,6 +43,10 @@ export interface ClientToServerEvents {
|
|
|
42
43
|
roomId: string;
|
|
43
44
|
itemIndex: number;
|
|
44
45
|
}) => void;
|
|
46
|
+
"battle:switchFighter": (payload: {
|
|
47
|
+
roomId: string;
|
|
48
|
+
newIndex: number;
|
|
49
|
+
}) => void;
|
|
45
50
|
"battle:rematchSameLoadout": (payload: {
|
|
46
51
|
roomId: string;
|
|
47
52
|
}) => void;
|
package/dist/api/socketTypes.js
CHANGED
package/dist/core/engine.d.ts
CHANGED
|
@@ -16,39 +16,7 @@ export interface BattleRuntime {
|
|
|
16
16
|
statusesById: Record<StatusId, StatusDefinition>;
|
|
17
17
|
typeEffectiveness: TypeEffectivenessMatrix;
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Slot de equipo: UN luchador con sus stats base y movimientos.
|
|
21
|
-
* OJO: aquí NO van los items; los items son por jugador.
|
|
22
|
-
*/
|
|
23
|
-
export interface PlayerFighterConfig {
|
|
24
|
-
fighter: FighterDefinition;
|
|
25
|
-
maxHp: number;
|
|
26
|
-
moves: MoveDefinition[];
|
|
27
|
-
amulet: AmuletDefinition | null;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Configuración de un jugador para el combate.
|
|
31
|
-
*
|
|
32
|
-
* - Forma nueva (multi-team): usar `team`.
|
|
33
|
-
* - Forma legacy (1 vs 1): usar fighter/maxHp/moves/items/amulet como hasta ahora.
|
|
34
|
-
* Si `team` no viene o está vacío, el engine construye un equipo de 1 con esos datos.
|
|
35
|
-
*/
|
|
36
|
-
export interface FighterSlotConfig {
|
|
37
|
-
fighter: FighterDefinition;
|
|
38
|
-
maxHp: number;
|
|
39
|
-
moves: MoveDefinition[];
|
|
40
|
-
amulet: AmuletDefinition | null;
|
|
41
|
-
}
|
|
42
19
|
export interface PlayerBattleConfig {
|
|
43
|
-
/**
|
|
44
|
-
* Nueva forma: equipo completo. Si existe y tiene elementos,
|
|
45
|
-
* el engine usará esto como fuente de verdad.
|
|
46
|
-
*/
|
|
47
|
-
team?: FighterSlotConfig[];
|
|
48
|
-
/**
|
|
49
|
-
* Forma legacy (1 vs 1). Se mantiene para compatibilidad
|
|
50
|
-
* con skins y backends actuales.
|
|
51
|
-
*/
|
|
52
20
|
fighter: FighterDefinition;
|
|
53
21
|
maxHp: number;
|
|
54
22
|
moves: MoveDefinition[];
|
package/dist/core/engine.js
CHANGED
|
@@ -30,26 +30,29 @@ const cloneStats = (stats) => ({
|
|
|
30
30
|
// ------------------------------------------------------
|
|
31
31
|
// Creación de estado inicial
|
|
32
32
|
// ------------------------------------------------------
|
|
33
|
-
const createBattleFighter = (
|
|
33
|
+
const createBattleFighter = (cfg) => {
|
|
34
|
+
if (cfg.moves.length !== 4) {
|
|
35
|
+
throw new Error("Each fighter must have exactly 4 moves in MVP");
|
|
36
|
+
}
|
|
37
|
+
if (cfg.items.length > 4) {
|
|
38
|
+
throw new Error("A fighter cannot have more than 4 items");
|
|
39
|
+
}
|
|
34
40
|
return {
|
|
35
|
-
fighterId:
|
|
36
|
-
classId:
|
|
37
|
-
maxHp:
|
|
38
|
-
currentHp:
|
|
39
|
-
baseStats:
|
|
40
|
-
effectiveStats:
|
|
41
|
-
moves:
|
|
42
|
-
moveId:
|
|
43
|
-
currentPP:
|
|
41
|
+
fighterId: cfg.fighter.id,
|
|
42
|
+
classId: cfg.fighter.classId,
|
|
43
|
+
maxHp: cfg.maxHp,
|
|
44
|
+
currentHp: cfg.maxHp,
|
|
45
|
+
baseStats: cloneStats(cfg.fighter.baseStats),
|
|
46
|
+
effectiveStats: cloneStats(cfg.fighter.baseStats),
|
|
47
|
+
moves: cfg.moves.map((move) => ({
|
|
48
|
+
moveId: move.id,
|
|
49
|
+
currentPP: move.maxPP,
|
|
44
50
|
})),
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
items: playerItems.map((it) => ({
|
|
49
|
-
itemId: it.id,
|
|
50
|
-
usesRemaining: it.maxUses ?? 1,
|
|
51
|
+
items: cfg.items.map((item) => ({
|
|
52
|
+
itemId: item.id,
|
|
53
|
+
usesRemaining: item.maxUses,
|
|
51
54
|
})),
|
|
52
|
-
amuletId:
|
|
55
|
+
amuletId: cfg.amulet ? cfg.amulet.id : null,
|
|
53
56
|
statuses: [],
|
|
54
57
|
isAlive: true,
|
|
55
58
|
};
|
|
@@ -86,36 +89,10 @@ const recomputeEffectiveStatsForFighter = (state, fighter) => {
|
|
|
86
89
|
effectiveStats: eff,
|
|
87
90
|
};
|
|
88
91
|
};
|
|
89
|
-
const normalizeTeamFromConfig = (cfg) => {
|
|
90
|
-
// Si ya viene en forma de equipo, úsalo tal cual
|
|
91
|
-
if (cfg.team && cfg.team.length > 0) {
|
|
92
|
-
return cfg.team;
|
|
93
|
-
}
|
|
94
|
-
// Forma legacy: un solo fighter → lo envolvemos en un array
|
|
95
|
-
return [
|
|
96
|
-
{
|
|
97
|
-
fighter: cfg.fighter,
|
|
98
|
-
maxHp: cfg.maxHp,
|
|
99
|
-
moves: cfg.moves,
|
|
100
|
-
amulet: cfg.amulet,
|
|
101
|
-
},
|
|
102
|
-
];
|
|
103
|
-
};
|
|
104
92
|
const createPlayerBattleState = (cfg) => {
|
|
105
|
-
|
|
106
|
-
const teamSlots = cfg.team && cfg.team.length > 0
|
|
107
|
-
? cfg.team
|
|
108
|
-
: [
|
|
109
|
-
{
|
|
110
|
-
fighter: cfg.fighter,
|
|
111
|
-
maxHp: cfg.maxHp,
|
|
112
|
-
moves: cfg.moves,
|
|
113
|
-
amulet: cfg.amulet,
|
|
114
|
-
},
|
|
115
|
-
];
|
|
116
|
-
const fighterTeam = teamSlots.map((slot) => createBattleFighter(slot, cfg.items));
|
|
93
|
+
const fighter = createBattleFighter(cfg);
|
|
117
94
|
return {
|
|
118
|
-
fighterTeam,
|
|
95
|
+
fighterTeam: [fighter],
|
|
119
96
|
activeIndex: 0,
|
|
120
97
|
};
|
|
121
98
|
};
|
|
@@ -503,6 +480,34 @@ const applyEffectsOnTarget = (state, actor, target, moveTypeId, isCritical, effe
|
|
|
503
480
|
}
|
|
504
481
|
return { actor: currentActor, target: currentTarget, events };
|
|
505
482
|
};
|
|
483
|
+
const resolveSwitchFighter = (state, playerKey, action) => {
|
|
484
|
+
if (action.kind !== "switch_fighter") {
|
|
485
|
+
return { state, events: [] };
|
|
486
|
+
}
|
|
487
|
+
const events = [];
|
|
488
|
+
const player = playerKey === "player1" ? state.player1 : state.player2;
|
|
489
|
+
const from = getActiveFighter(player);
|
|
490
|
+
const to = player.fighterTeam[action.newIndex];
|
|
491
|
+
if (!to)
|
|
492
|
+
return { state, events };
|
|
493
|
+
if (!to.isAlive || to.currentHp <= 0)
|
|
494
|
+
return { state, events };
|
|
495
|
+
if (action.newIndex === player.activeIndex)
|
|
496
|
+
return { state, events };
|
|
497
|
+
const updatedPlayer = {
|
|
498
|
+
...player,
|
|
499
|
+
activeIndex: action.newIndex,
|
|
500
|
+
};
|
|
501
|
+
const newState = playerKey === "player1"
|
|
502
|
+
? { ...state, player1: updatedPlayer }
|
|
503
|
+
: { ...state, player2: updatedPlayer };
|
|
504
|
+
events.push({
|
|
505
|
+
...createBaseEvent(state.turnNumber, "fighter_switched", `${from.fighterId} cambia a ${to.fighterId}`),
|
|
506
|
+
fromFighterId: from.fighterId,
|
|
507
|
+
toFighterId: to.fighterId,
|
|
508
|
+
});
|
|
509
|
+
return { state: newState, events };
|
|
510
|
+
};
|
|
506
511
|
const getMovePriorityAndSpeed = (state, playerKey, action) => {
|
|
507
512
|
const { self } = getOpponentAndSelf(state, playerKey);
|
|
508
513
|
if (action.kind === "no_action") {
|
|
@@ -520,6 +525,12 @@ const getMovePriorityAndSpeed = (state, playerKey, action) => {
|
|
|
520
525
|
speed: self.effectiveStats.speed,
|
|
521
526
|
};
|
|
522
527
|
}
|
|
528
|
+
if (action.kind === "switch_fighter") {
|
|
529
|
+
return {
|
|
530
|
+
priority: 10, // prioridad especial (ajústalo si quieres)
|
|
531
|
+
speed: self.effectiveStats.speed,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
523
534
|
// Items y switch: prioridad base 0 por ahora
|
|
524
535
|
return {
|
|
525
536
|
priority: 0,
|
|
@@ -785,35 +796,6 @@ const applyEndOfTurnStatuses = (state) => {
|
|
|
785
796
|
};
|
|
786
797
|
return { state: newState, events };
|
|
787
798
|
};
|
|
788
|
-
const applySwitchAction = (state, playerKey, newIndex, events) => {
|
|
789
|
-
const playerState = state[playerKey];
|
|
790
|
-
const team = playerState.fighterTeam;
|
|
791
|
-
// Validaciones básicas
|
|
792
|
-
if (newIndex < 0 || newIndex >= team.length)
|
|
793
|
-
return state;
|
|
794
|
-
if (!team[newIndex]?.isAlive)
|
|
795
|
-
return state;
|
|
796
|
-
if (newIndex === playerState.activeIndex)
|
|
797
|
-
return state;
|
|
798
|
-
const oldIndex = playerState.activeIndex;
|
|
799
|
-
const oldFighter = team[oldIndex];
|
|
800
|
-
const newFighter = team[newIndex];
|
|
801
|
-
const newPlayerState = {
|
|
802
|
-
...playerState,
|
|
803
|
-
activeIndex: newIndex,
|
|
804
|
-
};
|
|
805
|
-
const newState = {
|
|
806
|
-
...state,
|
|
807
|
-
[playerKey]: newPlayerState,
|
|
808
|
-
};
|
|
809
|
-
events.push({
|
|
810
|
-
...createBaseEvent(state.turnNumber, "fighter_switched", `${oldFighter.fighterId} se retira. ¡Adelante ${newFighter.fighterId}!`),
|
|
811
|
-
player: playerKey,
|
|
812
|
-
oldFighterId: oldFighter.fighterId,
|
|
813
|
-
newFighterId: newFighter.fighterId,
|
|
814
|
-
});
|
|
815
|
-
return newState;
|
|
816
|
-
};
|
|
817
799
|
// ------------------------------------------------------
|
|
818
800
|
// Bucle principal de turno
|
|
819
801
|
// ------------------------------------------------------
|
|
@@ -849,7 +831,6 @@ export const resolveTurn = (state, actions) => {
|
|
|
849
831
|
...getMovePriorityAndSpeed(runtimeState, "player2", actions.player2),
|
|
850
832
|
},
|
|
851
833
|
];
|
|
852
|
-
// Orden clásico por prioridad + velocidad
|
|
853
834
|
entries.sort((a, b) => {
|
|
854
835
|
if (b.priority !== a.priority) {
|
|
855
836
|
return b.priority - a.priority;
|
|
@@ -866,29 +847,7 @@ export const resolveTurn = (state, actions) => {
|
|
|
866
847
|
priority: e.priority,
|
|
867
848
|
speed: e.speed,
|
|
868
849
|
})));
|
|
869
|
-
|
|
870
|
-
const switchEntries = entries.filter((e) => e.action.kind === "switch_fighter");
|
|
871
|
-
for (const entry of switchEntries) {
|
|
872
|
-
const { playerKey, action } = entry;
|
|
873
|
-
if (action.kind !== "switch_fighter")
|
|
874
|
-
continue;
|
|
875
|
-
const { self } = getOpponentAndSelf(currentState, playerKey);
|
|
876
|
-
// Si el activo ya está muerto, no tiene sentido cambiar (o el combate ya está decidido).
|
|
877
|
-
if (!self.isAlive || self.currentHp <= 0)
|
|
878
|
-
continue;
|
|
879
|
-
if (hasHardCc(currentState, self)) {
|
|
880
|
-
// No puede cambiar tampoco si está bajo hard CC
|
|
881
|
-
events.push({
|
|
882
|
-
...createBaseEvent(currentState.turnNumber, "action_skipped_hard_cc", `${self.fighterId} no puede actuar por control total`),
|
|
883
|
-
actorId: self.fighterId,
|
|
884
|
-
});
|
|
885
|
-
continue;
|
|
886
|
-
}
|
|
887
|
-
currentState = applySwitchAction(currentState, playerKey, action.newIndex, events);
|
|
888
|
-
}
|
|
889
|
-
// 🔁 2) FASE DE ACCIONES (movimientos / objetos)
|
|
890
|
-
const actionEntries = entries.filter((e) => e.action.kind !== "switch_fighter");
|
|
891
|
-
for (const entry of actionEntries) {
|
|
850
|
+
for (const entry of entries) {
|
|
892
851
|
const { playerKey, action } = entry;
|
|
893
852
|
if (action.kind === "no_action")
|
|
894
853
|
continue;
|
|
@@ -907,13 +866,18 @@ export const resolveTurn = (state, actions) => {
|
|
|
907
866
|
currentState = result.state;
|
|
908
867
|
events.push(...result.events);
|
|
909
868
|
}
|
|
910
|
-
|
|
869
|
+
if (action.kind === "use_item") {
|
|
911
870
|
const result = resolveItemUse(currentState, playerKey, action);
|
|
912
871
|
currentState = result.state;
|
|
913
872
|
events.push(...result.events);
|
|
914
873
|
}
|
|
874
|
+
if (action.kind === "switch_fighter") {
|
|
875
|
+
const result = resolveSwitchFighter(currentState, playerKey, action);
|
|
876
|
+
currentState = result.state;
|
|
877
|
+
events.push(...result.events);
|
|
878
|
+
}
|
|
915
879
|
else {
|
|
916
|
-
//
|
|
880
|
+
// switch_fighter u otros no implementados aún
|
|
917
881
|
continue;
|
|
918
882
|
}
|
|
919
883
|
const winnerAfterAction = checkWinner(currentState);
|
|
@@ -932,7 +896,6 @@ export const resolveTurn = (state, actions) => {
|
|
|
932
896
|
};
|
|
933
897
|
}
|
|
934
898
|
}
|
|
935
|
-
// 🔁 3) Estados de final de turno
|
|
936
899
|
const statusResult = applyEndOfTurnStatuses(currentState);
|
|
937
900
|
currentState = statusResult.state;
|
|
938
901
|
events.push(...statusResult.events);
|
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" | "shield_applied" | "status_applied" | "status_refreshed" | "status_expired" | "fighter_fainted" | "
|
|
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" | "battle_end";
|
|
3
3
|
export interface BaseBattleEvent {
|
|
4
4
|
id: string;
|
|
5
5
|
turnNumber: number;
|
|
@@ -51,6 +51,11 @@ export interface StatusAppliedEvent extends BaseBattleEvent {
|
|
|
51
51
|
stacks: 1 | 2;
|
|
52
52
|
durationTurns: number;
|
|
53
53
|
}
|
|
54
|
+
export interface FighterSwitchedEvent extends BaseBattleEvent {
|
|
55
|
+
kind: "fighter_switched";
|
|
56
|
+
fromFighterId: FighterId;
|
|
57
|
+
toFighterId: FighterId;
|
|
58
|
+
}
|
|
54
59
|
export interface StatusExpiredEvent extends BaseBattleEvent {
|
|
55
60
|
kind: "status_expired";
|
|
56
61
|
targetId: FighterId;
|
|
@@ -60,12 +65,6 @@ export interface FighterFaintedEvent extends BaseBattleEvent {
|
|
|
60
65
|
kind: "fighter_fainted";
|
|
61
66
|
fighterId: FighterId;
|
|
62
67
|
}
|
|
63
|
-
export interface FighterSwitchedEvent extends BaseBattleEvent {
|
|
64
|
-
kind: "fighter_switched";
|
|
65
|
-
player: "player1" | "player2";
|
|
66
|
-
oldFighterId: FighterId;
|
|
67
|
-
newFighterId: FighterId;
|
|
68
|
-
}
|
|
69
68
|
export interface BattleEndEvent extends BaseBattleEvent {
|
|
70
69
|
kind: "battle_end";
|
|
71
70
|
winner: "player1" | "player2" | "draw";
|
|
@@ -2,24 +2,9 @@ import type { TypeDefinition, TypeEffectivenessMatrix, MoveDefinition, ItemDefin
|
|
|
2
2
|
import type { PlayerBattleConfig } from "../core/engine.js";
|
|
3
3
|
export interface FighterLoadout {
|
|
4
4
|
fighterId: string | null;
|
|
5
|
-
moveIds: string[];
|
|
6
5
|
itemIds: string[];
|
|
7
6
|
amuletId: string | null;
|
|
8
|
-
|
|
9
|
-
* Lista de luchadores del equipo (máx. 6).
|
|
10
|
-
* Si solo hay uno, puede contener un único id.
|
|
11
|
-
*/
|
|
12
|
-
fighterIds?: string[];
|
|
13
|
-
/**
|
|
14
|
-
* Movimientos por luchador. Si falta para alguno, se usan
|
|
15
|
-
* `moveIds` como fallback.
|
|
16
|
-
*/
|
|
17
|
-
movesByFighterId?: Record<string, string[]>;
|
|
18
|
-
/**
|
|
19
|
-
* (Opcional) Futuro: cuál es el activo actual.
|
|
20
|
-
* Si no se define, se asume fighterIds[0].
|
|
21
|
-
*/
|
|
22
|
-
activeFighterId?: string | null;
|
|
7
|
+
moveIds: string[];
|
|
23
8
|
}
|
|
24
9
|
export interface CombatSkin {
|
|
25
10
|
readonly id: string;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// pokemon-io-core/src/skins/
|
|
1
|
+
// pokemon-io-core/src/skins/pokemon/pokemonSkin.ts
|
|
2
2
|
import { POKEMON_ITEMS } from "../pokemon/index.js";
|
|
3
3
|
import { TRIBE_FIGHTERS, TRIBE_MOVES, TRIBE_STATUSES, TRIBE_TYPES, TRIBE_TYPE_MATRIX, } from "./index.js";
|
|
4
4
|
// --- helpers ---
|
|
5
|
-
const
|
|
5
|
+
const findPokemonById = (id) => {
|
|
6
6
|
const lower = id.toLowerCase();
|
|
7
7
|
return TRIBE_FIGHTERS.find((f) => f.id.toLowerCase() === lower) ?? null;
|
|
8
8
|
};
|
|
@@ -72,27 +72,15 @@ export class TribeSkin {
|
|
|
72
72
|
throw new Error("fighterId is required in FighterLoadout");
|
|
73
73
|
}
|
|
74
74
|
console.log("buildPlayerConfig", loadout.fighterId);
|
|
75
|
-
const fighter =
|
|
75
|
+
const fighter = findPokemonById(loadout.fighterId);
|
|
76
76
|
if (!fighter) {
|
|
77
77
|
throw new Error(`Unknown fighterId for Pokemon skin: ${loadout.fighterId}`);
|
|
78
78
|
}
|
|
79
79
|
const moves = buildMovesFromLoadout(fighter, loadout);
|
|
80
80
|
const items = buildItemsFromLoadout(loadout);
|
|
81
|
-
const maxHp = fighter.baseStats.defense + fighter.baseStats.offense;
|
|
82
81
|
return {
|
|
83
|
-
// ✅ NUEVO: modelo multi-equipo
|
|
84
|
-
team: [
|
|
85
|
-
{
|
|
86
|
-
fighter,
|
|
87
|
-
maxHp,
|
|
88
|
-
moves,
|
|
89
|
-
amulet: null,
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
// ✅ LEGACY: seguimos rellenando los campos antiguos
|
|
93
|
-
// por si algún sitio sigue leyéndolos.
|
|
94
82
|
fighter,
|
|
95
|
-
maxHp,
|
|
83
|
+
maxHp: fighter.baseStats.defense + fighter.baseStats.offense,
|
|
96
84
|
moves,
|
|
97
85
|
items,
|
|
98
86
|
amulet: null,
|
|
@@ -77,21 +77,9 @@ export class PokemonSkin {
|
|
|
77
77
|
}
|
|
78
78
|
const moves = buildMovesFromLoadout(fighter, loadout);
|
|
79
79
|
const items = buildItemsFromLoadout(loadout);
|
|
80
|
-
const maxHp = fighter.baseStats.defense + fighter.baseStats.offense;
|
|
81
80
|
return {
|
|
82
|
-
// ✅ NUEVO: modelo multi-equipo
|
|
83
|
-
team: [
|
|
84
|
-
{
|
|
85
|
-
fighter,
|
|
86
|
-
maxHp,
|
|
87
|
-
moves,
|
|
88
|
-
amulet: null,
|
|
89
|
-
},
|
|
90
|
-
],
|
|
91
|
-
// ✅ LEGACY: seguimos rellenando los campos antiguos
|
|
92
|
-
// por si algún sitio sigue leyéndolos.
|
|
93
81
|
fighter,
|
|
94
|
-
maxHp,
|
|
82
|
+
maxHp: fighter.baseStats.defense + fighter.baseStats.offense,
|
|
95
83
|
moves,
|
|
96
84
|
items,
|
|
97
85
|
amulet: null,
|