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
|
import asyncio
|
||||||
from random import choice, randint
|
from random import choice, randint
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import niobot
|
import niobot # type: ignore
|
||||||
from niobot import (
|
from niobot import (
|
||||||
CommandParserError,
|
CommandParserError,
|
||||||
Context,
|
Context,
|
||||||
Module,
|
Module,
|
||||||
NioBot,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from .stubs import ERROR_STARTS, get_random_stub, format_mention
|
from .stubs import ERROR_STARTS, get_random_stub
|
||||||
|
|
||||||
|
|
||||||
_DICE_LOCATIONS = [
|
_DICE_LOCATIONS = [
|
||||||
|
@ -19,20 +23,44 @@ _DICE_LOCATIONS = [
|
||||||
"spins around and a number of dice appear in his hands.",
|
"spins around and a number of dice appear in his hands.",
|
||||||
"opens the closest drawer and takes out a container of random dice.",
|
"opens the closest drawer and takes out a container of random dice.",
|
||||||
"powers on a tablet and executes a dice-rolling application.",
|
"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):
|
class ChanceModule(Module):
|
||||||
"""Commands that simulate chance (Dice rolls, card selection, etc)."""
|
"""Commands that simulate chance (Dice rolls, card selection, etc)."""
|
||||||
|
|
||||||
def __init__(self, bot: NioBot):
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
@niobot.command()
|
@niobot.command()
|
||||||
async def roll(self, ctx: Context, dice: str):
|
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)
|
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:
|
if valid is None:
|
||||||
await ctx.client.send_message(
|
await ctx.client.send_message(
|
||||||
ctx.room,
|
ctx.room,
|
||||||
|
@ -42,8 +70,9 @@ class ChanceModule(Module):
|
||||||
content_type="html",
|
content_type="html",
|
||||||
)
|
)
|
||||||
raise CommandParserError("Invalid dice roll notation.")
|
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:
|
if not 1 <= dice_parts[0] <= 100:
|
||||||
await ctx.client.send_message(
|
await ctx.client.send_message(
|
||||||
ctx.room,
|
ctx.room,
|
||||||
|
@ -64,26 +93,100 @@ class ChanceModule(Module):
|
||||||
)
|
)
|
||||||
raise CommandParserError("Dice must have between 2 and 100 faces.")
|
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] = []
|
rolls: list[int] = []
|
||||||
for _ in range(dice_parts[0]):
|
for _ in range(dice_parts[0]):
|
||||||
rolls.append(randint(1, dice_parts[1]))
|
rolls.append(randint(1, dice_parts[1]))
|
||||||
total = sum(rolls)
|
total = sum(rolls)
|
||||||
|
|
||||||
|
# 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(
|
await ctx.client.send_message(
|
||||||
ctx.room,
|
ctx.room,
|
||||||
choice(_DICE_LOCATIONS),
|
(
|
||||||
|
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",
|
message_type="m.emote",
|
||||||
content_type="html",
|
content_type="html",
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
# Give some delay to allow for reading the emote.
|
# html_sender = format_mention(ctx.message.sender)
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
html_sender = format_mention(ctx.message.sender)
|
|
||||||
await ctx.client.send_message(
|
await ctx.client.send_message(
|
||||||
ctx.room,
|
ctx.room,
|
||||||
f"{html_sender}: {dice} = {total} ({rolls})",
|
(
|
||||||
|
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,
|
reply_to=ctx.message,
|
||||||
message_type="m.text",
|
message_type="m.emote",
|
||||||
content_type="html",
|
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
|
from random import choice
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue