@zhin.js/plugin-dungeon-expedition 1.0.0

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/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # @zhin.js/plugin-dungeon-expedition
2
+
3
+ 多人、可恢复、确定性回放的 Zhin.js 地牢远征游戏。这个插件既是可玩的
4
+ 1-4 人回合游戏,也是 `@zhin.js/game-kit` 的稳定性验收样本。
5
+
6
+ ## 安装
7
+
8
+ ```bash
9
+ pnpm add @zhin.js/plugin-dungeon-expedition
10
+ ```
11
+
12
+ 将插件加入 Zhin 配置后,可通过 `/地牢`、`地牢` 或 `/dungeon` 进入。
13
+
14
+ ## 游戏流程
15
+
16
+ ```text
17
+ 创建队伍 -> 队员加入并准备 -> 三层探索
18
+ -> 随机事件 / 普通战斗 / 守层者
19
+ -> 胜利、失败或队长结束
20
+ ```
21
+
22
+ 常用指令:
23
+
24
+ ```text
25
+ 地牢 开始
26
+ 地牢 加入
27
+ 地牢 准备
28
+ 地牢 出发
29
+ 地牢 探索
30
+ 地牢 攻击
31
+ 地牢 防御
32
+ 地牢 药水
33
+ 地牢 状态
34
+ 地牢 结束
35
+ ```
36
+
37
+ 支持原生按钮和数字文本 fallback。每个按钮携带 Session revision,点击旧
38
+ 界面不会覆盖新回合,而会返回最新状态。
39
+
40
+ ## 稳定性设计
41
+
42
+ - `VersionedSessionService` 串行化同一 Session 的本地并发操作。
43
+ - 持久化 `revision` 提供乐观并发检查。
44
+ - 持久化 `processed_actions` 抵御 Adapter 重复投递。
45
+ - `rng_state` 保存确定性随机状态,进程重启后可继续同一随机序列。
46
+ - `schema_version` 为后续存档迁移保留稳定边界。
47
+ - 所有规则在纯 `engine.ts` 中执行,不依赖 IM、数据库或系统时间。
48
+ - `defineGamePlugin` 统一数据库、超时、事件、战绩与 HMR 生命周期。
49
+
50
+ ## 开发
51
+
52
+ ```bash
53
+ pnpm --filter @zhin.js/plugin-dungeon-expedition build
54
+ pnpm --filter @zhin.js/plugin-dungeon-expedition test
55
+ ```
56
+
57
+ 核心文件:
58
+
59
+ - `src/engine.ts`:纯领域状态机。
60
+ - `src/session-service.ts`:版本化持久化和跨游戏冲突检查。
61
+ - `src/game-flow.ts`:消息身份、频道和旧 revision 处理。
62
+ - `src/view.ts`:结构化状态与操作键盘。
63
+ - `commands/`、`middlewares/`:约定式能力入口。
@@ -0,0 +1,15 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from '@zhin.js/command';
3
+ import { messageFromCommandInput } from '@zhin.js/game-kit';
4
+ import { DUNGEON_HELP, normalizeDungeonAction, runDungeonCommand, } from "../../lib/dungeon-command.js";
5
+ import { resolveGameServices } from "../../lib/runtime-store.js";
6
+ export default defineCommand({
7
+ description: 'Multiplayer dungeon expedition',
8
+ async execute({ params, input, use, owner }) {
9
+ const rawAction = String(params.action ?? '');
10
+ if (normalizeDungeonAction(rawAction) === 'help')
11
+ return DUNGEON_HELP;
12
+ const service = resolveGameServices({ use, owner });
13
+ return runDungeonCommand(service, messageFromCommandInput(input), rawAction);
14
+ },
15
+ });
@@ -0,0 +1,22 @@
1
+ import { defineCommand } from '@zhin.js/command';
2
+ import { messageFromCommandInput } from '@zhin.js/game-kit';
3
+ import {
4
+ DUNGEON_HELP,
5
+ normalizeDungeonAction,
6
+ runDungeonCommand,
7
+ } from '../../src/dungeon-command.js';
8
+ import { resolveGameServices } from '../../src/runtime-store.js';
9
+
10
+ export default defineCommand({
11
+ description: 'Multiplayer dungeon expedition',
12
+ async execute({ params, input, use, owner }) {
13
+ const rawAction = String(params.action ?? '');
14
+ if (normalizeDungeonAction(rawAction) === 'help') return DUNGEON_HELP;
15
+ const service = resolveGameServices({ use, owner });
16
+ return runDungeonCommand(
17
+ service,
18
+ messageFromCommandInput(input),
19
+ rawAction,
20
+ );
21
+ },
22
+ });
@@ -0,0 +1,5 @@
1
+ import { type GameMessageLike, type GameReply } from '@zhin.js/game-kit';
2
+ import type { SessionService } from './session-service.js';
3
+ export declare const DUNGEON_HELP: string;
4
+ export declare function normalizeDungeonAction(action: string): string | null;
5
+ export declare function runDungeonCommand(service: SessionService, message: GameMessageLike, rawAction: string): Promise<GameReply>;
@@ -0,0 +1,62 @@
1
+ import { channelKey, } from '@zhin.js/game-kit';
2
+ import { continueDungeon, handleDungeonChoice, startDungeon, } from './game-flow.js';
3
+ import { dungeonSessionToken } from './view.js';
4
+ export const DUNGEON_HELP = [
5
+ '地牢远征 · 1-4 人确定性回合冒险',
6
+ '地牢 开始 — 创建远征队',
7
+ '地牢 加入 — 加入当前频道的队伍',
8
+ '地牢 准备 — 切换准备状态',
9
+ '地牢 出发 — 队长开始远征',
10
+ '地牢 探索 — 探索下一房间',
11
+ '地牢 攻击 / 防御 / 药水 — 战斗操作',
12
+ '地牢 状态 — 刷新当前界面',
13
+ '地牢 结束 — 队长结束远征',
14
+ ].join('\n');
15
+ const ACTION_ALIASES = {
16
+ '': 'status',
17
+ help: 'help',
18
+ 帮助: 'help',
19
+ start: 'create',
20
+ 开始: 'create',
21
+ create: 'create',
22
+ 创建: 'create',
23
+ status: 'status',
24
+ 状态: 'status',
25
+ continue: 'status',
26
+ 继续: 'status',
27
+ join: 'join',
28
+ 加入: 'join',
29
+ ready: 'ready',
30
+ 准备: 'ready',
31
+ go: 'start',
32
+ 出发: 'start',
33
+ explore: 'explore',
34
+ 探索: 'explore',
35
+ attack: 'attack',
36
+ 攻击: 'attack',
37
+ defend: 'defend',
38
+ 防御: 'defend',
39
+ potion: 'potion',
40
+ 药水: 'potion',
41
+ quit: 'abort',
42
+ abort: 'abort',
43
+ 结束: 'abort',
44
+ 放弃: 'abort',
45
+ };
46
+ export function normalizeDungeonAction(action) {
47
+ return ACTION_ALIASES[action.trim().toLowerCase()] ?? null;
48
+ }
49
+ export async function runDungeonCommand(service, message, rawAction) {
50
+ const action = normalizeDungeonAction(rawAction);
51
+ if (!action || action === 'help')
52
+ return DUNGEON_HELP;
53
+ if (action === 'create')
54
+ return startDungeon(service, message);
55
+ if (action === 'status')
56
+ return continueDungeon(service, message);
57
+ const session = await service.getActiveByChannel(channelKey(message));
58
+ if (!session)
59
+ return '当前频道没有远征队。发送「地牢 开始」创建。';
60
+ return handleDungeonChoice(service, message, dungeonSessionToken(session), action);
61
+ }
62
+ //# sourceMappingURL=dungeon-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dungeon-command.js","sourceRoot":"","sources":["../src/dungeon-command.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,GAGX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEhD,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,qBAAqB;IACrB,eAAe;IACf,mBAAmB;IACnB,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,wBAAwB;IACxB,gBAAgB;IAChB,gBAAgB;CACjB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,cAAc,GAAqC;IACvD,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,MAAM;IACZ,EAAE,EAAE,MAAM;IACV,KAAK,EAAE,QAAQ;IACf,EAAE,EAAE,QAAQ;IACZ,MAAM,EAAE,QAAQ;IAChB,EAAE,EAAE,QAAQ;IACZ,MAAM,EAAE,QAAQ;IAChB,EAAE,EAAE,QAAQ;IACZ,QAAQ,EAAE,QAAQ;IAClB,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,MAAM;IACZ,EAAE,EAAE,MAAM;IACV,KAAK,EAAE,OAAO;IACd,EAAE,EAAE,OAAO;IACX,EAAE,EAAE,OAAO;IACX,EAAE,EAAE,OAAO;IACX,OAAO,EAAE,SAAS;IAClB,EAAE,EAAE,SAAS;IACb,MAAM,EAAE,QAAQ;IAChB,EAAE,EAAE,QAAQ;IACZ,MAAM,EAAE,QAAQ;IAChB,EAAE,EAAE,QAAQ;IACZ,MAAM,EAAE,QAAQ;IAChB,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,OAAO;IACd,EAAE,EAAE,OAAO;IACX,EAAE,EAAE,OAAO;CACZ,CAAC;AAEF,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,OAAO,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAuB,EACvB,OAAwB,EACxB,SAAiB;IAEjB,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,YAAY,CAAC;IACtD,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/D,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAElE,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,kBAAkB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IACtE,IAAI,CAAC,OAAO;QAAE,OAAO,wBAAwB,CAAC;IAC9C,OAAO,mBAAmB,CACxB,OAAO,EACP,OAAO,EACP,mBAAmB,CAAC,OAAO,CAAC,EAC5B,MAAM,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,63 @@
1
+ import { DeterministicRandom } from '@zhin.js/game-kit';
2
+ export declare const DUNGEON_SCHEMA_VERSION = 1;
3
+ export declare const MAX_PARTY_SIZE = 4;
4
+ export declare const MAX_FLOOR = 3;
5
+ export declare const BOSS_ROOM = 4;
6
+ export type DungeonPhase = 'lobby' | 'exploring' | 'combat' | 'completed';
7
+ export type DungeonResult = 'victory' | 'defeat' | 'aborted' | null;
8
+ export interface DungeonPlayer {
9
+ readonly id: string;
10
+ readonly name: string;
11
+ readonly maxHp: number;
12
+ readonly hp: number;
13
+ readonly guard: number;
14
+ readonly potions: number;
15
+ readonly gold: number;
16
+ readonly ready: boolean;
17
+ }
18
+ export interface DungeonEnemy {
19
+ readonly id: string;
20
+ readonly name: string;
21
+ readonly hp: number;
22
+ readonly maxHp: number;
23
+ readonly attack: number;
24
+ readonly boss: boolean;
25
+ }
26
+ export interface DungeonState {
27
+ readonly schemaVersion: 1;
28
+ readonly ownerId: string;
29
+ readonly phase: DungeonPhase;
30
+ readonly result: DungeonResult;
31
+ readonly floor: number;
32
+ readonly room: number;
33
+ readonly roomsCleared: number;
34
+ readonly turnIndex: number;
35
+ readonly players: readonly DungeonPlayer[];
36
+ readonly enemy: DungeonEnemy | null;
37
+ readonly log: readonly string[];
38
+ }
39
+ export type DungeonAction = {
40
+ readonly type: 'join';
41
+ readonly name: string;
42
+ } | {
43
+ readonly type: 'ready';
44
+ } | {
45
+ readonly type: 'start';
46
+ } | {
47
+ readonly type: 'explore';
48
+ } | {
49
+ readonly type: 'attack';
50
+ } | {
51
+ readonly type: 'defend';
52
+ } | {
53
+ readonly type: 'potion';
54
+ } | {
55
+ readonly type: 'abort';
56
+ };
57
+ export declare class DungeonRuleError extends Error {
58
+ constructor(message: string);
59
+ }
60
+ export declare function createDungeonState(ownerId: string, ownerName: string): DungeonState;
61
+ export declare function applyDungeonAction(state: Readonly<DungeonState>, actorId: string, action: DungeonAction, random: DeterministicRandom): DungeonState;
62
+ export declare function activePlayer(state: Readonly<DungeonState>): DungeonPlayer | null;
63
+ export declare function decodeDungeonState(raw: string): DungeonState;
package/lib/engine.js ADDED
@@ -0,0 +1,325 @@
1
+ export const DUNGEON_SCHEMA_VERSION = 1;
2
+ export const MAX_PARTY_SIZE = 4;
3
+ export const MAX_FLOOR = 3;
4
+ export const BOSS_ROOM = 4;
5
+ export class DungeonRuleError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = 'DungeonRuleError';
9
+ }
10
+ }
11
+ export function createDungeonState(ownerId, ownerName) {
12
+ return {
13
+ schemaVersion: DUNGEON_SCHEMA_VERSION,
14
+ ownerId,
15
+ phase: 'lobby',
16
+ result: null,
17
+ floor: 1,
18
+ room: 0,
19
+ roomsCleared: 0,
20
+ turnIndex: 0,
21
+ players: [createPlayer(ownerId, ownerName, true)],
22
+ enemy: null,
23
+ log: [`${ownerName} 创建了远征队。`],
24
+ };
25
+ }
26
+ export function applyDungeonAction(state, actorId, action, random) {
27
+ if (state.phase === 'completed') {
28
+ throw new DungeonRuleError('本次远征已经结束。');
29
+ }
30
+ if (action.type === 'join')
31
+ return joinParty(state, actorId, action.name);
32
+ const actorIndex = state.players.findIndex((player) => player.id === actorId);
33
+ if (actorIndex < 0)
34
+ throw new DungeonRuleError('你还没有加入这支远征队。');
35
+ if (action.type === 'abort') {
36
+ if (state.ownerId !== actorId) {
37
+ throw new DungeonRuleError('只有队长可以结束整支远征。');
38
+ }
39
+ return appendLog({
40
+ ...state,
41
+ phase: 'completed',
42
+ result: 'aborted',
43
+ enemy: null,
44
+ }, '队长结束了本次远征。');
45
+ }
46
+ if (action.type === 'ready') {
47
+ if (state.phase !== 'lobby') {
48
+ throw new DungeonRuleError('远征已经开始,无法修改准备状态。');
49
+ }
50
+ const players = state.players.map((player, index) => index === actorIndex ? { ...player, ready: !player.ready } : player);
51
+ return appendLog({ ...state, players }, `${players[actorIndex]?.name ?? actorId}${players[actorIndex]?.ready ? '已准备' : '取消准备'}。`);
52
+ }
53
+ if (action.type === 'start')
54
+ return startExpedition(state, actorId);
55
+ assertActorTurn(state, actorId);
56
+ if (action.type === 'explore')
57
+ return explore(state, random);
58
+ if (action.type === 'attack')
59
+ return attack(state, actorIndex, random);
60
+ if (action.type === 'defend')
61
+ return defend(state, actorIndex, random);
62
+ return drinkPotion(state, actorIndex, random);
63
+ }
64
+ export function activePlayer(state) {
65
+ if (state.players.length === 0)
66
+ return null;
67
+ return state.players[state.turnIndex % state.players.length] ?? null;
68
+ }
69
+ export function decodeDungeonState(raw) {
70
+ const parsed = JSON.parse(raw);
71
+ if (!parsed || typeof parsed !== 'object') {
72
+ throw new TypeError('Invalid dungeon state');
73
+ }
74
+ const candidate = parsed;
75
+ if (candidate.schemaVersion !== DUNGEON_SCHEMA_VERSION
76
+ || !Array.isArray(candidate.players)
77
+ || typeof candidate.ownerId !== 'string') {
78
+ throw new TypeError('Unsupported dungeon state schema');
79
+ }
80
+ return candidate;
81
+ }
82
+ function createPlayer(id, name, ready) {
83
+ return {
84
+ id,
85
+ name: name.trim() || id,
86
+ maxHp: 100,
87
+ hp: 100,
88
+ guard: 0,
89
+ potions: 2,
90
+ gold: 0,
91
+ ready,
92
+ };
93
+ }
94
+ function joinParty(state, actorId, actorName) {
95
+ if (state.phase !== 'lobby') {
96
+ throw new DungeonRuleError('远征已经出发,无法中途加入。');
97
+ }
98
+ if (state.players.some((player) => player.id === actorId)) {
99
+ throw new DungeonRuleError('你已经在远征队中。');
100
+ }
101
+ if (state.players.length >= MAX_PARTY_SIZE) {
102
+ throw new DungeonRuleError('远征队已满,最多 4 人。');
103
+ }
104
+ const player = createPlayer(actorId, actorName, false);
105
+ return appendLog({ ...state, players: [...state.players, player] }, `${player.name} 加入了远征队。`);
106
+ }
107
+ function startExpedition(state, actorId) {
108
+ if (state.phase !== 'lobby') {
109
+ throw new DungeonRuleError('远征已经开始。');
110
+ }
111
+ if (state.ownerId !== actorId) {
112
+ throw new DungeonRuleError('只有队长可以带队出发。');
113
+ }
114
+ const waiting = state.players.filter((player) => !player.ready);
115
+ if (waiting.length > 0) {
116
+ throw new DungeonRuleError(`仍有队员未准备:${waiting.map((player) => player.name).join('、')}`);
117
+ }
118
+ return appendLog({ ...state, phase: 'exploring' }, '远征队进入了第一层地牢。');
119
+ }
120
+ function assertActorTurn(state, actorId) {
121
+ if (state.phase === 'lobby') {
122
+ throw new DungeonRuleError('请先由队长开始远征。');
123
+ }
124
+ const current = activePlayer(state);
125
+ if (!current || current.id !== actorId) {
126
+ throw new DungeonRuleError(`现在轮到 ${current?.name ?? '其他队员'} 行动。`);
127
+ }
128
+ }
129
+ function explore(state, random) {
130
+ if (state.phase !== 'exploring') {
131
+ throw new DungeonRuleError('战斗尚未结束,无法继续探索。');
132
+ }
133
+ const room = state.room + 1;
134
+ if (room >= BOSS_ROOM) {
135
+ const enemy = createEnemy(state.floor, true, random);
136
+ return appendLog({ ...state, room, phase: 'combat', enemy }, `守层者 ${enemy.name} 挡住了去路!`);
137
+ }
138
+ const roll = random.int(100);
139
+ if (roll < 58) {
140
+ const enemy = createEnemy(state.floor, false, random);
141
+ return appendLog({ ...state, room, phase: 'combat', enemy }, `遭遇了 ${enemy.name}!`);
142
+ }
143
+ if (roll < 78) {
144
+ const gold = random.intInclusive(8, 18);
145
+ return advanceAfterRoom({
146
+ ...state,
147
+ room,
148
+ players: state.players.map((player) => ({
149
+ ...player,
150
+ gold: player.gold + gold,
151
+ })),
152
+ }, `发现宝箱,每位队员获得 ${gold} 金币。`);
153
+ }
154
+ if (roll < 90) {
155
+ return advanceAfterRoom({
156
+ ...state,
157
+ room,
158
+ players: state.players.map((player) => ({
159
+ ...player,
160
+ hp: Math.min(player.maxHp, player.hp + 18),
161
+ })),
162
+ }, '发现治愈泉,队伍恢复了生命。');
163
+ }
164
+ const damage = random.intInclusive(8, 16);
165
+ const targetIndex = nextLivingIndex(state.players, state.turnIndex);
166
+ const target = state.players[targetIndex];
167
+ const players = state.players.map((player, index) => index === targetIndex
168
+ ? { ...player, hp: Math.max(0, player.hp - damage) }
169
+ : player);
170
+ const trapped = advanceAfterRoom({ ...state, room, players }, `${target?.name ?? '队员'} 触发陷阱,受到 ${damage} 点伤害。`);
171
+ const resolved = finishIfDefeated(trapped);
172
+ if (resolved.phase === 'completed' || (players[targetIndex]?.hp ?? 0) > 0) {
173
+ return resolved;
174
+ }
175
+ return advanceTurn(resolved);
176
+ }
177
+ function attack(state, actorIndex, random) {
178
+ if (state.phase !== 'combat' || !state.enemy) {
179
+ throw new DungeonRuleError('当前没有可以攻击的敌人。');
180
+ }
181
+ const actor = state.players[actorIndex];
182
+ if (!actor || actor.hp <= 0)
183
+ throw new DungeonRuleError('倒下的队员无法行动。');
184
+ const damage = random.intInclusive(12, 22);
185
+ const enemy = { ...state.enemy, hp: Math.max(0, state.enemy.hp - damage) };
186
+ let next = appendLog({ ...state, enemy }, `${actor.name} 对 ${enemy.name} 造成 ${damage} 点伤害。`);
187
+ if (enemy.hp <= 0)
188
+ return defeatEnemy(next, random);
189
+ next = enemyTurn(next, actorIndex, random);
190
+ return advanceTurn(finishIfDefeated(next));
191
+ }
192
+ function defend(state, actorIndex, random) {
193
+ if (state.phase !== 'combat' || !state.enemy) {
194
+ throw new DungeonRuleError('当前不需要防御。');
195
+ }
196
+ const actor = state.players[actorIndex];
197
+ if (!actor || actor.hp <= 0)
198
+ throw new DungeonRuleError('倒下的队员无法行动。');
199
+ const players = state.players.map((player, index) => index === actorIndex ? { ...player, guard: 8 } : player);
200
+ let next = appendLog({ ...state, players }, `${actor.name} 架起了盾牌。`);
201
+ next = enemyTurn(next, actorIndex, random);
202
+ return advanceTurn(finishIfDefeated(next));
203
+ }
204
+ function drinkPotion(state, actorIndex, random) {
205
+ const actor = state.players[actorIndex];
206
+ if (!actor || actor.hp <= 0)
207
+ throw new DungeonRuleError('倒下的队员无法行动。');
208
+ if (actor.potions <= 0)
209
+ throw new DungeonRuleError('你的药水已经用完了。');
210
+ if (actor.hp >= actor.maxHp)
211
+ throw new DungeonRuleError('你的生命值已经满了。');
212
+ const players = state.players.map((player, index) => index === actorIndex
213
+ ? {
214
+ ...player,
215
+ hp: Math.min(player.maxHp, player.hp + 30),
216
+ potions: player.potions - 1,
217
+ }
218
+ : player);
219
+ let next = appendLog({ ...state, players }, `${actor.name} 使用药水恢复生命。`);
220
+ if (state.phase === 'combat') {
221
+ next = enemyTurn(next, actorIndex, random);
222
+ return advanceTurn(finishIfDefeated(next));
223
+ }
224
+ return next;
225
+ }
226
+ function enemyTurn(state, targetIndex, random) {
227
+ const enemy = state.enemy;
228
+ const target = state.players[targetIndex];
229
+ if (!enemy || !target)
230
+ return state;
231
+ const rawDamage = random.intInclusive(Math.max(1, enemy.attack - 3), enemy.attack + 3);
232
+ const damage = Math.max(0, rawDamage - target.guard);
233
+ const players = state.players.map((player, index) => index === targetIndex
234
+ ? { ...player, hp: Math.max(0, player.hp - damage), guard: 0 }
235
+ : player);
236
+ return appendLog({ ...state, players }, `${enemy.name} 反击 ${target.name},造成 ${damage} 点伤害。`);
237
+ }
238
+ function defeatEnemy(state, random) {
239
+ const enemy = state.enemy;
240
+ if (!enemy)
241
+ return state;
242
+ const gold = random.intInclusive(enemy.boss ? 30 : 10, enemy.boss ? 50 : 22);
243
+ const rewarded = {
244
+ ...state,
245
+ enemy: null,
246
+ players: state.players.map((player) => ({
247
+ ...player,
248
+ gold: player.gold + gold,
249
+ })),
250
+ };
251
+ if (enemy.boss && state.floor >= MAX_FLOOR) {
252
+ return appendLog({
253
+ ...rewarded,
254
+ phase: 'completed',
255
+ result: 'victory',
256
+ roomsCleared: state.roomsCleared + 1,
257
+ }, `击败最终守层者,每人获得 ${gold} 金币。远征胜利!`);
258
+ }
259
+ if (enemy.boss) {
260
+ return appendLog({
261
+ ...rewarded,
262
+ phase: 'exploring',
263
+ floor: state.floor + 1,
264
+ room: 0,
265
+ roomsCleared: state.roomsCleared + 1,
266
+ }, `击败守层者,每人获得 ${gold} 金币。队伍进入下一层。`);
267
+ }
268
+ return advanceAfterRoom({ ...rewarded, phase: 'exploring' }, `击败 ${enemy.name},每人获得 ${gold} 金币。`);
269
+ }
270
+ function createEnemy(floor, boss, random) {
271
+ const names = boss
272
+ ? ['石像守卫', '深渊术士', '远古龙影']
273
+ : ['洞穴史莱姆', '骷髅巡卫', '暗影猎手', '失控魔像'];
274
+ const name = boss
275
+ ? names[Math.min(names.length - 1, floor - 1)]
276
+ : random.item(names);
277
+ const maxHp = boss
278
+ ? 90 + floor * 45
279
+ : random.intInclusive(35 + floor * 8, 52 + floor * 12);
280
+ return {
281
+ id: `${boss ? 'boss' : 'mob'}-${floor}-${random.nextUint32().toString(36)}`,
282
+ name,
283
+ hp: maxHp,
284
+ maxHp,
285
+ attack: boss ? 12 + floor * 3 : 7 + floor * 2,
286
+ boss,
287
+ };
288
+ }
289
+ function advanceAfterRoom(state, message) {
290
+ return appendLog({
291
+ ...state,
292
+ roomsCleared: state.roomsCleared + 1,
293
+ }, message);
294
+ }
295
+ function advanceTurn(state) {
296
+ if (state.phase === 'completed')
297
+ return state;
298
+ const nextIndex = nextLivingIndex(state.players, state.turnIndex + 1);
299
+ return { ...state, turnIndex: nextIndex };
300
+ }
301
+ function nextLivingIndex(players, start) {
302
+ for (let offset = 0; offset < players.length; offset += 1) {
303
+ const index = (start + offset) % players.length;
304
+ if ((players[index]?.hp ?? 0) > 0)
305
+ return index;
306
+ }
307
+ return 0;
308
+ }
309
+ function finishIfDefeated(state) {
310
+ if (state.players.some((player) => player.hp > 0))
311
+ return state;
312
+ return appendLog({
313
+ ...state,
314
+ phase: 'completed',
315
+ result: 'defeat',
316
+ enemy: null,
317
+ }, '远征队全员倒下,本次远征失败。');
318
+ }
319
+ function appendLog(state, message) {
320
+ return {
321
+ ...state,
322
+ log: [...state.log, message].slice(-8),
323
+ };
324
+ }
325
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { type GameMessageLike, type GameReply } from '@zhin.js/game-kit';
2
+ import type { DungeonSessionRow } from './models.js';
3
+ import { type SessionService } from './session-service.js';
4
+ export declare function startDungeon(service: SessionService, message: GameMessageLike): Promise<GameReply>;
5
+ export declare function continueDungeon(service: SessionService, message: GameMessageLike): Promise<GameReply>;
6
+ export declare function handleDungeonChoice(service: SessionService, message: GameMessageLike, sessionToken: string, choiceId: string): Promise<GameReply>;
7
+ export declare function commandActionId(session: DungeonSessionRow, actorId: string, choiceId: string): string;