From 1ae050c3b2bc90c7d7c86413530ff947bfc7852e Mon Sep 17 00:00:00 2001 From: silverwizard Date: Fri, 18 Jun 2021 21:19:40 -0400 Subject: [PATCH] Created basic tools for Shadowrun dice --- README.md | 3 + __pycache__/shadowdice.cpython-38.pyc | Bin 0 -> 12420 bytes aliases.csv | 2 + help.html | 9 ++ metadata.ini | 29 ++++ privileges.csv | 5 + resources/__pycache__/strings.cpython-38.pyc | Bin 0 -> 406 bytes resources/strings.py | 10 ++ shadowdice.py | 141 +++++++++++++++++++ tests/test_randomizer_metadata.py | 23 +++ 10 files changed, 222 insertions(+) create mode 100644 README.md create mode 100644 __pycache__/shadowdice.cpython-38.pyc create mode 100644 aliases.csv create mode 100644 help.html create mode 100644 metadata.ini create mode 100644 privileges.csv create mode 100644 resources/__pycache__/strings.cpython-38.pyc create mode 100644 resources/strings.py create mode 100644 shadowdice.py create mode 100644 tests/test_randomizer_metadata.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..b338411 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +A simple plugin for [JJMumbleBot](https://duckboss.github.io/JJMumbleBot/wiki/new/whats_new.html) which should do basic dice. See the !help shadowdice command for options. + +Simply drop this file in the extensions folder for JJMumbleBot and it should just start working diff --git a/__pycache__/shadowdice.cpython-38.pyc b/__pycache__/shadowdice.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b9659ba53dc9db13c888fcb4a87930a82456cf4 GIT binary patch literal 12420 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 OKo{fs|N8nz?0*3WvWc|- literal 0 HcmV?d00001 diff --git a/aliases.csv b/aliases.csv new file mode 100644 index 0000000..18daf09 --- /dev/null +++ b/aliases.csv @@ -0,0 +1,2 @@ +alias,command +predge,(preedge) diff --git a/help.html b/help.html new file mode 100644 index 0000000..19f638d --- /dev/null +++ b/help.html @@ -0,0 +1,9 @@ +All commands can be run by typing it in the chat or privately messaging JJMumbleBot.
+!init: Roll init dice !init 4 11 will roll 4d6+11
+!srun: Roll a bunch of dice, count successes and ones !srun 12 will roll a pool of 12 dice
+!preedge: Roll a pool with exploding 6s, counts successes, ones, and explosions !preedge 12 will roll a poll of 12 dice
+!assensing: Prints the Shadowrun assensing table
+!concealability: Prints the Shadowrun concealability modifiers table
+!perception_mods: Prints the Shadowrun perception modifiers table
+!delivery: Prints the Shadowrun delivery times table
+!environmental: Prints the Shadowrun environmental modifiers table
diff --git a/metadata.ini b/metadata.ini new file mode 100644 index 0000000..9c7d0ce --- /dev/null +++ b/metadata.ini @@ -0,0 +1,29 @@ +[Plugin Information] +PluginVersion = 1.0.0 +PluginName = Shadow Dice +PluginDescription = A plugin for rolling dice in Shadowrun 5e. +PluginLanguage = EN +PluginCommands: [ + "init", + "srun", + "preedge", + "assensing", + "delivery", + "perception_mods", + "environmental", + "concealability" + ] + +[Plugin Settings] +; List commands that need the core thread to wait for completion. +; This may include processes that require multiple commands in succession. +; For example: [Youtube Plugin - !yt -> !p] process requires 2 commands in that order. +ThreadWaitForCommands: [] +UseSingleThread = False + +[Plugin Type] +ControllablePlugin = True +AudioPlugin = False +ImagePlugin = False +CorePlugin = False +ExtensionPlugin = True diff --git a/privileges.csv b/privileges.csv new file mode 100644 index 0000000..f2deeff --- /dev/null +++ b/privileges.csv @@ -0,0 +1,5 @@ +command,level +init,1 +srun,1 +preedge,1 +assensing,1 diff --git a/resources/__pycache__/strings.cpython-38.pyc b/resources/__pycache__/strings.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ec22b70b2d5659ff03c927f6ba7bbee57111e31 GIT binary patch literal 406 zcmYjNJ5K^Z5Z*lyiGjk(_IxBKg?FK_A;zen34}*-hy~4h+zz;5A7ppW$F=?pV`A%H zqP6lbSQr;#!6fs|d^3~Gd^4(6caXFBhkws8LSHMgX+=jaoSOp&6j98Pz+;RN^=JX` zD|$n;_+FTKV~=hPQLXgt=1>hUcV}Qps)R@+C05Nr=113YJct{9Tc%2DNUi_h_oqsW z#9Ah2`+50hO&WfFoMy%tYkoScUBu)bM|7Cf6VX(D*GAPrM4)lRz-EC(HFFe=mT*vMGzbeeGSvMiqo TMKccP?(@i$98|VFT>1S2_Lq5L literal 0 HcmV?d00001 diff --git a/resources/strings.py b/resources/strings.py new file mode 100644 index 0000000..c92797f --- /dev/null +++ b/resources/strings.py @@ -0,0 +1,10 @@ +from JJMumbleBot.lib.utils.runtime_utils import get_command_token + +########################################################################### +# RANDOMIZER PLUGIN CONFIG PARAMETER STRINGS + +# COMMAND ERROR STRINGS +CMD_INVALID_CUSTOM_ROLL = [ + "ERROR: Incorrect command formatting!", + f"Format: {get_command_token()}customroll 'number_of_dice' 'dice_faces'" +] diff --git a/shadowdice.py b/shadowdice.py new file mode 100644 index 0000000..8c9c7ec --- /dev/null +++ b/shadowdice.py @@ -0,0 +1,141 @@ +from JJMumbleBot.lib.plugin_template import PluginBase +from JJMumbleBot.lib.utils.plugin_utils import PluginUtilityService +from JJMumbleBot.lib.utils.logging_utils import log +from JJMumbleBot.lib.utils.print_utils import PrintMode +from JJMumbleBot.plugins.extensions.randomizer.resources.strings import CMD_INVALID_CUSTOM_ROLL +from JJMumbleBot.settings import global_settings as gs +from JJMumbleBot.lib.resources.strings import * +import os +import random + + +class Plugin(PluginBase): + def __init__(self): + super().__init__() + from json import loads + self.plugin_name = os.path.basename(__file__).rsplit('.')[0] + self.metadata = PluginUtilityService.process_metadata(f'plugins/extensions/{self.plugin_name}') + self.plugin_cmds = loads(self.metadata.get(C_PLUGIN_INFO, P_PLUGIN_CMDS)) + self.is_running = True + log( + INFO, + f"{self.metadata[C_PLUGIN_INFO][P_PLUGIN_NAME]} v{self.metadata[C_PLUGIN_INFO][P_PLUGIN_VERS]} Plugin Initialized.", + origin=L_STARTUP, + print_mode=PrintMode.REG_PRINT.value + ) + + def quit(self): + self.is_running = False + log( + INFO, + f"Exiting {self.plugin_name} plugin...", + origin=L_SHUTDOWN, + print_mode=PrintMode.REG_PRINT.value + ) + + def stop(self): + if self.is_running: + self.quit() + + def start(self): + if not self.is_running: + self.__init__() + + def cmd_init(self, data): + all_data = data.message.strip().split() + try: + number_of_dice = int(all_data[1]) + init_bonus = int(all_data[2]) + ret_text = "
Init Roll:
" + result = 0 + for i in range(number_of_dice): + random.seed(int.from_bytes(os.urandom(8), byteorder="big")) + this_die = random.randint(1,6) + result = result + this_die + ret_text += f"{this_die} + " + result = result + init_bonus + ret_text += f" {init_bonus}
{result}" + gs.gui_service.quick_gui(ret_text, text_type='header', box_align='left') + return + except IndexError: + log(ERROR, CMD_INVALID_CUSTOM_ROLL, + origin=L_COMMAND, error_type=CMD_INVALID_ERR, print_mode=PrintMode.VERBOSE_PRINT.value) + gs.gui_service.quick_gui(CMD_INVALID_CUSTOM_ROLL, + text_type='header', box_align='left') + return + + def cmd_srun(self, data): + all_data = data.message.strip().split() + try: + number_of_dice = int(all_data[1]) + ret_text = "
Die Pool:
" + successes = 0 + ones = 0 + for i in range(number_of_dice): + random.seed(int.from_bytes(os.urandom(8), byteorder="big")) + this_die = random.randint(1,6) + ret_text += f"{this_die}, " + if this_die > 4: + successes = successes + 1 + if this_die == 1: + ones = ones + 1 + ret_text += f"
Successes: {successes} , Ones: {ones}" + gs.gui_service.quick_gui(ret_text, text_type='header', box_align='left') + return + except IndexError: + log(ERROR, CMD_INVALID_CUSTOM_ROLL, + origin=L_COMMAND, error_type=CMD_INVALID_ERR, print_mode=PrintMode.VERBOSE_PRINT.value) + gs.gui_service.quick_gui(CMD_INVALID_CUSTOM_ROLL, + text_type='header', box_align='left') + return + + def cmd_preedge(self, data): + all_data = data.message.strip().split() + try: + number_of_dice = int(all_data[1]) + ret_text = "
Die Pool:
" + successes = 0 + ones = 0 + explosions = 0 + i = 0 + while i < number_of_dice: + random.seed(int.from_bytes(os.urandom(8), byteorder="big")) + this_die = random.randint(1,6) + ret_text += f"{this_die}, " + if this_die > 4: + successes = successes + 1 + if this_die == 1: + ones = ones + 1 + if this_die == 6: + i = i-1 + explosions = explosions + 1 + i = i + 1 + ret_text += f"
Successes: {successes} , Ones: {ones} , Explosions: {explosions}" + gs.gui_service.quick_gui(ret_text, text_type='header', box_align='left') + return + except IndexError: + log(ERROR, CMD_INVALID_CUSTOM_ROLL, + origin=L_COMMAND, error_type=CMD_INVALID_ERR, print_mode=PrintMode.VERBOSE_PRINT.value) + gs.gui_service.quick_gui(CMD_INVALID_CUSTOM_ROLL, + text_type='header', box_align='left') + return + + def cmd_assensing(self, data): + ret_text = "
ASSENSING TABLE
HITSINFORMATION GAINED
0None
1The general state of the subject’s health (healthy, injured, ill, etc.). The subject’s general emotional state or impression (happy, sad, angry, etc.). Whether the subject is mundane or Awakened.
2The presence and location of cyberware implants. The class of a magical subject (fire elemental, manipulation spell, power focus, curse ritual, and so on). If you have seen the subject’s aura before, you may recognize it, regardless of physical disguises or alterations
3The presence and location of alphaware cyber implants. Whether the subject’s Essence and Magic are higher, lower, or equal to your own. Whether the subject’s Force is higher, lower, or equal to your Magic. A general diagnosis for any maladies (diseases or toxins) the subject suffers. Any astral signatures present on the subject.
4The presence and location of bioware implants and betaware cyber implants. The exact Essence, Magic, and Force of the subject. The general cause of any astral signature (combat spell, alchemical combat spell, air spirit, and so on).
5+The presence and location of deltaware implants, gene treatments, and nanotech. An accurate diagnosis of any disease or toxins which afflict the subject. The fact that a subject is a technomancer.
" + gs.gui_service.quick_gui(ret_text, text_type='header', box_align='center') + + def cmd_delivery(self, data): + ret_text = "
DELIVERY TIMES TABLE
GEAR COST DELIVERY TIME
Up to 100¥ 6 hours
101¥ to 1,000¥ 1 day
1,000¥ to 10,000¥ 2 days
10,001 to 100,000¥ 1 week
More than 100,000¥ 1 month
" + gs.gui_service.quick_gui(ret_text, text_type='header', box_align='center') + + def cmd_concealability(self, data): + ret_text = "
CONCEALABILITY MODIFIERS *Applies to observer
MODIFIER* EXAMPLE ITEMS
–6 RFID tag, bug slap patch, microdrone, contact lenses
–4 Hold-out pistol, monowhip, ammo clip, credstick, chips/softs, sequencer/passkey, autopicker, lockpick set, commlink, glasses
–2 Light pistol, knife, sap, minidrone, microgrenade, flash-pak, jammer, cyberdeck, rigger command console
0 Heavy pistol, machine pistol with folding stock collapsed, grenade, goggles, ammo belt/drum, club, extendable baton (collapsed)
+2 SMG, machine pistol with folding stock extended, medkit, small drone, extendable baton (extended), stun baton
+4 Sword, sawed-off shotgun, bullpup assault rifle
+6 Katana, monosword, shotgun, assault rifle, sport rifle, crossbow
+8 Sniper rifle, bow, grenade launcher, medium drone
+10/Forget about it Machine gun, rocket launcher, missile launcher, staff, claymore, metahuman body
" + gs.gui_service.quick_gui(ret_text, text_type='header', box_align='center') + + def cmd_perception_mods(self, data): + ret_text = "
PERCEPTION TEST MODIFIERS
SITUATION DICE POOL MODIFIER
Perceiver is distracted -2
Perciever is specifically looking/listening for it +3
Object/sound not in immediate vicinity -2
Object/sound far away -3
Object/sound stands out in some way +2
Interfering sight/odor/sound -2
Perceiver has active enhancements + Rating
Visibility and Light Environmental Factors (Above)
PERCEPTION THRESHOLDS
ITEM/EVENT IS: THRESHOLD EXAMPLES
Obvious 1 Neon sign, running crowd, yelling, gunfire
Normal 2 Street sign, pedestrian, conversation, silenced gunfire
Obscured/Small/Muffled 3 Item dropped under table, contact lens, whispering
Hidden/Micro/Silent 4 Secret door, needle in haystack, subvocal speech
" + gs.gui_service.quick_gui(ret_text, text_type='header', box_align='center') + + def cmd_environmental(self, data): + ret_text = "
ENVIRONMENTAL
VISIBILITY LIGHT / GLARE WIND RANGE MODIFIER
Clear Full Light / No Glare None / Light Breeze Short 0
Light Rain / Fog / Smoke Partial Light / Weak Glare Light Winds / Light Breeze Medium -1
Moderate Rain / Fog / Smoke Dim Light / Moderate Glare Moderate Winds / Light Breeze Long -3
Heavy Rain / Fog / Smoke Total Darkness/ Blinding Glare Strong Winds / Light Breeze Extreme -6
Combination of two or more conditions at the -6 level row -10
" + gs.gui_service.quick_gui(ret_text, text_type='header', box_align='center') diff --git a/tests/test_randomizer_metadata.py b/tests/test_randomizer_metadata.py new file mode 100644 index 0000000..f3abda3 --- /dev/null +++ b/tests/test_randomizer_metadata.py @@ -0,0 +1,23 @@ +import configparser +from json import loads +from JJMumbleBot.lib.utils.dir_utils import get_extension_plugin_dir +from JJMumbleBot.lib.resources.strings import C_PLUGIN_INFO, P_PLUGIN_VERS, P_PLUGIN_CMDS, C_PLUGIN_SET +from JJMumbleBot.plugins.extensions.randomizer.randomizer import Plugin + + +class TestRandomizer: + def setup_method(self): + # Initialize configs. + self.cfg = configparser.ConfigParser() + self.cfg.read(f"{get_extension_plugin_dir()}/randomizer/metadata.ini") + + def test_plugin_version(self): + assert self.cfg[C_PLUGIN_INFO][P_PLUGIN_VERS] == "1.0.0" + + def test_commands_list_size(self): + commands_list = list(loads(self.cfg[C_PLUGIN_INFO][P_PLUGIN_CMDS])) + assert len(commands_list) == 3 + + def test_match_commands_to_methods(self): + method_list = [item for item in dir(Plugin) if callable(getattr(Plugin, item)) and item.startswith("cmd_")] + assert len(method_list) == len(list(loads(self.cfg[C_PLUGIN_INFO][P_PLUGIN_CMDS])))