ygopro-msg-encode 1.0.1 → 1.0.2
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/MSG_RESPONSE_GUIDE.md +578 -0
- package/README.md +72 -0
- package/dist/index.cjs +553 -82
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.mjs +546 -82
- package/dist/index.mjs.map +4 -4
- package/dist/src/protos/msg/index-response.d.ts +8 -0
- package/dist/src/protos/msg/index.d.ts +2 -0
- package/dist/src/protos/msg/proto/announce-attrib.d.ts +4 -3
- package/dist/src/protos/msg/proto/announce-card.d.ts +4 -3
- package/dist/src/protos/msg/proto/announce-number.d.ts +4 -2
- package/dist/src/protos/msg/proto/announce-race.d.ts +4 -3
- package/dist/src/protos/msg/proto/select-battlecmd.d.ts +16 -2
- package/dist/src/protos/msg/proto/select-card.d.ts +10 -2
- package/dist/src/protos/msg/proto/select-chain.d.ts +13 -3
- package/dist/src/protos/msg/proto/select-counter.d.ts +12 -3
- package/dist/src/protos/msg/proto/select-disfield.d.ts +7 -2
- package/dist/src/protos/msg/proto/select-effectyn.d.ts +4 -2
- package/dist/src/protos/msg/proto/select-idlecmd.d.ts +23 -8
- package/dist/src/protos/msg/proto/select-option.d.ts +4 -2
- package/dist/src/protos/msg/proto/select-place.d.ts +7 -2
- package/dist/src/protos/msg/proto/select-position.d.ts +3 -2
- package/dist/src/protos/msg/proto/select-sum.d.ts +9 -2
- package/dist/src/protos/msg/proto/select-tribute.d.ts +10 -3
- package/dist/src/protos/msg/proto/select-unselect-card.d.ts +10 -2
- package/dist/src/protos/msg/proto/select-yesno.d.ts +4 -2
- package/dist/src/protos/msg/proto/sort-card.d.ts +10 -2
- package/dist/src/protos/msg/with-response-base.d.ts +4 -0
- package/index.ts +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
# MSG Response 使用指南
|
|
2
|
+
|
|
3
|
+
本文档介绍如何使用 YGOPro MSG 协议的响应功能。
|
|
4
|
+
|
|
5
|
+
## 概述
|
|
6
|
+
|
|
7
|
+
YGOPro 协议中有 18 种需要客户端响应的 MSG 消息。本库为这些消息提供了统一的响应生成接口。
|
|
8
|
+
|
|
9
|
+
## 基础概念
|
|
10
|
+
|
|
11
|
+
### YGOProMsgResponseBase
|
|
12
|
+
|
|
13
|
+
所有需要响应的 MSG 类都继承自 `YGOProMsgResponseBase`,该基类提供两个核心方法:
|
|
14
|
+
|
|
15
|
+
- **`prepareResponse(...)`**: 根据用户选择生成响应数据
|
|
16
|
+
- **`defaultResponse(): Uint8Array | undefined`**: 返回"保守默认"响应(如果适用)
|
|
17
|
+
|
|
18
|
+
## 响应方法
|
|
19
|
+
|
|
20
|
+
### 1. 简单是/否类型
|
|
21
|
+
|
|
22
|
+
#### MSG_SELECT_EFFECTYN / MSG_SELECT_YESNO
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { YGOProMsgSelectEffectyn, YGOProMsgSelectYesno } from 'ygopro-msg-encode';
|
|
26
|
+
|
|
27
|
+
// 用户选择是或否
|
|
28
|
+
const response = msg.prepareResponse(true); // 选择"是"
|
|
29
|
+
const response = msg.prepareResponse(false); // 选择"否"
|
|
30
|
+
|
|
31
|
+
// 默认响应(总是"否")
|
|
32
|
+
const defaultResp = msg.defaultResponse(); // 等同于 prepareResponse(false)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. 位置选择类型
|
|
36
|
+
|
|
37
|
+
#### MSG_SELECT_POSITION
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { YGOProMsgSelectPosition } from 'ygopro-msg-encode';
|
|
41
|
+
|
|
42
|
+
// 用户选择卡片位置(使用 POS_* 常量)
|
|
43
|
+
const response = msg.prepareResponse(0x1); // POS_FACEUP_ATTACK
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### MSG_SELECT_PLACE / MSG_SELECT_DISFIELD
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { YGOProMsgSelectPlace, YGOProMsgSelectDisfield } from 'ygopro-msg-encode';
|
|
50
|
+
|
|
51
|
+
// 选择单个位置
|
|
52
|
+
const response = msg.prepareResponse([
|
|
53
|
+
{ player: 0, location: 0x04, sequence: 2 } // LOCATION_MZONE, 序列 2
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
// 选择多个位置
|
|
57
|
+
const response = msg.prepareResponse([
|
|
58
|
+
{ player: 0, location: 0x04, sequence: 2 },
|
|
59
|
+
{ player: 0, location: 0x04, sequence: 3 }
|
|
60
|
+
]);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. 索引选择类型(支持 IndexResponse 和语义对象)
|
|
64
|
+
|
|
65
|
+
这类消息支持两种选择方式:
|
|
66
|
+
1. **直接索引选择**:使用 `IndexResponse(index)` 明确指定第几个选项
|
|
67
|
+
2. **语义对象选择**:通过卡片属性(code、controller、location、sequence、desc)进行匹配
|
|
68
|
+
|
|
69
|
+
#### MSG_SELECT_BATTLECMD / MSG_SELECT_IDLECMD
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import {
|
|
73
|
+
YGOProMsgSelectBattlecmd,
|
|
74
|
+
BattleCmdType,
|
|
75
|
+
YGOProMsgSelectIdlecmd,
|
|
76
|
+
IdleCmdType,
|
|
77
|
+
IndexResponse
|
|
78
|
+
} from 'ygopro-msg-encode';
|
|
79
|
+
|
|
80
|
+
// 方式 1: 使用 IndexResponse 直接选择第几个
|
|
81
|
+
const response = msg.prepareResponse(
|
|
82
|
+
BattleCmdType.ACTIVATE,
|
|
83
|
+
IndexResponse(0) // 选择第 0 个激活选项
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// 方式 2: 使用语义对象匹配
|
|
87
|
+
const response = msg.prepareResponse(
|
|
88
|
+
BattleCmdType.ACTIVATE,
|
|
89
|
+
{
|
|
90
|
+
code: 12345678, // 卡片代码
|
|
91
|
+
controller: 0, // 控制者
|
|
92
|
+
location: 0x04, // 位置
|
|
93
|
+
sequence: 2, // 序列
|
|
94
|
+
desc: 10 // 效果描述(用于区分同一卡片的不同效果)
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// 方式 3: 部分匹配(只提供部分属性)
|
|
99
|
+
const response = msg.prepareResponse(
|
|
100
|
+
IdleCmdType.SUMMON,
|
|
101
|
+
{ code: 12345678 } // 只匹配卡片代码
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// 特殊命令(无需选项)
|
|
105
|
+
const response = msg.prepareResponse(IdleCmdType.TO_BP); // 进入战斗阶段
|
|
106
|
+
const response = msg.prepareResponse(IdleCmdType.TO_EP); // 进入结束阶段
|
|
107
|
+
const response = msg.prepareResponse(IdleCmdType.SHUFFLE); // 洗手牌
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**命令类型枚举:**
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// BattleCmdType
|
|
114
|
+
enum BattleCmdType {
|
|
115
|
+
ACTIVATE = 0, // 激活效果
|
|
116
|
+
ATTACK = 1, // 攻击
|
|
117
|
+
TO_M2 = 2, // 进入 M2
|
|
118
|
+
TO_EP = 3 // 进入结束阶段
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// IdleCmdType
|
|
122
|
+
enum IdleCmdType {
|
|
123
|
+
SUMMON = 0, // 通常召唤
|
|
124
|
+
SPSUMMON = 1, // 特殊召唤
|
|
125
|
+
REPOS = 2, // 改变表示形式
|
|
126
|
+
MSET = 3, // 盖放怪兽
|
|
127
|
+
SSET = 4, // 盖放魔陷
|
|
128
|
+
ACTIVATE = 5, // 激活效果
|
|
129
|
+
TO_BP = 6, // 进入战斗阶段
|
|
130
|
+
TO_EP = 7, // 进入结束阶段
|
|
131
|
+
SHUFFLE = 8 // 洗手牌
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### MSG_SELECT_OPTION
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { YGOProMsgSelectOption, IndexResponse } from 'ygopro-msg-encode';
|
|
139
|
+
|
|
140
|
+
// 方式 1: 使用 IndexResponse
|
|
141
|
+
const response = msg.prepareResponse(IndexResponse(1)); // 选择第 1 个选项
|
|
142
|
+
|
|
143
|
+
// 方式 2: 直接使用数字(简写)
|
|
144
|
+
const response = msg.prepareResponse(1);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### MSG_SELECT_CHAIN
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { YGOProMsgSelectChain, IndexResponse } from 'ygopro-msg-encode';
|
|
151
|
+
|
|
152
|
+
// 选择连锁
|
|
153
|
+
const response = msg.prepareResponse(IndexResponse(0));
|
|
154
|
+
|
|
155
|
+
// 或使用语义对象(包含 desc 用于区分同一卡片的不同效果)
|
|
156
|
+
const response = msg.prepareResponse({
|
|
157
|
+
code: 12345678,
|
|
158
|
+
controller: 0,
|
|
159
|
+
location: 0x04,
|
|
160
|
+
sequence: 2,
|
|
161
|
+
desc: 10
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// 不连锁(如果允许取消)
|
|
165
|
+
const response = msg.prepareResponse(null);
|
|
166
|
+
|
|
167
|
+
// 默认响应(仅在无强制连锁时返回"不连锁")
|
|
168
|
+
const defaultResp = msg.defaultResponse();
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 4. 卡片选择类型(支持 IndexResponse 和语义对象数组)
|
|
172
|
+
|
|
173
|
+
#### MSG_SELECT_CARD
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { YGOProMsgSelectCard, IndexResponse } from 'ygopro-msg-encode';
|
|
177
|
+
|
|
178
|
+
// 选择卡片(使用 IndexResponse)
|
|
179
|
+
const response = msg.prepareResponse([
|
|
180
|
+
IndexResponse(0),
|
|
181
|
+
IndexResponse(2)
|
|
182
|
+
]);
|
|
183
|
+
|
|
184
|
+
// 使用语义对象选择
|
|
185
|
+
const response = msg.prepareResponse([
|
|
186
|
+
{ code: 12345678, controller: 0, location: 0x04, sequence: 0 },
|
|
187
|
+
{ code: 87654321, location: 0x08 } // 只匹配 code 和 location
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
// 取消选择(如果允许)
|
|
191
|
+
const response = msg.prepareResponse(null);
|
|
192
|
+
|
|
193
|
+
// 默认响应(仅在 cancelable 时返回"取消")
|
|
194
|
+
const defaultResp = msg.defaultResponse();
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### MSG_SELECT_UNSELECT_CARD
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { YGOProMsgSelectUnselectCard, IndexResponse } from 'ygopro-msg-encode';
|
|
201
|
+
|
|
202
|
+
// 选择一张卡片
|
|
203
|
+
const response = msg.prepareResponse(IndexResponse(0));
|
|
204
|
+
|
|
205
|
+
// 使用语义对象
|
|
206
|
+
const response = msg.prepareResponse({
|
|
207
|
+
code: 12345678,
|
|
208
|
+
controller: 0,
|
|
209
|
+
location: 0x04,
|
|
210
|
+
sequence: 1
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// 完成选择(如果 finishable)
|
|
214
|
+
const response = msg.prepareResponse(null);
|
|
215
|
+
|
|
216
|
+
// 默认响应(根据 cancelable/finishable 判断)
|
|
217
|
+
const defaultResp = msg.defaultResponse();
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### MSG_SELECT_TRIBUTE
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { YGOProMsgSelectTribute, IndexResponse } from 'ygopro-msg-encode';
|
|
224
|
+
|
|
225
|
+
// 选择祭品(方式同 MSG_SELECT_CARD)
|
|
226
|
+
const response = msg.prepareResponse([
|
|
227
|
+
IndexResponse(0),
|
|
228
|
+
IndexResponse(1)
|
|
229
|
+
]);
|
|
230
|
+
|
|
231
|
+
// 取消(如果允许)
|
|
232
|
+
const response = msg.prepareResponse(null);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
#### MSG_SORT_CARD
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { YGOProMsgSortCard, IndexResponse } from 'ygopro-msg-encode';
|
|
239
|
+
|
|
240
|
+
// 指定排序顺序
|
|
241
|
+
const response = msg.prepareResponse([
|
|
242
|
+
IndexResponse(2),
|
|
243
|
+
IndexResponse(0),
|
|
244
|
+
IndexResponse(1)
|
|
245
|
+
]);
|
|
246
|
+
|
|
247
|
+
// 使用语义对象
|
|
248
|
+
const response = msg.prepareResponse([
|
|
249
|
+
{ code: 12345678 },
|
|
250
|
+
{ code: 87654321 },
|
|
251
|
+
{ code: 11223344 }
|
|
252
|
+
]);
|
|
253
|
+
|
|
254
|
+
// 使用默认排序
|
|
255
|
+
const response = msg.prepareResponse(null);
|
|
256
|
+
|
|
257
|
+
// 默认响应(总是返回默认排序)
|
|
258
|
+
const defaultResp = msg.defaultResponse(); // 等同于 prepareResponse(null)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### MSG_SELECT_SUM
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import { YGOProMsgSelectSum, IndexResponse } from 'ygopro-msg-encode';
|
|
265
|
+
|
|
266
|
+
// 选择等级/刻度和的卡片
|
|
267
|
+
const response = msg.prepareResponse([
|
|
268
|
+
IndexResponse(0),
|
|
269
|
+
IndexResponse(2),
|
|
270
|
+
IndexResponse(5)
|
|
271
|
+
]);
|
|
272
|
+
|
|
273
|
+
// 使用语义对象
|
|
274
|
+
const response = msg.prepareResponse([
|
|
275
|
+
{ code: 12345678, controller: 0, location: 0x04, sequence: 0 },
|
|
276
|
+
{ code: 87654321, location: 0x02 }
|
|
277
|
+
]);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
#### MSG_SELECT_COUNTER
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import { YGOProMsgSelectCounter, IndexResponse } from 'ygopro-msg-encode';
|
|
284
|
+
|
|
285
|
+
// 为每张卡片指定移除的指示物数量
|
|
286
|
+
const response = msg.prepareResponse([
|
|
287
|
+
{ card: IndexResponse(0), count: 2 },
|
|
288
|
+
{ card: IndexResponse(1), count: 1 }
|
|
289
|
+
]);
|
|
290
|
+
|
|
291
|
+
// 使用语义对象
|
|
292
|
+
const response = msg.prepareResponse([
|
|
293
|
+
{ card: { code: 12345678, sequence: 0 }, count: 2 },
|
|
294
|
+
{ card: { code: 87654321, sequence: 1 }, count: 1 }
|
|
295
|
+
]);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### 5. 宣言类型
|
|
299
|
+
|
|
300
|
+
#### MSG_ANNOUNCE_RACE
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
import { YGOProMsgAnnounceRace } from 'ygopro-msg-encode';
|
|
304
|
+
|
|
305
|
+
// 宣言种族(直接传入种族值,使用 RACE_* 常量)
|
|
306
|
+
const response = msg.prepareResponse(0x1); // RACE_WARRIOR
|
|
307
|
+
|
|
308
|
+
// availableRaces 字段是一个位掩码,表示哪些种族可选
|
|
309
|
+
// 例如:availableRaces = 0x3 表示 RACE_WARRIOR (0x1) 和 RACE_SPELLCASTER (0x2) 可选
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
#### MSG_ANNOUNCE_ATTRIB
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { YGOProMsgAnnounceAttrib } from 'ygopro-msg-encode';
|
|
316
|
+
|
|
317
|
+
// 宣言属性(直接传入属性值,使用 ATTRIBUTE_* 常量)
|
|
318
|
+
const response = msg.prepareResponse(0x1); // ATTRIBUTE_EARTH
|
|
319
|
+
|
|
320
|
+
// availableAttributes 字段是一个位掩码,表示哪些属性可选
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
#### MSG_ANNOUNCE_CARD
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { YGOProMsgAnnounceCard } from 'ygopro-msg-encode';
|
|
327
|
+
|
|
328
|
+
// 宣言卡片代码(直接传入卡片代码)
|
|
329
|
+
const response = msg.prepareResponse(12345678);
|
|
330
|
+
|
|
331
|
+
// 注意:opcodes 字段不是可选的卡片列表,而是一个逆波兰表达式(RPN)
|
|
332
|
+
// 用于定义宣言条件(如种族、属性、类型等的组合条件)
|
|
333
|
+
// 服务器会验证宣言的卡片是否满足 opcodes 定义的条件
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
#### MSG_ANNOUNCE_NUMBER
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import { YGOProMsgAnnounceNumber, IndexResponse } from 'ygopro-msg-encode';
|
|
340
|
+
|
|
341
|
+
// 从可用数字中选择一个(使用 IndexResponse)
|
|
342
|
+
const response = msg.prepareResponse(IndexResponse(0));
|
|
343
|
+
|
|
344
|
+
// 或直接使用数字(简写,会自动查找索引)
|
|
345
|
+
const response = msg.prepareResponse(0);
|
|
346
|
+
|
|
347
|
+
// numbers 字段是可选的数字列表
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## 语义对象匹配规则
|
|
351
|
+
|
|
352
|
+
使用语义对象时,系统会在可选列表中查找匹配的项:
|
|
353
|
+
|
|
354
|
+
1. **所有提供的属性都必须匹配**
|
|
355
|
+
2. **未提供的属性会被忽略**
|
|
356
|
+
3. **如果找不到匹配项,会抛出 `TypeError`**
|
|
357
|
+
4. **如果索引超出范围,也会抛出 `TypeError`**
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
// 示例:精确匹配
|
|
361
|
+
const response = msg.prepareResponse(
|
|
362
|
+
BattleCmdType.ACTIVATE,
|
|
363
|
+
{
|
|
364
|
+
code: 12345678,
|
|
365
|
+
controller: 0,
|
|
366
|
+
location: 0x04,
|
|
367
|
+
sequence: 2,
|
|
368
|
+
desc: 10
|
|
369
|
+
}
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
// 示例:只匹配卡片代码
|
|
373
|
+
const response = msg.prepareResponse(
|
|
374
|
+
IdleCmdType.SUMMON,
|
|
375
|
+
{ code: 12345678 } // 找到第一个 code 为 12345678 的卡片
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
// 示例:匹配位置
|
|
379
|
+
const response = msg.prepareResponse(
|
|
380
|
+
IdleCmdType.SPSUMMON,
|
|
381
|
+
{ location: 0x40, sequence: 0 } // 额外卡组的第一张卡
|
|
382
|
+
);
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## 默认响应
|
|
386
|
+
|
|
387
|
+
某些消息提供了"保守默认"响应,通过 `defaultResponse()` 获取:
|
|
388
|
+
|
|
389
|
+
| 消息类型 | 默认响应 | 条件 |
|
|
390
|
+
|---------|---------|------|
|
|
391
|
+
| `MSG_SELECT_EFFECTYN` | `false` (否) | 总是可用 |
|
|
392
|
+
| `MSG_SELECT_YESNO` | `false` (否) | 总是可用 |
|
|
393
|
+
| `MSG_SELECT_CHAIN` | `null` (不连锁) | 无强制连锁时 |
|
|
394
|
+
| `MSG_SELECT_CARD` | `null` (取消) | `cancelable = 1` 时 |
|
|
395
|
+
| `MSG_SELECT_UNSELECT_CARD` | `null` (完成/取消) | `cancelable = 1` 或 `finishable = 1` 时 |
|
|
396
|
+
| `MSG_SELECT_TRIBUTE` | `null` (取消) | `cancelable = 1` 时 |
|
|
397
|
+
| `MSG_SORT_CARD` | `null` (默认排序) | 总是可用 |
|
|
398
|
+
| 其他消息 | `undefined` | 不可用 |
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
// 检查是否有默认响应
|
|
402
|
+
const defaultResp = msg.defaultResponse();
|
|
403
|
+
if (defaultResp !== undefined) {
|
|
404
|
+
// 使用默认响应
|
|
405
|
+
sendResponse(defaultResp);
|
|
406
|
+
} else {
|
|
407
|
+
// 必须由用户手动选择
|
|
408
|
+
promptUserForResponse(msg);
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## 错误处理
|
|
413
|
+
|
|
414
|
+
所有 `prepareResponse()` 方法在遇到无效输入时会抛出 `TypeError`:
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
try {
|
|
418
|
+
const response = msg.prepareResponse(
|
|
419
|
+
BattleCmdType.ACTIVATE,
|
|
420
|
+
IndexResponse(999) // 索引超出范围
|
|
421
|
+
);
|
|
422
|
+
} catch (e) {
|
|
423
|
+
if (e instanceof TypeError) {
|
|
424
|
+
console.error('无效的选择:', e.message);
|
|
425
|
+
// 可以尝试使用默认响应或提示用户重新选择
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
const response = msg.prepareResponse(
|
|
431
|
+
IdleCmdType.SUMMON,
|
|
432
|
+
{ code: 99999999 } // 找不到这个卡片
|
|
433
|
+
);
|
|
434
|
+
} catch (e) {
|
|
435
|
+
if (e instanceof TypeError) {
|
|
436
|
+
console.error('找不到匹配的卡片:', e.message);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
## 常见模式
|
|
442
|
+
|
|
443
|
+
### 模式 1: 优先使用默认响应
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
function autoRespond(msg: YGOProMsgResponseBase): Uint8Array {
|
|
447
|
+
const defaultResp = msg.defaultResponse();
|
|
448
|
+
if (defaultResp !== undefined) {
|
|
449
|
+
return defaultResp;
|
|
450
|
+
}
|
|
451
|
+
throw new Error('此消息需要用户手动响应');
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### 模式 2: 智能卡片选择
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
function selectByCardCode(
|
|
459
|
+
msg: YGOProMsgSelectCard,
|
|
460
|
+
preferredCodes: number[]
|
|
461
|
+
): Uint8Array {
|
|
462
|
+
const selections = [];
|
|
463
|
+
|
|
464
|
+
for (const code of preferredCodes) {
|
|
465
|
+
try {
|
|
466
|
+
selections.push({ code });
|
|
467
|
+
} catch (e) {
|
|
468
|
+
// 这张卡不可选,跳过
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (selections.length >= msg.max) {
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (selections.length >= msg.min) {
|
|
478
|
+
return msg.prepareResponse(selections);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// 无法满足最小数量要求
|
|
482
|
+
if (msg.cancelable) {
|
|
483
|
+
return msg.prepareResponse(null);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
throw new Error('无法选择足够的卡片');
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### 模式 3: 根据效果描述选择
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
function selectEffectByDescription(
|
|
494
|
+
msg: YGOProMsgSelectBattlecmd,
|
|
495
|
+
cardCode: number,
|
|
496
|
+
preferredDesc: number
|
|
497
|
+
): Uint8Array {
|
|
498
|
+
try {
|
|
499
|
+
return msg.prepareResponse(
|
|
500
|
+
BattleCmdType.ACTIVATE,
|
|
501
|
+
{ code: cardCode, desc: preferredDesc }
|
|
502
|
+
);
|
|
503
|
+
} catch (e) {
|
|
504
|
+
// 该效果不可用,使用第一个可用效果
|
|
505
|
+
return msg.prepareResponse(
|
|
506
|
+
BattleCmdType.ACTIVATE,
|
|
507
|
+
IndexResponse(0)
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
## 注意事项
|
|
514
|
+
|
|
515
|
+
1. **响应格式**: 所有 `prepareResponse()` 返回 `Uint8Array`,即使是单个整数值
|
|
516
|
+
2. **null vs undefined**: `null` 表示"不选择/取消",`undefined` 表示"不可用"
|
|
517
|
+
3. **desc 字段**: 在 `ACTIVATE` 类型的选择中,`desc` 用于区分同一卡片的不同效果
|
|
518
|
+
4. **索引从 0 开始**: 使用 `IndexResponse(0)` 表示第一个选项
|
|
519
|
+
5. **部分匹配**: 语义对象可以只提供部分属性,系统会找到第一个匹配项
|
|
520
|
+
6. **类型安全**: 使用 TypeScript 枚举(如 `BattleCmdType`)而不是魔法数字
|
|
521
|
+
7. **ANNOUNCE 类型的特殊性**:
|
|
522
|
+
- `MSG_ANNOUNCE_RACE` / `MSG_ANNOUNCE_ATTRIB`: 传入的是实际值(位掩码),不是索引
|
|
523
|
+
- `MSG_ANNOUNCE_CARD`: `opcodes` 是逆波兰表达式(条件定义),不是卡片列表;响应直接传入卡片代码
|
|
524
|
+
- `MSG_ANNOUNCE_NUMBER`: 既可以传入索引,也可以传入实际数字值(会自动查找)
|
|
525
|
+
|
|
526
|
+
## 完整示例
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
import {
|
|
530
|
+
YGOProMsgSelectBattlecmd,
|
|
531
|
+
BattleCmdType,
|
|
532
|
+
IndexResponse
|
|
533
|
+
} from 'ygopro-msg-encode';
|
|
534
|
+
|
|
535
|
+
// 解析收到的 MSG
|
|
536
|
+
const buffer = new Uint8Array([/* ... */]);
|
|
537
|
+
const msg = new YGOProMsgSelectBattlecmd();
|
|
538
|
+
fillBinaryFields(msg, buffer);
|
|
539
|
+
|
|
540
|
+
// 根据游戏逻辑生成响应
|
|
541
|
+
let response: Uint8Array;
|
|
542
|
+
|
|
543
|
+
if (msg.activatableCount > 0) {
|
|
544
|
+
// 优先激活特定卡片的效果
|
|
545
|
+
try {
|
|
546
|
+
response = msg.prepareResponse(
|
|
547
|
+
BattleCmdType.ACTIVATE,
|
|
548
|
+
{ code: 12345678, desc: 10 }
|
|
549
|
+
);
|
|
550
|
+
} catch (e) {
|
|
551
|
+
// 该效果不可用,激活第一个可用效果
|
|
552
|
+
response = msg.prepareResponse(
|
|
553
|
+
BattleCmdType.ACTIVATE,
|
|
554
|
+
IndexResponse(0)
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
} else if (msg.attackableCount > 0) {
|
|
558
|
+
// 攻击
|
|
559
|
+
response = msg.prepareResponse(
|
|
560
|
+
BattleCmdType.ATTACK,
|
|
561
|
+
IndexResponse(0)
|
|
562
|
+
);
|
|
563
|
+
} else if (msg.canToEp) {
|
|
564
|
+
// 进入结束阶段
|
|
565
|
+
response = msg.prepareResponse(BattleCmdType.TO_EP);
|
|
566
|
+
} else {
|
|
567
|
+
throw new Error('无可用操作');
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// 发送响应
|
|
571
|
+
sendCtosResponse(response);
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
## 相关文件
|
|
575
|
+
|
|
576
|
+
- `src/protos/msg/with-response-base.ts` - 响应基类
|
|
577
|
+
- `src/protos/msg/index-response.ts` - IndexResponse 工具
|
|
578
|
+
- `src/protos/msg/proto/*` - 各个消息类型的实现
|
package/README.md
CHANGED
|
@@ -247,8 +247,79 @@ const msgProtocol = YGOProMessages.getInstanceFromPayload(bodyPayload);
|
|
|
247
247
|
|
|
248
248
|
All YGOPro game messages are supported. See [MSG Protocol List](./CTOS_STOC_IMPLEMENTATION.md) for details.
|
|
249
249
|
|
|
250
|
+
**Response Support**: 18 MSG protocols require client responses. This library provides a unified response generation API with support for index-based and semantic object selection. See [MSG Response Guide](./MSG_RESPONSE_GUIDE.md) for details.
|
|
251
|
+
|
|
250
252
|
## Advanced Usage
|
|
251
253
|
|
|
254
|
+
### MSG Response Generation
|
|
255
|
+
|
|
256
|
+
18 MSG protocols require client responses (e.g., card selection, yes/no, position selection). This library provides two powerful methods for generating responses:
|
|
257
|
+
|
|
258
|
+
#### prepareResponse() - Generate Response Data
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import {
|
|
262
|
+
YGOProMsgSelectBattlecmd,
|
|
263
|
+
BattleCmdType,
|
|
264
|
+
IndexResponse
|
|
265
|
+
} from 'ygopro-msg-encode';
|
|
266
|
+
|
|
267
|
+
// Parse received MSG
|
|
268
|
+
const msg = new YGOProMsgSelectBattlecmd();
|
|
269
|
+
msg.fromPayload(msgData);
|
|
270
|
+
|
|
271
|
+
// Method 1: Index-based selection (explicit)
|
|
272
|
+
const response = msg.prepareResponse(
|
|
273
|
+
BattleCmdType.ACTIVATE,
|
|
274
|
+
IndexResponse(0) // Select first option
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
// Method 2: Semantic object selection (intelligent)
|
|
278
|
+
const response = msg.prepareResponse(
|
|
279
|
+
BattleCmdType.ACTIVATE,
|
|
280
|
+
{
|
|
281
|
+
code: 12345678, // Card code
|
|
282
|
+
location: 0x04, // LOCATION_MZONE
|
|
283
|
+
sequence: 2, // Sequence number
|
|
284
|
+
desc: 10 // Effect description (for multi-effect cards)
|
|
285
|
+
}
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
// Send response
|
|
289
|
+
sendCtosResponse(response);
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### defaultResponse() - Conservative Default Behavior
|
|
293
|
+
|
|
294
|
+
Some messages support "do nothing" or conservative default responses:
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import { YGOProMsgSelectChain } from 'ygopro-msg-encode';
|
|
298
|
+
|
|
299
|
+
const msg = new YGOProMsgSelectChain();
|
|
300
|
+
msg.fromPayload(msgData);
|
|
301
|
+
|
|
302
|
+
// Get default response (if available)
|
|
303
|
+
const defaultResp = msg.defaultResponse();
|
|
304
|
+
if (defaultResp !== undefined) {
|
|
305
|
+
// Use safe default (e.g., "no" for yes/no, cancel for optional selections)
|
|
306
|
+
sendCtosResponse(defaultResp);
|
|
307
|
+
} else {
|
|
308
|
+
// User input required
|
|
309
|
+
promptUserForResponse(msg);
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Supported features:**
|
|
314
|
+
- ✅ Index-based selection with `IndexResponse(index)`
|
|
315
|
+
- ✅ Semantic object selection by card properties
|
|
316
|
+
- ✅ Partial matching (match only specified fields)
|
|
317
|
+
- ✅ Conservative default responses when available
|
|
318
|
+
- ✅ Automatic error detection (invalid index, card not found)
|
|
319
|
+
- ✅ Type-safe enums for command types
|
|
320
|
+
|
|
321
|
+
**See [MSG Response Guide](./MSG_RESPONSE_GUIDE.md) for complete documentation.**
|
|
322
|
+
|
|
252
323
|
### Special Protocols
|
|
253
324
|
|
|
254
325
|
#### CTOS_UPDATE_DECK
|
|
@@ -439,6 +510,7 @@ npm run clean
|
|
|
439
510
|
|
|
440
511
|
## Documentation
|
|
441
512
|
|
|
513
|
+
- [MSG Response Guide](./MSG_RESPONSE_GUIDE.md) - **Complete guide for MSG response generation** ⭐
|
|
442
514
|
- [CTOS/STOC Implementation](./CTOS_STOC_IMPLEMENTATION.md) - Detailed protocol implementation
|
|
443
515
|
- [MSG Implementation](./MSG_IMPLEMENTATION_SUMMARY.md) - MSG protocol details
|
|
444
516
|
- [Full Payload API](./FULL_PAYLOAD_UPDATE.md) - `toFullPayload()` / `fromFullPayload()` documentation
|