Dice rolls are feature complete.
This commit is contained in:
parent
54f0ec1d29
commit
589ea29b6d
2 changed files with 135 additions and 27 deletions
|
@ -1,16 +1,20 @@
|
|||
"""
|
||||
Chance - Commands for simulating 'random' chance, such as dice rolls, picking
|
||||
cards from a deck, etc.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from random import choice, randint
|
||||
import re
|
||||
|
||||
import niobot
|
||||
import niobot # type: ignore
|
||||
from niobot import (
|
||||
CommandParserError,
|
||||
Context,
|
||||
Module,
|
||||
NioBot,
|
||||
)
|
||||
|
||||
from .stubs import ERROR_STARTS, get_random_stub, format_mention
|
||||
from .stubs import ERROR_STARTS, get_random_stub
|
||||
|
||||
|
||||
_DICE_LOCATIONS = [
|
||||
|
@ -19,20 +23,44 @@ _DICE_LOCATIONS = [
|
|||
"spins around and a number of dice appear in his hands.",
|
||||
"opens the closest drawer and takes out a container of random dice.",
|
||||
"powers on a tablet and executes a dice-rolling application.",
|
||||
"steals several dice from various board game boxes on the shelf.",
|
||||
]
|
||||
|
||||
_DICE_EXCLAMATIONS = [
|
||||
"Daddy needs a new pair of shoes!",
|
||||
"Blessed Mother of Acceleration, don't fail me now!",
|
||||
"Come on, Box Car Willy!",
|
||||
"I cast Magic Missle into the darkness!",
|
||||
"Lightning Bolt! Lightning Bolt! SLEEEP!",
|
||||
"RNGesus--please bless this roll we are about to receive.",
|
||||
"I'm going to backstab with a ballista!",
|
||||
"Who wants to blow on my dice!? anyone!? ...guys?",
|
||||
]
|
||||
|
||||
|
||||
class ChanceModule(Module):
|
||||
"""Commands that simulate chance (Dice rolls, card selection, etc)."""
|
||||
|
||||
def __init__(self, bot: NioBot):
|
||||
self.bot = bot
|
||||
|
||||
@niobot.command()
|
||||
async def roll(self, ctx: Context, dice: str):
|
||||
"""Roll some dice with an optional modifier."""
|
||||
"""
|
||||
Roll some dice with an optional modifier.
|
||||
|
||||
Syntax:
|
||||
!roll <n>d<s>[<+,-,*,/><m>]
|
||||
|
||||
n = number of dice (1-100)
|
||||
s = number of sides on dice (2-100)
|
||||
m = optional modifier (1-1000000)
|
||||
|
||||
Examples:
|
||||
!roll 1d20
|
||||
!roll 6d6+3
|
||||
"""
|
||||
err_prelude = get_random_stub(ERROR_STARTS, ctx.message.sender)
|
||||
valid = re.match(r"(\d+)d(\d+)([\+-/\*]\d+)?", dice)
|
||||
|
||||
# Validate the entire dice roll syntax
|
||||
valid = re.match(r"^(\d+)d(\d+)([\+-/\*]\d+)?", dice)
|
||||
if valid is None:
|
||||
await ctx.client.send_message(
|
||||
ctx.room,
|
||||
|
@ -42,8 +70,9 @@ class ChanceModule(Module):
|
|||
content_type="html",
|
||||
)
|
||||
raise CommandParserError("Invalid dice roll notation.")
|
||||
dice_parts = (int(valid.group(1)), int(valid.group(2)), valid.group(3))
|
||||
|
||||
# Validate number of dice and dice surface count.
|
||||
dice_parts = (int(valid.group(1)), int(valid.group(2)), valid.group(3))
|
||||
if not 1 <= dice_parts[0] <= 100:
|
||||
await ctx.client.send_message(
|
||||
ctx.room,
|
||||
|
@ -64,26 +93,100 @@ class ChanceModule(Module):
|
|||
)
|
||||
raise CommandParserError("Dice must have between 2 and 100 faces.")
|
||||
|
||||
# Validate the modifier, if one was detected.
|
||||
extra = dice.replace(f"{dice_parts[0]}d{dice_parts[1]}", "")
|
||||
if dice_parts[2] is None and extra != "":
|
||||
await ctx.client.send_message(
|
||||
ctx.room,
|
||||
(
|
||||
f"{err_prelude}that's an invalid modifier. I only take "
|
||||
f"addition (+), subtraction (-), multiplication (*), and "
|
||||
f"division (/). The number following must be between 1 and "
|
||||
f"1000000."
|
||||
),
|
||||
reply_to=ctx.message,
|
||||
message_type="m.text",
|
||||
content_type="html",
|
||||
)
|
||||
raise CommandParserError("Invalid dice roll modifier.")
|
||||
if dice_parts[2] is not None:
|
||||
modifier = int(dice_parts[2][1:])
|
||||
if not 1 <= modifier <= 1000000:
|
||||
await ctx.client.send_message(
|
||||
ctx.room,
|
||||
(
|
||||
f"{err_prelude}the modifier amount can only be between "
|
||||
f"1 and 1000000."
|
||||
),
|
||||
reply_to=ctx.message,
|
||||
message_type="m.text",
|
||||
content_type="html",
|
||||
)
|
||||
raise CommandParserError("Modifier must be between 1 and 1000000.")
|
||||
|
||||
# Tom gets the dice.
|
||||
await ctx.client.send_message(
|
||||
ctx.room,
|
||||
f"<em>{choice(_DICE_LOCATIONS)}</em>",
|
||||
message_type="m.emote",
|
||||
content_type="html",
|
||||
)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# Makes a silly comment before rolling.
|
||||
await ctx.client.send_message(
|
||||
ctx.room,
|
||||
f"{choice(_DICE_EXCLAMATIONS)}",
|
||||
message_type="m.text",
|
||||
content_type="html",
|
||||
)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
rolls: list[int] = []
|
||||
for _ in range(dice_parts[0]):
|
||||
rolls.append(randint(1, dice_parts[1]))
|
||||
total = sum(rolls)
|
||||
|
||||
await ctx.client.send_message(
|
||||
ctx.room,
|
||||
choice(_DICE_LOCATIONS),
|
||||
message_type="m.emote",
|
||||
content_type="html",
|
||||
)
|
||||
|
||||
# Give some delay to allow for reading the emote.
|
||||
await asyncio.sleep(1)
|
||||
|
||||
html_sender = format_mention(ctx.message.sender)
|
||||
await ctx.client.send_message(
|
||||
ctx.room,
|
||||
f"{html_sender}: {dice} = {total} ({rolls})",
|
||||
reply_to=ctx.message,
|
||||
message_type="m.text",
|
||||
content_type="html",
|
||||
)
|
||||
# Then reveals the results.
|
||||
if dice_parts[2] is not None:
|
||||
match dice_parts[2][0]:
|
||||
case "+":
|
||||
action = "adds"
|
||||
new_total = total + modifier
|
||||
case "-":
|
||||
action = "subtracts"
|
||||
new_total = total - modifier
|
||||
case "*":
|
||||
action = "multiplies by"
|
||||
new_total = total * modifier
|
||||
case "/":
|
||||
action = "divides by"
|
||||
new_total = total // modifier
|
||||
case _:
|
||||
raise CommandParserError("Strange dice modifier parse error.")
|
||||
await ctx.client.send_message(
|
||||
ctx.room,
|
||||
(
|
||||
f"<em>rolls <strong>{dice_parts[0]}d{dice_parts[1]}</strong> "
|
||||
f"with a count of <strong>{total}</strong>. He then "
|
||||
f"<strong>{action} {modifier}</strong> for a final count "
|
||||
f"of <strong>{new_total}</strong>.</em>"
|
||||
),
|
||||
reply_to=ctx.message,
|
||||
message_type="m.emote",
|
||||
content_type="html",
|
||||
)
|
||||
else:
|
||||
# html_sender = format_mention(ctx.message.sender)
|
||||
await ctx.client.send_message(
|
||||
ctx.room,
|
||||
(
|
||||
f"<em>rolls <strong>{dice_parts[0]}d{dice_parts[1]}</strong> "
|
||||
f"and the final count is <strong>{total}</strong>.</em>"
|
||||
),
|
||||
reply_to=ctx.message,
|
||||
message_type="m.emote",
|
||||
content_type="html",
|
||||
)
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
"""
|
||||
Stubs - A collection of personalized message starters/finishers and functions to
|
||||
give the bot some personality.
|
||||
"""
|
||||
|
||||
from random import choice
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue