@zhin.js/game-shared 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 +163 -0
- package/lib/board-sender.d.ts +14 -0
- package/lib/board-sender.d.ts.map +1 -0
- package/lib/board-sender.js +7 -0
- package/lib/board-sender.js.map +1 -0
- package/lib/choice-keyboard.d.ts +37 -0
- package/lib/choice-keyboard.d.ts.map +1 -0
- package/lib/choice-keyboard.js +60 -0
- package/lib/choice-keyboard.js.map +1 -0
- package/lib/game-action-alias.d.ts +8 -0
- package/lib/game-action-alias.d.ts.map +1 -0
- package/lib/game-action-alias.js +117 -0
- package/lib/game-action-alias.js.map +1 -0
- package/lib/game-hub-feature.d.ts +58 -0
- package/lib/game-hub-feature.d.ts.map +1 -0
- package/lib/game-hub-feature.js +89 -0
- package/lib/game-hub-feature.js.map +1 -0
- package/lib/game-hub-flow.d.ts +9 -0
- package/lib/game-hub-flow.d.ts.map +1 -0
- package/lib/game-hub-flow.js +90 -0
- package/lib/game-hub-flow.js.map +1 -0
- package/lib/game-hub-menu-context.d.ts +25 -0
- package/lib/game-hub-menu-context.d.ts.map +1 -0
- package/lib/game-hub-menu-context.js +61 -0
- package/lib/game-hub-menu-context.js.map +1 -0
- package/lib/game-hub-menu.d.ts +24 -0
- package/lib/game-hub-menu.d.ts.map +1 -0
- package/lib/game-hub-menu.js +114 -0
- package/lib/game-hub-menu.js.map +1 -0
- package/lib/game-hub-mount.d.ts +9 -0
- package/lib/game-hub-mount.d.ts.map +1 -0
- package/lib/game-hub-mount.js +72 -0
- package/lib/game-hub-mount.js.map +1 -0
- package/lib/game-middleware.d.ts +6 -0
- package/lib/game-middleware.d.ts.map +1 -0
- package/lib/game-middleware.js +9 -0
- package/lib/game-middleware.js.map +1 -0
- package/lib/game-registry.d.ts +3 -0
- package/lib/game-registry.d.ts.map +1 -0
- package/lib/game-registry.js +2 -0
- package/lib/game-registry.js.map +1 -0
- package/lib/game-session.d.ts +62 -0
- package/lib/game-session.d.ts.map +1 -0
- package/lib/game-session.js +34 -0
- package/lib/game-session.js.map +1 -0
- package/lib/grid-keyboard.d.ts +60 -0
- package/lib/grid-keyboard.d.ts.map +1 -0
- package/lib/grid-keyboard.js +82 -0
- package/lib/grid-keyboard.js.map +1 -0
- package/lib/index.d.ts +17 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +17 -0
- package/lib/index.js.map +1 -0
- package/package.json +32 -0
- package/src/board-sender.ts +16 -0
- package/src/choice-keyboard.ts +103 -0
- package/src/game-action-alias.ts +129 -0
- package/src/game-hub-feature.ts +147 -0
- package/src/game-hub-flow.ts +114 -0
- package/src/game-hub-menu-context.ts +91 -0
- package/src/game-hub-menu.ts +134 -0
- package/src/game-hub-mount.ts +108 -0
- package/src/game-middleware.ts +14 -0
- package/src/game-session.ts +88 -0
- package/src/grid-keyboard.ts +142 -0
- package/src/index.ts +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# @zhin.js/game-shared
|
|
2
|
+
|
|
3
|
+
Zhin.js 游戏插件通用工具包,提供网格棋盘、会话管理等通用抽象。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @zhin.js/game-shared
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 功能
|
|
12
|
+
|
|
13
|
+
### 网格键盘 (`grid-keyboard.ts`)
|
|
14
|
+
|
|
15
|
+
构建任意尺寸的按钮网格棋盘(井字棋、五子棋等)。
|
|
16
|
+
|
|
17
|
+
### 选项键盘 (`choice-keyboard.ts`)
|
|
18
|
+
|
|
19
|
+
构建分支选项按钮(文字冒险、问答、剧情选择等):
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { buildChoiceKeyboard, parseChoicePayload } from '@zhin.js/game-shared';
|
|
23
|
+
|
|
24
|
+
const content = buildChoiceKeyboard({
|
|
25
|
+
gamePrefix: 'adv',
|
|
26
|
+
sessionId: 's123',
|
|
27
|
+
narrative: '石门在前,雾霭缭绕……',
|
|
28
|
+
choices: [
|
|
29
|
+
{ id: 'enter', label: '🚪 推门而入' },
|
|
30
|
+
{ id: 'leave', label: '🏃 离开', style: 'danger' },
|
|
31
|
+
],
|
|
32
|
+
buttonsPerRow: 2,
|
|
33
|
+
fallbackHint: '回复数字选择',
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { buildGridKeyboard, parseGridPayload, parseCellButtonId } from '@zhin.js/game-shared';
|
|
39
|
+
|
|
40
|
+
// 构建 3×3 井字棋键盘
|
|
41
|
+
const content = buildGridKeyboard({
|
|
42
|
+
gamePrefix: 'ttt',
|
|
43
|
+
sessionId: 's123',
|
|
44
|
+
rows: 3,
|
|
45
|
+
cols: 3,
|
|
46
|
+
cells: board.map((state, i) => ({
|
|
47
|
+
state,
|
|
48
|
+
label: state === 0 ? '·' : state === 1 ? '✕' : '○',
|
|
49
|
+
disabled: state !== 0,
|
|
50
|
+
})),
|
|
51
|
+
statusLine: '轮到 玩家A (✕)',
|
|
52
|
+
omitAsciiBoard: message.$adapter === 'qq',
|
|
53
|
+
renderAscii: (cells, rows, cols) => { /* ... */ },
|
|
54
|
+
fallbackHint: '回复数字 1-9 落子',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// 解析 action payload
|
|
58
|
+
const parsed = parseGridPayload('ttt:s123:4');
|
|
59
|
+
// => { prefix: 'ttt', sessionId: 's123', cell: 4 }
|
|
60
|
+
|
|
61
|
+
// 解析按钮 ID(QQ 等平台)
|
|
62
|
+
const cell = parseCellButtonId('c4');
|
|
63
|
+
// => 4
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 棋盘消息发送
|
|
67
|
+
|
|
68
|
+
消息发送/编辑由 `Adapter.editMessage()` 统一处理:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { channelKey } from '@zhin.js/game-shared';
|
|
72
|
+
import type { Adapter } from 'zhin.js';
|
|
73
|
+
|
|
74
|
+
// 构建频道键
|
|
75
|
+
const chKey = channelKey(message);
|
|
76
|
+
|
|
77
|
+
// 发送或编辑棋盘消息(Adapter 层统一接口)
|
|
78
|
+
const adapter = plugin.root.inject(message.$adapter) as Adapter;
|
|
79
|
+
|
|
80
|
+
if (session.board_message_id) {
|
|
81
|
+
// Adapter.editMessage 内部处理平台差异:
|
|
82
|
+
// - 支持编辑的平台(Discord、Telegram):调用平台 API 编辑
|
|
83
|
+
// - 不支持编辑的平台(QQ):自动 fallback 到发送新消息
|
|
84
|
+
const msgId = await adapter.editMessage({
|
|
85
|
+
messageId: session.board_message_id,
|
|
86
|
+
context: message.$adapter,
|
|
87
|
+
endpoint: message.$endpoint,
|
|
88
|
+
id: message.$channel.id,
|
|
89
|
+
type: message.$channel.type,
|
|
90
|
+
content,
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
const msgId = await message.$reply?.(content);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 游戏会话 (`game-session.ts`)
|
|
98
|
+
|
|
99
|
+
通用回合制游戏会话接口:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import {
|
|
103
|
+
type TurnBasedSession,
|
|
104
|
+
generateSessionId,
|
|
105
|
+
currentPlayerId,
|
|
106
|
+
isPlayerTurn,
|
|
107
|
+
nextTurn,
|
|
108
|
+
} from '@zhin.js/game-shared';
|
|
109
|
+
|
|
110
|
+
// 会话结构
|
|
111
|
+
interface MyGameSession extends TurnBasedSession {
|
|
112
|
+
board: number[];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 回合验证
|
|
116
|
+
if (!isPlayerTurn(session, message.$sender.id)) {
|
|
117
|
+
return '还没轮到你';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 切换回合
|
|
121
|
+
session.turn = nextTurn(session.turn);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 游戏大厅(`game` 服务)
|
|
125
|
+
|
|
126
|
+
与 `addCommand` 同构:在 root 上注册 **`game` 服务**,通过 `usePlugin().registerGame()` 登记游戏。
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { ensureGameHubService } from '@zhin.js/game-shared';
|
|
130
|
+
import { usePlugin } from 'zhin.js';
|
|
131
|
+
|
|
132
|
+
const plugin = usePlugin();
|
|
133
|
+
ensureGameHubService(plugin); // 幂等,注册 game 服务
|
|
134
|
+
|
|
135
|
+
const { registerGame } = plugin;
|
|
136
|
+
|
|
137
|
+
registerGame({
|
|
138
|
+
id: 'my-game',
|
|
139
|
+
title: '我的游戏',
|
|
140
|
+
icon: '🎮',
|
|
141
|
+
description: '简介',
|
|
142
|
+
commandPrefix: '我的游戏',
|
|
143
|
+
menus: [{ id: 'start', label: '🎮 开始', style: 'primary' }],
|
|
144
|
+
runAction: async (actionId, ctx) => { /* ... */ },
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
首个游戏 `registerGame` 后自动挂载 `游戏` / `game` 命令;卸载插件时 `onDispose` 对称清理。
|
|
149
|
+
|
|
150
|
+
## 架构原则
|
|
151
|
+
|
|
152
|
+
**平台能力封装在适配器层**:游戏代码不关心平台是否支持消息编辑,统一调用 `Adapter.editMessage()`,由各适配器内部决定是真编辑还是 fallback 到发新消息。
|
|
153
|
+
|
|
154
|
+
**入站文本中间件**:须用 `registerGameTextMiddleware(plugin, …)` 注册到 **root**(子插件 `addMiddleware` 不会进入入站管线)。
|
|
155
|
+
|
|
156
|
+
## 支持的游戏模式
|
|
157
|
+
|
|
158
|
+
| 功能 | 井字棋 | 五子棋 | 四子棋 | ... |
|
|
159
|
+
|------|--------|--------|--------|------|
|
|
160
|
+
| 网格尺寸 | 3×3 | 15×15 | 7×6 | 任意 |
|
|
161
|
+
| 回合制 | ✓ | ✓ | ✓ | ✓ |
|
|
162
|
+
| 人机 | ✓ | 可选 | 可选 | ✓ |
|
|
163
|
+
| PvP 排队 | ✓ | ✓ | ✓ | ✓ |
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 游戏棋盘消息辅助工具
|
|
3
|
+
* 提供会话键生成和消息 ID 更新回调封装
|
|
4
|
+
*
|
|
5
|
+
* 注意:实际的发送/编辑逻辑由 Adapter.editMessage 统一处理
|
|
6
|
+
* - 支持编辑的平台:调用平台 API 编辑
|
|
7
|
+
* - 不支持编辑的平台:自动 fallback 到发送新消息
|
|
8
|
+
*/
|
|
9
|
+
import type { Message } from 'zhin.js';
|
|
10
|
+
/**
|
|
11
|
+
* 构建频道唯一键(用于会话查找)
|
|
12
|
+
*/
|
|
13
|
+
export declare function channelKey(message: Message<any>): string;
|
|
14
|
+
//# sourceMappingURL=board-sender.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-sender.d.ts","sourceRoot":"","sources":["../src/board-sender.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAExD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-sender.js","sourceRoot":"","sources":["../src/board-sender.ts"],"names":[],"mappings":"AAUA;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,OAAqB;IAC9C,OAAO,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;AACpG,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 选项式交互键盘(文字冒险、问答、剧情分支等)
|
|
3
|
+
*/
|
|
4
|
+
import { type SendContent } from 'zhin.js';
|
|
5
|
+
export interface ChoiceOption {
|
|
6
|
+
/** 选项 ID,写入 payload */
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
style?: 'primary' | 'danger' | 'secondary';
|
|
11
|
+
}
|
|
12
|
+
export interface ChoiceKeyboardOptions {
|
|
13
|
+
gamePrefix: string;
|
|
14
|
+
sessionId: string;
|
|
15
|
+
/** 剧情/题干正文 */
|
|
16
|
+
narrative: string;
|
|
17
|
+
choices: ChoiceOption[];
|
|
18
|
+
terminal?: boolean;
|
|
19
|
+
/** 文本 fallback 提示 */
|
|
20
|
+
fallbackHint?: string;
|
|
21
|
+
/** 每行最多几个按钮(默认全部一行) */
|
|
22
|
+
buttonsPerRow?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function buildChoiceFallbackMap(gamePrefix: string, sessionId: string, choices: ChoiceOption[]): Record<string, string>;
|
|
25
|
+
/**
|
|
26
|
+
* 构建选项键盘消息:正文 + 一行或多行按钮
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildChoiceKeyboard(options: ChoiceKeyboardOptions): SendContent;
|
|
29
|
+
/**
|
|
30
|
+
* 解析选项 payload:`{prefix}:{sessionId}:{choiceId}`
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseChoicePayload(payload: string, expectedPrefix?: string): {
|
|
33
|
+
prefix: string;
|
|
34
|
+
sessionId: string;
|
|
35
|
+
choiceId: string;
|
|
36
|
+
} | null;
|
|
37
|
+
//# sourceMappingURL=choice-keyboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"choice-keyboard.d.ts","sourceRoot":"","sources":["../src/choice-keyboard.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAEpD,MAAM,WAAW,YAAY;IAC3B,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;CAC5C;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EAAE,GACtB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUxB;AAWD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,qBAAqB,GAAG,WAAW,CAkC/E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,MAAM,GACtB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAMhE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 选项式交互键盘(文字冒险、问答、剧情分支等)
|
|
3
|
+
*/
|
|
4
|
+
import { segment } from 'zhin.js';
|
|
5
|
+
export function buildChoiceFallbackMap(gamePrefix, sessionId, choices) {
|
|
6
|
+
const map = {};
|
|
7
|
+
let n = 1;
|
|
8
|
+
for (const choice of choices) {
|
|
9
|
+
if (!choice.disabled) {
|
|
10
|
+
map[String(n)] = `${gamePrefix}:${sessionId}:${choice.id}`;
|
|
11
|
+
n++;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return map;
|
|
15
|
+
}
|
|
16
|
+
function chunkChoices(items, size) {
|
|
17
|
+
if (size <= 0 || items.length <= size)
|
|
18
|
+
return [items];
|
|
19
|
+
const rows = [];
|
|
20
|
+
for (let i = 0; i < items.length; i += size) {
|
|
21
|
+
rows.push(items.slice(i, i + size));
|
|
22
|
+
}
|
|
23
|
+
return rows;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 构建选项键盘消息:正文 + 一行或多行按钮
|
|
27
|
+
*/
|
|
28
|
+
export function buildChoiceKeyboard(options) {
|
|
29
|
+
const { gamePrefix, sessionId, narrative, choices, terminal, fallbackHint, buttonsPerRow, } = options;
|
|
30
|
+
const enabled = choices.filter((c) => !c.disabled);
|
|
31
|
+
const buttonRows = chunkChoices(enabled, buttonsPerRow ?? enabled.length).map((row) => row.map((choice) => segment.button({
|
|
32
|
+
id: choice.id,
|
|
33
|
+
label: choice.label,
|
|
34
|
+
payload: `${gamePrefix}:${sessionId}:${choice.id}`,
|
|
35
|
+
disabled: terminal || choice.disabled,
|
|
36
|
+
style: choice.style ?? 'secondary',
|
|
37
|
+
})));
|
|
38
|
+
const fallback = buildChoiceFallbackMap(gamePrefix, sessionId, choices);
|
|
39
|
+
return [
|
|
40
|
+
segment.text(narrative),
|
|
41
|
+
segment.keyboard(buttonRows, {
|
|
42
|
+
fallback: fallbackHint && Object.keys(fallback).length > 0
|
|
43
|
+
? { hint: fallbackHint, map: fallback }
|
|
44
|
+
: undefined,
|
|
45
|
+
}).toElement(),
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 解析选项 payload:`{prefix}:{sessionId}:{choiceId}`
|
|
50
|
+
*/
|
|
51
|
+
export function parseChoicePayload(payload, expectedPrefix) {
|
|
52
|
+
const m = /^([a-z0-9_]+):([^:]+):([a-z0-9_-]+)$/i.exec(payload);
|
|
53
|
+
if (!m)
|
|
54
|
+
return null;
|
|
55
|
+
const prefix = m[1];
|
|
56
|
+
if (expectedPrefix && prefix !== expectedPrefix)
|
|
57
|
+
return null;
|
|
58
|
+
return { prefix, sessionId: m[2], choiceId: m[3] };
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=choice-keyboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"choice-keyboard.js","sourceRoot":"","sources":["../src/choice-keyboard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function normalizeTttAction(raw: string): string;
|
|
2
|
+
export declare function normalizeAdvAction(raw: string): string;
|
|
3
|
+
export declare function normalizeRpsAction(raw: string): string;
|
|
4
|
+
export declare function normalizeGuessAction(raw: string): string;
|
|
5
|
+
export declare function normalizeDiceAction(raw: string): string;
|
|
6
|
+
export declare function normalizeChainAction(raw: string): string;
|
|
7
|
+
export declare function normalizeRiddleAction(raw: string): string;
|
|
8
|
+
//# sourceMappingURL=game-action-alias.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-action-alias.d.ts","sourceRoot":"","sources":["../src/game-action-alias.ts"],"names":[],"mappings":"AAsEA,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAkCD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEzD"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const TTT_ACTIONS = {
|
|
2
|
+
bot: 'bot',
|
|
3
|
+
join: 'join',
|
|
4
|
+
leave: 'leave',
|
|
5
|
+
quit: 'quit',
|
|
6
|
+
spectate: 'spectate',
|
|
7
|
+
help: 'help',
|
|
8
|
+
人机: 'bot',
|
|
9
|
+
排队: 'join',
|
|
10
|
+
加入: 'join',
|
|
11
|
+
离开: 'leave',
|
|
12
|
+
认输: 'quit',
|
|
13
|
+
观战: 'spectate',
|
|
14
|
+
帮助: 'help',
|
|
15
|
+
};
|
|
16
|
+
const ADV_ACTIONS = {
|
|
17
|
+
start: 'start',
|
|
18
|
+
continue: 'continue',
|
|
19
|
+
map: 'map',
|
|
20
|
+
achievements: 'achievements',
|
|
21
|
+
achievement: 'achievements',
|
|
22
|
+
quit: 'quit',
|
|
23
|
+
help: 'help',
|
|
24
|
+
开始: 'start',
|
|
25
|
+
继续: 'continue',
|
|
26
|
+
地图: 'map',
|
|
27
|
+
成就: 'achievements',
|
|
28
|
+
放弃: 'quit',
|
|
29
|
+
退出: 'quit',
|
|
30
|
+
帮助: 'help',
|
|
31
|
+
};
|
|
32
|
+
const RPS_ACTIONS = {
|
|
33
|
+
start: 'start',
|
|
34
|
+
continue: 'continue',
|
|
35
|
+
quit: 'quit',
|
|
36
|
+
help: 'help',
|
|
37
|
+
开始: 'start',
|
|
38
|
+
继续: 'continue',
|
|
39
|
+
放弃: 'quit',
|
|
40
|
+
帮助: 'help',
|
|
41
|
+
};
|
|
42
|
+
const GUESS_ACTIONS = {
|
|
43
|
+
start: 'start',
|
|
44
|
+
quit: 'quit',
|
|
45
|
+
help: 'help',
|
|
46
|
+
开始: 'start',
|
|
47
|
+
放弃: 'quit',
|
|
48
|
+
帮助: 'help',
|
|
49
|
+
};
|
|
50
|
+
const DICE_ACTIONS = {
|
|
51
|
+
start: 'start',
|
|
52
|
+
continue: 'continue',
|
|
53
|
+
quit: 'quit',
|
|
54
|
+
help: 'help',
|
|
55
|
+
开始: 'start',
|
|
56
|
+
继续: 'continue',
|
|
57
|
+
放弃: 'quit',
|
|
58
|
+
帮助: 'help',
|
|
59
|
+
};
|
|
60
|
+
function lookup(map, raw) {
|
|
61
|
+
const t = raw.trim();
|
|
62
|
+
if (!t)
|
|
63
|
+
return 'help';
|
|
64
|
+
return map[t] ?? map[t.toLowerCase()] ?? t.toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
export function normalizeTttAction(raw) {
|
|
67
|
+
return lookup(TTT_ACTIONS, raw);
|
|
68
|
+
}
|
|
69
|
+
export function normalizeAdvAction(raw) {
|
|
70
|
+
return lookup(ADV_ACTIONS, raw);
|
|
71
|
+
}
|
|
72
|
+
export function normalizeRpsAction(raw) {
|
|
73
|
+
return lookup(RPS_ACTIONS, raw);
|
|
74
|
+
}
|
|
75
|
+
export function normalizeGuessAction(raw) {
|
|
76
|
+
return lookup(GUESS_ACTIONS, raw);
|
|
77
|
+
}
|
|
78
|
+
export function normalizeDiceAction(raw) {
|
|
79
|
+
return lookup(DICE_ACTIONS, raw);
|
|
80
|
+
}
|
|
81
|
+
const CHAIN_ACTIONS = {
|
|
82
|
+
start: 'start_pinyin',
|
|
83
|
+
start_pinyin: 'start_pinyin',
|
|
84
|
+
start_char: 'start_char',
|
|
85
|
+
continue: 'continue',
|
|
86
|
+
quit: 'quit',
|
|
87
|
+
help: 'help',
|
|
88
|
+
pinyin: 'start_pinyin',
|
|
89
|
+
char: 'start_char',
|
|
90
|
+
开始: 'start_pinyin',
|
|
91
|
+
同音: 'start_pinyin',
|
|
92
|
+
同字: 'start_char',
|
|
93
|
+
继续: 'continue',
|
|
94
|
+
放弃: 'quit',
|
|
95
|
+
帮助: 'help',
|
|
96
|
+
};
|
|
97
|
+
const RIDDLE_ACTIONS = {
|
|
98
|
+
start: 'start',
|
|
99
|
+
continue: 'continue',
|
|
100
|
+
quit: 'quit',
|
|
101
|
+
help: 'help',
|
|
102
|
+
char: 'char',
|
|
103
|
+
idiom: 'idiom',
|
|
104
|
+
开始: 'start',
|
|
105
|
+
继续: 'continue',
|
|
106
|
+
放弃: 'quit',
|
|
107
|
+
帮助: 'help',
|
|
108
|
+
字谜: 'char',
|
|
109
|
+
成语: 'idiom',
|
|
110
|
+
};
|
|
111
|
+
export function normalizeChainAction(raw) {
|
|
112
|
+
return lookup(CHAIN_ACTIONS, raw);
|
|
113
|
+
}
|
|
114
|
+
export function normalizeRiddleAction(raw) {
|
|
115
|
+
return lookup(RIDDLE_ACTIONS, raw);
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=game-action-alias.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-action-alias.js","sourceRoot":"","sources":["../src/game-action-alias.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Feature, type FeatureJSON, type Plugin, type PluginLike } from 'zhin.js';
|
|
2
|
+
export interface GameHubContext {
|
|
3
|
+
plugin: Plugin;
|
|
4
|
+
message: import('zhin.js').Message<any>;
|
|
5
|
+
}
|
|
6
|
+
export interface GameMenuAction {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
style?: 'primary' | 'danger' | 'secondary';
|
|
10
|
+
groupOnly?: boolean;
|
|
11
|
+
privateOnly?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface RegisteredGame {
|
|
14
|
+
id: string;
|
|
15
|
+
title: string;
|
|
16
|
+
icon: string;
|
|
17
|
+
description: string;
|
|
18
|
+
commandPrefix: string;
|
|
19
|
+
quickStart?: string;
|
|
20
|
+
aliases?: string[];
|
|
21
|
+
menus: GameMenuAction[];
|
|
22
|
+
runAction: (actionId: string, ctx: GameHubContext) => Promise<string | undefined | void>;
|
|
23
|
+
}
|
|
24
|
+
export interface GameHubContextExtensions {
|
|
25
|
+
registerGame(game: RegisteredGame): () => void;
|
|
26
|
+
}
|
|
27
|
+
declare module 'zhin.js' {
|
|
28
|
+
namespace Plugin {
|
|
29
|
+
interface Extensions extends GameHubContextExtensions {
|
|
30
|
+
}
|
|
31
|
+
interface Contexts {
|
|
32
|
+
game: GameHubFeature;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export declare class GameHubFeature extends Feature<RegisteredGame> {
|
|
37
|
+
readonly name: "game";
|
|
38
|
+
readonly icon = "Gamepad2";
|
|
39
|
+
readonly desc = "\u6E38\u620F\u5927\u5385";
|
|
40
|
+
private readonly byId;
|
|
41
|
+
private hubDisposers;
|
|
42
|
+
register(game: RegisteredGame, pluginName: string): () => void;
|
|
43
|
+
getGame(id: string): RegisteredGame | undefined;
|
|
44
|
+
remove(item: RegisteredGame, pluginName?: string): boolean;
|
|
45
|
+
toJSON(pluginName?: string): FeatureJSON;
|
|
46
|
+
get extensions(): {
|
|
47
|
+
registerGame(game: RegisteredGame): () => void;
|
|
48
|
+
};
|
|
49
|
+
mounted(plugin: PluginLike): void;
|
|
50
|
+
dispose(): void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 在 root 上注册 `game` 服务(幂等)。游戏插件初始化时调用一次即可。
|
|
54
|
+
*/
|
|
55
|
+
export declare function ensureGameHubService(plugin: Plugin): GameHubFeature;
|
|
56
|
+
export declare function getRegisteredGames(): readonly RegisteredGame[];
|
|
57
|
+
export declare function getRegisteredGame(id: string): RegisteredGame | undefined;
|
|
58
|
+
//# sourceMappingURL=game-hub-feature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-hub-feature.d.ts","sourceRoot":"","sources":["../src/game-hub-feature.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EAEP,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,UAAU,EAChB,MAAM,SAAS,CAAC;AAGjB,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC3C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC;CAC1F;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,IAAI,CAAC;CAChD;AAED,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,MAAM,CAAC;QACf,UAAU,UAAW,SAAQ,wBAAwB;SAAG;QACxD,UAAU,QAAQ;YAChB,IAAI,EAAE,cAAc,CAAC;SACtB;KACF;CACF;AAID,qBAAa,cAAe,SAAQ,OAAO,CAAC,cAAc,CAAC;IACzD,QAAQ,CAAC,IAAI,EAAG,MAAM,CAAU;IAChC,QAAQ,CAAC,IAAI,cAAc;IAC3B,QAAQ,CAAC,IAAI,8BAAU;IAEvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqC;IAC1D,OAAO,CAAC,YAAY,CAAsB;IAE1C,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,IAAI;IAS9D,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAItC,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO;IAKnE,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW;IAexC,IAAI,UAAU;2BAGS,cAAc,SApCqB,IAAI;MA6C7D;IAED,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAOjC,OAAO,IAAI,IAAI;CAOhB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAenE;AAED,wBAAgB,kBAAkB,IAAI,SAAS,cAAc,EAAE,CAE9D;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAExE"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Feature, getPlugin, } from 'zhin.js';
|
|
2
|
+
import { mountGameHubUi, markGameHubUiMounted } from './game-hub-mount.js';
|
|
3
|
+
let activeFeature = null;
|
|
4
|
+
export class GameHubFeature extends Feature {
|
|
5
|
+
name = 'game';
|
|
6
|
+
icon = 'Gamepad2';
|
|
7
|
+
desc = '游戏大厅';
|
|
8
|
+
byId = new Map();
|
|
9
|
+
hubDisposers = [];
|
|
10
|
+
register(game, pluginName) {
|
|
11
|
+
const prev = this.byId.get(game.id);
|
|
12
|
+
if (prev) {
|
|
13
|
+
this.remove(prev);
|
|
14
|
+
}
|
|
15
|
+
this.byId.set(game.id, game);
|
|
16
|
+
return this.add(game, pluginName);
|
|
17
|
+
}
|
|
18
|
+
getGame(id) {
|
|
19
|
+
return this.byId.get(id);
|
|
20
|
+
}
|
|
21
|
+
remove(item, pluginName) {
|
|
22
|
+
this.byId.delete(item.id);
|
|
23
|
+
return super.remove(item, pluginName);
|
|
24
|
+
}
|
|
25
|
+
toJSON(pluginName) {
|
|
26
|
+
const list = pluginName ? this.getByPlugin(pluginName) : [...this.items];
|
|
27
|
+
return {
|
|
28
|
+
name: this.name,
|
|
29
|
+
icon: this.icon,
|
|
30
|
+
desc: this.desc,
|
|
31
|
+
count: list.length,
|
|
32
|
+
items: list.map((g) => ({
|
|
33
|
+
name: g.id,
|
|
34
|
+
desc: g.title,
|
|
35
|
+
usage: `${g.commandPrefix} ${g.quickStart ?? '开始'}`,
|
|
36
|
+
})),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
get extensions() {
|
|
40
|
+
const feature = this;
|
|
41
|
+
return {
|
|
42
|
+
registerGame(game) {
|
|
43
|
+
const plugin = getPlugin();
|
|
44
|
+
ensureGameHubService(plugin);
|
|
45
|
+
const dispose = feature.register(game, plugin.name);
|
|
46
|
+
plugin.recordFeatureContribution(feature.name, game.id);
|
|
47
|
+
plugin.onDispose(dispose);
|
|
48
|
+
return dispose;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
mounted(plugin) {
|
|
53
|
+
activeFeature = this;
|
|
54
|
+
const root = plugin;
|
|
55
|
+
this.hubDisposers = mountGameHubUi(root.root ?? root);
|
|
56
|
+
markGameHubUiMounted();
|
|
57
|
+
}
|
|
58
|
+
dispose() {
|
|
59
|
+
for (const d of this.hubDisposers)
|
|
60
|
+
d();
|
|
61
|
+
this.hubDisposers = [];
|
|
62
|
+
if (activeFeature === this) {
|
|
63
|
+
activeFeature = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 在 root 上注册 `game` 服务(幂等)。游戏插件初始化时调用一次即可。
|
|
69
|
+
*/
|
|
70
|
+
export function ensureGameHubService(plugin) {
|
|
71
|
+
const root = plugin.root;
|
|
72
|
+
if (root.contextIsReady('game')) {
|
|
73
|
+
return root.inject('game');
|
|
74
|
+
}
|
|
75
|
+
const feature = new GameHubFeature();
|
|
76
|
+
root.provide(feature);
|
|
77
|
+
activeFeature = feature;
|
|
78
|
+
if (root.started && feature.mounted) {
|
|
79
|
+
feature.mounted(root);
|
|
80
|
+
}
|
|
81
|
+
return root.inject('game');
|
|
82
|
+
}
|
|
83
|
+
export function getRegisteredGames() {
|
|
84
|
+
return activeFeature?.items ?? [];
|
|
85
|
+
}
|
|
86
|
+
export function getRegisteredGame(id) {
|
|
87
|
+
return activeFeature?.getGame(id);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=game-hub-feature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-hub-feature.js","sourceRoot":"","sources":["../src/game-hub-feature.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Message, Plugin } from 'zhin.js';
|
|
2
|
+
import { buildMainHubMenu } from './game-hub-menu.js';
|
|
3
|
+
export declare function openMainMenu(message: Message<any>): ReturnType<typeof buildMainHubMenu> | string;
|
|
4
|
+
export declare function handleHubChoice(plugin: Plugin, message: Message<any>, scopeId: string, choiceId: string): Promise<boolean>;
|
|
5
|
+
export declare function parseHubPayload(payload: string): {
|
|
6
|
+
scopeId: string;
|
|
7
|
+
choiceId: string;
|
|
8
|
+
} | null;
|
|
9
|
+
//# sourceMappingURL=game-hub-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-hub-flow.d.ts","sourceRoot":"","sources":["../src/game-hub-flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,EAEL,gBAAgB,EAMjB,MAAM,oBAAoB,CAAC;AAkC5B,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,GAAG,MAAM,CAShG;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EACrB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CA+ClB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAI7F"}
|