From 6d8ba18380692c238edaada535b34f5e9925b8d9 Mon Sep 17 00:00:00 2001 From: "Brian S. Stephan" Date: Sat, 8 Feb 2025 23:51:00 -0600 Subject: [PATCH] cypher: distinguish between a task roll and an attack roll attack = A, otherwise the same options as with a generic task (= T) Signed-off-by: Brian S. Stephan --- dice/ircplugin.py | 8 +++++--- dice/lib.py | 7 ++++--- tests/test_dice_ircplugin.py | 18 ++++++++++++++---- tests/test_dice_lib.py | 14 ++++++++++++-- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/dice/ircplugin.py b/dice/ircplugin.py index 13e29fe..eb28eea 100644 --- a/dice/ircplugin.py +++ b/dice/ircplugin.py @@ -13,7 +13,7 @@ from ircbot.lib import Plugin logger = logging.getLogger(__name__) -CYPHER_ROLL_REGEX = r'(T(?P\d+))?(?P(?:\s*(-|\+)\d+)*)\s*(?P.*)?' +CYPHER_ROLL_REGEX = r'((?PA|T)(?P\d+))?(?P(?:\s*(-|\+)\d+)*)\s*(?P.*)?' CYPHER_COMMAND_REGEX = r'^!cypher\s+(' + CYPHER_ROLL_REGEX + ')' @@ -52,8 +52,9 @@ class Dice(Plugin): 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) + result, beats, success, effect = cypher_roll(difficulty=difficulty, mods=mods, is_attack=is_attack) if success is not None: if success: @@ -81,7 +82,8 @@ class Dice(Plugin): if comment: return self.bot.reply(event, f"{nick}: {comment} {result_str} {detail_str}") else: - return self.bot.reply(event, f"{nick}: your check {result_str} {detail_str}") + 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.""" diff --git a/dice/lib.py b/dice/lib.py index 993b018..69ad230 100644 --- a/dice/lib.py +++ b/dice/lib.py @@ -6,12 +6,13 @@ import numexpr rand = random.SystemRandom() -def cypher_roll(difficulty=None, mods=0): +def cypher_roll(difficulty=None, mods=0, is_attack=False): """Make a Cypher System roll. Args: difficulty: the original difficulty to beat; if provided, success or failure is indicated in the results mods: eases(-) and hindrances(+) to apply to the check, as a string (e.g. '-3+1') + is_attack: if the roll is an attack (in which case the damage-only effects are included) Returns: tuple of: - the result on the d20 @@ -24,9 +25,9 @@ def cypher_roll(difficulty=None, mods=0): return (roll, None, False if difficulty else None, 'a GM intrusion') effect = None - if roll == 17: + if roll == 17 and is_attack: effect = '+1 damage' - elif roll == 18: + elif roll == 18 and is_attack: effect = '+2 damage' elif roll == 19: effect = 'a minor effect' diff --git a/tests/test_dice_ircplugin.py b/tests/test_dice_ircplugin.py index cdb88dc..10ed6dc 100644 --- a/tests/test_dice_ircplugin.py +++ b/tests/test_dice_ircplugin.py @@ -30,14 +30,24 @@ class MarkovTestCase(TestCase): mock_event.target = '#test' mock_event.recursing = False - # general task roll + # general task roll (no damage output on a 17) mock_event.arguments = ['!cypher T3'] match = re.search(dice.ircplugin.CYPHER_COMMAND_REGEX, mock_event.arguments[0]) with mock.patch('random.SystemRandom.randint', return_value=17): self.plugin.handle_cypher_roll(self.mock_connection, mock_event, match) self.mock_bot.reply.assert_called_with( mock_event, - 'test: your check 9succeeded, with +1 damage! 14(d20=17 vs. diff. 3)' + 'test: your check 9succeeded! 14(d20=17 vs. diff. 3)' + ) + + # general attack roll (incl. damage output on a 17) + mock_event.arguments = ['!cypher A3'] + match = re.search(dice.ircplugin.CYPHER_COMMAND_REGEX, mock_event.arguments[0]) + with mock.patch('random.SystemRandom.randint', return_value=17): + self.plugin.handle_cypher_roll(self.mock_connection, mock_event, match) + self.mock_bot.reply.assert_called_with( + mock_event, + 'test: your attack 9succeeded, with +1 damage! 14(d20=17 vs. diff. 3)' ) # general task roll, case insensitive @@ -47,7 +57,7 @@ class MarkovTestCase(TestCase): self.plugin.handle_cypher_roll(self.mock_connection, mock_event, match) self.mock_bot.reply.assert_called_with( mock_event, - 'test: your check 9succeeded, with +1 damage! 14(d20=17 vs. diff. 3)' + 'test: your check 9succeeded! 14(d20=17 vs. diff. 3)' ) # unknown target roll @@ -57,7 +67,7 @@ class MarkovTestCase(TestCase): self.plugin.handle_cypher_roll(self.mock_connection, mock_event, match) self.mock_bot.reply.assert_called_with( mock_event, - 'test: your check beats a difficulty 4 task, with +1 damage! 14(d20=17 with +1 levels)' + 'test: your check beats a difficulty 4 task. 14(d20=17 with +1 levels)' ) # no mod or known difficulty diff --git a/tests/test_dice_lib.py b/tests/test_dice_lib.py index 51156c6..89eeafd 100644 --- a/tests/test_dice_lib.py +++ b/tests/test_dice_lib.py @@ -26,15 +26,25 @@ class DiceLibTestCase(TestCase): result = dice.lib.cypher_roll(difficulty=1) self.assertEqual(result, (1, None, False, 'a GM intrusion')) + # rolled a 17 on an attack + with mock.patch('random.SystemRandom.randint', return_value=17): + result = dice.lib.cypher_roll(difficulty=1, is_attack=True) + self.assertEqual(result, (17, 5, True, '+1 damage')) + + # rolled a 18 on an attack + with mock.patch('random.SystemRandom.randint', return_value=18): + result = dice.lib.cypher_roll(difficulty=1, is_attack=True) + self.assertEqual(result, (18, 6, True, '+2 damage')) + # rolled a 17 with mock.patch('random.SystemRandom.randint', return_value=17): result = dice.lib.cypher_roll(difficulty=1) - self.assertEqual(result, (17, 5, True, '+1 damage')) + self.assertEqual(result, (17, 5, True, None)) # rolled a 18 with mock.patch('random.SystemRandom.randint', return_value=18): result = dice.lib.cypher_roll(difficulty=1) - self.assertEqual(result, (18, 6, True, '+2 damage')) + self.assertEqual(result, (18, 6, True, None)) # rolled a 19 with mock.patch('random.SystemRandom.randint', return_value=19):