nonebot-plugin-werewolf 1.1.7__py3-none-any.whl → 1.1.8__py3-none-any.whl
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/__init__.py +1 -1
- nonebot_plugin_werewolf/config.py +73 -15
- nonebot_plugin_werewolf/constant.py +54 -46
- nonebot_plugin_werewolf/exception.py +2 -4
- nonebot_plugin_werewolf/game.py +154 -136
- nonebot_plugin_werewolf/matchers/__init__.py +1 -0
- nonebot_plugin_werewolf/matchers/depends.py +4 -4
- nonebot_plugin_werewolf/matchers/edit_behavior.py +205 -0
- nonebot_plugin_werewolf/matchers/edit_preset.py +11 -11
- nonebot_plugin_werewolf/matchers/message_in_game.py +3 -1
- nonebot_plugin_werewolf/matchers/poke/chronocat_poke.py +8 -5
- nonebot_plugin_werewolf/matchers/poke/ob11_poke.py +3 -3
- nonebot_plugin_werewolf/matchers/start_game.py +213 -175
- nonebot_plugin_werewolf/matchers/superuser_ops.py +3 -3
- nonebot_plugin_werewolf/models.py +31 -19
- nonebot_plugin_werewolf/player_set.py +10 -8
- nonebot_plugin_werewolf/players/__init__.py +1 -1
- nonebot_plugin_werewolf/players/can_shoot.py +15 -15
- nonebot_plugin_werewolf/players/civilian.py +1 -1
- nonebot_plugin_werewolf/players/guard.py +16 -14
- nonebot_plugin_werewolf/players/hunter.py +1 -1
- nonebot_plugin_werewolf/players/idiot.py +3 -3
- nonebot_plugin_werewolf/players/{joker.py → jester.py} +4 -5
- nonebot_plugin_werewolf/players/player.py +90 -28
- nonebot_plugin_werewolf/players/prophet.py +11 -10
- nonebot_plugin_werewolf/players/werewolf.py +46 -11
- nonebot_plugin_werewolf/players/witch.py +29 -12
- nonebot_plugin_werewolf/players/wolfking.py +1 -1
- nonebot_plugin_werewolf/utils.py +105 -6
- {nonebot_plugin_werewolf-1.1.7.dist-info → nonebot_plugin_werewolf-1.1.8.dist-info}/METADATA +23 -20
- nonebot_plugin_werewolf-1.1.8.dist-info/RECORD +35 -0
- {nonebot_plugin_werewolf-1.1.7.dist-info → nonebot_plugin_werewolf-1.1.8.dist-info}/WHEEL +1 -1
- nonebot_plugin_werewolf-1.1.7.dist-info/RECORD +0 -34
- {nonebot_plugin_werewolf-1.1.7.dist-info → nonebot_plugin_werewolf-1.1.8.dist-info}/LICENSE +0 -0
- {nonebot_plugin_werewolf-1.1.7.dist-info → nonebot_plugin_werewolf-1.1.8.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,8 @@
|
|
1
|
-
from nonebot_plugin_alconna.uniseg import UniMessage
|
2
1
|
from typing_extensions import override
|
3
2
|
|
4
|
-
from
|
3
|
+
from nonebot_plugin_alconna.uniseg import UniMessage
|
4
|
+
|
5
|
+
from ..constant import stop_command_prompt
|
5
6
|
from ..models import KillReason
|
6
7
|
from .player import Player
|
7
8
|
|
@@ -9,7 +10,7 @@ from .player import Player
|
|
9
10
|
class CanShoot(Player):
|
10
11
|
@override
|
11
12
|
async def post_kill(self) -> None:
|
12
|
-
if self.kill_info and self.kill_info.reason == KillReason.
|
13
|
+
if self.kill_info and self.kill_info.reason == KillReason.POISON:
|
13
14
|
await self.send("⚠️你昨晚被女巫毒杀,无法使用技能")
|
14
15
|
return await super().post_kill()
|
15
16
|
|
@@ -21,33 +22,32 @@ class CanShoot(Player):
|
|
21
22
|
|
22
23
|
self.game.state.shoot = None
|
23
24
|
shoot = await self.shoot()
|
24
|
-
|
25
|
+
msg = UniMessage.text("玩家 ").at(self.user_id).text(" ")
|
25
26
|
if shoot is not None:
|
26
27
|
self.game.state.shoot = self
|
27
|
-
await self.send(
|
28
|
-
|
29
|
-
.at(self.user_id)
|
30
|
-
.text(" 射杀了玩家 ")
|
31
|
-
.at(shoot.user_id)
|
32
|
-
)
|
33
|
-
await shoot.kill(KillReason.Shoot, self)
|
28
|
+
await self.game.send("🔫" + msg.text("射杀了玩家 ").at(shoot.user_id))
|
29
|
+
await shoot.kill(KillReason.SHOOT, self)
|
34
30
|
self.selected = shoot
|
35
31
|
else:
|
36
|
-
await self.send(
|
32
|
+
await self.game.send("ℹ️" + msg.text("选择了取消技能"))
|
33
|
+
|
37
34
|
return await super().post_kill()
|
38
35
|
|
39
36
|
async def shoot(self) -> Player | None:
|
40
37
|
players = self.game.players.alive().exclude(self)
|
41
38
|
await self.send(
|
42
39
|
"💫请选择需要射杀的玩家:\n"
|
43
|
-
|
44
|
-
|
45
|
-
|
40
|
+
f"{players.show()}\n\n"
|
41
|
+
"🔫发送编号选择玩家\n"
|
42
|
+
f"❌发送 “{stop_command_prompt()}” 取消技能",
|
43
|
+
stop_btn_label="取消技能",
|
44
|
+
select_players=players,
|
46
45
|
)
|
47
46
|
|
48
47
|
if selected := await self._select_player(
|
49
48
|
players,
|
50
49
|
on_stop="ℹ️已取消技能,回合结束",
|
50
|
+
stop_btn_label="取消技能",
|
51
51
|
):
|
52
52
|
await self.send(f"🎯选择射杀的玩家: {selected.name}")
|
53
53
|
|
@@ -1,31 +1,33 @@
|
|
1
|
-
from nonebot_plugin_alconna.uniseg import UniMessage
|
2
1
|
from typing_extensions import override
|
3
2
|
|
4
|
-
from ..constant import
|
5
|
-
from ..models import Role, RoleGroup
|
3
|
+
from ..constant import stop_command_prompt
|
4
|
+
from ..models import GameState, Role, RoleGroup
|
6
5
|
from .player import Player
|
7
6
|
|
8
7
|
|
9
|
-
@Player.register_role(Role.
|
8
|
+
@Player.register_role(Role.GUARD, RoleGroup.GOODGUY)
|
10
9
|
class Guard(Player):
|
11
10
|
@override
|
12
11
|
async def _check_selected(self, player: Player) -> Player | None:
|
13
|
-
if player is
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
if self.game.state.state == GameState.State.NIGHT and player is self.selected:
|
13
|
+
await self.send("⚠️守卫不能连续两晚保护同一目标,请重新选择")
|
14
|
+
return None
|
15
|
+
|
16
|
+
return player
|
17
17
|
|
18
18
|
@override
|
19
|
-
async def
|
19
|
+
async def _interact(self) -> None:
|
20
20
|
players = self.game.players.alive()
|
21
21
|
await self.send(
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
"💫请选择需要保护的玩家:\n"
|
23
|
+
f"{players.show()}\n\n"
|
24
|
+
"🛡️发送编号选择玩家\n"
|
25
|
+
f"❌发送 “{stop_command_prompt()}” 结束回合",
|
26
|
+
stop_btn_label="结束回合",
|
27
|
+
select_players=players,
|
26
28
|
)
|
27
29
|
|
28
|
-
self.selected = await self._select_player(players)
|
30
|
+
self.selected = await self._select_player(players, stop_btn_label="结束回合")
|
29
31
|
if self.selected:
|
30
32
|
self.game.state.protected.add(self.selected)
|
31
33
|
await self.send(f"✅本回合保护的玩家: {self.selected.name}")
|
@@ -1,9 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from typing import TYPE_CHECKING
|
4
|
+
from typing_extensions import override
|
4
5
|
|
5
6
|
from nonebot_plugin_alconna.uniseg import UniMessage
|
6
|
-
from typing_extensions import override
|
7
7
|
|
8
8
|
from ..models import KillReason, Role, RoleGroup
|
9
9
|
from .player import Player
|
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
|
12
12
|
from ..player_set import PlayerSet
|
13
13
|
|
14
14
|
|
15
|
-
@Player.register_role(Role.
|
15
|
+
@Player.register_role(Role.IDIOT, RoleGroup.GOODGUY)
|
16
16
|
class Idiot(Player):
|
17
17
|
voted: bool = False
|
18
18
|
|
@@ -25,7 +25,7 @@ class Idiot(Player):
|
|
25
25
|
|
26
26
|
@override
|
27
27
|
async def kill(self, reason: KillReason, *killers: Player) -> bool:
|
28
|
-
if reason == KillReason.
|
28
|
+
if reason == KillReason.VOTE and not self.voted:
|
29
29
|
self.voted = True
|
30
30
|
await self.game.send(
|
31
31
|
UniMessage.text("⚙️玩家")
|
@@ -1,5 +1,4 @@
|
|
1
1
|
from typing import TYPE_CHECKING
|
2
|
-
|
3
2
|
from typing_extensions import override
|
4
3
|
|
5
4
|
from ..exception import GameFinished
|
@@ -7,8 +6,8 @@ from ..models import GameStatus, KillReason, Role, RoleGroup
|
|
7
6
|
from .player import Player
|
8
7
|
|
9
8
|
|
10
|
-
@Player.register_role(Role.
|
11
|
-
class
|
9
|
+
@Player.register_role(Role.JESTER, RoleGroup.OTHERS)
|
10
|
+
class Jester(Player):
|
12
11
|
@override
|
13
12
|
async def notify_role(self) -> None:
|
14
13
|
await super().notify_role()
|
@@ -17,9 +16,9 @@ class Joker(Player):
|
|
17
16
|
@override
|
18
17
|
async def kill(self, reason: KillReason, *killers: Player) -> bool:
|
19
18
|
await super().kill(reason, *killers)
|
20
|
-
if reason == KillReason.
|
19
|
+
if reason == KillReason.VOTE:
|
21
20
|
if TYPE_CHECKING:
|
22
21
|
assert self.kill_info is not None
|
23
22
|
self.game.killed_players.append((self.name, self.kill_info))
|
24
|
-
raise GameFinished(GameStatus.
|
23
|
+
raise GameFinished(GameStatus.JESTER)
|
25
24
|
return True
|
@@ -10,9 +10,17 @@ from nonebot.utils import escape_tag
|
|
10
10
|
from nonebot_plugin_alconna.uniseg import Receipt, Target, UniMessage
|
11
11
|
from nonebot_plugin_uninfo import SceneType
|
12
12
|
|
13
|
-
from ..
|
13
|
+
from ..config import GameBehavior
|
14
|
+
from ..constant import ROLE_EMOJI, ROLE_NAME_CONV, STOP_COMMAND, stop_command_prompt
|
14
15
|
from ..models import KillInfo, KillReason, Role, RoleGroup
|
15
|
-
from ..utils import
|
16
|
+
from ..utils import (
|
17
|
+
InputStore,
|
18
|
+
SendHandler,
|
19
|
+
add_players_button,
|
20
|
+
add_stop_button,
|
21
|
+
check_index,
|
22
|
+
link,
|
23
|
+
)
|
16
24
|
|
17
25
|
if TYPE_CHECKING:
|
18
26
|
from ..game import Game
|
@@ -24,6 +32,17 @@ _P = TypeVar("_P", bound=type["Player"])
|
|
24
32
|
logger = nonebot.logger.opt(colors=True)
|
25
33
|
|
26
34
|
|
35
|
+
class _SendHandler(SendHandler[str | None]):
|
36
|
+
def solve_msg(
|
37
|
+
self,
|
38
|
+
msg: UniMessage,
|
39
|
+
stop_btn_label: str | None = None,
|
40
|
+
) -> UniMessage:
|
41
|
+
if stop_btn_label is not None:
|
42
|
+
msg = add_stop_button(msg, stop_btn_label)
|
43
|
+
return msg
|
44
|
+
|
45
|
+
|
27
46
|
class Player:
|
28
47
|
__player_class: ClassVar[dict[Role, type["Player"]]] = {}
|
29
48
|
role: ClassVar[Role]
|
@@ -47,6 +66,8 @@ class Player:
|
|
47
66
|
self.bot = bot
|
48
67
|
self.killed = anyio.Event()
|
49
68
|
self._member = None
|
69
|
+
self._send_handler = _SendHandler()
|
70
|
+
self._send_handler.update(self.__user, bot)
|
50
71
|
|
51
72
|
@classmethod
|
52
73
|
def register_role(cls, role: Role, role_group: RoleGroup, /) -> Callable[[_P], _P]:
|
@@ -67,9 +88,7 @@ class Player:
|
|
67
88
|
return cls.__player_class[role](bot, game, user_id)
|
68
89
|
|
69
90
|
def __repr__(self) -> str:
|
70
|
-
return
|
71
|
-
f"<Player {self.role_name}: user={self.user_id!r} " f"alive={self.alive}>"
|
72
|
-
)
|
91
|
+
return f"<Player {self.role_name}: user={self.user_id!r} alive={self.alive}>"
|
73
92
|
|
74
93
|
@property
|
75
94
|
def game(self) -> "Game":
|
@@ -83,7 +102,7 @@ class Player:
|
|
83
102
|
|
84
103
|
@functools.cached_property
|
85
104
|
def role_name(self) -> str:
|
86
|
-
return
|
105
|
+
return ROLE_NAME_CONV[self.role]
|
87
106
|
|
88
107
|
async def _fetch_member(self) -> None:
|
89
108
|
member = await self.game.interface.get_member(
|
@@ -128,41 +147,75 @@ class Player:
|
|
128
147
|
return name
|
129
148
|
|
130
149
|
@final
|
131
|
-
def
|
150
|
+
def log(self, text: str) -> None:
|
132
151
|
text = text.replace("\n", "\\n")
|
133
|
-
|
134
|
-
f"{self.game.colored_name} | "
|
135
|
-
f"[<b><m>{self.role_name}</m></b>] "
|
136
|
-
f"{self.colored_name} | {text}",
|
137
|
-
)
|
152
|
+
self.game.log(f"[<b><m>{self.role_name}</m></b>] {self.colored_name} | {text}")
|
138
153
|
|
139
154
|
@final
|
140
|
-
async def send(
|
155
|
+
async def send(
|
156
|
+
self,
|
157
|
+
message: str | UniMessage,
|
158
|
+
*,
|
159
|
+
stop_btn_label: str | None = None,
|
160
|
+
select_players: "PlayerSet | None" = None,
|
161
|
+
skip_handler: bool = False,
|
162
|
+
) -> Receipt:
|
141
163
|
if isinstance(message, str):
|
142
164
|
message = UniMessage.text(message)
|
143
165
|
|
144
|
-
self.
|
145
|
-
return await message.send(target=self.__user, bot=self.bot)
|
166
|
+
self.log(f"<g>Send</g> | {escape_tag(str(message))}")
|
146
167
|
|
147
|
-
|
148
|
-
|
149
|
-
if
|
150
|
-
await
|
168
|
+
if select_players:
|
169
|
+
message = add_players_button(message, select_players)
|
170
|
+
if skip_handler:
|
171
|
+
return await message.send(self.__user, self.bot)
|
172
|
+
return await self._send_handler.send(message, stop_btn_label)
|
151
173
|
|
174
|
+
@final
|
175
|
+
async def receive(self) -> UniMessage:
|
152
176
|
result = await InputStore.fetch(self.user_id)
|
153
|
-
self.
|
177
|
+
self.log(f"<y>Recv</y> | {escape_tag(str(result))}")
|
154
178
|
return result
|
155
179
|
|
156
180
|
@final
|
157
181
|
async def receive_text(self) -> str:
|
158
182
|
return (await self.receive()).extract_plain_text()
|
159
183
|
|
160
|
-
|
184
|
+
@property
|
185
|
+
def interact_timeout(self) -> float:
|
186
|
+
return GameBehavior.get().timeout.interact
|
187
|
+
|
188
|
+
async def _before_interact(self) -> None:
|
161
189
|
return
|
162
190
|
|
191
|
+
async def _interact(self) -> None:
|
192
|
+
return
|
193
|
+
|
194
|
+
async def _after_interact(self) -> None:
|
195
|
+
return
|
196
|
+
|
197
|
+
async def interact(self) -> None:
|
198
|
+
if not getattr(self._interact, "__override__", False):
|
199
|
+
await self.send("ℹ️请等待其他玩家结束交互...")
|
200
|
+
return
|
201
|
+
|
202
|
+
await self._before_interact()
|
203
|
+
|
204
|
+
timeout = self.interact_timeout
|
205
|
+
await self.send(f"✏️{self.role_name}交互开始,限时 {timeout / 60:.2f} 分钟")
|
206
|
+
|
207
|
+
try:
|
208
|
+
with anyio.fail_after(timeout):
|
209
|
+
await self._interact()
|
210
|
+
except TimeoutError:
|
211
|
+
logger.debug(f"{self.role_name}交互超时 (<y>{timeout}</y>s)")
|
212
|
+
await self.send(f"⚠️{self.role_name}交互超时")
|
213
|
+
|
214
|
+
await self._after_interact()
|
215
|
+
|
163
216
|
async def notify_role(self) -> None:
|
164
217
|
await self._fetch_member()
|
165
|
-
await self.send(f"⚙️你的身份: {
|
218
|
+
await self.send(f"⚙️你的身份: {ROLE_EMOJI[self.role]}{self.role_name}")
|
166
219
|
|
167
220
|
async def kill(self, reason: KillReason, *killers: "Player") -> bool:
|
168
221
|
if self.alive:
|
@@ -175,13 +228,17 @@ class Player:
|
|
175
228
|
|
176
229
|
async def vote(self, players: "PlayerSet") -> "Player | None":
|
177
230
|
await self.send(
|
178
|
-
f"💫请选择需要投票的玩家:\n
|
179
|
-
f"\n\n
|
180
|
-
|
231
|
+
f"💫请选择需要投票的玩家:\n"
|
232
|
+
f"{players.show()}\n\n"
|
233
|
+
"🗳️发送编号选择玩家\n"
|
234
|
+
f"❌发送 “{stop_command_prompt()}” 弃票\n\n"
|
235
|
+
"限时1分钟,超时将视为弃票",
|
236
|
+
stop_btn_label="弃票",
|
237
|
+
select_players=players,
|
181
238
|
)
|
182
239
|
|
183
240
|
try:
|
184
|
-
with anyio.fail_after(
|
241
|
+
with anyio.fail_after(GameBehavior.get().timeout.vote):
|
185
242
|
selected = await self._select_player(
|
186
243
|
players,
|
187
244
|
on_stop="⚠️你选择了弃票",
|
@@ -204,10 +261,11 @@ class Player:
|
|
204
261
|
*,
|
205
262
|
on_stop: str | None = None,
|
206
263
|
on_index_error: str | None = None,
|
264
|
+
stop_btn_label: str | None = None,
|
207
265
|
) -> "Player | None":
|
208
266
|
on_stop = on_stop or "ℹ️你选择了取消,回合结束"
|
209
267
|
on_index_error = (
|
210
|
-
on_index_error or f"⚠️输入错误: 请发送玩家编号或 “{
|
268
|
+
on_index_error or f"⚠️输入错误: 请发送玩家编号或 “{stop_command_prompt()}”"
|
211
269
|
)
|
212
270
|
selected = None
|
213
271
|
|
@@ -219,7 +277,11 @@ class Player:
|
|
219
277
|
return None
|
220
278
|
index = check_index(text, len(players))
|
221
279
|
if index is None:
|
222
|
-
await self.send(
|
280
|
+
await self.send(
|
281
|
+
on_index_error,
|
282
|
+
stop_btn_label=stop_btn_label,
|
283
|
+
select_players=players,
|
284
|
+
)
|
223
285
|
continue
|
224
286
|
selected = await self._check_selected(players[index - 1])
|
225
287
|
|
@@ -1,23 +1,24 @@
|
|
1
|
-
from nonebot_plugin_alconna.uniseg import UniMessage
|
2
1
|
from typing_extensions import override
|
3
2
|
|
4
|
-
from ..constant import
|
3
|
+
from ..constant import stop_command_prompt
|
5
4
|
from ..models import Role, RoleGroup
|
6
5
|
from .player import Player
|
7
6
|
|
8
7
|
|
9
|
-
@Player.register_role(Role.
|
8
|
+
@Player.register_role(Role.PROPHET, RoleGroup.GOODGUY)
|
10
9
|
class Prophet(Player):
|
11
10
|
@override
|
12
|
-
async def
|
11
|
+
async def _interact(self) -> None:
|
13
12
|
players = self.game.players.alive().exclude(self)
|
14
13
|
await self.send(
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
"💫请选择需要查验身份的玩家:\n"
|
15
|
+
f"{players.show()}\n\n"
|
16
|
+
"🔮发送编号选择玩家\n"
|
17
|
+
f"❌发送 “{stop_command_prompt()}” 结束回合(不查验身份)",
|
18
|
+
stop_btn_label="结束回合",
|
19
|
+
select_players=players,
|
19
20
|
)
|
20
21
|
|
21
|
-
if selected := await self._select_player(players):
|
22
|
-
result = "狼人" if selected.role_group == RoleGroup.
|
22
|
+
if selected := await self._select_player(players, stop_btn_label="结束回合"):
|
23
|
+
result = "狼人" if selected.role_group == RoleGroup.WEREWOLF else "好人"
|
23
24
|
await self.send(f"✏️玩家 {selected.name} 的阵营是『{result}』")
|
@@ -1,10 +1,13 @@
|
|
1
|
+
import secrets
|
1
2
|
from typing import TYPE_CHECKING
|
3
|
+
from typing_extensions import override
|
2
4
|
|
3
5
|
import anyio
|
6
|
+
import nonebot
|
4
7
|
from nonebot_plugin_alconna.uniseg import UniMessage
|
5
|
-
from typing_extensions import override
|
6
8
|
|
7
|
-
from ..
|
9
|
+
from ..config import GameBehavior
|
10
|
+
from ..constant import STOP_COMMAND, stop_command_prompt
|
8
11
|
from ..models import Role, RoleGroup
|
9
12
|
from ..utils import ObjectStream, check_index
|
10
13
|
from .player import Player
|
@@ -12,15 +15,22 @@ from .player import Player
|
|
12
15
|
if TYPE_CHECKING:
|
13
16
|
from ..player_set import PlayerSet
|
14
17
|
|
18
|
+
logger = nonebot.logger.opt(colors=True)
|
19
|
+
|
15
20
|
|
16
|
-
@Player.register_role(Role.
|
21
|
+
@Player.register_role(Role.WEREWOLF, RoleGroup.WEREWOLF)
|
17
22
|
class Werewolf(Player):
|
18
23
|
stream: ObjectStream[str | UniMessage]
|
19
24
|
|
25
|
+
@property
|
26
|
+
@override
|
27
|
+
def interact_timeout(self) -> float:
|
28
|
+
return GameBehavior.get().timeout.werewolf
|
29
|
+
|
20
30
|
@override
|
21
31
|
async def notify_role(self) -> None:
|
22
32
|
await super().notify_role()
|
23
|
-
partners = self.game.players.alive().select(RoleGroup.
|
33
|
+
partners = self.game.players.alive().select(RoleGroup.WEREWOLF).exclude(self)
|
24
34
|
if partners:
|
25
35
|
await self.send(
|
26
36
|
"🐺你的队友:\n"
|
@@ -37,7 +47,11 @@ class Werewolf(Player):
|
|
37
47
|
if index is not None:
|
38
48
|
self.selected = players[index - 1]
|
39
49
|
msg = f"当前选择玩家: {self.selected.name}"
|
40
|
-
await self.send(
|
50
|
+
await self.send(
|
51
|
+
f"🎯{msg}\n发送 “{stop_command_prompt()}” 结束回合",
|
52
|
+
stop_btn_label="结束回合",
|
53
|
+
select_players=players,
|
54
|
+
)
|
41
55
|
await self.stream.send(f"📝队友 {self.name} {msg}")
|
42
56
|
if text == STOP_COMMAND:
|
43
57
|
if self.selected is not None:
|
@@ -45,9 +59,14 @@ class Werewolf(Player):
|
|
45
59
|
await self.stream.send(f"📝队友 {self.name} 结束当前回合")
|
46
60
|
self.stream.close()
|
47
61
|
return
|
48
|
-
await self.send(
|
62
|
+
await self.send(
|
63
|
+
"⚠️当前未选择玩家,无法结束回合",
|
64
|
+
select_players=players,
|
65
|
+
)
|
49
66
|
else:
|
50
|
-
await self.stream.send(
|
67
|
+
await self.stream.send(
|
68
|
+
UniMessage.text(f"💬队友 {self.name}:\n") + input_msg
|
69
|
+
)
|
51
70
|
|
52
71
|
async def _handle_broadcast(self, partners: "PlayerSet") -> None:
|
53
72
|
while not self.stream.closed:
|
@@ -59,9 +78,9 @@ class Werewolf(Player):
|
|
59
78
|
await partners.broadcast(message)
|
60
79
|
|
61
80
|
@override
|
62
|
-
async def
|
81
|
+
async def _interact(self) -> None:
|
63
82
|
players = self.game.players.alive()
|
64
|
-
partners = players.select(RoleGroup.
|
83
|
+
partners = players.select(RoleGroup.WEREWOLF).exclude(self)
|
65
84
|
|
66
85
|
msg = UniMessage()
|
67
86
|
if partners:
|
@@ -74,8 +93,9 @@ class Werewolf(Player):
|
|
74
93
|
msg.text("💫请选择今晚的目标:\n")
|
75
94
|
.text(players.show())
|
76
95
|
.text("\n\n🔪发送编号选择玩家")
|
77
|
-
.text(f"\n❌发送 “{
|
78
|
-
.text("\n\n⚠️意见未统一将空刀")
|
96
|
+
.text(f"\n❌发送 “{stop_command_prompt()}” 结束回合")
|
97
|
+
.text("\n\n⚠️意见未统一将空刀"),
|
98
|
+
select_players=players,
|
79
99
|
)
|
80
100
|
|
81
101
|
self.stream = ObjectStream[str | UniMessage](8)
|
@@ -86,3 +106,18 @@ class Werewolf(Player):
|
|
86
106
|
tg.start_soon(self._handle_broadcast, partners)
|
87
107
|
finally:
|
88
108
|
del self.stream
|
109
|
+
|
110
|
+
@override
|
111
|
+
async def _after_interact(self) -> None:
|
112
|
+
state = self.game.state
|
113
|
+
if not state.werewolf_finished.is_set():
|
114
|
+
state.werewolf_finished.set()
|
115
|
+
w = self.game.players.alive().select(RoleGroup.WEREWOLF)
|
116
|
+
if (s := w.player_selected()).size == 1:
|
117
|
+
state.killed = s.pop()
|
118
|
+
await w.broadcast(f"🔪今晚选择的目标为: {state.killed.name}")
|
119
|
+
else:
|
120
|
+
await w.broadcast("⚠️狼人阵营意见未统一,此晚空刀")
|
121
|
+
|
122
|
+
if not self.game.players.alive().select(Role.WITCH):
|
123
|
+
await anyio.sleep(5 + secrets.randbelow(15))
|
@@ -1,13 +1,17 @@
|
|
1
|
-
from nonebot_plugin_alconna.uniseg import UniMessage
|
2
1
|
from typing_extensions import override
|
3
2
|
|
4
|
-
|
3
|
+
import nonebot
|
4
|
+
from nonebot_plugin_alconna.uniseg import UniMessage
|
5
|
+
|
6
|
+
from ..constant import stop_command_prompt
|
5
7
|
from ..models import Role, RoleGroup
|
6
8
|
from ..utils import as_player_set
|
7
9
|
from .player import Player
|
8
10
|
|
11
|
+
logger = nonebot.logger.opt(colors=True)
|
12
|
+
|
9
13
|
|
10
|
-
@Player.register_role(Role.
|
14
|
+
@Player.register_role(Role.WITCH, RoleGroup.GOODGUY)
|
11
15
|
class Witch(Player):
|
12
16
|
antidote: bool = True
|
13
17
|
poison: bool = True
|
@@ -23,13 +27,18 @@ class Witch(Player):
|
|
23
27
|
await self.send(msg.text("⚙️你已经用过解药了"))
|
24
28
|
return False
|
25
29
|
|
26
|
-
msg.text(f"✏️使用解药请发送 “1”\n❌不使用解药请发送 “{
|
27
|
-
await self.send(
|
30
|
+
msg.text(f"✏️使用解药请发送 “1”\n❌不使用解药请发送 “{stop_command_prompt()}”")
|
31
|
+
await self.send(
|
32
|
+
msg,
|
33
|
+
stop_btn_label="不使用解药",
|
34
|
+
select_players=as_player_set(killed),
|
35
|
+
)
|
28
36
|
|
29
37
|
if not await self._select_player(
|
30
38
|
as_player_set(killed),
|
31
39
|
on_stop=f"ℹ️你选择不对 {killed.name} 使用解药",
|
32
|
-
on_index_error=f"⚠️输入错误: 请输入 “1” 或 “{
|
40
|
+
on_index_error=f"⚠️输入错误: 请输入 “1” 或 “{stop_command_prompt()}”",
|
41
|
+
stop_btn_label="不使用解药",
|
33
42
|
):
|
34
43
|
return False
|
35
44
|
|
@@ -40,7 +49,12 @@ class Witch(Player):
|
|
40
49
|
return True
|
41
50
|
|
42
51
|
@override
|
43
|
-
async def
|
52
|
+
async def _before_interact(self) -> None:
|
53
|
+
await self.send("ℹ️请等待狼人决定目标...")
|
54
|
+
await self.game.state.werewolf_finished.wait()
|
55
|
+
|
56
|
+
@override
|
57
|
+
async def _interact(self) -> None:
|
44
58
|
if await self.handle_killed():
|
45
59
|
return
|
46
60
|
|
@@ -50,16 +64,19 @@ class Witch(Player):
|
|
50
64
|
|
51
65
|
players = self.game.players.alive()
|
52
66
|
await self.send(
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
67
|
+
"💫你有一瓶毒药\n"
|
68
|
+
"玩家列表:\n"
|
69
|
+
f"{players.show()}\n\n"
|
70
|
+
"🧪发送玩家编号使用毒药\n"
|
71
|
+
f"❌发送 “{stop_command_prompt()}” 结束回合(不使用药水)",
|
72
|
+
stop_btn_label="结束回合",
|
73
|
+
select_players=players,
|
58
74
|
)
|
59
75
|
|
60
76
|
if selected := await self._select_player(
|
61
77
|
players,
|
62
78
|
on_stop="ℹ️你选择不使用毒药,回合结束",
|
79
|
+
stop_btn_label="结束回合",
|
63
80
|
):
|
64
81
|
self.poison = False
|
65
82
|
self.selected = selected
|
@@ -6,7 +6,7 @@ from .player import Player
|
|
6
6
|
from .werewolf import Werewolf
|
7
7
|
|
8
8
|
|
9
|
-
@Player.register_role(Role.
|
9
|
+
@Player.register_role(Role.WOLFKING, RoleGroup.WEREWOLF)
|
10
10
|
class WolfKing(CanShoot, Werewolf):
|
11
11
|
@override
|
12
12
|
async def notify_role(self) -> None:
|