@zhin.js/plugin-text-adventure 1.0.2 → 1.0.4
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/commands/adv/[action:string=].ts +2 -2
- package/lib/adv-command.d.ts +2 -4
- package/lib/adv-command.js +3 -7
- package/lib/adv-command.js.map +1 -1
- package/lib/game-flow.d.ts +5 -5
- package/lib/game-flow.js +17 -40
- package/lib/game-flow.js.map +1 -1
- package/lib/session-service.d.ts +3 -2
- package/lib/session-service.js.map +1 -1
- package/middlewares/adv-alias.ts +6 -3
- package/middlewares/adv-choice.ts +2 -2
- package/package.json +6 -6
- package/src/adv-command.ts +5 -16
- package/src/game-flow.ts +19 -46
- package/src/session-service.ts +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineCommand } from '@zhin.js/command';
|
|
2
2
|
import { messageFromCommandInput, normalizeAdvAction } from '@zhin.js/game-kit';
|
|
3
|
-
import { ADV_HELP,
|
|
3
|
+
import { ADV_HELP, runAdvCommand } from '../../src/adv-command.js';
|
|
4
4
|
import { resolveGameServices } from '../../src/runtime-store.js';
|
|
5
5
|
|
|
6
6
|
export default defineCommand({
|
|
@@ -10,6 +10,6 @@ export default defineCommand({
|
|
|
10
10
|
if (!action || action === 'help') return ADV_HELP;
|
|
11
11
|
const services = resolveGameServices({ use, owner });
|
|
12
12
|
const message = messageFromCommandInput(input);
|
|
13
|
-
return
|
|
13
|
+
return runAdvCommand(services, message, action);
|
|
14
14
|
},
|
|
15
15
|
});
|
package/lib/adv-command.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type GameMessageLike } from '@zhin.js/game-kit';
|
|
2
2
|
import type { GameServices } from './session-service.js';
|
|
3
3
|
export declare const ADV_HELP: string;
|
|
4
|
-
export declare function runAdvCommand(
|
|
5
|
-
/** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
|
|
6
|
-
export declare function runAdvCommandText(services: GameServices, message: Message<any>, action: string): Promise<string>;
|
|
4
|
+
export declare function runAdvCommand(services: GameServices, message: GameMessageLike, action: string): Promise<string>;
|
package/lib/adv-command.js
CHANGED
|
@@ -12,7 +12,7 @@ export const ADV_HELP = [
|
|
|
12
12
|
'',
|
|
13
13
|
'推进方式:点击选项按钮,或回复数字(1、2…)',
|
|
14
14
|
].join('\n');
|
|
15
|
-
export async function runAdvCommand(
|
|
15
|
+
export async function runAdvCommand(services, message, action) {
|
|
16
16
|
const ch = channelKey(message);
|
|
17
17
|
const userId = message.$sender.id;
|
|
18
18
|
const userName = message.$sender.name?.trim() || userId;
|
|
@@ -30,10 +30,10 @@ export async function runAdvCommand(plugin, services, message, action) {
|
|
|
30
30
|
return lines.join('\n');
|
|
31
31
|
}
|
|
32
32
|
if (action === 'start') {
|
|
33
|
-
return startAdventure(
|
|
33
|
+
return (await startAdventure(services, message)) ?? '';
|
|
34
34
|
}
|
|
35
35
|
if (action === 'continue') {
|
|
36
|
-
return continueAdventure(
|
|
36
|
+
return continueAdventure(services, message);
|
|
37
37
|
}
|
|
38
38
|
if (action === 'map') {
|
|
39
39
|
const profile = await services.profiles.getOrCreate(userId, userName);
|
|
@@ -52,8 +52,4 @@ export async function runAdvCommand(plugin, services, message, action) {
|
|
|
52
52
|
}
|
|
53
53
|
return `未知子命令:${action}\n\n${ADV_HELP}`;
|
|
54
54
|
}
|
|
55
|
-
/** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
|
|
56
|
-
export async function runAdvCommandText(services, message, action) {
|
|
57
|
-
return (await runAdvCommand(null, services, message, action)) ?? '';
|
|
58
|
-
}
|
|
59
55
|
//# sourceMappingURL=adv-command.js.map
|
package/lib/adv-command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adv-command.js","sourceRoot":"","sources":["../src/adv-command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adv-command.js","sourceRoot":"","sources":["../src/adv-command.ts"],"names":[],"mappings":"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"}
|
package/lib/game-flow.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type GameMessageLike } from '@zhin.js/game-kit';
|
|
2
2
|
import type { AdvProfileRow, AdvSessionRow } from './models.js';
|
|
3
3
|
import type { GameServices } from './session-service.js';
|
|
4
|
-
export declare function sendOrEditScene(
|
|
5
|
-
export declare function startAdventure(
|
|
6
|
-
export declare function handleChoice(
|
|
7
|
-
export declare function continueAdventure(
|
|
4
|
+
export declare function sendOrEditScene(services: GameServices, message: GameMessageLike, session: AdvSessionRow, extraNarrative?: string, profile?: AdvProfileRow): Promise<string>;
|
|
5
|
+
export declare function startAdventure(services: GameServices, message: GameMessageLike): Promise<string | undefined>;
|
|
6
|
+
export declare function handleChoice(services: GameServices, message: GameMessageLike, sessionId: string, choiceId: string): Promise<string | null>;
|
|
7
|
+
export declare function continueAdventure(services: GameServices, message: GameMessageLike): Promise<string>;
|
|
8
8
|
export declare function sessionSummary(session: AdvSessionRow): string;
|
package/lib/game-flow.js
CHANGED
|
@@ -1,38 +1,17 @@
|
|
|
1
1
|
import { plainTextFromSendContent } from '@zhin.js/game-kit';
|
|
2
2
|
import { formatNewAchievements } from './profile-format.js';
|
|
3
3
|
import { buildSceneInteractive } from './scene-view.js';
|
|
4
|
-
import { applyChoiceResult, getScene, resolveChoice, stateFromSession, } from './story.js';
|
|
5
|
-
export async function sendOrEditScene(
|
|
4
|
+
import { applyChoiceResult, getScene, resolveChoice, stateFromSession, visibleChoices, } from './story.js';
|
|
5
|
+
export async function sendOrEditScene(services, message, session, extraNarrative = '', profile) {
|
|
6
6
|
const prof = profile
|
|
7
7
|
?? await services.profiles.getOrCreate(session.player_id, session.player_name);
|
|
8
8
|
const content = buildSceneInteractive(session, extraNarrative, prof, message.$channel.type);
|
|
9
9
|
if (!content) {
|
|
10
10
|
return '场景数据异常,请 adv quit 后重新开始。';
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
return plainTextFromSendContent(content);
|
|
14
|
-
const adapter = plugin.root.inject(message.$adapter);
|
|
15
|
-
if (session.board_message_id) {
|
|
16
|
-
const msgId = await adapter.editMessage({
|
|
17
|
-
messageId: session.board_message_id,
|
|
18
|
-
context: String(message.$adapter),
|
|
19
|
-
endpoint: message.$endpoint,
|
|
20
|
-
id: message.$channel.id,
|
|
21
|
-
type: message.$channel.type,
|
|
22
|
-
content,
|
|
23
|
-
});
|
|
24
|
-
if (msgId !== session.board_message_id) {
|
|
25
|
-
await services.sessions.updateSession(session.id, { board_message_id: msgId });
|
|
26
|
-
}
|
|
27
|
-
return msgId;
|
|
28
|
-
}
|
|
29
|
-
const msgId = await message.$reply?.(content);
|
|
30
|
-
if (msgId) {
|
|
31
|
-
await services.sessions.updateSession(session.id, { board_message_id: msgId });
|
|
32
|
-
}
|
|
33
|
-
return msgId ?? '';
|
|
12
|
+
return plainTextFromSendContent(content);
|
|
34
13
|
}
|
|
35
|
-
export async function startAdventure(
|
|
14
|
+
export async function startAdventure(services, message) {
|
|
36
15
|
const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
|
|
37
16
|
const active = await services.sessions.getActiveByChannel(ch);
|
|
38
17
|
if (active) {
|
|
@@ -44,12 +23,12 @@ export async function startAdventure(plugin, services, message) {
|
|
|
44
23
|
await services.profiles.onRunStarted(message.$sender.id, message.$sender.name?.trim() || message.$sender.id);
|
|
45
24
|
const session = await services.sessions.createSession(message);
|
|
46
25
|
await services.profiles.onStep(message.$sender.id, session.player_name, 'start', []);
|
|
47
|
-
|
|
48
|
-
return plugin ? undefined : text;
|
|
26
|
+
return sendOrEditScene(services, message, session);
|
|
49
27
|
}
|
|
50
|
-
export async function handleChoice(
|
|
28
|
+
export async function handleChoice(services, message, sessionId, choiceId) {
|
|
51
29
|
const session = await services.sessions.getById(sessionId);
|
|
52
|
-
|
|
30
|
+
// 踏进 terminal 场景时 status 必为 completed,终局需放行「再玩一次」(restart)
|
|
31
|
+
if (!session || (session.status !== 'active' && !(session.status === 'completed' && choiceId === 'restart'))) {
|
|
53
32
|
return '冒险不存在或已结束。';
|
|
54
33
|
}
|
|
55
34
|
if (session.player_id !== message.$sender.id) {
|
|
@@ -74,10 +53,13 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
|
|
|
74
53
|
board_message_id: '',
|
|
75
54
|
});
|
|
76
55
|
const updated = (await services.sessions.getById(session.id));
|
|
56
|
+
await services.profiles.onRunStarted(updated.player_id, updated.player_name);
|
|
77
57
|
await services.profiles.onStep(updated.player_id, updated.player_name, 'start', []);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
58
|
+
return sendOrEditScene(services, message, updated);
|
|
59
|
+
}
|
|
60
|
+
// 服务端校验:选项必须当前可见(requires 门槛不能只靠视图层过滤,payload 可绕过)
|
|
61
|
+
if (!visibleChoices(scene, state).some((c) => c.id === choiceId)) {
|
|
62
|
+
return '该选项不可用。';
|
|
81
63
|
}
|
|
82
64
|
const result = resolveChoice(state, choiceId);
|
|
83
65
|
if (!result)
|
|
@@ -108,18 +90,13 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
|
|
|
108
90
|
}
|
|
109
91
|
const updated = (await services.sessions.getById(session.id));
|
|
110
92
|
const extra = formatNewAchievements(newAchievements);
|
|
111
|
-
|
|
112
|
-
// text-only 模式(plugin===null)下 sendOrEditScene 的唯一输出就是返回文本
|
|
113
|
-
return plugin ? null : text;
|
|
93
|
+
return sendOrEditScene(services, message, updated, extra);
|
|
114
94
|
}
|
|
115
|
-
export async function continueAdventure(
|
|
95
|
+
export async function continueAdventure(services, message) {
|
|
116
96
|
const session = await services.sessions.getActiveForUser(`${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`, message.$sender.id);
|
|
117
97
|
if (!session)
|
|
118
98
|
return '你没有进行中的冒险,发送 adv start 开始。';
|
|
119
|
-
|
|
120
|
-
if (!plugin)
|
|
121
|
-
return text;
|
|
122
|
-
return '已刷新当前场景。';
|
|
99
|
+
return sendOrEditScene(services, message, session);
|
|
123
100
|
}
|
|
124
101
|
export function sessionSummary(session) {
|
|
125
102
|
const scene = getScene(session.scene_id);
|
package/lib/game-flow.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"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"}
|
package/lib/session-service.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Database,
|
|
1
|
+
import type { Database, Models } from '@zhin.js/core';
|
|
2
|
+
import { type GameMessageLike } from '@zhin.js/game-kit';
|
|
2
3
|
import type { AdvSessionRow } from './models.js';
|
|
3
4
|
import { ProfileService } from './profile-service.js';
|
|
4
5
|
export type AdvDatabase = Database<unknown, Models, string>;
|
|
@@ -13,7 +14,7 @@ export declare class SessionService {
|
|
|
13
14
|
getActiveForUser(channel: string, userId: string): Promise<AdvSessionRow | null>;
|
|
14
15
|
getById(id: string): Promise<AdvSessionRow | null>;
|
|
15
16
|
getActiveByBoardMessageId(messageId: string): Promise<AdvSessionRow | null>;
|
|
16
|
-
createSession(message:
|
|
17
|
+
createSession(message: GameMessageLike): Promise<AdvSessionRow>;
|
|
17
18
|
updateSession(id: string, patch: Partial<AdvSessionRow>): Promise<void>;
|
|
18
19
|
abortStale(idleMs: number): Promise<number>;
|
|
19
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"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"}
|
package/middlewares/adv-alias.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defineGameCommandAliasMiddleware,
|
|
3
|
+
isAdvAction,
|
|
3
4
|
messageFromCommandInput,
|
|
4
5
|
normalizeAdvAction,
|
|
5
6
|
} from '@zhin.js/game-kit';
|
|
6
|
-
import { ADV_HELP,
|
|
7
|
+
import { ADV_HELP, runAdvCommand } from '../src/adv-command.js';
|
|
7
8
|
import { resolveGameServices } from '../src/runtime-store.js';
|
|
8
9
|
|
|
9
10
|
export default defineGameCommandAliasMiddleware({
|
|
10
11
|
aliases: ['冒险', 'adv', '秘境'],
|
|
11
12
|
async run(action, input, context) {
|
|
12
13
|
const normalized = normalizeAdvAction(String(action ?? ''));
|
|
13
|
-
if (
|
|
14
|
+
if (normalized === 'help') return ADV_HELP;
|
|
15
|
+
// action 无法识别:放行给后续中间件,避免劫持普通聊天
|
|
16
|
+
if (!isAdvAction(normalized)) return null;
|
|
14
17
|
const services = resolveGameServices(context);
|
|
15
18
|
const message = messageFromCommandInput(input);
|
|
16
|
-
return
|
|
19
|
+
return runAdvCommand(services, message, normalized);
|
|
17
20
|
},
|
|
18
21
|
});
|
|
@@ -34,7 +34,7 @@ export default defineMiddleware<Message>({
|
|
|
34
34
|
if (parsed) {
|
|
35
35
|
const session = await services.sessions.getById(parsed.sessionId);
|
|
36
36
|
if (session?.channel_key === ch) {
|
|
37
|
-
const reply = await handleChoice(
|
|
37
|
+
const reply = await handleChoice(services, message, parsed.sessionId, parsed.choiceId);
|
|
38
38
|
if (reply) await context.input.$reply(reply);
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
@@ -64,7 +64,7 @@ export default defineMiddleware<Message>({
|
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const reply = await handleChoice(
|
|
67
|
+
const reply = await handleChoice(services, message, session.id, parsed.choiceId);
|
|
68
68
|
if (reply) await context.input.$reply(reply);
|
|
69
69
|
},
|
|
70
70
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/plugin-text-adventure",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Zhin.js 交互式文字冒险 (Plugin Runtime)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
],
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@zhin.js/command": "1.0.
|
|
28
|
-
"@zhin.js/core": "1.
|
|
29
|
-
"@zhin.js/game-kit": "
|
|
30
|
-
"@zhin.js/middleware": "1.0.
|
|
31
|
-
"@zhin.js/plugin-runtime": "1.
|
|
27
|
+
"@zhin.js/command": "1.0.3",
|
|
28
|
+
"@zhin.js/core": "1.4.1",
|
|
29
|
+
"@zhin.js/game-kit": "2.0.1",
|
|
30
|
+
"@zhin.js/middleware": "1.0.3",
|
|
31
|
+
"@zhin.js/plugin-runtime": "1.1.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"typescript": "^6.0.3",
|
package/src/adv-command.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { channelKey } from '@zhin.js/game-kit';
|
|
1
|
+
import { channelKey, type GameMessageLike } from '@zhin.js/game-kit';
|
|
3
2
|
import {
|
|
4
3
|
continueAdventure,
|
|
5
4
|
sessionSummary,
|
|
@@ -21,11 +20,10 @@ export const ADV_HELP = [
|
|
|
21
20
|
].join('\n');
|
|
22
21
|
|
|
23
22
|
export async function runAdvCommand(
|
|
24
|
-
plugin: Plugin | null,
|
|
25
23
|
services: GameServices,
|
|
26
|
-
message:
|
|
24
|
+
message: GameMessageLike,
|
|
27
25
|
action: string,
|
|
28
|
-
): Promise<string
|
|
26
|
+
): Promise<string> {
|
|
29
27
|
const ch = channelKey(message);
|
|
30
28
|
const userId = message.$sender.id;
|
|
31
29
|
const userName = message.$sender.name?.trim() || userId;
|
|
@@ -44,11 +42,11 @@ export async function runAdvCommand(
|
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
if (action === 'start') {
|
|
47
|
-
return startAdventure(
|
|
45
|
+
return (await startAdventure(services, message)) ?? '';
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
if (action === 'continue') {
|
|
51
|
-
return continueAdventure(
|
|
49
|
+
return continueAdventure(services, message);
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
if (action === 'map') {
|
|
@@ -70,12 +68,3 @@ export async function runAdvCommand(
|
|
|
70
68
|
|
|
71
69
|
return `未知子命令:${action}\n\n${ADV_HELP}`;
|
|
72
70
|
}
|
|
73
|
-
|
|
74
|
-
/** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
|
|
75
|
-
export async function runAdvCommandText(
|
|
76
|
-
services: GameServices,
|
|
77
|
-
message: Message<any>,
|
|
78
|
-
action: string,
|
|
79
|
-
): Promise<string> {
|
|
80
|
-
return (await runAdvCommand(null, services, message, action)) ?? '';
|
|
81
|
-
}
|
package/src/game-flow.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { plainTextFromSendContent } from '@zhin.js/game-kit';
|
|
1
|
+
import { plainTextFromSendContent, type GameMessageLike } from '@zhin.js/game-kit';
|
|
3
2
|
import type { AdvProfileRow, AdvSessionRow } from './models.js';
|
|
4
3
|
import { formatNewAchievements } from './profile-format.js';
|
|
5
4
|
import { buildSceneInteractive } from './scene-view.js';
|
|
@@ -8,13 +7,13 @@ import {
|
|
|
8
7
|
getScene,
|
|
9
8
|
resolveChoice,
|
|
10
9
|
stateFromSession,
|
|
10
|
+
visibleChoices,
|
|
11
11
|
} from './story.js';
|
|
12
12
|
import type { GameServices } from './session-service.js';
|
|
13
13
|
|
|
14
14
|
export async function sendOrEditScene(
|
|
15
|
-
plugin: Plugin | null,
|
|
16
15
|
services: GameServices,
|
|
17
|
-
message:
|
|
16
|
+
message: GameMessageLike,
|
|
18
17
|
session: AdvSessionRow,
|
|
19
18
|
extraNarrative = '',
|
|
20
19
|
profile?: AdvProfileRow,
|
|
@@ -25,36 +24,12 @@ export async function sendOrEditScene(
|
|
|
25
24
|
if (!content) {
|
|
26
25
|
return '场景数据异常,请 adv quit 后重新开始。';
|
|
27
26
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const adapter = plugin.root.inject(message.$adapter) as Adapter;
|
|
31
|
-
|
|
32
|
-
if (session.board_message_id) {
|
|
33
|
-
const msgId = await adapter.editMessage({
|
|
34
|
-
messageId: session.board_message_id,
|
|
35
|
-
context: String(message.$adapter),
|
|
36
|
-
endpoint: message.$endpoint,
|
|
37
|
-
id: message.$channel.id,
|
|
38
|
-
type: message.$channel.type,
|
|
39
|
-
content,
|
|
40
|
-
});
|
|
41
|
-
if (msgId !== session.board_message_id) {
|
|
42
|
-
await services.sessions.updateSession(session.id, { board_message_id: msgId });
|
|
43
|
-
}
|
|
44
|
-
return msgId;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const msgId = await message.$reply?.(content);
|
|
48
|
-
if (msgId) {
|
|
49
|
-
await services.sessions.updateSession(session.id, { board_message_id: msgId });
|
|
50
|
-
}
|
|
51
|
-
return msgId ?? '';
|
|
27
|
+
return plainTextFromSendContent(content);
|
|
52
28
|
}
|
|
53
29
|
|
|
54
30
|
export async function startAdventure(
|
|
55
|
-
plugin: Plugin | null,
|
|
56
31
|
services: GameServices,
|
|
57
|
-
message:
|
|
32
|
+
message: GameMessageLike,
|
|
58
33
|
): Promise<string | undefined> {
|
|
59
34
|
const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
|
|
60
35
|
const active = await services.sessions.getActiveByChannel(ch);
|
|
@@ -73,19 +48,18 @@ export async function startAdventure(
|
|
|
73
48
|
'start',
|
|
74
49
|
[],
|
|
75
50
|
);
|
|
76
|
-
|
|
77
|
-
return plugin ? undefined : text;
|
|
51
|
+
return sendOrEditScene(services, message, session);
|
|
78
52
|
}
|
|
79
53
|
|
|
80
54
|
export async function handleChoice(
|
|
81
|
-
plugin: Plugin | null,
|
|
82
55
|
services: GameServices,
|
|
83
|
-
message:
|
|
56
|
+
message: GameMessageLike,
|
|
84
57
|
sessionId: string,
|
|
85
58
|
choiceId: string,
|
|
86
59
|
): Promise<string | null> {
|
|
87
60
|
const session = await services.sessions.getById(sessionId);
|
|
88
|
-
|
|
61
|
+
// 踏进 terminal 场景时 status 必为 completed,终局需放行「再玩一次」(restart)
|
|
62
|
+
if (!session || (session.status !== 'active' && !(session.status === 'completed' && choiceId === 'restart'))) {
|
|
89
63
|
return '冒险不存在或已结束。';
|
|
90
64
|
}
|
|
91
65
|
if (session.player_id !== message.$sender.id) {
|
|
@@ -112,10 +86,14 @@ export async function handleChoice(
|
|
|
112
86
|
board_message_id: '',
|
|
113
87
|
});
|
|
114
88
|
const updated = (await services.sessions.getById(session.id))!;
|
|
89
|
+
await services.profiles.onRunStarted(updated.player_id, updated.player_name);
|
|
115
90
|
await services.profiles.onStep(updated.player_id, updated.player_name, 'start', []);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
91
|
+
return sendOrEditScene(services, message, updated);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 服务端校验:选项必须当前可见(requires 门槛不能只靠视图层过滤,payload 可绕过)
|
|
95
|
+
if (!visibleChoices(scene, state).some((c) => c.id === choiceId)) {
|
|
96
|
+
return '该选项不可用。';
|
|
119
97
|
}
|
|
120
98
|
|
|
121
99
|
const result = resolveChoice(state, choiceId);
|
|
@@ -163,24 +141,19 @@ export async function handleChoice(
|
|
|
163
141
|
|
|
164
142
|
const updated = (await services.sessions.getById(session.id))!;
|
|
165
143
|
const extra = formatNewAchievements(newAchievements);
|
|
166
|
-
|
|
167
|
-
// text-only 模式(plugin===null)下 sendOrEditScene 的唯一输出就是返回文本
|
|
168
|
-
return plugin ? null : text;
|
|
144
|
+
return sendOrEditScene(services, message, updated, extra);
|
|
169
145
|
}
|
|
170
146
|
|
|
171
147
|
export async function continueAdventure(
|
|
172
|
-
plugin: Plugin | null,
|
|
173
148
|
services: GameServices,
|
|
174
|
-
message:
|
|
149
|
+
message: GameMessageLike,
|
|
175
150
|
): Promise<string> {
|
|
176
151
|
const session = await services.sessions.getActiveForUser(
|
|
177
152
|
`${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`,
|
|
178
153
|
message.$sender.id,
|
|
179
154
|
);
|
|
180
155
|
if (!session) return '你没有进行中的冒险,发送 adv start 开始。';
|
|
181
|
-
|
|
182
|
-
if (!plugin) return text;
|
|
183
|
-
return '已刷新当前场景。';
|
|
156
|
+
return sendOrEditScene(services, message, session);
|
|
184
157
|
}
|
|
185
158
|
|
|
186
159
|
export function sessionSummary(session: AdvSessionRow): string {
|
package/src/session-service.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Database,
|
|
2
|
-
import { channelKey, generateSessionId, boardMessageMatches } from '@zhin.js/game-kit';
|
|
1
|
+
import type { Database, Models, RelatedModel } from '@zhin.js/core';
|
|
2
|
+
import { channelKey, generateSessionId, boardMessageMatches, type GameMessageLike } from '@zhin.js/game-kit';
|
|
3
3
|
import type { AdvModelName, AdvSessionRow } from './models.js';
|
|
4
4
|
import { createProfileService, ProfileService } from './profile-service.js';
|
|
5
5
|
|
|
@@ -47,7 +47,7 @@ export class SessionService {
|
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
async createSession(message:
|
|
50
|
+
async createSession(message: GameMessageLike): Promise<AdvSessionRow> {
|
|
51
51
|
const now = Date.now();
|
|
52
52
|
const id = generateSessionId();
|
|
53
53
|
const row: AdvSessionRow = {
|