nonebot-plugin-werewolf 1.0.6__tar.gz → 1.0.7__tar.gz
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.
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/PKG-INFO +63 -12
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/README.md +62 -10
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/__init__.py +1 -1
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/config.py +5 -1
- nonebot_plugin_werewolf-1.0.7/nonebot_plugin_werewolf/constant.py +124 -0
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/game.py +35 -19
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/matchers.py +3 -0
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/player.py +29 -40
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/player_set.py +8 -6
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/utils.py +16 -10
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/pyproject.toml +14 -2
- nonebot_plugin_werewolf-1.0.6/nonebot_plugin_werewolf/constant.py +0 -88
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/LICENSE +0 -0
- {nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/ob11_ext.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: nonebot-plugin-werewolf
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.7
|
4
4
|
Summary: Default template for PDM package
|
5
5
|
Author-Email: wyf7685 <wyf7685@163.com>
|
6
6
|
License: MIT
|
@@ -9,7 +9,6 @@ Requires-Dist: nonebot2>=2.3.3
|
|
9
9
|
Requires-Dist: nonebot-plugin-alconna>=0.52.1
|
10
10
|
Requires-Dist: nonebot-plugin-userinfo>=0.2.6
|
11
11
|
Requires-Dist: nonebot-plugin-waiter>=0.7.1
|
12
|
-
Requires-Dist: StrEnum>=0.4.15
|
13
12
|
Description-Content-Type: text/markdown
|
14
13
|
|
15
14
|
<div align="center">
|
@@ -89,10 +88,14 @@ _✨ 简单的狼人杀插件 ✨_
|
|
89
88
|
|
90
89
|
在 nonebot2 项目的`.env`文件中添加下表中的必填配置
|
91
90
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
| 配置项 | 必填 | 默认值 | 说明 |
|
92
|
+
| :-----------------------------: | :--: | :----: | :-----------------------------------------------------------: |
|
93
|
+
| `werewolf__enable_poke` | 否 | `True` | 是否使用戳一戳简化操作流程<br/>仅在 `OneBot V11` 适配器下生效 |
|
94
|
+
| `werewolf__role_preset` | 否 | - | 覆写插件内置的职业预设 |
|
95
|
+
| `werewolf__werewolf_priority` | 否 | - | 自定义狼人职业优先级 |
|
96
|
+
| `werewolf__priesthood_proirity` | 否 | - | 自定义神职职业优先级 |
|
97
|
+
|
98
|
+
`werewolf__role_preset`, `werewolf__werewolf_priority`, `werewolf__priesthood_proirity` 的配置格式请参考 [`游戏内容`](#游戏内容) 部分
|
96
99
|
|
97
100
|
## 🎉 使用
|
98
101
|
|
@@ -150,15 +153,15 @@ _其他交互参考游戏内提示_
|
|
150
153
|
| 11 | 3 | 5 | 3 |
|
151
154
|
| 12 | 4 | 5 | 3 |
|
152
155
|
|
153
|
-
职业预设可以通过配置项 `
|
156
|
+
职业预设可以通过配置项 `werewolf__role_preset` 修改
|
154
157
|
|
155
158
|
<details>
|
156
159
|
<summary>示例</summary>
|
157
160
|
|
158
|
-
配置项 `
|
161
|
+
配置项 `werewolf__role_preset`
|
159
162
|
|
160
163
|
```env
|
161
|
-
|
164
|
+
werewolf__role_preset='
|
162
165
|
[
|
163
166
|
[6, 1, 3, 2],
|
164
167
|
[7, 2, 3, 2]
|
@@ -171,16 +174,63 @@ werewolf__override_preset='
|
|
171
174
|
</details>
|
172
175
|
<br/>
|
173
176
|
|
174
|
-
|
177
|
+
对于`狼人`和`神职`的职业分配,默认有如下优先级:
|
175
178
|
|
176
179
|
- `狼人`: `狼人`, `狼人`, `狼王`, `狼人`
|
177
|
-
- `神职`: `预言家`,
|
180
|
+
- `神职`: `女巫`, `预言家`, `猎人`, `守卫`, `白痴`
|
181
|
+
|
182
|
+
职业分配优先级可以通过配置项 `werewolf__werewolf_priority` 和 `werewolf__priesthood_proirity` 修改
|
183
|
+
|
184
|
+
<details>
|
185
|
+
<summary>示例</summary>
|
186
|
+
|
187
|
+
#### 配置项 `werewolf__werewolf_priority`
|
188
|
+
|
189
|
+
```env
|
190
|
+
werewolf__werewolf_priority=[1, 2, 1, 1]
|
191
|
+
```
|
192
|
+
|
193
|
+
上述配置中,`[1, 2, 1, 1]` 表示狼人的职业优先级为 `狼人`, `狼王`, `狼人`, `狼人`
|
194
|
+
|
195
|
+
#### 配置项 `werewolf__werewolf_priority`
|
196
|
+
|
197
|
+
```env
|
198
|
+
werewolf__priesthood_proirity=[11, 12, 13, 14, 15]
|
199
|
+
```
|
200
|
+
|
201
|
+
上述配置中,`[11, 12, 13, 14, 15]` 表示神职的职业优先级为 `预言家`, `女巫`, `猎人`, `守卫`, `白痴`
|
202
|
+
|
203
|
+
#### 职业与数字的对应关系
|
204
|
+
|
205
|
+
上述配置示例中有大量~~意义不明的~~数字, 其对应的是 [`这里`](./nonebot_plugin_werewolf/constant.py) 的枚举类 `Role` 的值
|
206
|
+
|
207
|
+
以下列出目前的枚举值供参考
|
208
|
+
|
209
|
+
| 职业 | 枚举值 |
|
210
|
+
| -------- | ------ |
|
211
|
+
| `狼人` | `1` |
|
212
|
+
| `狼王` | `2` |
|
213
|
+
| `预言家` | `11` |
|
214
|
+
| `女巫` | `12` |
|
215
|
+
| `猎人` | `13` |
|
216
|
+
| `守卫` | `14` |
|
217
|
+
| `白痴` | `15` |
|
218
|
+
| `平民` | `0` |
|
219
|
+
|
220
|
+
</details>
|
178
221
|
|
179
222
|
## 📝 更新日志
|
180
223
|
|
181
224
|
<details>
|
182
225
|
<summary>更新日志</summary>
|
183
226
|
|
227
|
+
<!-- CHANGELOG -->
|
228
|
+
|
229
|
+
- 2024.09.04 v1.0.7
|
230
|
+
|
231
|
+
- 优先使用群名片作为玩家名
|
232
|
+
- 支持通过配置项修改职业分配优先级
|
233
|
+
|
184
234
|
- 2024.09.03 v1.0.6
|
185
235
|
|
186
236
|
- 修复预言家查验狼王返回好人的 bug
|
@@ -192,7 +242,7 @@ werewolf__override_preset='
|
|
192
242
|
|
193
243
|
- 2024.08.31 v1.0.1
|
194
244
|
|
195
|
-
-
|
245
|
+
- 支持通过配置项修改职业预设
|
196
246
|
|
197
247
|
- 2024.08.31 v1.0.0
|
198
248
|
|
@@ -206,3 +256,4 @@ werewolf__override_preset='
|
|
206
256
|
- [`nonebot/plugin-alconna`](https://github.com/nonebot/plugin-alconna): 跨平台的消息处理接口
|
207
257
|
- [`noneplugin/nonebot-plugin-userinfo`](https://github.com/noneplugin/nonebot-plugin-userinfo): 用户信息获取
|
208
258
|
- [`RF-Tar-Railt/nonebot-plugin-waiter`](https://github.com/RF-Tar-Railt/nonebot-plugin-waiter): 灵活获取用户输入
|
259
|
+
- `热心群友`: 协助测试插件
|
@@ -75,10 +75,14 @@ _✨ 简单的狼人杀插件 ✨_
|
|
75
75
|
|
76
76
|
在 nonebot2 项目的`.env`文件中添加下表中的必填配置
|
77
77
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
| 配置项 | 必填 | 默认值 | 说明 |
|
79
|
+
| :-----------------------------: | :--: | :----: | :-----------------------------------------------------------: |
|
80
|
+
| `werewolf__enable_poke` | 否 | `True` | 是否使用戳一戳简化操作流程<br/>仅在 `OneBot V11` 适配器下生效 |
|
81
|
+
| `werewolf__role_preset` | 否 | - | 覆写插件内置的职业预设 |
|
82
|
+
| `werewolf__werewolf_priority` | 否 | - | 自定义狼人职业优先级 |
|
83
|
+
| `werewolf__priesthood_proirity` | 否 | - | 自定义神职职业优先级 |
|
84
|
+
|
85
|
+
`werewolf__role_preset`, `werewolf__werewolf_priority`, `werewolf__priesthood_proirity` 的配置格式请参考 [`游戏内容`](#游戏内容) 部分
|
82
86
|
|
83
87
|
## 🎉 使用
|
84
88
|
|
@@ -136,15 +140,15 @@ _其他交互参考游戏内提示_
|
|
136
140
|
| 11 | 3 | 5 | 3 |
|
137
141
|
| 12 | 4 | 5 | 3 |
|
138
142
|
|
139
|
-
职业预设可以通过配置项 `
|
143
|
+
职业预设可以通过配置项 `werewolf__role_preset` 修改
|
140
144
|
|
141
145
|
<details>
|
142
146
|
<summary>示例</summary>
|
143
147
|
|
144
|
-
配置项 `
|
148
|
+
配置项 `werewolf__role_preset`
|
145
149
|
|
146
150
|
```env
|
147
|
-
|
151
|
+
werewolf__role_preset='
|
148
152
|
[
|
149
153
|
[6, 1, 3, 2],
|
150
154
|
[7, 2, 3, 2]
|
@@ -157,16 +161,63 @@ werewolf__override_preset='
|
|
157
161
|
</details>
|
158
162
|
<br/>
|
159
163
|
|
160
|
-
|
164
|
+
对于`狼人`和`神职`的职业分配,默认有如下优先级:
|
161
165
|
|
162
166
|
- `狼人`: `狼人`, `狼人`, `狼王`, `狼人`
|
163
|
-
- `神职`: `预言家`,
|
167
|
+
- `神职`: `女巫`, `预言家`, `猎人`, `守卫`, `白痴`
|
168
|
+
|
169
|
+
职业分配优先级可以通过配置项 `werewolf__werewolf_priority` 和 `werewolf__priesthood_proirity` 修改
|
170
|
+
|
171
|
+
<details>
|
172
|
+
<summary>示例</summary>
|
173
|
+
|
174
|
+
#### 配置项 `werewolf__werewolf_priority`
|
175
|
+
|
176
|
+
```env
|
177
|
+
werewolf__werewolf_priority=[1, 2, 1, 1]
|
178
|
+
```
|
179
|
+
|
180
|
+
上述配置中,`[1, 2, 1, 1]` 表示狼人的职业优先级为 `狼人`, `狼王`, `狼人`, `狼人`
|
181
|
+
|
182
|
+
#### 配置项 `werewolf__werewolf_priority`
|
183
|
+
|
184
|
+
```env
|
185
|
+
werewolf__priesthood_proirity=[11, 12, 13, 14, 15]
|
186
|
+
```
|
187
|
+
|
188
|
+
上述配置中,`[11, 12, 13, 14, 15]` 表示神职的职业优先级为 `预言家`, `女巫`, `猎人`, `守卫`, `白痴`
|
189
|
+
|
190
|
+
#### 职业与数字的对应关系
|
191
|
+
|
192
|
+
上述配置示例中有大量~~意义不明的~~数字, 其对应的是 [`这里`](./nonebot_plugin_werewolf/constant.py) 的枚举类 `Role` 的值
|
193
|
+
|
194
|
+
以下列出目前的枚举值供参考
|
195
|
+
|
196
|
+
| 职业 | 枚举值 |
|
197
|
+
| -------- | ------ |
|
198
|
+
| `狼人` | `1` |
|
199
|
+
| `狼王` | `2` |
|
200
|
+
| `预言家` | `11` |
|
201
|
+
| `女巫` | `12` |
|
202
|
+
| `猎人` | `13` |
|
203
|
+
| `守卫` | `14` |
|
204
|
+
| `白痴` | `15` |
|
205
|
+
| `平民` | `0` |
|
206
|
+
|
207
|
+
</details>
|
164
208
|
|
165
209
|
## 📝 更新日志
|
166
210
|
|
167
211
|
<details>
|
168
212
|
<summary>更新日志</summary>
|
169
213
|
|
214
|
+
<!-- CHANGELOG -->
|
215
|
+
|
216
|
+
- 2024.09.04 v1.0.7
|
217
|
+
|
218
|
+
- 优先使用群名片作为玩家名
|
219
|
+
- 支持通过配置项修改职业分配优先级
|
220
|
+
|
170
221
|
- 2024.09.03 v1.0.6
|
171
222
|
|
172
223
|
- 修复预言家查验狼王返回好人的 bug
|
@@ -178,7 +229,7 @@ werewolf__override_preset='
|
|
178
229
|
|
179
230
|
- 2024.08.31 v1.0.1
|
180
231
|
|
181
|
-
-
|
232
|
+
- 支持通过配置项修改职业预设
|
182
233
|
|
183
234
|
- 2024.08.31 v1.0.0
|
184
235
|
|
@@ -192,3 +243,4 @@ werewolf__override_preset='
|
|
192
243
|
- [`nonebot/plugin-alconna`](https://github.com/nonebot/plugin-alconna): 跨平台的消息处理接口
|
193
244
|
- [`noneplugin/nonebot-plugin-userinfo`](https://github.com/noneplugin/nonebot-plugin-userinfo): 用户信息获取
|
194
245
|
- [`RF-Tar-Railt/nonebot-plugin-waiter`](https://github.com/RF-Tar-Railt/nonebot-plugin-waiter): 灵活获取用户输入
|
246
|
+
- `热心群友`: 协助测试插件
|
{nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/config.py
RENAMED
@@ -1,10 +1,14 @@
|
|
1
1
|
from nonebot import get_plugin_config
|
2
2
|
from pydantic import BaseModel
|
3
3
|
|
4
|
+
from .constant import Role
|
5
|
+
|
4
6
|
|
5
7
|
class PluginConfig(BaseModel):
|
6
8
|
enable_poke: bool = True
|
7
|
-
|
9
|
+
role_preset: list[tuple[int, int, int, int]] | None = None
|
10
|
+
werewolf_priority: list[Role] | None = None
|
11
|
+
priesthood_proirity: list[Role] | None = None
|
8
12
|
|
9
13
|
|
10
14
|
class Config(BaseModel):
|
@@ -0,0 +1,124 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from enum import Enum, auto
|
5
|
+
from typing import TYPE_CHECKING
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from .player import Player
|
9
|
+
|
10
|
+
|
11
|
+
class Role(Enum):
|
12
|
+
# 狼人
|
13
|
+
Werewolf = 1
|
14
|
+
WolfKing = 2
|
15
|
+
|
16
|
+
# 神职
|
17
|
+
Prophet = 11
|
18
|
+
Witch = 12
|
19
|
+
Hunter = 13
|
20
|
+
Guard = 14
|
21
|
+
Idiot = 15
|
22
|
+
|
23
|
+
# 平民
|
24
|
+
Civilian = 0
|
25
|
+
|
26
|
+
|
27
|
+
class RoleGroup(Enum):
|
28
|
+
Werewolf = auto()
|
29
|
+
GoodGuy = auto()
|
30
|
+
|
31
|
+
|
32
|
+
class KillReason(Enum):
|
33
|
+
Kill = auto()
|
34
|
+
Poison = auto()
|
35
|
+
Shoot = auto()
|
36
|
+
Vote = auto()
|
37
|
+
|
38
|
+
|
39
|
+
class GameStatus(Enum):
|
40
|
+
Good = auto()
|
41
|
+
Bad = auto()
|
42
|
+
Unset = auto()
|
43
|
+
|
44
|
+
|
45
|
+
@dataclass
|
46
|
+
class GameState:
|
47
|
+
day: int
|
48
|
+
killed: Player | None = None
|
49
|
+
shoot: tuple[Player, Player] | tuple[None, None] = (None, None)
|
50
|
+
protected: Player | None = None
|
51
|
+
potion: tuple[Player | None, tuple[bool, bool]] = (None, (False, False))
|
52
|
+
|
53
|
+
|
54
|
+
role_name_conv: dict[Role | RoleGroup, str] = {
|
55
|
+
Role.Werewolf: "狼人",
|
56
|
+
Role.WolfKing: "狼王",
|
57
|
+
Role.Prophet: "预言家",
|
58
|
+
Role.Witch: "女巫",
|
59
|
+
Role.Hunter: "猎人",
|
60
|
+
Role.Guard: "守卫",
|
61
|
+
Role.Idiot: "白痴",
|
62
|
+
Role.Civilian: "平民",
|
63
|
+
RoleGroup.Werewolf: "狼人",
|
64
|
+
RoleGroup.GoodGuy: "好人",
|
65
|
+
}
|
66
|
+
|
67
|
+
role_preset: dict[int, tuple[int, int, int]] = {
|
68
|
+
# 总人数: (狼, 神, 民)
|
69
|
+
6: (1, 2, 3),
|
70
|
+
7: (2, 2, 3),
|
71
|
+
8: (2, 3, 3),
|
72
|
+
9: (2, 4, 3),
|
73
|
+
10: (3, 4, 3),
|
74
|
+
11: (3, 5, 3),
|
75
|
+
12: (4, 5, 3),
|
76
|
+
}
|
77
|
+
|
78
|
+
werewolf_priority: list[Role] = [
|
79
|
+
Role.Werewolf,
|
80
|
+
Role.Werewolf,
|
81
|
+
Role.WolfKing,
|
82
|
+
Role.Werewolf,
|
83
|
+
]
|
84
|
+
priesthood_proirity: list[Role] = [
|
85
|
+
Role.Witch,
|
86
|
+
Role.Prophet,
|
87
|
+
Role.Hunter,
|
88
|
+
Role.Guard,
|
89
|
+
Role.Idiot,
|
90
|
+
]
|
91
|
+
|
92
|
+
|
93
|
+
def __apply_config():
|
94
|
+
from .config import config
|
95
|
+
|
96
|
+
global role_preset, werewolf_priority, priesthood_proirity
|
97
|
+
|
98
|
+
if config.role_preset is not None:
|
99
|
+
for preset in config.role_preset:
|
100
|
+
if preset[0] != preset[1:]:
|
101
|
+
raise RuntimeError(
|
102
|
+
"配置项 `role_preset` 错误: "
|
103
|
+
f"预设总人数为 {preset[0]}, 实际总人数为 {sum(preset[1:])}"
|
104
|
+
)
|
105
|
+
role_preset |= {i[0]: i[1:] for i in config.role_preset}
|
106
|
+
|
107
|
+
if (priority := config.werewolf_priority) is not None:
|
108
|
+
min_length = max(i[0] for i in role_preset.values())
|
109
|
+
if len(priority) < min_length:
|
110
|
+
raise RuntimeError(
|
111
|
+
f"配置项 `werewolf_priority` 错误: 应至少为 {min_length} 项"
|
112
|
+
)
|
113
|
+
werewolf_priority = priority
|
114
|
+
|
115
|
+
if (priority := config.priesthood_proirity) is not None:
|
116
|
+
min_length = max(i[1] for i in role_preset.values())
|
117
|
+
if len(priority) < min_length:
|
118
|
+
raise RuntimeError(
|
119
|
+
f"配置项 `priesthood_proirity` 错误: 应至少为 {min_length} 项"
|
120
|
+
)
|
121
|
+
priesthood_proirity = priority
|
122
|
+
|
123
|
+
|
124
|
+
__apply_config()
|
{nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/game.py
RENAMED
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import asyncio
|
2
4
|
import asyncio.timeouts
|
3
5
|
import contextlib
|
@@ -8,30 +10,35 @@ from nonebot.adapters import Bot
|
|
8
10
|
from nonebot.log import logger
|
9
11
|
from nonebot_plugin_alconna import Target, UniMessage
|
10
12
|
|
11
|
-
from .constant import
|
13
|
+
from .constant import (
|
14
|
+
GameState,
|
15
|
+
GameStatus,
|
16
|
+
KillReason,
|
17
|
+
Role,
|
18
|
+
RoleGroup,
|
19
|
+
priesthood_proirity,
|
20
|
+
role_preset,
|
21
|
+
werewolf_priority,
|
22
|
+
)
|
12
23
|
from .player import Player
|
13
24
|
from .player_set import PlayerSet
|
14
25
|
from .utils import InputStore
|
15
26
|
|
16
27
|
starting_games: dict[str, dict[str, str]] = {}
|
17
|
-
running_games: dict[str,
|
28
|
+
running_games: dict[str, Game] = {}
|
18
29
|
|
19
30
|
|
20
|
-
def init_players(bot: Bot, game:
|
21
|
-
preset =
|
31
|
+
def init_players(bot: Bot, game: Game, players: dict[str, str]) -> PlayerSet:
|
32
|
+
preset = role_preset.get(len(players))
|
22
33
|
if preset is None:
|
23
34
|
raise ValueError(
|
24
35
|
f"玩家人数不符: "
|
25
|
-
f"应为 {', '.join(map(str,
|
36
|
+
f"应为 {', '.join(map(str, role_preset))} 人, 传入{len(players)}人"
|
26
37
|
)
|
27
38
|
|
28
39
|
roles: list[Role] = []
|
29
|
-
roles.extend(
|
30
|
-
|
31
|
-
)
|
32
|
-
roles.extend(
|
33
|
-
[Role.Prophet, Role.Witch, Role.Hunter, Role.Guard, Role.Idiot][: preset[1]]
|
34
|
-
)
|
40
|
+
roles.extend(werewolf_priority[: preset[0]])
|
41
|
+
roles.extend(priesthood_proirity[: preset[1]])
|
35
42
|
roles.extend([Role.Civilian] * preset[2])
|
36
43
|
|
37
44
|
r = random.Random(time.time())
|
@@ -130,7 +137,7 @@ class Game:
|
|
130
137
|
return msg.strip()
|
131
138
|
|
132
139
|
async def notify_player_role(self) -> None:
|
133
|
-
preset =
|
140
|
+
preset = role_preset[len(self.players)]
|
134
141
|
await asyncio.gather(
|
135
142
|
self.send(
|
136
143
|
self.at_all()
|
@@ -412,23 +419,32 @@ class Game:
|
|
412
419
|
await self.send(f"玩家死亡报告:\n\n{self.show_killed_players()}")
|
413
420
|
|
414
421
|
def start(self):
|
415
|
-
|
422
|
+
event = asyncio.Event()
|
423
|
+
game_task = asyncio.create_task(self.run())
|
424
|
+
game_task.add_done_callback(lambda _: event.set())
|
416
425
|
dead_channel = asyncio.create_task(self.run_dead_channel())
|
417
426
|
|
418
427
|
async def daemon():
|
419
|
-
|
420
|
-
await asyncio.sleep(1)
|
428
|
+
await event.wait()
|
421
429
|
|
422
430
|
try:
|
423
|
-
|
431
|
+
game_task.result()
|
432
|
+
logger.info(f"{self.group.id} 的狼人杀游戏进程正常退出")
|
424
433
|
except asyncio.CancelledError as err:
|
425
|
-
logger.warning(f"
|
434
|
+
logger.warning(f"{self.group.id} 的狼人杀游戏进程被取消: {err}")
|
426
435
|
except Exception as err:
|
427
|
-
msg = f"
|
436
|
+
msg = f"{self.group.id} 的狼人杀游戏进程出现错误: {err!r}"
|
428
437
|
logger.opt(exception=err).error(msg)
|
429
438
|
await self.send(msg)
|
430
439
|
finally:
|
431
440
|
dead_channel.cancel()
|
432
441
|
running_games.pop(self.group.id, None)
|
433
442
|
|
434
|
-
|
443
|
+
def daemon_callback(task: asyncio.Task[None]):
|
444
|
+
if err := task.exception():
|
445
|
+
logger.opt(exception=err).error(
|
446
|
+
f"{self.group.id} 的狼人杀守护进程出现错误: {err!r}"
|
447
|
+
)
|
448
|
+
|
449
|
+
running_games[self.group.id] = self
|
450
|
+
asyncio.create_task(daemon()).add_done_callback(daemon_callback)
|
{nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/matchers.py
RENAMED
@@ -4,6 +4,7 @@ from typing import Annotated
|
|
4
4
|
|
5
5
|
from nonebot import on_command, on_message
|
6
6
|
from nonebot.adapters import Bot, Event
|
7
|
+
from nonebot.exception import FinishedException
|
7
8
|
from nonebot.rule import to_me
|
8
9
|
from nonebot_plugin_alconna import MsgTarget, UniMessage, UniMsg
|
9
10
|
from nonebot_plugin_userinfo import EventUserInfo, UserInfo
|
@@ -56,6 +57,8 @@ async def handle_start(
|
|
56
57
|
try:
|
57
58
|
async with asyncio.timeouts.timeout(5 * 60):
|
58
59
|
await prepare_game(event, players)
|
60
|
+
except FinishedException:
|
61
|
+
raise
|
59
62
|
except TimeoutError:
|
60
63
|
await UniMessage.text("游戏准备超时,已自动结束").finish()
|
61
64
|
finally:
|
{nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/player.py
RENAMED
@@ -9,7 +9,7 @@ from typing_extensions import override
|
|
9
9
|
from nonebot.adapters import Bot
|
10
10
|
from nonebot_plugin_alconna.uniseg import Receipt, Target, UniMessage
|
11
11
|
|
12
|
-
from .constant import KillReason, Role, RoleGroup, role_name_conv
|
12
|
+
from .constant import KillReason, Role, RoleGroup, role_name_conv
|
13
13
|
from .utils import InputStore, check_index
|
14
14
|
|
15
15
|
if TYPE_CHECKING:
|
@@ -21,9 +21,14 @@ P = TypeVar("P", bound=type["Player"])
|
|
21
21
|
PLAYER_CLASS: dict[Role, type[Player]] = {}
|
22
22
|
|
23
23
|
|
24
|
-
def register_role(
|
25
|
-
|
26
|
-
|
24
|
+
def register_role(role: Role, role_group: RoleGroup, /):
|
25
|
+
def decorator(cls: P, /) -> P:
|
26
|
+
cls.role = role
|
27
|
+
cls.role_group = role_group
|
28
|
+
PLAYER_CLASS[role] = cls
|
29
|
+
return cls
|
30
|
+
|
31
|
+
return decorator
|
27
32
|
|
28
33
|
|
29
34
|
@dataclass
|
@@ -190,11 +195,8 @@ class CanShoot(Player):
|
|
190
195
|
return players[selected]
|
191
196
|
|
192
197
|
|
193
|
-
@register_role
|
194
|
-
class
|
195
|
-
role: ClassVar[Role] = Role.Werewolf
|
196
|
-
role_group: ClassVar[RoleGroup] = RoleGroup.Werewolf
|
197
|
-
|
198
|
+
@register_role(Role.Werewolf, RoleGroup.Werewolf)
|
199
|
+
class Werewolf(Player):
|
198
200
|
@override
|
199
201
|
async def notify_role(self) -> None:
|
200
202
|
await super().notify_role()
|
@@ -252,17 +254,13 @@ class 狼人(Player):
|
|
252
254
|
self.selected = players[selected]
|
253
255
|
|
254
256
|
|
255
|
-
@register_role
|
256
|
-
class
|
257
|
-
|
258
|
-
role_group: ClassVar[RoleGroup] = RoleGroup.Werewolf
|
257
|
+
@register_role(Role.WolfKing, RoleGroup.Werewolf)
|
258
|
+
class WolfKing(CanShoot, Werewolf):
|
259
|
+
pass
|
259
260
|
|
260
261
|
|
261
|
-
@register_role
|
262
|
-
class
|
263
|
-
role: ClassVar[Role] = Role.Prophet
|
264
|
-
role_group: ClassVar[RoleGroup] = RoleGroup.GoodGuy
|
265
|
-
|
262
|
+
@register_role(Role.Prophet, RoleGroup.GoodGuy)
|
263
|
+
class Prophet(Player):
|
266
264
|
@override
|
267
265
|
async def interact(self) -> None:
|
268
266
|
players = self.game.players.alive().exclude(self)
|
@@ -281,14 +279,12 @@ class 预言家(Player):
|
|
281
279
|
await self.send("输入错误,请发送编号选择玩家")
|
282
280
|
|
283
281
|
player = players[selected]
|
284
|
-
result =
|
282
|
+
result = role_name_conv[player.role_group]
|
285
283
|
await self.send(f"玩家 {player.name} 的阵营是『{result}』")
|
286
284
|
|
287
285
|
|
288
|
-
@register_role
|
289
|
-
class
|
290
|
-
role: ClassVar[Role] = Role.Witch
|
291
|
-
role_group: ClassVar[RoleGroup] = RoleGroup.GoodGuy
|
286
|
+
@register_role(Role.Witch, RoleGroup.GoodGuy)
|
287
|
+
class Witch(Player):
|
292
288
|
antidote: int = 1
|
293
289
|
poison: int = 1
|
294
290
|
|
@@ -374,17 +370,13 @@ class 女巫(Player):
|
|
374
370
|
await self.send(f"当前回合选择对玩家 {player.name} 使用毒药\n回合结束")
|
375
371
|
|
376
372
|
|
377
|
-
@register_role
|
378
|
-
class
|
379
|
-
|
380
|
-
role_group: ClassVar[RoleGroup] = RoleGroup.GoodGuy
|
381
|
-
|
373
|
+
@register_role(Role.Hunter, RoleGroup.GoodGuy)
|
374
|
+
class Hunter(CanShoot, Player):
|
375
|
+
pass
|
382
376
|
|
383
|
-
@register_role
|
384
|
-
class 守卫(Player):
|
385
|
-
role: ClassVar[Role] = Role.Guard
|
386
|
-
role_group: ClassVar[RoleGroup] = RoleGroup.GoodGuy
|
387
377
|
|
378
|
+
@register_role(Role.Guard, RoleGroup.GoodGuy)
|
379
|
+
class Guard(Player):
|
388
380
|
@override
|
389
381
|
async def interact(self) -> None:
|
390
382
|
players = self.game.players.alive().exclude(self)
|
@@ -412,10 +404,8 @@ class 守卫(Player):
|
|
412
404
|
await self.send(f"本回合保护的玩家: {self.selected.name}")
|
413
405
|
|
414
406
|
|
415
|
-
@register_role
|
416
|
-
class
|
417
|
-
role: ClassVar[Role] = Role.Idiot
|
418
|
-
role_group: ClassVar[RoleGroup] = RoleGroup.GoodGuy
|
407
|
+
@register_role(Role.Idiot, RoleGroup.GoodGuy)
|
408
|
+
class Idiot(Player):
|
419
409
|
voted: bool = False
|
420
410
|
|
421
411
|
@override
|
@@ -442,7 +432,6 @@ class 白痴(Player):
|
|
442
432
|
return await super().vote(players)
|
443
433
|
|
444
434
|
|
445
|
-
@register_role
|
446
|
-
class
|
447
|
-
|
448
|
-
role_group: ClassVar[RoleGroup] = RoleGroup.GoodGuy
|
435
|
+
@register_role(Role.Civilian, RoleGroup.GoodGuy)
|
436
|
+
class Civilian(Player):
|
437
|
+
pass
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import asyncio
|
2
4
|
import asyncio.timeouts
|
3
5
|
|
@@ -12,23 +14,23 @@ class PlayerSet(set[Player]):
|
|
12
14
|
def size(self) -> int:
|
13
15
|
return len(self)
|
14
16
|
|
15
|
-
def alive(self) ->
|
17
|
+
def alive(self) -> PlayerSet:
|
16
18
|
return PlayerSet(p for p in self if p.alive)
|
17
19
|
|
18
|
-
def dead(self) ->
|
20
|
+
def dead(self) -> PlayerSet:
|
19
21
|
return PlayerSet(p for p in self if not p.alive)
|
20
22
|
|
21
|
-
def include(self, *types: Player | Role | RoleGroup) ->
|
23
|
+
def include(self, *types: Player | Role | RoleGroup) -> PlayerSet:
|
22
24
|
return PlayerSet(
|
23
25
|
player
|
24
26
|
for player in self
|
25
27
|
if (player in types or player.role in types or player.role_group in types)
|
26
28
|
)
|
27
29
|
|
28
|
-
def select(self, *types: Player | Role | RoleGroup) ->
|
30
|
+
def select(self, *types: Player | Role | RoleGroup) -> PlayerSet:
|
29
31
|
return self.include(*types)
|
30
32
|
|
31
|
-
def exclude(self, *types: Player | Role | RoleGroup) ->
|
33
|
+
def exclude(self, *types: Player | Role | RoleGroup) -> PlayerSet:
|
32
34
|
return PlayerSet(
|
33
35
|
player
|
34
36
|
for player in self
|
@@ -39,7 +41,7 @@ class PlayerSet(set[Player]):
|
|
39
41
|
)
|
40
42
|
)
|
41
43
|
|
42
|
-
def player_selected(self) ->
|
44
|
+
def player_selected(self) -> PlayerSet:
|
43
45
|
return PlayerSet(p.selected for p in self.alive() if p.selected is not None)
|
44
46
|
|
45
47
|
def sorted(self) -> list[Player]:
|
{nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/utils.py
RENAMED
@@ -1,5 +1,6 @@
|
|
1
1
|
import asyncio
|
2
2
|
import asyncio.timeouts
|
3
|
+
import re
|
3
4
|
from collections import defaultdict
|
4
5
|
from typing import Annotated, Any, ClassVar
|
5
6
|
|
@@ -9,7 +10,7 @@ from nonebot.rule import to_me
|
|
9
10
|
from nonebot_plugin_alconna import MsgTarget, UniMessage, UniMsg
|
10
11
|
from nonebot_plugin_userinfo import EventUserInfo, UserInfo
|
11
12
|
|
12
|
-
from .constant import
|
13
|
+
from .constant import role_preset
|
13
14
|
|
14
15
|
|
15
16
|
def check_index(text: str, arrlen: int) -> int | None:
|
@@ -47,7 +48,7 @@ def user_in_game(user_id: str, group_id: str | None) -> bool:
|
|
47
48
|
if group_id is not None and group_id not in running_games:
|
48
49
|
return False
|
49
50
|
games = running_games.values() if group_id is None else [running_games[group_id]]
|
50
|
-
for game
|
51
|
+
for game in games:
|
51
52
|
return any(user_id == player.user_id for player in game.players)
|
52
53
|
return False
|
53
54
|
|
@@ -92,13 +93,18 @@ async def _prepare_game_receive(
|
|
92
93
|
) -> tuple[str, str, str]:
|
93
94
|
return (
|
94
95
|
event.get_user_id(),
|
95
|
-
|
96
|
+
(
|
97
|
+
(info.user_displayname or info.user_name)
|
98
|
+
if info is not None
|
99
|
+
else event.get_user_id()
|
100
|
+
),
|
96
101
|
msg.extract_plain_text().strip(),
|
97
102
|
)
|
98
103
|
|
99
104
|
async for user, name, text in wait(default=(None, "", "")):
|
100
105
|
if user is None:
|
101
106
|
continue
|
107
|
+
name = re.sub(r"[\u2066-\u2069]", "", name)
|
102
108
|
await queue.put((user, name, text))
|
103
109
|
|
104
110
|
|
@@ -114,19 +120,19 @@ async def _prepare_game_handle(
|
|
114
120
|
match (text, user == admin_id):
|
115
121
|
case ("开始游戏", True):
|
116
122
|
player_num = len(players)
|
117
|
-
if player_num < min(
|
123
|
+
if player_num < min(role_preset):
|
118
124
|
await (
|
119
|
-
msg.text(f"游戏至少需要 {min(
|
125
|
+
msg.text(f"游戏至少需要 {min(role_preset)} 人, ")
|
120
126
|
.text(f"当前已有 {player_num} 人")
|
121
127
|
.send()
|
122
128
|
)
|
123
|
-
elif player_num > max(
|
129
|
+
elif player_num > max(role_preset):
|
124
130
|
await (
|
125
|
-
msg.text(f"游戏最多需要 {max(
|
131
|
+
msg.text(f"游戏最多需要 {max(role_preset)} 人, ")
|
126
132
|
.text(f"当前已有 {player_num} 人")
|
127
133
|
.send()
|
128
134
|
)
|
129
|
-
elif player_num not in
|
135
|
+
elif player_num not in role_preset:
|
130
136
|
await (
|
131
137
|
msg.text(f"不存在总人数为 {player_num} 的预设, ")
|
132
138
|
.text("无法开始游戏")
|
@@ -167,8 +173,8 @@ async def _prepare_game_handle(
|
|
167
173
|
|
168
174
|
case ("当前玩家", _):
|
169
175
|
msg.text("\n当前玩家:\n")
|
170
|
-
for name in players.values():
|
171
|
-
msg.text(f"\n{name}")
|
176
|
+
for idx, name in enumerate(players.values(), 1):
|
177
|
+
msg.text(f"\n{idx}. {name}")
|
172
178
|
await msg.send()
|
173
179
|
|
174
180
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "nonebot-plugin-werewolf"
|
3
|
-
version = "1.0.
|
3
|
+
version = "1.0.7"
|
4
4
|
description = "Default template for PDM package"
|
5
5
|
authors = [
|
6
6
|
{ name = "wyf7685", email = "wyf7685@163.com" },
|
@@ -10,7 +10,6 @@ dependencies = [
|
|
10
10
|
"nonebot-plugin-alconna>=0.52.1",
|
11
11
|
"nonebot-plugin-userinfo>=0.2.6",
|
12
12
|
"nonebot-plugin-waiter>=0.7.1",
|
13
|
-
"StrEnum>=0.4.15",
|
14
13
|
]
|
15
14
|
requires-python = ">=3.10"
|
16
15
|
readme = "README.md"
|
@@ -27,6 +26,9 @@ build-backend = "pdm.backend"
|
|
27
26
|
[tool.pdm]
|
28
27
|
distribution = true
|
29
28
|
|
29
|
+
[tool.pdm.scripts]
|
30
|
+
updver = "pdm run python ./scripts/update_version.py"
|
31
|
+
|
30
32
|
[tool.pdm.scripts.lint]
|
31
33
|
composite = [
|
32
34
|
"isort .",
|
@@ -97,3 +99,13 @@ pythonPlatform = "All"
|
|
97
99
|
typeCheckingMode = "standard"
|
98
100
|
reportShadowedImports = false
|
99
101
|
disableBytesTypePromotions = true
|
102
|
+
|
103
|
+
[tool.nonebot]
|
104
|
+
adapters = [
|
105
|
+
{ name = "OneBot V11", module_name = "nonebot.adapters.onebot.v11" },
|
106
|
+
]
|
107
|
+
plugins = [
|
108
|
+
"nonebot_plugin_werewolf",
|
109
|
+
]
|
110
|
+
plugin_dirs = []
|
111
|
+
builtin_plugins = []
|
@@ -1,88 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from dataclasses import dataclass
|
4
|
-
from enum import Enum, auto
|
5
|
-
from typing import TYPE_CHECKING
|
6
|
-
|
7
|
-
from strenum import StrEnum
|
8
|
-
|
9
|
-
from .config import config
|
10
|
-
|
11
|
-
if TYPE_CHECKING:
|
12
|
-
from .player import Player
|
13
|
-
|
14
|
-
|
15
|
-
class Role(StrEnum):
|
16
|
-
# 狼人
|
17
|
-
Werewolf = auto()
|
18
|
-
WolfKing = auto()
|
19
|
-
|
20
|
-
# 神职
|
21
|
-
Prophet = auto()
|
22
|
-
Witch = auto()
|
23
|
-
Hunter = auto()
|
24
|
-
Guard = auto()
|
25
|
-
Idiot = auto()
|
26
|
-
|
27
|
-
# 平民
|
28
|
-
Civilian = auto()
|
29
|
-
|
30
|
-
|
31
|
-
class RoleGroup(StrEnum):
|
32
|
-
Werewolf = auto()
|
33
|
-
GoodGuy = auto()
|
34
|
-
|
35
|
-
|
36
|
-
class KillReason(Enum):
|
37
|
-
Kill = auto()
|
38
|
-
Poison = auto()
|
39
|
-
Shoot = auto()
|
40
|
-
Vote = auto()
|
41
|
-
|
42
|
-
|
43
|
-
class GameStatus(Enum):
|
44
|
-
Good = auto()
|
45
|
-
Bad = auto()
|
46
|
-
Unset = auto()
|
47
|
-
|
48
|
-
|
49
|
-
@dataclass
|
50
|
-
class GameState:
|
51
|
-
day: int
|
52
|
-
killed: Player | None = None
|
53
|
-
shoot: tuple[Player, Player] | tuple[None, None] = (None, None)
|
54
|
-
protected: Player | None = None
|
55
|
-
potion: tuple[Player | None, tuple[bool, bool]] = (None, (False, False))
|
56
|
-
|
57
|
-
|
58
|
-
role_name_conv: dict[Role, str] = {
|
59
|
-
Role.Werewolf: "狼人",
|
60
|
-
Role.WolfKing: "狼王",
|
61
|
-
Role.Prophet: "预言家",
|
62
|
-
Role.Witch: "女巫",
|
63
|
-
Role.Hunter: "猎人",
|
64
|
-
Role.Guard: "守卫",
|
65
|
-
Role.Idiot: "白痴",
|
66
|
-
Role.Civilian: "平民",
|
67
|
-
}
|
68
|
-
|
69
|
-
|
70
|
-
role_group_name_conv: dict[RoleGroup, str] = {
|
71
|
-
RoleGroup.Werewolf: "狼人",
|
72
|
-
RoleGroup.GoodGuy: "好人",
|
73
|
-
}
|
74
|
-
|
75
|
-
player_preset: dict[int, tuple[int, int, int]] = {
|
76
|
-
# 总人数: (狼, 神, 民)
|
77
|
-
6: (1, 2, 3),
|
78
|
-
7: (2, 2, 3),
|
79
|
-
8: (2, 3, 3),
|
80
|
-
9: (2, 4, 3),
|
81
|
-
10: (3, 4, 3),
|
82
|
-
11: (3, 5, 3),
|
83
|
-
12: (4, 5, 3),
|
84
|
-
}
|
85
|
-
|
86
|
-
if config.override_preset is not None:
|
87
|
-
player_preset |= {i[0]: i[1:] for i in config.override_preset}
|
88
|
-
player_preset |= {i[0]: i[1:] for i in config.override_preset}
|
File without changes
|
{nonebot_plugin_werewolf-1.0.6 → nonebot_plugin_werewolf-1.0.7}/nonebot_plugin_werewolf/ob11_ext.py
RENAMED
File without changes
|