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
|
|||
|
|
```
|
|||
|
|
"""
|
|||
|
|
|