pokemon-io-core 0.0.82 → 0.0.84
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/core/battleState.d.ts +1 -1
- package/dist/core/engine.js +43 -7
- package/dist/core/index.d.ts +0 -1
- package/dist/core/index.js +0 -1
- package/dist/engine/actions/forcedSwitch.d.ts +5 -0
- package/dist/engine/actions/forcedSwitch.js +44 -0
- package/dist/engine/actions/index.d.ts +5 -0
- package/dist/engine/actions/index.js +5 -0
- package/dist/engine/actions/item.d.ts +6 -0
- package/dist/engine/actions/item.js +61 -0
- package/dist/engine/actions/move.d.ts +6 -0
- package/dist/engine/actions/move.js +120 -0
- package/dist/engine/actions/priority.d.ts +6 -0
- package/dist/engine/actions/priority.js +30 -0
- package/dist/engine/actions/switch.d.ts +6 -0
- package/dist/engine/actions/switch.js +55 -0
- package/dist/engine/combat/crit.d.ts +2 -0
- package/dist/engine/combat/crit.js +4 -0
- package/dist/engine/combat/damage.d.ts +20 -0
- package/dist/engine/combat/damage.js +66 -0
- package/dist/engine/combat/heal.d.ts +6 -0
- package/dist/engine/combat/heal.js +25 -0
- package/dist/engine/combat/index.d.ts +5 -0
- package/dist/engine/combat/index.js +5 -0
- package/dist/engine/combat/typeEffectiveness.d.ts +3 -0
- package/dist/engine/combat/typeEffectiveness.js +7 -0
- package/dist/engine/combat/winner.d.ts +2 -0
- package/dist/engine/combat/winner.js +11 -0
- package/dist/engine/debug.d.ts +1 -0
- package/dist/engine/debug.js +6 -0
- package/dist/engine/effects/applyEffects.d.ts +7 -0
- package/dist/engine/effects/applyEffects.js +103 -0
- package/dist/engine/effects/index.d.ts +2 -0
- package/dist/engine/effects/index.js +2 -0
- package/dist/engine/effects/target.d.ts +6 -0
- package/dist/engine/effects/target.js +26 -0
- package/dist/engine/engine.d.ts +53 -0
- package/dist/engine/engine.js +1046 -0
- package/dist/engine/events.d.ts +4 -0
- package/dist/engine/events.js +12 -0
- package/dist/engine/fighters/fighter.d.ts +4 -0
- package/dist/engine/fighters/fighter.js +53 -0
- package/dist/engine/fighters/index.d.ts +3 -0
- package/dist/engine/fighters/index.js +3 -0
- package/dist/engine/fighters/selectors.d.ts +13 -0
- package/dist/engine/fighters/selectors.js +19 -0
- package/dist/engine/fighters/update.d.ts +3 -0
- package/dist/engine/fighters/update.js +30 -0
- package/dist/engine/index.d.ts +11 -1
- package/dist/engine/index.js +11 -1
- package/dist/engine/rng.d.ts +2 -0
- package/dist/engine/rng.js +8 -0
- package/dist/engine/rules.d.ts +44 -0
- package/dist/engine/rules.js +10 -0
- package/dist/engine/runtime.d.ts +7 -0
- package/dist/engine/runtime.js +49 -0
- package/dist/engine/status/apply.d.ts +6 -0
- package/dist/engine/status/apply.js +49 -0
- package/dist/engine/status/clear.d.ts +6 -0
- package/dist/engine/status/clear.js +47 -0
- package/dist/engine/status/endOfTurn.d.ts +6 -0
- package/dist/engine/status/endOfTurn.js +80 -0
- package/dist/engine/status/hardCc.d.ts +3 -0
- package/dist/engine/status/hardCc.js +4 -0
- package/dist/engine/status/index.d.ts +4 -0
- package/dist/engine/status/index.js +4 -0
- package/dist/engine/turn/index.d.ts +1 -0
- package/dist/engine/turn/index.js +1 -0
- package/dist/engine/turn/resolveTurn.d.ts +5 -0
- package/dist/engine/turn/resolveTurn.js +139 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/skins/CombatSkin.d.ts +1 -1
- package/dist/skins/cliches/clicheSkin.d.ts +2 -1
- package/dist/skins/pokemon/pokemonSkin.d.ts +2 -1
- package/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
let eventCounter = 0;
|
|
2
|
+
const nextEventId = () => {
|
|
3
|
+
eventCounter += 1;
|
|
4
|
+
return `evt_${eventCounter}`;
|
|
5
|
+
};
|
|
6
|
+
export const createBaseEvent = (turnNumber, kind, message) => ({
|
|
7
|
+
id: nextEventId(),
|
|
8
|
+
kind,
|
|
9
|
+
turnNumber,
|
|
10
|
+
message,
|
|
11
|
+
timestamp: Date.now(),
|
|
12
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { BattleFighter } from "../../core";
|
|
2
|
+
import { PlayerFighterBattleConfig, RuntimeBattleState } from "../rules";
|
|
3
|
+
export declare const createBattleFighter: (cfg: PlayerFighterBattleConfig) => BattleFighter;
|
|
4
|
+
export declare const recomputeEffectiveStatsForFighter: (state: RuntimeBattleState, fighter: BattleFighter) => BattleFighter;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { cloneStats } from "../runtime";
|
|
2
|
+
export const createBattleFighter = (cfg) => {
|
|
3
|
+
if (cfg.moves.length !== 4) {
|
|
4
|
+
throw new Error("Each fighter must have exactly 4 moves in MVP");
|
|
5
|
+
}
|
|
6
|
+
return {
|
|
7
|
+
fighterId: cfg.fighter.id,
|
|
8
|
+
classId: cfg.fighter.classId,
|
|
9
|
+
maxHp: cfg.maxHp,
|
|
10
|
+
currentHp: cfg.maxHp,
|
|
11
|
+
baseStats: cloneStats(cfg.fighter.baseStats),
|
|
12
|
+
effectiveStats: cloneStats(cfg.fighter.baseStats),
|
|
13
|
+
moves: cfg.moves.map((move) => ({
|
|
14
|
+
moveId: move.id,
|
|
15
|
+
currentPP: move.maxPP,
|
|
16
|
+
})),
|
|
17
|
+
amuletId: cfg.amulet ? cfg.amulet.id : null,
|
|
18
|
+
statuses: [],
|
|
19
|
+
isAlive: true,
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export const recomputeEffectiveStatsForFighter = (state, fighter) => {
|
|
23
|
+
// Partimos de base
|
|
24
|
+
let eff = { ...fighter.baseStats };
|
|
25
|
+
// 1) Aplicar estados
|
|
26
|
+
for (const st of fighter.statuses) {
|
|
27
|
+
const def = state.runtime.statusesById[st.statusId];
|
|
28
|
+
if (!def)
|
|
29
|
+
continue;
|
|
30
|
+
const stacks = st.stacks === 2 ? 2 : 1;
|
|
31
|
+
for (const effDef of def.effectsPerStack) {
|
|
32
|
+
const totalStacks = stacks; // podrías multiplicar efectos por stacks
|
|
33
|
+
switch (effDef.kind) {
|
|
34
|
+
case "modify_stats":
|
|
35
|
+
eff.offense += (effDef.offenseDelta ?? 0) * totalStacks;
|
|
36
|
+
eff.defense += (effDef.defenseDelta ?? 0) * totalStacks;
|
|
37
|
+
eff.speed += (effDef.speedDelta ?? 0) * totalStacks;
|
|
38
|
+
eff.crit += (effDef.critDelta ?? 0) * totalStacks;
|
|
39
|
+
break;
|
|
40
|
+
case "modify_effective_speed":
|
|
41
|
+
eff.speed = Math.floor(eff.speed * Math.pow(effDef.multiplier, totalStacks));
|
|
42
|
+
break;
|
|
43
|
+
default:
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// 2) Aquí podrías aplicar amuletos, buffs temporales o lo que quieras
|
|
49
|
+
return {
|
|
50
|
+
...fighter,
|
|
51
|
+
effectiveStats: eff,
|
|
52
|
+
};
|
|
53
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BattleFighter, PlayerBattleState } from "../../core";
|
|
2
|
+
import { RuntimeBattleState } from "../rules";
|
|
3
|
+
export declare const getActiveFighter: (player: PlayerBattleState) => BattleFighter;
|
|
4
|
+
export declare const getOpponentAndSelf: (state: RuntimeBattleState, playerKey: "player1" | "player2") => {
|
|
5
|
+
self: BattleFighter;
|
|
6
|
+
opponent: BattleFighter;
|
|
7
|
+
};
|
|
8
|
+
export declare const getPlayersAndActives: (state: RuntimeBattleState, playerKey: "player1" | "player2") => {
|
|
9
|
+
selfPlayer: PlayerBattleState;
|
|
10
|
+
oppPlayer: PlayerBattleState;
|
|
11
|
+
self: BattleFighter;
|
|
12
|
+
opponent: BattleFighter;
|
|
13
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const getActiveFighter = (player) => player.fighterTeam[player.activeIndex];
|
|
2
|
+
export const getOpponentAndSelf = (state, playerKey) => {
|
|
3
|
+
const selfPlayer = playerKey === "player1" ? state.player1 : state.player2;
|
|
4
|
+
const oppPlayer = playerKey === "player1" ? state.player2 : state.player1;
|
|
5
|
+
return {
|
|
6
|
+
self: getActiveFighter(selfPlayer),
|
|
7
|
+
opponent: getActiveFighter(oppPlayer),
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export const getPlayersAndActives = (state, playerKey) => {
|
|
11
|
+
const selfPlayer = playerKey === "player1" ? state.player1 : state.player2;
|
|
12
|
+
const oppPlayer = playerKey === "player1" ? state.player2 : state.player1;
|
|
13
|
+
return {
|
|
14
|
+
selfPlayer,
|
|
15
|
+
oppPlayer,
|
|
16
|
+
self: getActiveFighter(selfPlayer),
|
|
17
|
+
opponent: getActiveFighter(oppPlayer),
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { BattleFighter } from "../../core";
|
|
2
|
+
import { RuntimeBattleState } from "../rules";
|
|
3
|
+
export declare const updateFightersInState: (state: RuntimeBattleState, actingPlayerKey: "player1" | "player2", updatedSelf: BattleFighter, updatedOpponent: BattleFighter) => RuntimeBattleState;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// ------------------------------------------------------
|
|
2
|
+
// Actualizar fighters en el estado
|
|
3
|
+
// ------------------------------------------------------
|
|
4
|
+
export const updateFightersInState = (state, actingPlayerKey, updatedSelf, updatedOpponent) => {
|
|
5
|
+
const player1 = { ...state.player1 };
|
|
6
|
+
const player2 = { ...state.player2 };
|
|
7
|
+
const updateInPlayer = (player, updated) => {
|
|
8
|
+
const newTeam = player.fighterTeam.map((f, idx) => idx === player.activeIndex ? updated : f);
|
|
9
|
+
return {
|
|
10
|
+
...player,
|
|
11
|
+
fighterTeam: newTeam,
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
if (actingPlayerKey === "player1") {
|
|
15
|
+
const selfPlayer = updateInPlayer(player1, updatedSelf);
|
|
16
|
+
const oppPlayer = updateInPlayer(player2, updatedOpponent);
|
|
17
|
+
return {
|
|
18
|
+
...state,
|
|
19
|
+
player1: selfPlayer,
|
|
20
|
+
player2: oppPlayer,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const selfPlayer = updateInPlayer(player2, updatedSelf);
|
|
24
|
+
const oppPlayer = updateInPlayer(player1, updatedOpponent);
|
|
25
|
+
return {
|
|
26
|
+
...state,
|
|
27
|
+
player1: oppPlayer,
|
|
28
|
+
player2: selfPlayer,
|
|
29
|
+
};
|
|
30
|
+
};
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './debug.js';
|
|
2
|
+
export * from './events.js';
|
|
3
|
+
export * from './rng.js';
|
|
4
|
+
export * from './rules.js';
|
|
5
|
+
export * from './runtime.js';
|
|
6
|
+
export * from './actions/index.js';
|
|
7
|
+
export * from './combat/index.js';
|
|
8
|
+
export * from './effects/index.js';
|
|
9
|
+
export * from './fighters/index.js';
|
|
10
|
+
export * from './status/index.js';
|
|
11
|
+
export * from './turn/index.js';
|
package/dist/engine/index.js
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './debug.js';
|
|
2
|
+
export * from './events.js';
|
|
3
|
+
export * from './rng.js';
|
|
4
|
+
export * from './rules.js';
|
|
5
|
+
export * from './runtime.js';
|
|
6
|
+
export * from './actions/index.js';
|
|
7
|
+
export * from './combat/index.js';
|
|
8
|
+
export * from './effects/index.js';
|
|
9
|
+
export * from './fighters/index.js';
|
|
10
|
+
export * from './status/index.js';
|
|
11
|
+
export * from './turn/index.js';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AmuletDefinition, BattleState, FighterDefinition, ItemDefinition, ItemId, MoveDefinition, MoveId, StatusDefinition, StatusId, TypeDefinition, TypeEffectivenessMatrix, TypeId } from "../core";
|
|
2
|
+
export interface BattleRules {
|
|
3
|
+
baseCritChance: number;
|
|
4
|
+
critPerStat: number;
|
|
5
|
+
critMultiplier: number;
|
|
6
|
+
stabMultiplier: number;
|
|
7
|
+
randomMinDamageFactor: number;
|
|
8
|
+
randomMaxDamageFactor: number;
|
|
9
|
+
}
|
|
10
|
+
export interface BattleRuntime {
|
|
11
|
+
rules: BattleRules;
|
|
12
|
+
typesById: Record<TypeId, TypeDefinition>;
|
|
13
|
+
movesById: Record<MoveId, MoveDefinition>;
|
|
14
|
+
itemsById: Record<ItemId, ItemDefinition>;
|
|
15
|
+
amuletsById: Record<string, AmuletDefinition>;
|
|
16
|
+
statusesById: Record<StatusId, StatusDefinition>;
|
|
17
|
+
typeEffectiveness: TypeEffectivenessMatrix;
|
|
18
|
+
}
|
|
19
|
+
export interface PlayerFighterBattleConfig {
|
|
20
|
+
fighter: FighterDefinition;
|
|
21
|
+
maxHp: number;
|
|
22
|
+
moves: MoveDefinition[];
|
|
23
|
+
amulet: AmuletDefinition | null;
|
|
24
|
+
}
|
|
25
|
+
export interface PlayerBattleConfig {
|
|
26
|
+
fighters: PlayerFighterBattleConfig[];
|
|
27
|
+
items: ItemDefinition[];
|
|
28
|
+
amulet: AmuletDefinition | null;
|
|
29
|
+
}
|
|
30
|
+
export interface BattleConfig {
|
|
31
|
+
types: TypeDefinition[];
|
|
32
|
+
typeEffectiveness: TypeEffectivenessMatrix;
|
|
33
|
+
moves: MoveDefinition[];
|
|
34
|
+
items: ItemDefinition[];
|
|
35
|
+
amulets: AmuletDefinition[];
|
|
36
|
+
statuses: StatusDefinition[];
|
|
37
|
+
player1: PlayerBattleConfig;
|
|
38
|
+
player2: PlayerBattleConfig;
|
|
39
|
+
rules?: Partial<BattleRules>;
|
|
40
|
+
}
|
|
41
|
+
export interface RuntimeBattleState extends BattleState {
|
|
42
|
+
runtime: BattleRuntime;
|
|
43
|
+
}
|
|
44
|
+
export declare const DEFAULT_RULES: BattleRules;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// ------------------------------------------------------
|
|
2
|
+
// Config & runtime
|
|
3
|
+
export const DEFAULT_RULES = {
|
|
4
|
+
baseCritChance: 0.05,
|
|
5
|
+
critPerStat: 0.002,
|
|
6
|
+
critMultiplier: 1.1,
|
|
7
|
+
stabMultiplier: 1.1,
|
|
8
|
+
randomMinDamageFactor: 0.8,
|
|
9
|
+
randomMaxDamageFactor: 1.0,
|
|
10
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BattleState, FighterStats } from "../core";
|
|
2
|
+
import { BattleConfig } from "./rules";
|
|
3
|
+
export declare const buildMap: <T extends {
|
|
4
|
+
id: string;
|
|
5
|
+
}>(list: T[]) => Record<string, T>;
|
|
6
|
+
export declare const cloneStats: (stats: FighterStats) => FighterStats;
|
|
7
|
+
export declare const createInitialBattleState: (config: BattleConfig) => BattleState;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createBattleFighter } from "./fighters/fighter";
|
|
2
|
+
import { DEFAULT_RULES, } from "./rules";
|
|
3
|
+
export const buildMap = (list) => list.reduce((acc, item) => {
|
|
4
|
+
acc[item.id] = item;
|
|
5
|
+
return acc;
|
|
6
|
+
}, {});
|
|
7
|
+
export const cloneStats = (stats) => ({
|
|
8
|
+
offense: stats.offense,
|
|
9
|
+
defense: stats.defense,
|
|
10
|
+
speed: stats.speed,
|
|
11
|
+
crit: stats.crit,
|
|
12
|
+
});
|
|
13
|
+
const createPlayerBattleState = (cfg) => {
|
|
14
|
+
const team = cfg.fighters.map((fCfg) => createBattleFighter(fCfg));
|
|
15
|
+
const inventory = cfg.items.map((item) => ({
|
|
16
|
+
itemId: item.id,
|
|
17
|
+
usesRemaining: item.maxUses,
|
|
18
|
+
}));
|
|
19
|
+
return {
|
|
20
|
+
fighterTeam: team,
|
|
21
|
+
activeIndex: 0,
|
|
22
|
+
inventory,
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export const createInitialBattleState = (config) => {
|
|
26
|
+
const rules = {
|
|
27
|
+
...DEFAULT_RULES,
|
|
28
|
+
...config.rules,
|
|
29
|
+
};
|
|
30
|
+
const runtime = {
|
|
31
|
+
rules,
|
|
32
|
+
typesById: buildMap(config.types),
|
|
33
|
+
movesById: buildMap(config.moves),
|
|
34
|
+
itemsById: buildMap(config.items),
|
|
35
|
+
amuletsById: buildMap(config.amulets),
|
|
36
|
+
statusesById: buildMap(config.statuses),
|
|
37
|
+
typeEffectiveness: config.typeEffectiveness,
|
|
38
|
+
};
|
|
39
|
+
const player1 = createPlayerBattleState(config.player1);
|
|
40
|
+
const player2 = createPlayerBattleState(config.player2);
|
|
41
|
+
const state = {
|
|
42
|
+
turnNumber: 1,
|
|
43
|
+
player1,
|
|
44
|
+
player2,
|
|
45
|
+
runtime,
|
|
46
|
+
forcedSwitch: null,
|
|
47
|
+
};
|
|
48
|
+
return state;
|
|
49
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BattleEvent, BattleFighter, StatusId } from "../../core";
|
|
2
|
+
import { RuntimeBattleState } from "../rules";
|
|
3
|
+
export declare const applyStatusToFighter: (state: RuntimeBattleState, target: BattleFighter, statusId: StatusId) => {
|
|
4
|
+
updated: BattleFighter;
|
|
5
|
+
events: BattleEvent[];
|
|
6
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { dbg } from "../debug";
|
|
2
|
+
import { createBaseEvent } from "../events";
|
|
3
|
+
import { randomInRange } from "../rng";
|
|
4
|
+
export const applyStatusToFighter = (state, target, statusId) => {
|
|
5
|
+
const def = state.runtime.statusesById[statusId];
|
|
6
|
+
dbg("APPLY_STATUS_FN", {
|
|
7
|
+
targetId: target.fighterId,
|
|
8
|
+
statusId,
|
|
9
|
+
hasDef: !!def,
|
|
10
|
+
currentStatuses: target.statuses,
|
|
11
|
+
});
|
|
12
|
+
if (!def)
|
|
13
|
+
return { updated: target, events: [] };
|
|
14
|
+
const events = [];
|
|
15
|
+
const existing = target.statuses.find((s) => s.statusId === statusId);
|
|
16
|
+
const duration = Math.floor(randomInRange(def.minDurationTurns, def.maxDurationTurns + 1)) ||
|
|
17
|
+
def.minDurationTurns;
|
|
18
|
+
let newStatuses = [...target.statuses];
|
|
19
|
+
if (!existing) {
|
|
20
|
+
newStatuses.push({
|
|
21
|
+
statusId,
|
|
22
|
+
stacks: 1,
|
|
23
|
+
remainingTurns: duration,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
else if (existing.stacks === 1) {
|
|
27
|
+
newStatuses = newStatuses.map((s) => s.statusId === statusId
|
|
28
|
+
? { ...s, stacks: 2, remainingTurns: duration }
|
|
29
|
+
: s);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
newStatuses = newStatuses.map((s) => s.statusId === statusId ? { ...s, remainingTurns: duration } : s);
|
|
33
|
+
}
|
|
34
|
+
dbg("STATUS_APPLY", {
|
|
35
|
+
fighterId: target.fighterId,
|
|
36
|
+
statusId,
|
|
37
|
+
duration,
|
|
38
|
+
prevStacks: existing?.stacks ?? 0,
|
|
39
|
+
});
|
|
40
|
+
events.push({
|
|
41
|
+
...createBaseEvent(state.turnNumber, "status_applied", `Se aplica ${statusId} a ${target.fighterId}`),
|
|
42
|
+
targetId: target.fighterId,
|
|
43
|
+
statusId,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
updated: { ...target, statuses: newStatuses },
|
|
47
|
+
events,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BattleEvent, BattleFighter, ClearStatusEffect } from "../../core";
|
|
2
|
+
import { RuntimeBattleState } from "../rules";
|
|
3
|
+
export declare const clearStatusFromFighter: (state: RuntimeBattleState, target: BattleFighter, effect: ClearStatusEffect) => {
|
|
4
|
+
updated: BattleFighter;
|
|
5
|
+
events: BattleEvent[];
|
|
6
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { dbg } from "../debug";
|
|
2
|
+
import { createBaseEvent } from "../events";
|
|
3
|
+
export const clearStatusFromFighter = (state, target, effect) => {
|
|
4
|
+
const { statusIds, kinds, clearAll } = effect;
|
|
5
|
+
const events = [];
|
|
6
|
+
const before = target.statuses;
|
|
7
|
+
let after = before;
|
|
8
|
+
if (clearAll) {
|
|
9
|
+
after = [];
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
after = before.filter((st) => {
|
|
13
|
+
const def = state.runtime.statusesById[st.statusId];
|
|
14
|
+
if (statusIds && statusIds.includes(st.statusId)) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (kinds && def && kinds.includes(def.kind)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
if (after.length === before.length) {
|
|
24
|
+
// nada que limpiar
|
|
25
|
+
return { updated: target, events };
|
|
26
|
+
}
|
|
27
|
+
const removed = before
|
|
28
|
+
.filter((old) => !after.some((st) => st.statusId === old.statusId))
|
|
29
|
+
.map((st) => st.statusId);
|
|
30
|
+
dbg("STATUS_CLEAR", {
|
|
31
|
+
fighterId: target.fighterId,
|
|
32
|
+
cleared: removed,
|
|
33
|
+
clearAll: !!clearAll,
|
|
34
|
+
});
|
|
35
|
+
events.push({
|
|
36
|
+
...createBaseEvent(state.turnNumber, "status_cleared", `Se limpian estados (${removed.join(", ")}) de ${target.fighterId}`),
|
|
37
|
+
targetId: target.fighterId,
|
|
38
|
+
statusIds: removed,
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
updated: {
|
|
42
|
+
...target,
|
|
43
|
+
statuses: after,
|
|
44
|
+
},
|
|
45
|
+
events,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { applyDamageToFighter } from "../combat/damage";
|
|
2
|
+
import { dbg } from "../debug";
|
|
3
|
+
import { createBaseEvent } from "../events";
|
|
4
|
+
import { getActiveFighter } from "../fighters/selectors";
|
|
5
|
+
export const applyEndOfTurnStatuses = (state) => {
|
|
6
|
+
const events = [];
|
|
7
|
+
const applyForPlayer = (player) => {
|
|
8
|
+
const active = getActiveFighter(player);
|
|
9
|
+
let updated = { ...active };
|
|
10
|
+
const updatedStatuses = [];
|
|
11
|
+
for (const st of active.statuses) {
|
|
12
|
+
const def = state.runtime.statusesById[st.statusId];
|
|
13
|
+
if (!def) {
|
|
14
|
+
dbg("STATUS_MISSING_DEF", {
|
|
15
|
+
fighterId: active.fighterId,
|
|
16
|
+
statusId: st.statusId,
|
|
17
|
+
});
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
let damageFromStatus = 0;
|
|
21
|
+
def.effectsPerStack.forEach((eff) => {
|
|
22
|
+
if (eff.kind === "damage") {
|
|
23
|
+
const stacksMultiplier = st.stacks === 2 ? 2 : 1;
|
|
24
|
+
const base = typeof eff.flatAmount === "number"
|
|
25
|
+
? eff.flatAmount
|
|
26
|
+
: typeof eff.basePower === "number"
|
|
27
|
+
? eff.basePower
|
|
28
|
+
: 10; // fallback
|
|
29
|
+
damageFromStatus += base * stacksMultiplier;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
if (damageFromStatus > 0 && updated.isAlive) {
|
|
33
|
+
dbg("STATUS_DOT", {
|
|
34
|
+
fighterId: updated.fighterId,
|
|
35
|
+
statusId: st.statusId,
|
|
36
|
+
damageFromStatus,
|
|
37
|
+
stacks: st.stacks,
|
|
38
|
+
});
|
|
39
|
+
const damageRes = applyDamageToFighter(state, updated, damageFromStatus, updated.fighterId, false);
|
|
40
|
+
updated = damageRes.updatedDefender;
|
|
41
|
+
events.push(...damageRes.events);
|
|
42
|
+
}
|
|
43
|
+
const remaining = st.remainingTurns - 1;
|
|
44
|
+
if (remaining > 0) {
|
|
45
|
+
updatedStatuses.push({
|
|
46
|
+
...st,
|
|
47
|
+
remainingTurns: remaining,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
dbg("STATUS_EXPIRE", {
|
|
52
|
+
fighterId: updated.fighterId,
|
|
53
|
+
statusId: st.statusId,
|
|
54
|
+
});
|
|
55
|
+
events.push({
|
|
56
|
+
...createBaseEvent(state.turnNumber, "status_expired", `El estado ${st.statusId} expira en ${updated.fighterId}`),
|
|
57
|
+
targetId: updated.fighterId,
|
|
58
|
+
statusId: st.statusId,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
updated = {
|
|
63
|
+
...updated,
|
|
64
|
+
statuses: updatedStatuses,
|
|
65
|
+
};
|
|
66
|
+
const newTeam = player.fighterTeam.map((f, idx) => idx === player.activeIndex ? updated : f);
|
|
67
|
+
return {
|
|
68
|
+
...player,
|
|
69
|
+
fighterTeam: newTeam,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
const p1 = applyForPlayer(state.player1);
|
|
73
|
+
const p2 = applyForPlayer(state.player2);
|
|
74
|
+
const newState = {
|
|
75
|
+
...state,
|
|
76
|
+
player1: p1,
|
|
77
|
+
player2: p2,
|
|
78
|
+
};
|
|
79
|
+
return { state: newState, events };
|
|
80
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './resolveTurn.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './resolveTurn.js';
|