nonebot-plugin-werewolf 1.1.3__py3-none-any.whl → 1.1.5__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 +2 -1
- nonebot_plugin_werewolf/constant.py +23 -1
- nonebot_plugin_werewolf/game.py +172 -113
- nonebot_plugin_werewolf/matchers/depends.py +38 -0
- nonebot_plugin_werewolf/matchers/message_in_game.py +14 -4
- nonebot_plugin_werewolf/matchers/poke/__init__.py +8 -0
- nonebot_plugin_werewolf/matchers/poke/chronocat_poke.py +117 -0
- nonebot_plugin_werewolf/matchers/{ob11_ext.py → poke/ob11_poke.py} +21 -19
- nonebot_plugin_werewolf/matchers/start_game.py +161 -15
- nonebot_plugin_werewolf/player_set.py +33 -34
- nonebot_plugin_werewolf/players/can_shoot.py +12 -18
- nonebot_plugin_werewolf/players/civilian.py +2 -2
- nonebot_plugin_werewolf/players/guard.py +15 -22
- nonebot_plugin_werewolf/players/hunter.py +2 -2
- nonebot_plugin_werewolf/players/idiot.py +3 -3
- nonebot_plugin_werewolf/players/joker.py +2 -2
- nonebot_plugin_werewolf/players/player.py +137 -65
- nonebot_plugin_werewolf/players/prophet.py +7 -15
- nonebot_plugin_werewolf/players/werewolf.py +51 -29
- nonebot_plugin_werewolf/players/witch.py +32 -38
- nonebot_plugin_werewolf/players/wolfking.py +2 -2
- nonebot_plugin_werewolf/utils.py +56 -190
- {nonebot_plugin_werewolf-1.1.3.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/METADATA +14 -2
- nonebot_plugin_werewolf-1.1.5.dist-info/RECORD +31 -0
- {nonebot_plugin_werewolf-1.1.3.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/WHEEL +1 -1
- nonebot_plugin_werewolf/_timeout.py +0 -110
- nonebot_plugin_werewolf-1.1.3.dist-info/RECORD +0 -29
- {nonebot_plugin_werewolf-1.1.3.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/LICENSE +0 -0
- {nonebot_plugin_werewolf-1.1.3.dist-info → nonebot_plugin_werewolf-1.1.5.dist-info}/top_level.txt +0 -0
@@ -2,10 +2,10 @@ from typing_extensions import override
|
|
2
2
|
|
3
3
|
from ..constant import GameStatus, KillReason, Role, RoleGroup
|
4
4
|
from ..exception import GameFinished
|
5
|
-
from .player import Player
|
5
|
+
from .player import Player
|
6
6
|
|
7
7
|
|
8
|
-
@register_role(Role.Joker, RoleGroup.Others)
|
8
|
+
@Player.register_role(Role.Joker, RoleGroup.Others)
|
9
9
|
class Joker(Player):
|
10
10
|
@override
|
11
11
|
async def notify_role(self) -> None:
|
@@ -1,95 +1,151 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import asyncio
|
4
1
|
import functools
|
5
2
|
import weakref
|
3
|
+
from collections.abc import Callable
|
6
4
|
from dataclasses import dataclass
|
7
5
|
from typing import TYPE_CHECKING, ClassVar, Final, TypeVar, final
|
8
6
|
|
7
|
+
import anyio
|
8
|
+
from nonebot.adapters import Bot
|
9
9
|
from nonebot.log import logger
|
10
|
+
from nonebot.utils import escape_tag
|
10
11
|
from nonebot_plugin_alconna.uniseg import Receipt, Target, UniMessage
|
11
|
-
|
12
|
-
|
13
|
-
from ..
|
12
|
+
from nonebot_plugin_uninfo import SceneType
|
13
|
+
|
14
|
+
from ..constant import (
|
15
|
+
STOP_COMMAND,
|
16
|
+
STOP_COMMAND_PROMPT,
|
17
|
+
KillReason,
|
18
|
+
Role,
|
19
|
+
RoleGroup,
|
20
|
+
role_emoji,
|
21
|
+
role_name_conv,
|
22
|
+
)
|
23
|
+
from ..utils import InputStore, as_player_set, check_index, link
|
14
24
|
|
15
25
|
if TYPE_CHECKING:
|
16
|
-
from collections.abc import Callable
|
17
|
-
|
18
|
-
from nonebot.adapters import Bot
|
19
|
-
|
20
26
|
from ..game import Game
|
21
27
|
from ..player_set import PlayerSet
|
22
28
|
|
23
29
|
|
24
|
-
|
25
|
-
PLAYER_CLASS: dict[Role, type[Player]] = {}
|
30
|
+
_P = TypeVar("_P", bound=type["Player"])
|
26
31
|
|
27
32
|
|
28
33
|
@dataclass
|
29
34
|
class KillInfo:
|
30
35
|
reason: KillReason
|
31
|
-
killers: PlayerSet
|
36
|
+
killers: "PlayerSet"
|
32
37
|
|
33
38
|
|
34
39
|
class Player:
|
40
|
+
__player_class: ClassVar[dict[Role, type["Player"]]] = {}
|
35
41
|
role: ClassVar[Role]
|
36
42
|
role_group: ClassVar[RoleGroup]
|
37
43
|
|
38
44
|
bot: Final[Bot]
|
39
|
-
_game_ref: Final[weakref.ReferenceType[Game]]
|
40
|
-
user: Final[Target]
|
41
|
-
name: Final[str]
|
42
45
|
alive: bool = True
|
43
|
-
killed: Final[
|
46
|
+
killed: Final[anyio.Event]
|
44
47
|
kill_info: KillInfo | None = None
|
45
|
-
selected: Player | None = None
|
48
|
+
selected: "Player | None" = None
|
46
49
|
|
47
50
|
@final
|
48
|
-
def __init__(self, bot: Bot, game: Game, user_id: str
|
49
|
-
self.
|
50
|
-
self._game_ref = weakref.ref(game)
|
51
|
-
self.user = Target(
|
51
|
+
def __init__(self, bot: Bot, game: "Game", user_id: str) -> None:
|
52
|
+
self.__user = Target(
|
52
53
|
user_id,
|
53
54
|
private=True,
|
54
55
|
self_id=bot.self_id,
|
55
56
|
adapter=bot.adapter.get_name(),
|
56
57
|
)
|
57
|
-
self.
|
58
|
-
self.
|
58
|
+
self.__game_ref = weakref.ref(game)
|
59
|
+
self.bot = bot
|
60
|
+
self.killed = anyio.Event()
|
61
|
+
self._member = None
|
62
|
+
|
63
|
+
@classmethod
|
64
|
+
def register_role(cls, role: Role, role_group: RoleGroup, /) -> Callable[[_P], _P]:
|
65
|
+
def decorator(c: _P, /) -> _P:
|
66
|
+
c.role = role
|
67
|
+
c.role_group = role_group
|
68
|
+
cls.__player_class[role] = c
|
69
|
+
return c
|
70
|
+
|
71
|
+
return decorator
|
59
72
|
|
60
73
|
@final
|
61
74
|
@classmethod
|
62
|
-
def new(cls, role: Role, bot: Bot, game: Game, user_id: str
|
63
|
-
if role not in
|
75
|
+
def new(cls, role: Role, bot: Bot, game: "Game", user_id: str) -> "Player":
|
76
|
+
if role not in cls.__player_class:
|
64
77
|
raise ValueError(f"Unexpected role: {role!r}")
|
65
78
|
|
66
|
-
return
|
79
|
+
return cls.__player_class[role](bot, game, user_id)
|
67
80
|
|
68
81
|
def __repr__(self) -> str:
|
69
|
-
return
|
82
|
+
return (
|
83
|
+
f"<Player {self.role_name}: user={self.user_id!r} " f"alive={self.alive}>"
|
84
|
+
)
|
70
85
|
|
71
86
|
@property
|
72
|
-
def game(self) -> Game:
|
73
|
-
if game := self.
|
87
|
+
def game(self) -> "Game":
|
88
|
+
if game := self.__game_ref():
|
74
89
|
return game
|
75
90
|
raise ValueError("Game not exist")
|
76
91
|
|
77
92
|
@functools.cached_property
|
78
93
|
def user_id(self) -> str:
|
79
|
-
return self.
|
94
|
+
return self.__user.id
|
80
95
|
|
81
96
|
@functools.cached_property
|
82
97
|
def role_name(self) -> str:
|
83
98
|
return role_name_conv[self.role]
|
84
99
|
|
100
|
+
async def _fetch_member(self) -> None:
|
101
|
+
member = await self.game.interface.get_member(
|
102
|
+
SceneType.GROUP,
|
103
|
+
self.game.group.id,
|
104
|
+
self.user_id,
|
105
|
+
)
|
106
|
+
if member is None:
|
107
|
+
member = await self.game.interface.get_member(
|
108
|
+
SceneType.GUILD,
|
109
|
+
self.game.group.id,
|
110
|
+
self.user_id,
|
111
|
+
)
|
112
|
+
|
113
|
+
self._member = member
|
114
|
+
|
115
|
+
@final
|
116
|
+
@property
|
117
|
+
def _member_nick(self) -> str | None:
|
118
|
+
return self._member and (
|
119
|
+
self._member.nick or self._member.user.nick or self._member.user.name
|
120
|
+
)
|
121
|
+
|
122
|
+
@final
|
123
|
+
@property
|
124
|
+
def name(self) -> str:
|
125
|
+
return self._member_nick or self.user_id
|
126
|
+
|
85
127
|
@final
|
86
|
-
|
128
|
+
@property
|
129
|
+
def colored_name(self) -> str:
|
130
|
+
name = escape_tag(self.user_id)
|
131
|
+
|
132
|
+
if self._member is None or (nick := self._member_nick) is None:
|
133
|
+
name = f"<b><e>{name}</e></b>"
|
134
|
+
else:
|
135
|
+
name = f"<y>{nick}</y>(<b><e>{name}</e></b>)"
|
136
|
+
|
137
|
+
if self._member is not None and self._member.user.avatar is not None:
|
138
|
+
name = link(name, self._member.user.avatar)
|
139
|
+
|
140
|
+
return name
|
141
|
+
|
142
|
+
@final
|
143
|
+
async def _log(self, text: str) -> None:
|
87
144
|
text = text.replace("\n", "\\n")
|
88
145
|
logger.opt(colors=True).info(
|
89
|
-
f"
|
146
|
+
f"{self.game.colored_name} | "
|
90
147
|
f"[<b><m>{self.role_name}</m></b>] "
|
91
|
-
f"
|
92
|
-
f"{text}",
|
148
|
+
f"{self.colored_name} | {text}",
|
93
149
|
)
|
94
150
|
|
95
151
|
@final
|
@@ -97,16 +153,16 @@ class Player:
|
|
97
153
|
if isinstance(message, str):
|
98
154
|
message = UniMessage.text(message)
|
99
155
|
|
100
|
-
self._log(f"<g>Send</g> | {message}")
|
101
|
-
return await message.send(target=self.
|
156
|
+
await self._log(f"<g>Send</g> | {escape_tag(str(message))}")
|
157
|
+
return await message.send(target=self.__user, bot=self.bot)
|
102
158
|
|
103
159
|
@final
|
104
160
|
async def receive(self, prompt: str | UniMessage | None = None) -> UniMessage:
|
105
161
|
if prompt:
|
106
162
|
await self.send(prompt)
|
107
163
|
|
108
|
-
result = await InputStore.fetch(self.
|
109
|
-
self._log(f"<y>Recv</y> | {result}")
|
164
|
+
result = await InputStore.fetch(self.user_id)
|
165
|
+
await self._log(f"<y>Recv</y> | {escape_tag(str(result))}")
|
110
166
|
return result
|
111
167
|
|
112
168
|
@final
|
@@ -117,45 +173,61 @@ class Player:
|
|
117
173
|
return
|
118
174
|
|
119
175
|
async def notify_role(self) -> None:
|
176
|
+
await self._fetch_member()
|
120
177
|
await self.send(f"⚙️你的身份: {role_emoji[self.role]}{self.role_name}")
|
121
178
|
|
122
|
-
async def kill(self, reason: KillReason, *killers: Player) -> bool:
|
123
|
-
from ..player_set import PlayerSet
|
124
|
-
|
179
|
+
async def kill(self, reason: KillReason, *killers: "Player") -> bool:
|
125
180
|
self.alive = False
|
126
|
-
self.kill_info = KillInfo(reason=reason, killers=
|
181
|
+
self.kill_info = KillInfo(reason=reason, killers=as_player_set(*killers))
|
127
182
|
return True
|
128
183
|
|
129
184
|
async def post_kill(self) -> None:
|
130
185
|
self.killed.set()
|
131
186
|
|
132
|
-
async def vote(self, players: PlayerSet) ->
|
187
|
+
async def vote(self, players: "PlayerSet") -> "Player | None":
|
133
188
|
await self.send(
|
134
189
|
f"💫请选择需要投票的玩家:\n{players.show()}"
|
135
|
-
"\n\n🗳️发送编号选择玩家\n❌发送
|
190
|
+
f"\n\n🗳️发送编号选择玩家\n❌发送 “{STOP_COMMAND_PROMPT}” 弃票"
|
191
|
+
f"\n\n限时1分钟,超时将视为弃票"
|
136
192
|
)
|
137
193
|
|
138
|
-
|
194
|
+
try:
|
195
|
+
with anyio.fail_after(60):
|
196
|
+
selected = await self._select_player(
|
197
|
+
players,
|
198
|
+
on_stop="⚠️你选择了弃票",
|
199
|
+
on_index_error="⚠️输入错误: 请发送编号选择玩家",
|
200
|
+
)
|
201
|
+
except TimeoutError:
|
202
|
+
selected = None
|
203
|
+
await self.send("⚠️投票超时,将视为弃票")
|
204
|
+
|
205
|
+
if selected is not None:
|
206
|
+
await self.send(f"🔨投票的玩家: {selected.name}")
|
207
|
+
return selected
|
208
|
+
|
209
|
+
async def _check_selected(self, player: "Player") -> "Player | None":
|
210
|
+
return player
|
211
|
+
|
212
|
+
async def _select_player(
|
213
|
+
self,
|
214
|
+
players: "PlayerSet",
|
215
|
+
*,
|
216
|
+
on_stop: str | None = "ℹ️你选择了取消,回合结束",
|
217
|
+
on_index_error: str = f"⚠️输入错误: 请发送玩家编号或 “{STOP_COMMAND_PROMPT}”",
|
218
|
+
) -> "Player | None":
|
219
|
+
selected = None
|
220
|
+
|
221
|
+
while selected is None:
|
139
222
|
text = await self.receive_text()
|
140
|
-
if text ==
|
141
|
-
|
223
|
+
if text == STOP_COMMAND:
|
224
|
+
if on_stop is not None:
|
225
|
+
await self.send(on_stop)
|
142
226
|
return None
|
143
227
|
index = check_index(text, len(players))
|
144
|
-
if index is
|
145
|
-
|
146
|
-
|
147
|
-
await self.
|
148
|
-
|
149
|
-
player = players[selected]
|
150
|
-
await self.send(f"🔨投票的玩家: {player.name}")
|
151
|
-
return self, player
|
152
|
-
|
153
|
-
|
154
|
-
def register_role(role: Role, role_group: RoleGroup, /) -> Callable[[P], P]:
|
155
|
-
def decorator(cls: P, /) -> P:
|
156
|
-
cls.role = role
|
157
|
-
cls.role_group = role_group
|
158
|
-
PLAYER_CLASS[role] = cls
|
159
|
-
return cls
|
228
|
+
if index is None:
|
229
|
+
await self.send(on_index_error)
|
230
|
+
continue
|
231
|
+
selected = await self._check_selected(players[index - 1])
|
160
232
|
|
161
|
-
|
233
|
+
return selected
|
@@ -1,12 +1,11 @@
|
|
1
1
|
from nonebot_plugin_alconna.uniseg import UniMessage
|
2
2
|
from typing_extensions import override
|
3
3
|
|
4
|
-
from ..constant import Role, RoleGroup
|
5
|
-
from
|
6
|
-
from .player import Player, register_role
|
4
|
+
from ..constant import STOP_COMMAND_PROMPT, Role, RoleGroup
|
5
|
+
from .player import Player
|
7
6
|
|
8
7
|
|
9
|
-
@register_role(Role.Prophet, RoleGroup.GoodGuy)
|
8
|
+
@Player.register_role(Role.Prophet, RoleGroup.GoodGuy)
|
10
9
|
class Prophet(Player):
|
11
10
|
@override
|
12
11
|
async def interact(self) -> None:
|
@@ -15,16 +14,9 @@ class Prophet(Player):
|
|
15
14
|
UniMessage.text("💫请选择需要查验身份的玩家:\n")
|
16
15
|
.text(players.show())
|
17
16
|
.text("\n\n🔮发送编号选择玩家")
|
17
|
+
.text(f"\n❌发送 “{STOP_COMMAND_PROMPT}” 结束回合(不查验身份)")
|
18
18
|
)
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
if index is not None:
|
24
|
-
selected = index - 1
|
25
|
-
break
|
26
|
-
await self.send("⚠️输入错误: 请发送编号选择玩家")
|
27
|
-
|
28
|
-
player = players[selected]
|
29
|
-
result = "狼人" if player.role_group == RoleGroup.Werewolf else "好人"
|
30
|
-
await self.send(f"✏️玩家 {player.name} 的阵营是『{result}』")
|
20
|
+
if selected := await self._select_player(players):
|
21
|
+
result = "狼人" if selected.role_group == RoleGroup.Werewolf else "好人"
|
22
|
+
await self.send(f"✏️玩家 {selected.name} 的阵营是『{result}』")
|
@@ -1,14 +1,19 @@
|
|
1
|
-
import
|
1
|
+
from typing import TYPE_CHECKING
|
2
2
|
|
3
|
+
import anyio
|
4
|
+
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
3
5
|
from nonebot_plugin_alconna.uniseg import UniMessage
|
4
6
|
from typing_extensions import override
|
5
7
|
|
6
|
-
from ..constant import Role, RoleGroup
|
8
|
+
from ..constant import STOP_COMMAND, STOP_COMMAND_PROMPT, Role, RoleGroup
|
7
9
|
from ..utils import check_index
|
8
|
-
from .player import Player
|
10
|
+
from .player import Player
|
9
11
|
|
12
|
+
if TYPE_CHECKING:
|
13
|
+
from ..player_set import PlayerSet
|
10
14
|
|
11
|
-
|
15
|
+
|
16
|
+
@Player.register_role(Role.Werewolf, RoleGroup.Werewolf)
|
12
17
|
class Werewolf(Player):
|
13
18
|
@override
|
14
19
|
async def notify_role(self) -> None:
|
@@ -20,15 +25,47 @@ class Werewolf(Player):
|
|
20
25
|
+ "\n".join(f" {p.role_name}: {p.name}" for p in partners)
|
21
26
|
)
|
22
27
|
|
28
|
+
async def _handle_interact(
|
29
|
+
self,
|
30
|
+
players: "PlayerSet",
|
31
|
+
stream: MemoryObjectSendStream[str | UniMessage],
|
32
|
+
finished: anyio.Event,
|
33
|
+
) -> None:
|
34
|
+
self.selected = None
|
35
|
+
|
36
|
+
while True:
|
37
|
+
input_msg = await self.receive()
|
38
|
+
text = input_msg.extract_plain_text()
|
39
|
+
index = check_index(text, len(players))
|
40
|
+
if index is not None:
|
41
|
+
self.selected = players[index - 1]
|
42
|
+
msg = f"当前选择玩家: {self.selected.name}"
|
43
|
+
await self.send(f"🎯{msg}\n发送 “{STOP_COMMAND_PROMPT}” 结束回合")
|
44
|
+
await stream.send(f"📝队友 {self.name} {msg}")
|
45
|
+
if text == STOP_COMMAND:
|
46
|
+
if self.selected is not None:
|
47
|
+
await self.send("✅你已结束当前回合")
|
48
|
+
await stream.send(f"📝队友 {self.name} 结束当前回合")
|
49
|
+
finished.set()
|
50
|
+
return
|
51
|
+
await self.send("⚠️当前未选择玩家,无法结束回合")
|
52
|
+
else:
|
53
|
+
await stream.send(UniMessage.text(f"💬队友 {self.name}:\n") + input_msg)
|
54
|
+
|
55
|
+
async def _handle_broadcast(
|
56
|
+
self,
|
57
|
+
partners: "PlayerSet",
|
58
|
+
stream: MemoryObjectReceiveStream[str | UniMessage],
|
59
|
+
finished: anyio.Event,
|
60
|
+
) -> None:
|
61
|
+
while not finished.is_set() or stream.statistics().tasks_waiting_receive:
|
62
|
+
await partners.broadcast(await stream.receive())
|
63
|
+
|
23
64
|
@override
|
24
65
|
async def interact(self) -> None:
|
25
66
|
players = self.game.players.alive()
|
26
67
|
partners = players.select(RoleGroup.Werewolf).exclude(self)
|
27
68
|
|
28
|
-
# 避免阻塞
|
29
|
-
def broadcast(msg: str | UniMessage) -> asyncio.Task[None]:
|
30
|
-
return asyncio.create_task(partners.broadcast(msg))
|
31
|
-
|
32
69
|
msg = UniMessage()
|
33
70
|
if partners:
|
34
71
|
msg = (
|
@@ -40,28 +77,13 @@ class Werewolf(Player):
|
|
40
77
|
msg.text("💫请选择今晚的目标:\n")
|
41
78
|
.text(players.show())
|
42
79
|
.text("\n\n🔪发送编号选择玩家")
|
43
|
-
.text("\n❌发送
|
80
|
+
.text(f"\n❌发送 “{STOP_COMMAND_PROMPT}” 结束回合")
|
44
81
|
.text("\n\n⚠️意见未统一将空刀")
|
45
82
|
)
|
46
83
|
|
47
|
-
|
48
|
-
finished =
|
49
|
-
while selected is None or not finished:
|
50
|
-
input_msg = await self.receive()
|
51
|
-
text = input_msg.extract_plain_text()
|
52
|
-
index = check_index(text, len(players))
|
53
|
-
if index is not None:
|
54
|
-
selected = index - 1
|
55
|
-
msg = f"当前选择玩家: {players[selected].name}"
|
56
|
-
await self.send(f"🎯{msg}\n发送 “/stop” 结束回合")
|
57
|
-
broadcast(f"📝队友 {self.name} {msg}")
|
58
|
-
if text == "/stop":
|
59
|
-
if selected is not None:
|
60
|
-
finished = True
|
61
|
-
await self.send("✅你已结束当前回合")
|
62
|
-
broadcast(f"📝队友 {self.name} 结束当前回合")
|
63
|
-
else:
|
64
|
-
await self.send("⚠️当前未选择玩家,无法结束回合")
|
65
|
-
broadcast(UniMessage.text(f"💬队友 {self.name}:\n") + input_msg)
|
84
|
+
send, recv = anyio.create_memory_object_stream[str | UniMessage]()
|
85
|
+
finished = anyio.Event()
|
66
86
|
|
67
|
-
|
87
|
+
async with anyio.create_task_group() as tg:
|
88
|
+
tg.start_soon(self._handle_interact, players, send, finished)
|
89
|
+
tg.start_soon(self._handle_broadcast, partners, recv, finished)
|
@@ -1,41 +1,42 @@
|
|
1
1
|
from nonebot_plugin_alconna.uniseg import UniMessage
|
2
2
|
from typing_extensions import override
|
3
3
|
|
4
|
-
from ..constant import Role, RoleGroup
|
5
|
-
from ..utils import
|
6
|
-
from .player import Player
|
4
|
+
from ..constant import STOP_COMMAND_PROMPT, Role, RoleGroup
|
5
|
+
from ..utils import as_player_set
|
6
|
+
from .player import Player
|
7
7
|
|
8
8
|
|
9
|
-
@register_role(Role.Witch, RoleGroup.GoodGuy)
|
9
|
+
@Player.register_role(Role.Witch, RoleGroup.GoodGuy)
|
10
10
|
class Witch(Player):
|
11
|
-
antidote:
|
12
|
-
poison:
|
11
|
+
antidote: bool = True
|
12
|
+
poison: bool = True
|
13
13
|
|
14
14
|
async def handle_killed(self) -> bool:
|
15
|
-
|
16
|
-
if (killed := self.game.state.killed) is not None:
|
17
|
-
msg.text(f"🔪今晚 {killed.name} 被刀了\n\n")
|
18
|
-
else:
|
15
|
+
if (killed := self.game.state.killed) is None:
|
19
16
|
await self.send("ℹ️今晚没有人被刀")
|
20
17
|
return False
|
21
18
|
|
19
|
+
msg = UniMessage.text(f"🔪今晚 {killed.name} 被刀了\n\n")
|
20
|
+
|
22
21
|
if not self.antidote:
|
23
22
|
await self.send(msg.text("⚙️你已经用过解药了"))
|
24
23
|
return False
|
25
24
|
|
26
|
-
|
25
|
+
msg.text(f"✏️使用解药请发送 “1”\n❌不使用解药请发送 “{STOP_COMMAND_PROMPT}”")
|
26
|
+
await self.send(msg)
|
27
|
+
|
28
|
+
if not await self._select_player(
|
29
|
+
as_player_set(killed),
|
30
|
+
on_stop=f"ℹ️你选择不对 {killed.name} 使用解药",
|
31
|
+
on_index_error=f"⚠️输入错误: 请输入 “1” 或 “{STOP_COMMAND_PROMPT}”",
|
32
|
+
):
|
33
|
+
return False
|
27
34
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
self.game.state.antidote.add(killed)
|
34
|
-
await self.send(f"✅你对 {killed.name} 使用了解药,回合结束")
|
35
|
-
return True
|
36
|
-
if text == "/stop":
|
37
|
-
return False
|
38
|
-
await self.send("⚠️输入错误: 请输入 “1” 或 “/stop”")
|
35
|
+
self.antidote = False
|
36
|
+
self.selected = killed
|
37
|
+
self.game.state.antidote.add(killed)
|
38
|
+
await self.send(f"✅你对 {killed.name} 使用了解药,回合结束")
|
39
|
+
return True
|
39
40
|
|
40
41
|
@override
|
41
42
|
async def interact(self) -> None:
|
@@ -52,21 +53,14 @@ class Witch(Player):
|
|
52
53
|
.text("玩家列表:\n")
|
53
54
|
.text(players.show())
|
54
55
|
.text("\n\n🧪发送玩家编号使用毒药")
|
55
|
-
.text("\n❌发送
|
56
|
+
.text(f"\n❌发送 “{STOP_COMMAND_PROMPT}” 结束回合(不使用药水)")
|
56
57
|
)
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
return
|
67
|
-
await self.send("⚠️输入错误: 请发送玩家编号或 “/stop”")
|
68
|
-
|
69
|
-
self.poison = 0
|
70
|
-
self.selected = players[selected]
|
71
|
-
self.game.state.poison.add(self)
|
72
|
-
await self.send(f"✅当前回合选择对玩家 {self.selected.name} 使用毒药\n回合结束")
|
59
|
+
if selected := await self._select_player(
|
60
|
+
players,
|
61
|
+
on_stop="ℹ️你选择不使用毒药,回合结束",
|
62
|
+
):
|
63
|
+
self.poison = False
|
64
|
+
self.selected = selected
|
65
|
+
self.game.state.poison.add(self)
|
66
|
+
await self.send(f"✅当前回合选择对玩家 {selected.name} 使用毒药\n回合结束")
|
@@ -2,11 +2,11 @@ from typing_extensions import override
|
|
2
2
|
|
3
3
|
from ..constant import Role, RoleGroup
|
4
4
|
from .can_shoot import CanShoot
|
5
|
-
from .player import
|
5
|
+
from .player import Player
|
6
6
|
from .werewolf import Werewolf
|
7
7
|
|
8
8
|
|
9
|
-
@register_role(Role.WolfKing, RoleGroup.Werewolf)
|
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:
|