176 lines
5.6 KiB
Python
176 lines
5.6 KiB
Python
"""骰娘系统"""
|
||
import re
|
||
import random
|
||
import logging
|
||
from typing import Tuple, Optional, List
|
||
from games.base import BaseGame
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class DiceGame(BaseGame):
|
||
"""骰娘游戏"""
|
||
|
||
# 骰子指令正则模式
|
||
# 匹配:.r 3d6, .r 1d20+5, .r 2d10-3等
|
||
DICE_PATTERN = re.compile(
|
||
r'^\.r(?:oll)?\s+(\d+)d(\d+)(?:([+-])(\d+))?',
|
||
re.IGNORECASE
|
||
)
|
||
|
||
# 最大限制
|
||
MAX_DICE_COUNT = 100
|
||
MAX_DICE_SIDES = 1000
|
||
|
||
async def handle(self, command: str, chat_id: int, user_id: int) -> str:
|
||
"""处理骰子指令
|
||
|
||
Args:
|
||
command: 指令,如 ".r 1d20" 或 ".r 3d6+5"
|
||
chat_id: 会话ID
|
||
user_id: 用户ID
|
||
|
||
Returns:
|
||
回复消息
|
||
"""
|
||
try:
|
||
# 解析指令
|
||
result = self._parse_command(command)
|
||
if not result:
|
||
return self.get_help()
|
||
|
||
dice_count, dice_sides, modifier, modifier_value = result
|
||
|
||
# 验证参数
|
||
if dice_count > self.MAX_DICE_COUNT:
|
||
return f"❌ 骰子数量不能超过 {self.MAX_DICE_COUNT}"
|
||
|
||
if dice_sides > self.MAX_DICE_SIDES:
|
||
return f"❌ 骰子面数不能超过 {self.MAX_DICE_SIDES}"
|
||
|
||
if dice_count <= 0 or dice_sides <= 0:
|
||
return "❌ 骰子数量和面数必须大于0"
|
||
|
||
# 掷骰子
|
||
rolls = [random.randint(1, dice_sides) for _ in range(dice_count)]
|
||
total = sum(rolls)
|
||
|
||
# 应用修正值
|
||
final_result = total
|
||
if modifier:
|
||
if modifier == '+':
|
||
final_result = total + modifier_value
|
||
elif modifier == '-':
|
||
final_result = total - modifier_value
|
||
|
||
# 格式化输出
|
||
return self._format_result(
|
||
dice_count, dice_sides, rolls, total,
|
||
modifier, modifier_value, final_result
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.error(f"处理骰子指令错误: {e}", exc_info=True)
|
||
return f"❌ 处理指令出错: {str(e)}"
|
||
|
||
def _parse_command(self, command: str) -> Optional[Tuple[int, int, Optional[str], int]]:
|
||
"""解析骰子指令
|
||
|
||
Args:
|
||
command: 指令字符串
|
||
|
||
Returns:
|
||
(骰子数量, 骰子面数, 修正符号, 修正值) 或 None
|
||
"""
|
||
match = self.DICE_PATTERN.match(command.strip())
|
||
if not match:
|
||
return None
|
||
|
||
dice_count = int(match.group(1))
|
||
dice_sides = int(match.group(2))
|
||
modifier = match.group(3) # '+' 或 '-' 或 None
|
||
modifier_value = int(match.group(4)) if match.group(4) else 0
|
||
|
||
return dice_count, dice_sides, modifier, modifier_value
|
||
|
||
def _format_result(self, dice_count: int, dice_sides: int, rolls: List[int],
|
||
total: int, modifier: Optional[str], modifier_value: int,
|
||
final_result: int) -> str:
|
||
"""格式化骰子结果
|
||
|
||
Args:
|
||
dice_count: 骰子数量
|
||
dice_sides: 骰子面数
|
||
rolls: 各个骰子结果
|
||
total: 骰子总和
|
||
modifier: 修正符号
|
||
modifier_value: 修正值
|
||
final_result: 最终结果
|
||
|
||
Returns:
|
||
格式化的Markdown消息
|
||
"""
|
||
# 构建表达式
|
||
expression = f"{dice_count}d{dice_sides}"
|
||
if modifier:
|
||
expression += f"{modifier}{modifier_value}"
|
||
|
||
# Markdown格式输出
|
||
text = f"## 🎲 掷骰结果\n\n"
|
||
text += f"**表达式**:{expression}\n\n"
|
||
|
||
# 显示每个骰子的结果
|
||
if dice_count <= 20: # 骰子数量不多时,显示详细结果
|
||
rolls_str = ", ".join([f"**{r}**" for r in rolls])
|
||
text += f"**骰子**:[{rolls_str}]\n\n"
|
||
text += f"**点数和**:{total}\n\n"
|
||
|
||
if modifier:
|
||
text += f"**修正**:{modifier}{modifier_value}\n\n"
|
||
text += f"**最终结果**:<font color='#FF6B6B'>{final_result}</font>\n\n"
|
||
else:
|
||
text += f"**最终结果**:<font color='#FF6B6B'>{final_result}</font>\n\n"
|
||
else:
|
||
# 骰子太多,只显示总和
|
||
text += f"**点数和**:{total}\n\n"
|
||
if modifier:
|
||
text += f"**修正**:{modifier}{modifier_value}\n\n"
|
||
text += f"**最终结果**:<font color='#FF6B6B'>{final_result}</font>\n\n"
|
||
|
||
# 特殊提示
|
||
if dice_count == 1:
|
||
if rolls[0] == dice_sides:
|
||
text += "✨ **大成功!**\n"
|
||
elif rolls[0] == 1:
|
||
text += "💥 **大失败!**\n"
|
||
|
||
return text
|
||
|
||
def get_help(self) -> str:
|
||
"""获取帮助信息"""
|
||
return """## 🎲 骰娘系统帮助
|
||
|
||
### 基础用法
|
||
- `.r 1d20` - 掷一个20面骰
|
||
- `.r 3d6` - 掷三个6面骰
|
||
- `.r 2d10+5` - 掷两个10面骰,结果加5
|
||
- `.r 1d20-3` - 掷一个20面骰,结果减3
|
||
|
||
### 说明
|
||
- 格式:`.r XdY+Z`
|
||
- X = 骰子数量(最多100个)
|
||
- Y = 骰子面数(最多1000面)
|
||
- Z = 修正值(可选)
|
||
- 支持 + 和 - 修正
|
||
- 单个d20骰出20为大成功,骰出1为大失败
|
||
|
||
### 示例
|
||
```
|
||
.r 1d6 → 掷一个6面骰
|
||
.r 4d6 → 掷四个6面骰
|
||
.r 1d20+5 → 1d20并加5
|
||
.r 3d6-2 → 3d6并减2
|
||
```
|
||
"""
|
||
|