"""Roll dice when asked, intended for RPGs.""" import logging import random import re from django.conf import settings from irc.client import NickMask from dice.lib import cypher_roll, reaction_roll from dice.roller import DiceRoller from ircbot.lib import Plugin logger = logging.getLogger(__name__) CYPHER_ROLL_REGEX = r'((?PA|T)(?P\d+))?(?P(?:\s*(-|\+)\d+)*)\s*(?P.*)?' CYPHER_COMMAND_REGEX = r'^!cypher\s+(' + CYPHER_ROLL_REGEX + ')' REACTION_COMMAND_REGEX = r'^!reaction$' class Dice(Plugin): """Roll simple or complex dice strings.""" def __init__(self, bot, connection, event): """Set up the plugin.""" self.roller = DiceRoller() super(Dice, self).__init__(bot, connection, event) def start(self): """Set up the handlers.""" self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], CYPHER_COMMAND_REGEX, self.handle_cypher_roll, -20) self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!roll\s+(.*)$', self.handle_roll, -20) self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!random\s+(.*)$', self.handle_random, -20) self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], REACTION_COMMAND_REGEX, self.handle_reaction_roll, -20) super(Dice, self).start() def stop(self): """Tear down handlers.""" self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_roll) self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_random) self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_cypher_roll) self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_reaction_roll) super(Dice, self).stop() def handle_cypher_roll(self, connection, event, match): """Handle the !cypher roll.""" nick = NickMask(event.source).nick task = match.group(1) task_group = re.search(CYPHER_ROLL_REGEX, task, re.IGNORECASE) difficulty = int(task_group.group('difficulty')) if task_group.group('difficulty') else None mods = task_group.group('mods') is_attack = True if task_group.group('type') and task_group.group('type').upper() == 'A' else False comment = task_group.group('comment') result, beats, success, effect = cypher_roll(difficulty=difficulty, mods=mods, is_attack=is_attack) if success is not None: if success: if effect: result_str = f"9succeeded, with {effect}!" else: result_str = "9succeeded!" else: if effect: result_str = f"4failed, with {effect}!" else: result_str = "4failed." else: if effect: result_str = f"beats a difficulty {beats} task, with {effect}!" else: result_str = f"beats a difficulty {beats} task." if success is not None: # show the adjusted difficulty detail_str = f"14(d20={result} vs. diff. {difficulty}{mods})" else: detail_str = f"14(d20={result}{f' with {mods} levels' if mods else ''})" if comment: return self.bot.reply(event, f"{nick}: {comment} {result_str} {detail_str}") else: type_str = 'attack' if is_attack else 'check' return self.bot.reply(event, f"{nick}: your {type_str} {result_str} {detail_str}") def handle_random(self, connection, event, match): """Handle the !random command which picks an item from a list.""" nick = NickMask(event.source).nick choices = match.group(1) choices_list = choices.split(' ') choice = random.SystemRandom().choice(choices_list) logger.debug(event.recursing) if event.recursing: reply = "{0:s}".format(choice) elif settings.DICE_PREFIX_ROLLER: reply = "{0:s}: {1:s}".format(nick, choice) else: reply = "{0:s}".format(choice) return self.bot.reply(event, reply) def handle_reaction_roll(self, connection, event, match): """Handle the !reaction roll.""" nick = NickMask(event.source).nick roll, label, summary = reaction_roll() if summary in ('--', '-'): result_str = f"4{label}" elif summary == '~': result_str = f"9{label}" else: result_str = f"9{label}" return self.bot.reply(event, f"{nick}: the current disposition is {result_str} 14({roll})") def handle_roll(self, connection, event, match): """Handle the !roll command which covers most common dice stuff.""" nick = NickMask(event.source).nick dicestr = match.group(1) logger.debug(event.recursing) try: reply_str = self.roller.do_roll(dicestr) except AssertionError as aex: reply_str = f"Could not roll dice: {aex}" except ValueError: reply_str = "Unable to parse roll" if event.recursing: reply = "{0:s}".format(reply_str) elif settings.DICE_PREFIX_ROLLER: reply = "{0:s}: {1:s}".format(nick, reply_str) else: reply = "{0:s}".format(reply_str) return self.bot.reply(event, re.sub(r'(\d+)(.*?\s+)(\(.*?\))', r'\1\214\3', reply)) plugin = Dice