Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import discord
- import typing
- import json
- import random
- import os.path
- import dice
- import re
- import json
- import gzip
- import subprocess
- import tempfile
- from discord.ui import Item
- from math import sqrt
- from typing import Optional
- from typing import Union
- from discord.ext import commands
- from dotenvy import load_env, read_file
- from os import environ
- load_env(read_file('combatbot.env'))
- TOKEN = environ.get('DISCORD_TOKEN')
- p = '=' # blocks for health in hpbar
- d = '_' # blocks for damage in hpbar
- chunk_total = 12 # total number of blocks in hpbar
- init_order = [] # list of names and initiative rolls, sorted highest>lowest
- init_names = [] # names in init order w/out rolls
- init_names_e = [] # enemy names in initiative
- init_names_p = [] # player names in initiative
- current_turn = 0 # the current player turn number
- turn_num = 1 # current actual turn number
- current_round = 1 # the current round number
- total_turns = 1 # total number of turns
- ko = 0
- player_turn = '' # the player(or enemy) who's turn it is
- init_round = False # determines whether or not the initiative round is active
- in_combat = False # determines whether or not combat is active
- hpbar = '' # variable for drawing hpbar
- statlist = '' # variable for drawing stat list
- stats_name = '' # variable for drawing name
- entity = ''
- embed = '' # variable for defining embeds
- hp_out = ''
- all_hp = {} # list of everyone's hp
- available_dice_assets = ['d4', 'd6', 'd8', 'd10', 'd12', 'd20']
- # cdn_base_url = 'https://cdn.discordapp.com/attachments/1000024222514561027/1000'
- def dump_enemylist():
- with open('enemy_list', 'w') as f:
- json.dump(enemies, f) # check if 'enemy_list' file exists & if not then create it
- def dump_playerlist():
- with open('player_list', 'w') as f:
- json.dump(players, f) # check if 'enemy_list' file exists & if not then create it
- def dump_enemyhp():
- with open('enemy_hp', 'w') as f:
- json.dump(enemy_hp, f)
- def dump_playerhp():
- with open('player_hp', 'w') as f:
- json.dump(player_hp, f)
- def roll_dice(
- die: str,
- ) -> tuple[int, str, list[int], int, Optional[list[str]]]:
- """Roll a dice with dice notation and return
- :param die: dice notation
- :return: (number_of_dice, dice_sides, result, total, Optional[dice_images], bonus)
- """
- dice_sides: str = ""
- if mo := re.search(r"(?P<dice_sides>d\d+)", die):
- (dice_sides,) = mo.groups()
- bonus = 0
- if not '+' in die and not '-' in die:
- result = dice.roll(die)
- total = int(result) + int(bonus)
- if '+' in die:
- die, bonus = die.split('+')
- result = dice.roll(die)
- total = int(result) + int(bonus)
- if '-' in die:
- die, bonus = die.split('-')
- result = dice.roll(die)
- total = int(result) - int(bonus)
- bonus = int(bonus)
- bonus = -abs(bonus)
- # result = dice.roll(die)
- number_of_dice = len(result)
- dice_sides = str(dice_sides)
- ret_value = (number_of_dice, dice_sides, result, total, int(bonus))
- list_images = []
- for number in result:
- dice_face_file_name = f"assets/{dice_sides}-{number}.png"
- if (
- dice_sides in available_dice_assets
- and os.access(dice_face_file_name, os.R_OK)
- ):
- list_images.append(dice_face_file_name)
- if dice_sides in available_dice_assets and number_of_dice <= 25:
- tmp = tempfile.NamedTemporaryFile(suffix='.png')
- tmp.close()
- process = subprocess.Popen([
- 'montage',
- '-alpha', 'Set',
- '-background', 'None',
- '-resize', '200x200>',
- '-gravity', 'Center',
- '-extent', '220x220',
- '-tile', f'{round(sqrt(number_of_dice))}x',
- '+repage', *list_images, tmp.name
- ], shell=False, stdout=subprocess.PIPE)
- process.wait()
- if dice_sides in available_dice_assets and number_of_dice <= 25:
- return number_of_dice, dice_sides, result, total, [tmp.name], int(bonus)
- return number_of_dice, dice_sides, result, total, '', int(bonus)
- class Reroll(discord.ui.View):
- def __init__(self, die: str, *items: Item):
- self.die = die
- super().__init__(*items)
- @discord.ui.button(label="Reroll!", style=discord.ButtonStyle.primary, emoji="🎲")
- async def button_callback(self, button, interaction):
- (embed, file) = create_roll_embed(self.die, interaction.user)
- if file:
- return await interaction.response.send_message(file=file, embed=embed, view=Reroll(self.die))
- await interaction.response.send_message(embed=embed, view=Reroll(self.die))
- def create_roll_embed(die: str, user: discord.User):
- (number_of_dice, dice_sides, result, total, dice_images, bonus) = roll_dice(die)
- embed = discord.Embed(
- title="Rolled a dice",
- description=f"",
- color=discord.Color.blue(),
- )
- embed.set_author(name=user.display_name, icon_url=user.avatar.url)
- embed.set_footer(
- text="Information requested by: {}".format(user.display_name)
- )
- embed.add_field(name='dice', value=die, inline=True)
- if bonus > 0:
- embed.add_field(name='result', value=f'{result}+{bonus}', inline=True)
- if bonus < 0:
- embed.add_field(name='result', value=f'{result}{bonus}', inline=True)
- if bonus == 0:
- embed.add_field(name='result', value=f'{result}', inline=True)
- embed.add_field(name='total', value=f'{total}', inline=True)
- if len(dice_images) > 0:
- file = discord.File(dice_images[0])
- embed.set_image(url=f'attachment://{os.path.basename(dice_images[0])}')
- return embed, file
- return embed, None
- def drawhpbar():
- global chunks, hpbar
- if chunks > 0:
- if chunks < 1:
- chunks = 1
- chunks = round(chunks)
- chunk_count = 0 # define starting point for hp blocks
- hpbar = '' # define empty hpbar
- dmg_total = chunk_total - chunks # calculate total damage taken
- dmg_count = 0 # define starting point for hp blocks
- if chunk_count < chunk_total:
- while chunk_count < chunks:
- hpbar = hpbar + p
- chunk_count += 1 # fill hbar with current health
- if dmg_count < dmg_total:
- while dmg_count < dmg_total:
- hpbar = hpbar + d
- dmg_count += 1 # fill remaining space with total damage taken
- def drawstatlist():
- global embed, statlist, stats_name
- slstatlist = statlist.splitlines()
- statlist1 = slstatlist[:len(slstatlist) // 2]
- statlist2 = slstatlist[len(slstatlist) // 2:]
- sl1 = ''
- sl2 = ''
- for x in statlist1:
- sl1 = sl1 + x + '\n'
- for x in statlist2:
- sl2 = sl2 + x + '\n'
- if stats_name in enemies.keys():
- embed = discord.Embed(title=stats_name + ": " + hp_out + str(enemies[stats_name]['Percent']) + '%',
- color=0x00ff00)
- embed.add_field(name="Stats:", value=sl1, inline=True)
- embed.add_field(name="Abilities:", value=sl2, inline=True)
- statlist = ''
- if stats_name in players.keys():
- embed = discord.Embed(title=stats_name + ": " + hp_out + str(players[stats_name]['Percent']) + '%',
- color=0x00ff00)
- embed.add_field(name="Stats:", value=sl1, inline=True)
- embed.add_field(name="Abilities:", value=sl2, inline=True)
- statlist = ''
- def drawhplist():
- global embed, statlist, entity
- embed = discord.Embed(title='', color=0x00ff00)
- embed.add_field(name=entity + " Healthbars:", value=statlist, inline=True)
- statlist = ''
- entity = ''
- def drawcombatinfo():
- global embed
- line1 = "Round: " + str(current_round) + '\n'
- line2 = "Turn: " + str(turn_num) + ' (' + str(total_turns) + ')\n'
- line3 = "It is now " + player_turn + "'s turn."
- lines = line1 + line2 + line3
- embed = discord.Embed(title='', description="", color=0x00ff00)
- embed.add_field(name="Combat info:", value=lines, inline=True)
- def turncleanup():
- global total_turns, current_turn, current_round, embed, turn_num, player_turn, ko
- turn_num += 1
- total_turns += 1
- current_turn -= ko
- if current_turn >= len(init_order):
- current_turn = 0
- turn_num = 1
- current_round += 1
- player_turn = str(init_names[current_turn])
- ko = 0
- drawcombatinfo()
- def combatcleanup():
- global chunk_total, init_order, init_names, init_names_e, init_names_p, current_turn, current_round, total_turns, \
- player_turn, init_round, in_combat, turn_num
- chunk_total = 12
- init_order = []
- init_names = []
- init_names_e = []
- init_names_p = []
- current_turn = 0
- turn_num = 1
- current_round = 1
- total_turns = 1
- player_turn = ''
- init_round = False
- in_combat = False
- if not os.path.isfile('player_list'):
- players = {}
- dump_playerlist()
- if not os.path.isfile('enemy_list'):
- enemies = {}
- dump_enemylist()
- if not os.path.isfile('player_hp'):
- player_hp = {}
- dump_playerhp()
- if not os.path.isfile('enemy_hp'):
- enemy_hp = {}
- dump_enemyhp()
- with open('player_list') as f:
- players = json.load(f) # load the player_list into the players dict
- with open('enemy_list') as f:
- enemies = json.load(f) # load the enemy_list into the enemies dict
- with open('player_hp') as f:
- player_hp = json.load(f)
- with open('enemy_hp') as f:
- enemy_hp = json.load(f)
- all_hp.update(player_hp)
- all_hp.update(enemy_hp)
- attack_types = ['Phys', 'Ranged', 'Spell', 'Spell Save']
- stats = ['Name', 'HP', 'MaxHP', 'Percent', 'Healthbar', 'AC', 'Init Roll', 'Prof Bonus', 'Init Bonus', 'STR', 'DEX',
- # 0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10
- 'CON', 'INT', 'WIS', 'CHA', 'Spell DC', 'Spell Atk'] # , 'To Hit', 'Atk Die']
- # 11 #12 #13 #14 #15 #16 #17 #18
- mods = ['STR Mod', 'DEX Mod', 'CON Mod', 'INT Mod', 'WIS Mod', "CHA Mod"]
- # ^^assign various stats to the stats[] list
- description = '''A bot designed to handle D&D combat, keep track of player and enemy statistics,
- and provide other quality of life utilities.'''
- bot = commands.Bot(command_prefix='.', description=description)
- @bot.event
- async def on_ready():
- print('Logged in as')
- print(bot.user.name)
- print(bot.user.id)
- print('------')
- for guild in bot.guilds:
- print(guild.name) # prints all server's names
- @bot.slash_command()
- async def roll(ctx, die: str = None):
- """Roll dice in 'NdN' or 'NdN .+ N' format
- :param die: dice in 'NdN' format
- """
- await ctx.defer()
- (embed, file) = create_roll_embed(die, ctx.author)
- if file:
- return await ctx.followup.send(file=file, embed=embed, view=Reroll(die))
- await ctx.followup.send(embed=embed, view=Reroll(die))
- @bot.slash_command(description='Make a choice between any number of defined choices.')
- async def choose(ctx, *choices: str):
- """Chooses between multiple choices."""
- await ctx.respond(random.choice(choices))
- @bot.slash_command()
- async def addenemy(ctx, name: str, hp: int, maxhp: int, ac: int, prof_bonus: int, initbonus: int):
- """Define a new enemy in the enemy list."""
- global chunk_total, chunks, enemy_hp, all_hp
- if hp > 2000:
- hp = 2000
- if maxhp > 2000:
- maxhp = 2000
- if ac > 2000:
- ac = 2000
- if prof_bonus > 2000:
- prof_bonus = 2000
- if initbonus > 2000:
- initbonus = 2000
- percent = hp / maxhp * 100 # <calculate health percentage
- enemies[name] = {stats[1]: hp, stats[2]: maxhp, stats[3]: round(percent)}
- # ^^write name to the enemies dictionary and assign name, stats, and hp%
- chunks = (enemies[name]['HP'] / enemies[name]['MaxHP']) * chunk_total # calculate total # of blocks for hpbar
- drawhpbar()
- enemy_hp.update({name: '`[' + hpbar + ']`'})
- all_hp[name] = {enemy_hp[name]}
- enemies[name].update({stats[5]: ac, stats[6]: 0, stats[7]: prof_bonus, stats[8]: initbonus})
- percent = enemies[name]['HP'] / enemies[name]['MaxHP'] * 100 # calc hp percentage
- enemies[name]['Percent'] = round(percent) # assign new hp percentage
- sl = ''
- for x, y in zip(enemies[name].keys(), enemies[name].values()):
- sl = sl + str(x) + ':' + str(y) + '\n'
- embed = discord.Embed(title=name, color=0x00ff00)
- embed.add_field(name="Stats:", value=sl, inline=True)
- dump_enemyhp()
- dump_enemylist()
- await ctx.respond(embed=embed)
- await ctx.respond("Stats added! Add ability scores with </addabts> for help type </help> <addabts>.")
- @bot.slash_command()
- async def addplayer(ctx, name: str, hp: int, maxhp: int, ac: int, prof_bonus: int, initbonus: int):
- """Define a new player in the player list."""
- global chunk_total, chunks, player_hp, all_hp
- if hp > 2000:
- hp = 2000
- if maxhp > 2000:
- maxhp = 2000
- if ac > 2000:
- ac = 2000
- if prof_bonus > 2000:
- prof_bonus = 2000
- if initbonus > 2000:
- initbonus = 2000
- percent = hp / maxhp * 100 # <calculate health percentage
- players[name] = {stats[1]: hp, stats[2]: maxhp, stats[3]: round(percent)}
- # ^^write name to the players dictionary and assign name, stats, and hp%
- chunks = (players[name]['HP'] / players[name]['MaxHP']) * chunk_total # calculate total # of blocks for hpbar
- drawhpbar()
- player_hp.update({name: '`[' + hpbar + ']`'})
- all_hp[name] = {player_hp[name]}
- players[name].update(
- {stats[5]: ac, stats[6]: 0, stats[7]: prof_bonus, stats[8]: initbonus}) # assign remaining stats
- percent = players[name]['HP'] / players[name]['MaxHP'] * 100 # calc hp percentage
- players[name]['Percent'] = round(percent) # assign new hp percentage
- sl = ''
- for x, y in zip(players[name].keys(), players[name].values()):
- sl = sl + str(x) + ':' + str(y) + '\n'
- embed = discord.Embed(title=name, color=0x00ff00)
- embed.add_field(name="Stats:", value=sl, inline=True)
- dump_playerhp()
- dump_playerlist()
- await ctx.respond(embed=embed)
- await ctx.respond("Stats added! Add ability scores with </addabts> for help type </help> <addabts>.")
- @bot.slash_command()
- async def addabts(ctx, name: str, strength: int, dexterity: int, constitution: int, intelligence: int, wisdom: int,
- charisma: int, spellsave: typing.Optional[int], *, spellatk: typing.Optional[int]):
- """Define enemy/player abilities."""
- if strg > 40:
- strg = 40
- if dex > 40:
- dex = 40
- if con > 40:
- con = 40
- if intel > 40:
- intel = 40
- if wis > 40:
- wis = 40
- if cha > 40:
- cha = 40
- if name in enemies.keys():
- dexmod = int((dex - 10) / 2)
- enemies[name].update({stats[9]: strg, stats[10]: dex, stats[11]: con, stats[12]: intel,
- stats[13]: wis, stats[14]: cha})
- if spellsave == None and spellatk == None:
- enemies[name].update({stats[15]: 0, stats[16]: 0})
- elif spellsave and spellatk:
- enemies[name].update({stats[15]: spellsave, stats[16]: spellatk})
- if enemies[name][15] > 40:
- enemies[name][15] = 40
- if enemies[name][16] > 40:
- enemies[name][16] = 40
- dump_enemylist()
- dump_enemyhp()
- if name in players.keys():
- dexmod = int((dex - 10) / 2)
- players[name].update(
- {stats[9]: strg, stats[10]: dex, stats[11]: con, stats[12]: intel, stats[13]: wis,
- stats[14]: cha})
- if spellsave == None and spellatk == None:
- players[name].update({stats[15]: 0, stats[16]: 0})
- elif spellsave and spellatk:
- players[name].update({stats[15]: spellsave, stats[16]: spellatk})
- if players[name][15] > 40:
- players[name][15] = 40
- if players[name][16] > 40:
- players[name][16] = 40
- dump_playerlist()
- dump_playerhp()
- global statlist, stats_name, hp_out
- if name in enemies.keys():
- for x, y in zip(enemies[name].keys(), enemies[name].values()):
- statlist = statlist + x + ': ' + str(y) + '\n'
- stats_name = name
- hp_out = str(all_hp[name]).replace("{'", "").replace("'}", "")
- drawstatlist()
- if name in players.keys():
- for x, y in zip(players[name].keys(), players[name].values()):
- statlist = statlist + x + ': ' + str(y) + '\n'
- stats_name = name
- hp_out = str(all_hp[name]).replace("{'", "").replace("'}", "")
- drawstatlist()
- await ctx.respond(embed=embed)
- await ctx.respond("Abilites added! Check stats with <.sts> <name>.")
- # @bot.slash_command()
- # async def elist(ctx):
- # """Lists every enemy name in the enemy list."""
- # enemy_names = []
- # enames = ''
- # embed = ''
- # for x in enemies.keys():
- # enemy_names.append(x) # write all enemy names from the enemy dict to the enemy_names list
- # for y in enemy_names:
- # enames = enames + y + '\n'
- # embed = discord.Embed(color=0x00ff00)
- # embed.add_field(name="Enemies:", value=enames, inline=True)
- # await ctx.respond(embed=embed)
- @bot.slash_command()
- async def elist(ctx):
- """Lists every enemy's current healthbar."""
- global statlist, entity, hp_out, enemy_hp, all_hp
- for x, y in zip(enemy_hp.keys(), enemy_hp.values()):
- statlist = statlist + str(x) + ': ' + str(y) + '\n'
- entity = 'Enemy'
- drawhplist()
- await ctx.respond(embed=embed)
- # @bot.slash_command()
- # async def plist(ctx):
- # """Lists every player name in the player list."""
- # player_names = []
- # pnames = ''
- # embed = ''
- # for x in players.keys():
- # player_names.append(x) # write all player names from the player dict to the player_names list
- # for y in player_names:
- # pnames = pnames + y + '\n'
- # embed = discord.Embed(color=0x00ff00)
- # embed.add_field(name="Players:", value=pnames, inline=True)
- # await ctx.respond(embed=embed)
- @bot.slash_command()
- async def plist(ctx):
- """Lists every player's current healthbar."""
- global statlist, entity, hp_out, player_hp, all_hp
- for x, y in zip(player_hp.keys(), player_hp.values()):
- name = str(x)
- statlist = statlist + str(x) + ': ' + str(y) + str(players[name]['HP']) + '/' + str(
- players[name]['MaxHP']) + ' - ' + str(players[name]['Percent']) + '%' + '\n'
- entity = 'Player'
- drawhplist()
- await ctx.respond(embed=embed)
- @bot.slash_command()
- async def hp(ctx, name: str):
- """Displays the specified player/enemy's Healthbar."""
- global hp_out
- hp_out = str(all_hp[name]).replace("{'", "").replace("'}", "")
- percentage = ''
- if name in players.keys():
- percentage = str(players[name]['HP']) + '/' + str(players[name]['MaxHP']) + ' - ' + str(
- players[name]['Percent']) + '%'
- await ctx.respond(name + ': ' + hp_out + percentage) # send the specified player/enemy name and their current hpbar
- @bot.slash_command()
- async def sts(ctx, name: str):
- """Shows the full stat list of the specified player/enemy."""
- global statlist, stats_name, hp_out
- if name in enemies.keys():
- for x, y in zip(enemies[name].keys(), enemies[name].values()):
- statlist = statlist + x + ': ' + str(y) + '\n'
- stats_name = name
- hp_out = str(all_hp[name]).replace("{'", "").replace("'}", "")
- drawstatlist()
- if name in players.keys():
- for x, y in zip(players[name].keys(), players[name].values()):
- statlist = statlist + x + ': ' + str(y) + '\n'
- stats_name = name
- hp_out = str(all_hp[name]).replace("{'", "").replace("'}", "")
- drawstatlist()
- await ctx.respond(embed=embed) # send every enemy stat and its' current value as a message
- @bot.slash_command()
- async def dmg(ctx, name: str, dmg: int):
- """Deals damage to the specified player/enemy by the specified amount."""
- global chunk_total, init_order, init_names, player_turn, current_turn, current_round, total_turns, chunks, hp_out, \
- enemy_hp, player_hp, all_hp, ko
- if dmg > 2000:
- dmg = 2000
- if name in enemies.keys(): # check if specified entity is in enemy list
- enemies[name]['HP'] -= dmg # deal specified damage to enemy
- percent = enemies[name]['HP'] / enemies[name]['MaxHP'] * 100 # calc hp percentage
- enemies[name]['Percent'] = round(percent) # assign new hp percentage to specified enemy
- chunks = (enemies[name]['HP'] / enemies[name]['MaxHP']) * chunk_total # calculate total # of blocks for hpbar
- drawhpbar()
- enemy_hp.update({name: '`[' + hpbar + ']`'})
- all_hp.update(enemy_hp)
- dump_enemyhp()
- dump_enemylist()
- hp_out = str(all_hp[name]).replace("{'", "").replace("'}", "")
- await ctx.respond(name + ": " + hp_out)
- if enemies[name]['HP'] <= 0:
- del_list = [name, enemies[name][stats[6]]]
- init_order = [i for i in init_order if i != del_list]
- init_names = [item[0] for item in init_order]
- ko = 1
- if name in init_names_e:
- init_names_e.remove(name)
- if init_names_e != []:
- if name == player_turn:
- current_turn += 1
- turncleanup()
- await ctx.respond(embed=embed)
- hp_out = str(all_hp[player_turn]).replace("{'", "").replace("'}", "")
- await ctx.respond(player_turn + ': ' + hp_out)
- if init_names_e == [] and in_combat:
- combatcleanup()
- await ctx.respond('All enemies defeated!\nCombat is now over.')
- current_turn += 1
- if name in players.keys(): # check if specified entity is in player list
- players[name]['HP'] -= dmg # deal specified damage to player
- percent = players[name]['HP'] / players[name]['MaxHP'] * 100 # calc hp percentage
- players[name]['Percent'] = round(percent) # assign new hp percentage to specified player
- chunks = (players[name]['HP'] / players[name]['MaxHP']) * chunk_total # calculate total # of blocks for hpbar
- drawhpbar()
- player_hp.update({name: '`[' + hpbar + ']`'})
- all_hp.update(player_hp)
- dump_playerhp()
- dump_playerlist()
- hp_out = str(all_hp[name]).replace("{'", "").replace("'}", "")
- await ctx.respond(
- name + ": " + hp_out + str(players[name]['HP']) + '/' + str(players[name]['MaxHP']) + ' - ' + str(
- players[name]['Percent']) + '%')
- if players[name]['HP'] <= 0:
- del_list = [name, players[name][stats[6]]]
- init_order = [i for i in init_order if i != del_list]
- init_names = [item[0] for item in init_order]
- ko = 1
- if name in init_names_p:
- init_names_p.remove(name)
- if init_names_p != []:
- if name == player_turn:
- current_turn += 1
- turncleanup()
- await ctx.respond(embed=embed)
- hp_out = str(all_hp[player_turn]).replace("{'", "").replace("'}", "")
- await ctx.respond(player_turn + ': ' + hp_out)
- if init_names_p == [] and in_combat:
- combatcleanup()
- await ctx.respond('All players defeated!\nCombat is now over.')
- current_turn += 1
- @bot.slash_command()
- async def heal(ctx, name: str, dmg: int):
- """Deals damage to the specified player/enemy by the specified amount."""
- global chunk_total, init_order, init_names, player_turn, current_turn, current_round, total_turns, chunks, hp_out, \
- enemy_hp, player_hp, all_hp, ko
- if dmg > 2000:
- dmg = 2000
- if name in enemies.keys(): # check if specified entity is in enemy list
- enemies[name]['HP'] += dmg # deal specified damage to enemy
- percent = enemies[name]['HP'] / enemies[name]['MaxHP'] * 100 # calc hp percentage
- enemies[name]['Percent'] = round(percent) # assign new hp percentage to specified enemy
- chunks = (enemies[name]['HP'] / enemies[name]['MaxHP']) * chunk_total # calculate total # of blocks for hpbar
- drawhpbar()
- enemy_hp.update({name: '`[' + hpbar + ']`'})
- all_hp.update(enemy_hp)
- dump_enemyhp()
- dump_enemylist()
- hp_out = str(all_hp[name]).replace("{'", "").replace("'}", "")
- await ctx.respond(name + ": " + hp_out)
- if enemies[name]['HP'] <= 0:
- del_list = [name, enemies[name][stats[6]]]
- init_order = [i for i in init_order if i != del_list]
- init_names = [item[0] for item in init_order]
- ko = 1
- if name in init_names_e:
- init_names_e.remove(name)
- if init_names_e != []:
- if name == player_turn:
- current_turn += 1
- turncleanup()
- await ctx.respond(embed=embed)
- hp_out = str(all_hp[player_turn]).replace("{'", "").replace("'}", "")
- await ctx.respond(player_turn + ': ' + hp_out)
- if init_names_e == [] and in_combat:
- combatcleanup()
- await ctx.respond('All enemies defeated!')
- await ctx.respond("Combat is now over.")
- if name in players.keys(): # check if specified entity is in player list
- players[name]['HP'] += dmg # deal specified damage to player
- percent = players[name]['HP'] / players[name]['MaxHP'] * 100 # calc hp percentage
- players[name]['Percent'] = round(percent) # assign new hp percentage to specified player
- chunks = (players[name]['HP'] / players[name]['MaxHP']) * chunk_total # calculate total # of blocks for hpbar
- drawhpbar()
- player_hp.update({name: '`[' + hpbar + ']`'})
- all_hp.update(player_hp)
- dump_playerhp()
- dump_playerlist()
- hp_out = str(all_hp[name]).replace("{'", "").replace("'}", "")
- await ctx.respond(
- name + ": " + hp_out + str(players[name]['HP']) + '/' + str(players[name]['MaxHP']) + ' - ' + str(
- players[name]['Percent']) + '%')
- if players[name]['HP'] <= 0:
- del_list = [name, players[name][stats[6]]]
- init_order = [i for i in init_order if i != del_list]
- init_names = [item[0] for item in init_order]
- ko = 1
- if name in init_names_p:
- init_names_p.remove(name)
- if init_names_p != []:
- if name == player_turn:
- current_turn += 1
- turncleanup()
- await ctx.respond(embed=embed)
- hp_out = str(all_hp[player_turn]).replace("{'", "").replace("'}", "")
- await ctx.respond(player_turn + ': ' + hp_out)
- if init_names_p == [] and in_combat:
- combatcleanup()
- await ctx.respond('All players defeated!\nCombat is now over.')
- @bot.slash_command()
- async def combatinfo(ctx):
- """Displays the current turn/round description."""
- if in_combat:
- drawcombatinfo()
- await ctx.respond(embed=embed)
- @bot.slash_command()
- async def startinit(ctx):
- """Start the initiatve round to make initiative rolls."""
- global init_round, in_combat
- if init_round and not in_combat:
- print(init_round)
- await ctx.respond(
- "It is already the initiative round.\n </startcombat> to end the initiative round or </clrinit> to start over")
- if not init_round:
- init_round = True
- await ctx.respond("Initiative round started.")
- @bot.slash_command()
- async def clrinit(ctx):
- """Clears current initiative order."""
- if not in_combat:
- combatcleanup()
- await ctx.respond("Initiative cleared. Start the initiative round again with </startinit>.")
- if in_combat:
- await ctx.respond("Cannot clear initiative while in combat.")
- @bot.slash_command()
- async def initr(ctx, name: str):
- """Roll initiative for specified enemy/player."""
- global init_order, init_round
- if name not in players and name not in enemies:
- await ctx.respond('Name not found.')
- if init_round and not in_combat:
- if any(name in sl for sl in init_order):
- if name in enemies:
- del_list = [name, enemies[name][stats[6]]]
- init_order = [i for i in init_order if i != del_list]
- if name in players:
- del_list = [name, players[name][stats[6]]]
- init_order = [i for i in init_order if i != del_list]
- if name in enemies.keys():
- roll = dice.roll('1d20+' + str(enemies[name][stats[8]]))
- enemies[name][stats[6]] = roll
- if not name in init_names_e:
- init_names_e.append(name)
- if name in players.keys():
- roll = dice.roll('1d20+' + str(players[name][stats[8]]))
- players[name][stats[6]] = roll
- if not name in init_names_p:
- init_names_p.append(name)
- if not any(name in sl for sl in init_order):
- init_order.append([name, roll])
- def Sort(init_order):
- init_order.sort(key=lambda x: x[1], reverse=True)
- return init_order
- Sort(init_order)
- def conversion(tup, dict):
- for x, y in init_order:
- init_order_dict.setdefault(x, str(y))
- return init_order_dict
- init_order_dict = {}
- init_list = ''
- conversion(init_order, init_order_dict)
- for x, y in zip(init_order_dict.keys(), init_order_dict.values()):
- init_list = init_list + x + ': ' + str(y) + '\n'
- embed = discord.Embed(title='', color=0x00ff00)
- embed.add_field(name='Initiative Order: ', value=init_list, inline=True)
- await ctx.respond(embed=embed)
- if in_combat:
- await ctx.respond("Cannot reroll initiative while in combat. Use </addinit> to add/change one.")
- if not init_round and not in_combat:
- await ctx.respond("Begin initiative with </startinit> before making initiative rolls!")
- @bot.slash_command()
- async def initorder(ctx):
- """Show the current initiative order."""
- if not init_round and not in_combat:
- await ctx.respond('Not currently in combat.')
- if init_round or in_combat:
- def Sort(init_order):
- init_order.sort(key=lambda x: x[1], reverse=True)
- return init_order
- Sort(init_order)
- def conversion(tup, dict):
- for x, y in init_order:
- init_order_dict.setdefault(x, str(y))
- return init_order_dict
- init_order_dict = {}
- init_list = ''
- conversion(init_order, init_order_dict)
- for x, y in zip(init_order_dict.keys(), init_order_dict.values()):
- init_list = init_list + x + ': ' + str(y) + '\n'
- embed = discord.Embed(title='', color=0x00ff00)
- embed.add_field(name='Initiative Order: ', value=init_list, inline=True)
- await ctx.respond(embed=embed)
- @bot.slash_command()
- async def addinit(ctx, name: str, amt: int):
- """Add initiative rolls to initiative order."""
- global init_names, init_order, current_turn, current_round, total_turns, player_turn, init_round, in_combat, players, enemies
- if name in players or name in enemies:
- if init_round or in_combat:
- turn_add = init_order[current_turn]
- turn_add = turn_add[1]
- if amt > turn_add:
- current_turn += 1
- if any(name in sl for sl in init_order):
- if name in enemies:
- del_list = [name, enemies[name][stats[6]]]
- init_order = [i for i in init_order if i != del_list]
- if name in players:
- del_list = [name, players[name][stats[6]]]
- init_order = [i for i in init_order if i != del_list]
- if name in enemies.keys():
- enemies[name][stats[6]] = amt
- if not name in init_names_e:
- init_names_e.append(name)
- if name in players.keys():
- players[name][stats[6]] = amt
- if not name in init_names_p:
- init_names_p.append(name)
- if not any(name in sl for sl in init_order):
- init_order.append([name, amt])
- def Sort(init_order):
- init_order.sort(key=lambda x: x[1], reverse=True)
- return init_order
- Sort(init_order)
- def conversion(tup, dict):
- for x, y in init_order:
- init_order_dict.setdefault(x, str(y))
- return init_order_dict
- init_order_dict = {}
- init_list = ''
- conversion(init_order, init_order_dict)
- init_names = [item[0] for item in init_order]
- player_turn = str(init_names[current_turn])
- drawcombatinfo()
- hp_out = str({all_hp[player_turn]}).replace("{'", "").replace("'}", "")
- for x, y in zip(init_order_dict.keys(), init_order_dict.values()):
- init_list = init_list + x + ': ' + str(y) + '\n'
- embed = discord.Embed(title='', color=0x00ff00)
- embed.add_field(name='Initiative Order: ', value=init_list, inline=True)
- await ctx.respond(name + ' added to initiative order.')
- await ctx.respond(embed=embed)
- if name not in players and name not in enemies:
- await ctx.respond('Name not found.')
- if not init_round and not in_combat:
- await ctx.respond("Begin initiative with </startinit> before making initiative rolls!")
- @bot.slash_command()
- async def startcombat(ctx):
- """Start combat after initiative rolls."""
- global init_names, init_order, current_turn, current_round, total_turns, player_turn, init_round, in_combat
- if not init_round and not in_combat:
- await ctx.respond("Roll for initiative before starting combat. Start the initiative round with </startinit>.")
- if in_combat:
- await ctx.respond("End the current combat before starting a new one. Defeat all enemies or use </endcombat>.")
- if init_round:
- if len(init_names_e) < 1 or len(init_names_p) < 1:
- await ctx.respond(
- "Make at least two initiative rolls before starting combat. (one enemy and one player minimum)")
- if init_round == True and len(init_order) > 1 and len(init_names_e) > 0 and len(init_names_p) > 0 and not in_combat:
- in_combat = True
- init_round = False
- init_names = [item[0] for item in init_order]
- await ctx.respond("Combat has now begun.")
- player_turn = str(init_names[0])
- drawcombatinfo()
- hp_out = str(all_hp[player_turn]).replace("{'", "").replace("'}", "")
- await ctx.respond(embed=embed)
- if player_turn in enemies.keys():
- await ctx.respond(player_turn + ': ' + hp_out)
- if player_turn in players.keys():
- percentage = str(players[player_turn]['HP']) + '/' + str(players[player_turn]['MaxHP']) + ' - ' + str(
- players[player_turn]['Percent']) + '%'
- await ctx.respond(player_turn + ': ' + hp_out + percentage)
- @bot.slash_command()
- async def endturn(ctx):
- """End current player's turn."""
- global current_turn, player_turn, current_round, total_turns, hp_out, init_names
- if in_combat:
- current_turn += 1
- turncleanup()
- await ctx.respond(embed=embed)
- hp_out = str(all_hp[player_turn]).replace("{'", "").replace("'}", "")
- if player_turn in enemies.keys():
- await ctx.respond(player_turn + ': ' + hp_out)
- if player_turn in players.keys():
- percentage = str(players[player_turn]['HP']) + '/' + str(players[player_turn]['MaxHP']) + ' - ' + str(
- players[player_turn]['Percent']) + '%'
- await ctx.respond(player_turn + ': ' + hp_out + percentage)
- if not in_combat:
- await ctx.respond("Must be in combat to end a turn.")
- @bot.slash_command()
- async def endcombat(ctx):
- """End the current combat."""
- if not in_combat:
- await ctx.respond(
- "Cannot end combat while not in combat. Start initiative with </startinit> or combat with </startcombat>.")
- if in_combat:
- combatcleanup()
- await ctx.respond("Combat is now over.")
- @bot.slash_command()
- async def delete(ctx, name: str):
- """Delete the specified enemy/player from the respective list"""
- if name in enemies.keys():
- del enemies[name]
- if name in enemy_hp.keys():
- del enemy_hp[name]
- if name in players.keys():
- del players[name]
- if name in player_hp.keys():
- del player_hp[name]
- dump_enemyhp()
- dump_enemylist()
- dump_playerhp()
- dump_playerlist()
- await ctx.respond(name + ' deleted from appropriate list.')
- bot.run(TOKEN)
Add Comment
Please, Sign In to add comment