219 lines
6.6 KiB
Python
219 lines
6.6 KiB
Python
import os
|
|
import re
|
|
import random
|
|
from typing import Dict, List, Tuple
|
|
import discord
|
|
from discord import app_commands
|
|
from dotenv import load_dotenv
|
|
|
|
# Load environment variables
|
|
load_dotenv()
|
|
TOKEN = os.getenv('DISCORD_TOKEN')
|
|
|
|
# Initialize Discord client
|
|
class DiceBot(discord.Client):
|
|
def __init__(self):
|
|
super().__init__(intents=discord.Intents.default())
|
|
self.tree = app_commands.CommandTree(self)
|
|
|
|
async def setup_hook(self):
|
|
await self.tree.sync()
|
|
|
|
client = DiceBot()
|
|
|
|
# Genesys dice definitions
|
|
GENESYS_DICE = {
|
|
'g': [ # Green (Ability)
|
|
('', ''),
|
|
('s', ''),
|
|
('s', ''),
|
|
('ss', ''),
|
|
('a', ''),
|
|
('a', ''),
|
|
('s,a', ''),
|
|
('aa', ''),
|
|
],
|
|
'y': [ # Yellow (Proficiency)
|
|
('', ''),
|
|
('s', ''),
|
|
('s', ''),
|
|
('ss', ''),
|
|
('ss', ''),
|
|
('a', ''),
|
|
('s,a', ''),
|
|
('s,a', ''),
|
|
('s,a', ''),
|
|
('aa', ''),
|
|
('aa', ''),
|
|
('t', ''), # Triumph counts as success
|
|
],
|
|
'p': [ # Purple (Difficulty)
|
|
('', ''),
|
|
('f', ''),
|
|
('f,d', ''),
|
|
('d', ''),
|
|
('d', ''),
|
|
('d', ''),
|
|
('dd', ''),
|
|
('f', ''),
|
|
],
|
|
'r': [ # Red (Challenge)
|
|
('', ''),
|
|
('f', ''),
|
|
('f', ''),
|
|
('f,d', ''),
|
|
('f,d', ''),
|
|
('d', ''),
|
|
('d', ''),
|
|
('dd', ''),
|
|
('dd', ''),
|
|
('d,f', ''),
|
|
('d,f', ''),
|
|
('d', ''), # Despair counts as failure
|
|
],
|
|
'b': [ # Blue (Boost)
|
|
('', ''),
|
|
('', ''),
|
|
('s', ''),
|
|
('s,a', ''),
|
|
('aa', ''),
|
|
('a', ''),
|
|
],
|
|
'k': [ # Black (Setback)
|
|
('', ''),
|
|
('', ''),
|
|
('f', ''),
|
|
('f', ''),
|
|
('d', ''),
|
|
('d', ''),
|
|
]
|
|
}
|
|
|
|
def parse_dice_notation(notation: str) -> List[Tuple[int, str]]:
|
|
"""Parse dice notation into a list of (count, die_type) tuples."""
|
|
parts = notation.lower().replace(' ', '').split('+')
|
|
dice = []
|
|
|
|
for part in parts:
|
|
match = re.match(r'(\d+)d([0-9gpbkry]+)', part)
|
|
if match:
|
|
count = int(match.group(1))
|
|
die_type = match.group(2)
|
|
dice.append((count, die_type))
|
|
|
|
return dice
|
|
|
|
def roll_traditional_dice(count: int, sides: int) -> List[int]:
|
|
"""Roll traditional dice."""
|
|
return [random.randint(1, sides) for _ in range(count)]
|
|
|
|
def roll_genesys_die(die_type: str) -> Tuple[str, str]:
|
|
"""Roll a single Genesys die and return its result."""
|
|
return random.choice(GENESYS_DICE[die_type])
|
|
|
|
def calculate_genesys_results(results: List[Tuple[str, str]]) -> Dict[str, int]:
|
|
"""Calculate net results from Genesys dice rolls."""
|
|
success = 0
|
|
advantage = 0
|
|
triumph = 0
|
|
despair = 0
|
|
|
|
for result, _ in results:
|
|
for symbol in result.split(','):
|
|
if symbol == 's':
|
|
success += 1
|
|
elif symbol == 'f':
|
|
success -= 1
|
|
elif symbol == 'a':
|
|
advantage += 1
|
|
elif symbol == 'd':
|
|
advantage -= 1
|
|
elif symbol == 't':
|
|
triumph += 1
|
|
success += 1
|
|
elif symbol == 'x':
|
|
despair += 1
|
|
success -= 1
|
|
|
|
return {
|
|
'success': success,
|
|
'advantage': advantage,
|
|
'triumph': triumph,
|
|
'despair': despair
|
|
}
|
|
|
|
@client.tree.command(name="roll", description="Roll traditional dice (e.g., 2d6 + 1d8)")
|
|
async def roll(interaction: discord.Interaction, dice: str):
|
|
try:
|
|
dice_sets = parse_dice_notation(dice)
|
|
if not dice_sets:
|
|
await interaction.response.send_message("Invalid dice notation. Use format like '2d6 + 1d8'")
|
|
return
|
|
|
|
results = []
|
|
total = 0
|
|
|
|
for count, die in dice_sets:
|
|
if not die.isdigit():
|
|
await interaction.response.send_message(f"Invalid die type: {die} did you include +? Use format like '2d6 + 1d8'")
|
|
return
|
|
|
|
sides = int(die)
|
|
rolls = roll_traditional_dice(count, sides)
|
|
results.append(f"{count}d{sides}: {rolls} = {sum(rolls)}")
|
|
total += sum(rolls)
|
|
|
|
response = "\n".join(results)
|
|
if len(dice_sets) > 1:
|
|
response += f"\nTotal: {total}"
|
|
|
|
await interaction.response.send_message(response)
|
|
except Exception as e:
|
|
await interaction.response.send_message(f"Error: {str(e)}")
|
|
|
|
@client.tree.command(name="genesys", description="Roll Genesys/Star Wars dice (e.g., 2dg + 1dy)")
|
|
async def genesys(interaction: discord.Interaction, dice: str):
|
|
try:
|
|
dice_sets = parse_dice_notation(dice)
|
|
if not dice_sets:
|
|
await interaction.response.send_message("Invalid dice notation. Use format like '2dg + 1dy'")
|
|
return
|
|
|
|
all_results = []
|
|
for count, die_type in dice_sets:
|
|
if die_type not in GENESYS_DICE:
|
|
await interaction.response.send_message(f"Invalid die type: {die_type}, did you include +? Use format like '2dg + 1dy'")
|
|
return
|
|
|
|
for _ in range(count):
|
|
all_results.append(roll_genesys_die(die_type))
|
|
|
|
net_results = calculate_genesys_results(all_results)
|
|
|
|
response = []
|
|
if net_results['success'] > 0:
|
|
response.append(f"{net_results['success']} Success{'es' if net_results['success'] > 1 else ''}")
|
|
elif net_results['success'] < 0:
|
|
response.append(f"{abs(net_results['success'])} Failure{'s' if abs(net_results['success']) > 1 else ''}")
|
|
|
|
if net_results['advantage'] > 0:
|
|
response.append(f"{net_results['advantage']} Advantage{'s' if net_results['advantage'] > 1 else ''}")
|
|
elif net_results['advantage'] < 0:
|
|
response.append(f"{abs(net_results['advantage'])} Disadvantage{'s' if abs(net_results['advantage']) > 1 else ''}")
|
|
|
|
if net_results['triumph'] > 0:
|
|
response.append(f"{net_results['triumph']} Triumph{'s' if net_results['triumph'] > 1 else ''}")
|
|
if net_results['despair'] > 0:
|
|
response.append(f"{net_results['despair']} Despair{'s' if net_results['despair'] > 1 else ''}")
|
|
|
|
await interaction.response.send_message(" | ".join(response) if response else "No symbols rolled")
|
|
except Exception as e:
|
|
await interaction.response.send_message(f"Error: {str(e)}")
|
|
|
|
@client.event
|
|
async def on_ready():
|
|
print(f'{client.user} has connected to Discord!')
|
|
|
|
if __name__ == "__main__":
|
|
client.run(TOKEN)
|