Ejemplo n.º 1
0
    def __init__(self):
        self.cfeats = self.load_json('srd-classfeats.json', [])
        self.classes = self.load_json('srd-classes.json', [])
        self.conditions = self.load_json('conditions.json', [])
        self.feats = self.load_json('srd-feats.json', [])
        self.itemprops = self.load_json('itemprops.json', {})
        self.monsters = self.load_json('srd-bestiary.json', [])
        self.names = self.load_json('names.json', [])
        self.rules = self.load_json('rules.json', [])

        self.spells = [Spell.from_data(r) for r in self.load_json('srd-spells.json', [])]
        self.backgrounds = [Background.from_data(b) for b in self.load_json('srd-backgrounds.json', [])]
        self.items = [i for i in self.load_json('srd-items.json', []) if i.get('type') is not '$']
        self.monster_mash = [Monster.from_data(m) for m in self.monsters]

        self.subclasses = self.load_subclasses()

        srd_races = self.load_json('srd-races.json', [])
        self.fancyraces = [Race.from_data(r) for r in srd_races]
        self.rfeats = []
        for race in srd_races:
            for entry in race['entries']:
                if isinstance(entry, dict) and 'name' in entry:
                    temp = {'name': "{}: {}".format(race['name'], entry['name']),
                            'text': parse_data_entry(entry['entries']), 'srd': race['srd']}
                    self.rfeats.append(temp)
Ejemplo n.º 2
0
    async def subclass(self, ctx, name: str):
        """Looks up a subclass."""
        guild_settings = await self.get_settings(ctx.message.server)
        pm = guild_settings.get("pm_result", False)
        srd = guild_settings.get("srd", False)

        destination = ctx.message.author if pm else ctx.message.channel

        result = await search_and_select(ctx,
                                         c.subclasses,
                                         name,
                                         lambda e: e['name'],
                                         srd=srd)

        if not result.get('srd') and srd:
            return await self.send_srd_error(ctx, result)

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']

        for level_features in result['subclassFeatures']:
            for feature in level_features:
                for entry in feature['entries']:
                    if not isinstance(entry, dict): continue
                    if not entry.get('type') == 'entries': continue
                    text = parse_data_entry(entry['entries'])
                    embed.add_field(
                        name=entry['name'],
                        value=(text[:1019] +
                               "...") if len(text) > 1023 else text)

        embed.set_footer(
            text="Use !classfeat to look up a feature if it is cut off.")

        await self.bot.send_message(destination, embed=embed)
Ejemplo n.º 3
0
    async def subclass(self, ctx, name: str):
        """Looks up a subclass."""
        guild_settings = await self.get_settings(ctx.guild)
        pm = guild_settings.get("pm_result", False)
        srd = guild_settings.get("srd", False)

        destination = ctx.author if pm else ctx.channel

        result, metadata = await search_and_select(ctx, c.subclasses, name, lambda e: e['name'], srd=srd,
                                                   return_metadata=True)

        metadata['srd'] = srd
        await self.add_training_data("subclass", name, result['name'], metadata=metadata)

        if not result.get('srd') and srd:
            return await self.send_srd_error(ctx, result)

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']
        embed.description = f"*Source: {result['source']}*"

        for level_features in result['subclassFeatures']:
            for feature in level_features:
                for entry in feature['entries']:
                    if not isinstance(entry, dict): continue
                    if not entry.get('type') == 'entries': continue
                    text = parse_data_entry(entry['entries'])
                    embed.add_field(name=entry['name'], value=(text[:1019] + "...") if len(text) > 1023 else text)

        embed.set_footer(text=f"Use {ctx.prefix}classfeat to look up a feature if it is cut off.")

        await destination.send(embed=embed)
Ejemplo n.º 4
0
 def load_rfeats(self):
     ret = []
     for race in self.srd_races:
         for entry in race['entries']:
             if isinstance(entry, dict) and 'name' in entry:
                 temp = {'name': "{}: {}".format(race['name'], entry['name']),
                         'text': parse_data_entry(entry['entries']), 'srd': race['srd']}
                 ret.append(temp)
     return ret
Ejemplo n.º 5
0
 def get_traits(self):
     traits = []
     for entry in self.entries:
         if isinstance(entry, dict) and 'name' in entry:
             temp = {
                 'name': entry['name'],
                 'text': parse_data_entry(entry['entries'])
             }
             traits.append(temp)
     return traits
Ejemplo n.º 6
0
    async def feat(self, ctx, *, name: str):
        """Looks up a feat."""
        try:
            guild_id = ctx.message.server.id
            pm = self.settings.get(guild_id, {}).get("pm_result", False)
            srd = self.settings.get(guild_id, {}).get("srd", False)
        except:
            pm = False
            srd = False
        destination = ctx.message.author if pm else ctx.message.channel

        result = await search_and_select(ctx, c.feats, name, lambda e: e['name'])

        if not result['name'] == 'Grappler' and srd:  # the only SRD feat.
            return await self.send_srd_error(ctx, result)

        text = parse_data_entry(result['entries'])
        prereq = "None"

        if 'prerequisite' in result:
            for entry in result['prerequisite']:
                if 'race' in entry:
                    prereq = ' or '.join(
                        f"{r['name']}" + (f" ({r['subrace']})" if 'subrace' in r else '') for r in entry['race'])
                if 'ability' in entry:
                    abilities = []
                    for ab in entry['ability']:
                        abilities.extend(f"{ABILITY_MAP.get(a)} {s}" for a, s in ab.items())
                    prereq = ' or '.join(abilities)
                if 'spellcasting' in entry:
                    prereq = "The ability to cast at least one spell"
                if 'proficiency' in entry:
                    prereq = f"Proficiency with {entry['proficiency'][0]['armor']} armor"

        ability = None
        if 'ability' in result:
            if 'choose' in result['ability']:
                ability = ' or '.join(ABILITY_MAP.get(a) for a in result['ability']['choose'][0]['from'])
            else:
                ability = ' or '.join(ABILITY_MAP.get(a) for a in result['ability'].keys())

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']
        embed.add_field(name="Prerequisite", value=prereq)
        embed.add_field(name="Source", value=result['source'])
        if ability:
            embed.add_field(name="Ability Improvement",
                            value=f"Increase your {ability} score by 1, up to a maximum of 20.")
        _name = 'Description'
        for piece in [text[i:i + 1024] for i in range(0, len(text), 1024)]:
            embed.add_field(name=_name, value=piece)
            _name = '** **'
        await self.bot.send_message(destination, embed=embed)
Ejemplo n.º 7
0
 def __init__(self):
     with open('./res/conditions.json', 'r') as f:
         self.conditions = json.load(f)
     with open('./res/rules.json', 'r') as f:
         self.rules = json.load(f)
     with open('./res/feats.json', 'r') as f:
         self.feats = json.load(f)
     with open('./res/races.json', 'r') as f:
         _raw = json.load(f)
         self.rfeats = []
         self.fancyraces = [Race.from_data(r) for r in _raw]
         for race in _raw:
             for entry in race['entries']:
                 if isinstance(entry, dict) and 'name' in entry:
                     temp = {
                         'name': "{}: {}".format(race['name'],
                                                 entry['name']),
                         'text': parse_data_entry(entry['entries']),
                         'srd': race['srd']
                     }
                     self.rfeats.append(temp)
     with open('./res/classes.json', 'r') as f:
         self.classes = json.load(f)
     with open('./res/classfeats.json') as f:
         self.cfeats = json.load(f)
     with open('./res/bestiary.json', 'r') as f:
         self.monsters = json.load(f)
         self.monster_mash = [Monster.from_data(m) for m in self.monsters]
     with open('./res/spells.json', 'r') as f:
         self.spells = [Spell.from_data(r) for r in json.load(f)]
     with open('./res/items.json', 'r') as f:
         _items = json.load(f)
         self.items = [i for i in _items if i.get('type') is not '$']
     with open('./res/backgrounds.json', 'r') as f:
         self.backgrounds = [Background.from_data(b) for b in json.load(f)]
     self.subclasses = self.load_subclasses()
     with open('./res/itemprops.json', 'r') as f:
         self.itemprops = json.load(f)
     with open('./res/names.json', 'r') as f:
         self.names = json.load(f)
Ejemplo n.º 8
0
    async def subclass(self, ctx, name: str):
        """Looks up a subclass."""
        choices = compendium.subclasses + compendium.nsubclass_names
        result = await self._lookup_search(ctx, choices, name, lambda e: e['name'], search_type='subclass')
        if not result:
            return

        embed = EmbedWithAuthor(ctx)
        embed.title = result['name']
        embed.description = f"*Source: {result['source']}*"

        for level_features in result['subclassFeatures']:
            for feature in level_features:
                for entry in feature['entries']:
                    if not isinstance(entry, dict): continue
                    if not entry.get('type') == 'entries': continue
                    text = parse_data_entry(entry['entries'])
                    embed.add_field(name=entry['name'], value=(text[:1019] + "...") if len(text) > 1023 else text,
                                    inline=False)

        embed.set_footer(text=f"Use {ctx.prefix}classfeat to look up a feature if it is cut off.")

        await (await self._get_destination(ctx)).send(embed=embed)
Ejemplo n.º 9
0
    async def item_lookup(self, ctx, *, name):
        """Looks up an item."""
        try:
            guild_id = ctx.message.server.id
            pm = self.settings.get(guild_id, {}).get("pm_result", False)
            srd = self.settings.get(guild_id, {}).get("srd", False)
        except:
            pm = False
            srd = False

        self.bot.db.incr('items_looked_up_life')

        result = await search_and_select(ctx,
                                         c.items,
                                         name,
                                         lambda e: e['name'],
                                         srd=srd)

        embed = EmbedWithAuthor(ctx)
        item = result

        if not item['srd'] and srd:
            return await self.send_srd_error(ctx, result)

        name = item['name']
        damage = ''
        extras = ''
        properties = []
        proptext = ""

        if 'type' in item:
            type_ = ', '.join(i for i in (
                [ITEM_TYPES.get(t, 'n/a') for t in item['type'].split(',')] +
                ["Wondrous Item" if item.get('wondrous') else '']) if i)
            for iType in item['type'].split(','):
                if iType in ('M', 'R', 'GUN'):
                    damage = f"{item.get('dmg1', 'n/a')} {DMGTYPES.get(item.get('dmgType'), 'n/a')}" \
                        if 'dmg1' in item and 'dmgType' in item else ''
                    type_ += f', {item.get("weaponCategory")}'
                if iType == 'S': damage = f"AC +{item.get('ac', 'n/a')}"
                if iType == 'LA': damage = f"AC {item.get('ac', 'n/a')} + DEX"
                if iType == 'MA':
                    damage = f"AC {item.get('ac', 'n/a')} + DEX (Max 2)"
                if iType == 'HA': damage = f"AC {item.get('ac', 'n/a')}"
                if iType == 'SHP':  # ships
                    for p in ("CREW", "PASS", "CARGO", "DMGT", "SHPREP"):
                        a = PROPS.get(p, 'n/a')
                        proptext += f"**{a.title()}**: {c.itemprops[p]}\n"
                    extras = f"Speed: {item.get('speed')}\nCarrying Capacity: {item.get('carryingcapacity')}\n" \
                             f"Crew {item.get('crew')}, AC {item.get('vehAc')}, HP {item.get('vehHp')}"
                    if 'vehDmgThresh' in item:
                        extras += f", Damage Threshold {item['vehDmgThresh']}"
                if iType == 'siege weapon':
                    extras = f"Size: {SIZES.get(item.get('size'), 'Unknown')}\n" \
                             f"AC {item.get('ac')}, HP {item.get('hp')}\n" \
                             f"Immunities: {item.get('immune')}"
        else:
            type_ = ', '.join(
                i for i in ("Wondrous Item" if item.get('wondrous') else '',
                            item.get('technology')) if i)
        rarity = str(item.get('rarity')).replace('None', '')
        if 'tier' in item:
            if rarity:
                rarity += f', {item["tier"]}'
            else:
                rarity = item['tier']
        type_and_rarity = type_ + (f", {rarity}" if rarity else '')
        value = (item.get('value', 'n/a') +
                 (', ' if 'weight' in item else '')) if 'value' in item else ''
        weight = (item.get('weight', 'n/a') + (' lb.' if item.get('weight') == '1' else ' lbs.')) \
            if 'weight' in item else ''
        weight_and_value = value + weight
        for prop in item.get('property', []):
            if not prop: continue
            a = b = prop
            a = PROPS.get(a, 'n/a')
            if b in c.itemprops:
                proptext += f"**{a.title()}**: {c.itemprops[b]}\n"
            if b == 'V': a += " (" + item.get('dmg2', 'n/a') + ")"
            if b in ('T', 'A'): a += " (" + item.get('range', 'n/a') + "ft.)"
            if b == 'RLD': a += " (" + item.get('reload', 'n/a') + " shots)"
            properties.append(a)
        properties = ', '.join(properties)
        damage_and_properties = f"{damage} - {properties}" if properties else damage
        damage_and_properties = (' --- ' + damage_and_properties) if weight_and_value and damage_and_properties else \
            damage_and_properties

        embed.title = name
        desc = f"*{type_and_rarity}*\n{weight_and_value}{damage_and_properties}\n{extras}"
        embed.description = parse_data_entry(desc)

        if 'reqAttune' in item:
            if item['reqAttune'] is True:  # can be truthy, but not true
                embed.add_field(name="Attunement",
                                value=f"Requires Attunement")
            else:
                embed.add_field(
                    name="Attunement",
                    value=f"Requires Attunement {item['reqAttune']}")

        text = parse_data_entry(item.get('entries', []))
        if proptext:
            text = f"{text}\n{proptext}"
        if len(text) > 5500:
            text = text[:5500] + "..."

        field_name = "Description"
        for piece in [text[i:i + 1024] for i in range(0, len(text), 1024)]:
            embed.add_field(name=field_name, value=piece)
            field_name = "** **"

        embed.set_footer(
            text=
            f"Item | {item.get('source', 'Unknown')} {item.get('page', 'Unknown')}"
        )

        if pm:
            await self.bot.send_message(ctx.message.author, embed=embed)
        else:
            await self.bot.say(embed=embed)
Ejemplo n.º 10
0
    async def _class(self, ctx, name: str, level: int = None):
        """Looks up a class, or all features of a certain level."""
        try:
            guild_id = ctx.message.server.id
            pm = self.settings.get(guild_id, {}).get("pm_result", False)
            srd = self.settings.get(guild_id, {}).get("srd", False)
        except:
            pm = False
            srd = False
        destination = ctx.message.author if pm else ctx.message.channel

        if level is not None and not 0 < level < 21:
            return await self.bot.say("Invalid level.")

        result = await search_and_select(ctx,
                                         c.classes,
                                         name,
                                         lambda e: e['name'],
                                         srd=srd)

        if not result['srd'] and srd:
            return await self.send_srd_error(ctx, result)

        embed = EmbedWithAuthor(ctx)
        if level is None:
            embed.title = result['name']
            embed.add_field(name="Hit Die", value=f"1d{result['hd']['faces']}")
            embed.add_field(name="Saving Throws",
                            value=', '.join(
                                ABILITY_MAP.get(p)
                                for p in result['proficiency']))

            levels = []
            starting_profs = f"You are proficient with the following items, " \
                             f"in addition to any proficiencies provided by your race or background.\n" \
                             f"Armor: {', '.join(result['startingProficiencies'].get('armor', ['None']))}\n" \
                             f"Weapons: {', '.join(result['startingProficiencies'].get('weapons', ['None']))}\n" \
                             f"Tools: {', '.join(result['startingProficiencies'].get('tools', ['None']))}\n" \
                             f"Skills: Choose {result['startingProficiencies']['skills']['choose']} from " \
                             f"{', '.join(result['startingProficiencies']['skills']['from'])}"

            equip_choices = '\n'.join(
                f"• {i}" for i in result['startingEquipment']['default'])
            gold_alt = f"Alternatively, you may start with {result['startingEquipment']['goldAlternative']} gp " \
                       f"to buy your own equipment." if 'goldAlternative' in result['startingEquipment'] else ''
            starting_items = f"You start with the following items, plus anything provided by your background.\n" \
                             f"{equip_choices}\n" \
                             f"{gold_alt}"
            for level in range(1, 21):
                level_str = []
                level_features = result['classFeatures'][level - 1]
                for feature in level_features:
                    level_str.append(feature.get('name'))
                levels.append(', '.join(level_str))

            embed.add_field(name="Starting Proficiencies",
                            value=starting_profs)
            embed.add_field(name="Starting Equipment", value=starting_items)

            level_features_str = ""
            for i, l in enumerate(levels):
                level_features_str += f"`{i+1}` {l}\n"
            embed.description = level_features_str

            embed.set_footer(text="Use !classfeat to look up a feature.")
        else:
            embed.title = f"{result['name']}, Level {level}"

            level_resources = {}
            level_features = result['classFeatures'][level - 1]

            for table in result['classTableGroups']:
                relevant_row = table['rows'][level - 1]
                for i, col in enumerate(relevant_row):
                    level_resources[table['colLabels'][i]] = parse_data_entry(
                        [col])

            for res_name, res_value in level_resources.items():
                embed.add_field(name=res_name, value=res_value)

            for f in level_features:
                text = parse_data_entry(f['entries'])
                embed.add_field(name=f['name'],
                                value=(text[:1019] +
                                       "...") if len(text) > 1023 else text)

            embed.set_footer(
                text="Use !classfeat to look up a feature if it is cut off.")

        await self.bot.send_message(destination, embed=embed)
Ejemplo n.º 11
0
    async def createCharSheet(self, ctx, final_level, dicecloud_userId, race=None, _class=None, subclass=None,
                              background=None):
        dc = DicecloudClient.getInstance()
        caveats = []  # a to do list for the user

        # Name Gen + Setup
        #    DMG name gen
        name = self.nameGen()
        race = race or random.choice([r for r in c.fancyraces if r['source'] in ('PHB', 'VGM', 'MTF')])
        _class = _class or random.choice([cl for cl in c.classes if not 'UA' in cl.get('source')])
        subclass = subclass or random.choice([s for s in _class['subclasses'] if not 'UA' in s['source']])
        background = background or random.choice(c.backgrounds)

        try:
            char_id = dc.create_character(name=name, race=race.name, backstory=background['name'])
        except MeteorClientException:
            return await self.bot.say("I am having problems connecting to Dicecloud. Please try again later.")

        try:
            await dc.share_character(char_id, dicecloud_userId)
        except:
            dc.delete_character(char_id)  # clean up
            return await self.bot.say("Invalid dicecloud username.")

        loadingMessage = await self.bot.send_message(ctx.message.channel, "Generating character, please wait...")

        # Stat Gen
        # Allow user to enter base values
        caveats.append("**Base Ability Scores**: Enter your base ability scores (without modifiers) in the feature "
                       "titled Base Ability Scores.")

        # Race Gen
        #    Racial Features
        speed = race.get_speed_int()
        if speed:
            dc.insert_effect(char_id, Parent.race(char_id), 'base', value=int(speed), stat='speed')

        for k, v in race.ability.items():
            if not k == 'choose':
                dc.insert_effect(char_id, Parent.race(char_id), 'add', value=int(v), stat=ABILITY_MAP[k].lower())
            else:
                dc.insert_effect(char_id, Parent.race(char_id), 'add', value=int(v[0].get('amount', 1)))
                caveats.append(
                    f"**Racial Ability Bonus ({int(v[0].get('amount', 1)):+})**: In your race (Journal tab), select the"
                    f" score you want a bonus to (choose {v[0]['count']} from {', '.join(v[0]['from'])}).")

        for t in race.get_traits():
            dc.insert_feature(char_id, t['name'], t['text'])
        caveats.append("**Racial Features**: Check that the number of uses for each feature is correct, and apply "
                       "any effects they grant.")

        # Class Gen
        #    Class Features
        class_id = dc.insert_class(char_id, final_level, _class['name'])
        dc.insert_effect(char_id, Parent.class_(class_id), 'add', stat=f"d{_class['hd']['faces']}HitDice",
                         calculation=f"{_class['name']}Level")
        hpPerLevel = (int(_class['hd']['faces']) / 2) + 1
        firstLevelHp = int(_class['hd']['faces']) - hpPerLevel
        dc.insert_effect(char_id, Parent.class_(class_id), 'add', stat='hitPoints',
                         calculation=f"{hpPerLevel}*{_class['name']}Level+{firstLevelHp}")
        caveats.append("**HP**: HP is currently calculated using class average; change the value in the Journal tab "
                       "under your class if you wish to change it.")

        for saveProf in _class['proficiency']:
            profKey = ABILITY_MAP.get(saveProf).lower() + 'Save'
            dc.insert_proficiency(char_id, Parent.class_(class_id), profKey, type_='save')
        for prof in _class['startingProficiencies'].get('armor', []):
            dc.insert_proficiency(char_id, Parent.class_(class_id), prof, type_='armor')
        for prof in _class['startingProficiencies'].get('weapons', []):
            dc.insert_proficiency(char_id, Parent.class_(class_id), prof, type_='weapon')
        for prof in _class['startingProficiencies'].get('tools', []):
            dc.insert_proficiency(char_id, Parent.class_(class_id), prof, type_='tool')
        for _ in range(int(_class['startingProficiencies']['skills']['choose'])):
            dc.insert_proficiency(char_id, Parent.class_(class_id), type_='skill')  # add placeholders
        caveats.append(f"**Skill Proficiencies**: You get to choose your skill proficiencies. Under your class "
                       f"in the Journal tab, you may select {_class['startingProficiencies']['skills']['choose']} "
                       f"skills from {', '.join(_class['startingProficiencies']['skills']['from'])}.")

        equip_choices = '\n'.join(f"• {i}" for i in _class['startingEquipment']['default'])
        gold_alt = f"Alternatively, you may start with {_class['startingEquipment']['goldAlternative']} gp " \
                   f"to buy your own equipment." if 'goldAlternative' in _class['startingEquipment'] else ''
        starting_items = f"You start with the following items, plus anything provided by your background.\n" \
                         f"{equip_choices}\n" \
                         f"{gold_alt}"
        caveats.append(f"**Starting Class Equipment**: {starting_items}")

        level_resources = {}
        for table in _class['classTableGroups']:
            relevant_row = table['rows'][final_level - 1]
            for i, col in enumerate(relevant_row):
                level_resources[table['colLabels'][i]] = parse_data_entry([col])

        for res_name, res_value in level_resources.items():
            stat_name = CLASS_RESOURCE_NAMES.get(res_name)
            if stat_name:
                try:
                    dc.insert_effect(char_id, Parent.class_(class_id), 'base', value=int(res_value), stat=stat_name)
                except ValueError:  # edge case: level 20 barb rage
                    pass

        num_subclass_features = 0
        for level in range(1, final_level + 1):
            level_features = _class['classFeatures'][level - 1]
            for f in level_features:
                if f.get('gainSubclassFeature'):
                    num_subclass_features += 1
                text = parse_data_entry(f['entries'], True)
                dc.insert_feature(char_id, f['name'], text)
        for num in range(num_subclass_features):
            level_features = subclass['subclassFeatures'][num]
            for feature in level_features:
                for entry in feature.get('entries', []):
                    if not isinstance(entry, dict): continue
                    if not entry.get('type') == 'entries': continue
                    fe = {'name': entry['name'],
                          'text': parse_data_entry(entry['entries'], True)}
                    dc.insert_feature(char_id, fe['name'], fe['text'])
        caveats.append("**Class Features**: Check that the number of uses for each feature is correct, and apply "
                       "any effects they grant.")
        caveats.append("**Spellcasting**: If your class can cast spells, be sure to set your number of known spells, "
                       "max prepared, DC, attack bonus, and what spells you know in the Spells tab. You can add a "
                       "spell to your spellbook by connecting the character to Avrae and running `!sb add <SPELL>`.")

        # Background Gen
        #    Inventory/Trait Gen
        for trait in background['trait']:
            text = '\n'.join(t for t in trait['text'] if t)
            if 'proficiency' in trait['name'].lower():
                for skill in text.split(', '):
                    if skill in SKILL_MAP:
                        dc.insert_proficiency(char_id, Parent.background(char_id), SKILL_MAP.get(skill))
                    else:
                        dc.insert_proficiency(char_id, Parent.background(char_id), skill, type_='tool')
            elif 'language' in trait['name'].lower():
                for lang in text.split(', '):
                    dc.insert_proficiency(char_id, Parent.background(char_id), lang, type_='language')
                caveats.append("**Languages**: Some backgrounds' languages may ask you to choose one or more. Fill "
                               "this out in the Persona tab.")
            elif trait['name'].lower().startswith('feature'):
                tname = trait['name'][9:]
                dc.insert_feature(char_id, tname, text)
            elif trait['name'].lower().startswith('equipment'):
                caveats.append(f"**Background Equipment**: Your background grants you {text}")

        # await dc.transfer_ownership(char_id, dicecloud_userId)  TODO fix your stuff, dicecloud

        out = f"Generated {name}! I have PMed you the link."
        await self.bot.send_message(ctx.message.author, f"https://dicecloud.com/character/{char_id}/{name}")
        await self.bot.send_message(ctx.message.author,
                                    "**__Caveats__**\nNot everything is automagical! Here are some things you still "
                                    "have to do manually:\n" + '\n\n'.join(caveats))
        await self.bot.send_message(ctx.message.author,
                                    f"When you're ready, load your character into Avrae with the command "
                                    f"`!dicecloud https://dicecloud.com/character/{char_id}/{name} -cc`")
        await self.bot.edit_message(loadingMessage, out)
Ejemplo n.º 12
0
    async def genChar(self, ctx, final_level, race=None, _class=None, subclass=None, background=None):
        loadingMessage = await self.bot.send_message(ctx.message.channel, "Generating character, please wait...")
        color = random.randint(0, 0xffffff)

        # Name Gen
        #    DMG name gen
        name = self.nameGen()
        # Stat Gen
        #    4d6d1
        #        reroll if too low/high
        stats = self.genStats()
        await self.bot.send_message(ctx.message.author, "**Stats for {0}:** `{1}`".format(name, stats))
        # Race Gen
        #    Racial Features
        race = race or random.choice([r for r in c.fancyraces if r.source in ('PHB', 'VGM', 'MTF')])

        embed = EmbedWithAuthor(ctx)
        embed.title = race.name
        embed.description = f"Source: {race.source}"
        embed.add_field(name="Speed", value=race.get_speed_str())
        embed.add_field(name="Size", value=race.size)
        embed.add_field(name="Ability Bonuses", value=race.get_asi_str())
        for t in race.get_traits():
            f_text = t['text']
            f_text = [f_text[i:i + 1024] for i in range(0, len(f_text), 1024)]
            embed.add_field(name=t['name'], value=f_text[0])
            for piece in f_text[1:]:
                embed.add_field(name="** **", value=piece)

        embed.colour = color
        await self.bot.send_message(ctx.message.author, embed=embed)

        # Class Gen
        #    Class Features
        _class = _class or random.choice([cl for cl in c.classes if not 'UA' in cl.get('source')])
        subclass = subclass or random.choice([s for s in _class['subclasses'] if not 'UA' in s['source']])
        embed = EmbedWithAuthor(ctx)
        embed.title = f"{_class['name']} ({subclass['name']})"
        embed.add_field(name="Hit Die", value=f"1d{_class['hd']['faces']}")
        embed.add_field(name="Saving Throws", value=', '.join(ABILITY_MAP.get(p) for p in _class['proficiency']))

        levels = []
        starting_profs = f"You are proficient with the following items, " \
                         f"in addition to any proficiencies provided by your race or background.\n" \
                         f"Armor: {', '.join(_class['startingProficiencies'].get('armor', ['None']))}\n" \
                         f"Weapons: {', '.join(_class['startingProficiencies'].get('weapons', ['None']))}\n" \
                         f"Tools: {', '.join(_class['startingProficiencies'].get('tools', ['None']))}\n" \
                         f"Skills: Choose {_class['startingProficiencies']['skills']['choose']} from " \
                         f"{', '.join(_class['startingProficiencies']['skills']['from'])}"

        equip_choices = '\n'.join(f"• {i}" for i in _class['startingEquipment']['default'])
        gold_alt = f"Alternatively, you may start with {_class['startingEquipment']['goldAlternative']} gp " \
                   f"to buy your own equipment." if 'goldAlternative' in _class['startingEquipment'] else ''
        starting_items = f"You start with the following items, plus anything provided by your background.\n" \
                         f"{equip_choices}\n" \
                         f"{gold_alt}"

        for level in range(1, final_level + 1):
            level_str = []
            level_features = _class['classFeatures'][level - 1]
            for feature in level_features:
                level_str.append(feature.get('name'))
            levels.append(', '.join(level_str))

        embed.add_field(name="Starting Proficiencies", value=starting_profs)
        embed.add_field(name="Starting Equipment", value=starting_items)

        level_features_str = ""
        for i, l in enumerate(levels):
            level_features_str += f"`{i+1}` {l}\n"
        embed.description = level_features_str

        embed.colour = color
        await self.bot.send_message(ctx.message.author, embed=embed)

        embed = EmbedWithAuthor(ctx)
        level_resources = {}
        for table in _class['classTableGroups']:
            relevant_row = table['rows'][final_level - 1]
            for i, col in enumerate(relevant_row):
                level_resources[table['colLabels'][i]] = parse_data_entry([col])

        for res_name, res_value in level_resources.items():
            embed.add_field(name=res_name, value=res_value)

        embed.colour = color
        await self.bot.send_message(ctx.message.author, embed=embed)

        embed_queue = [EmbedWithAuthor(ctx)]
        num_subclass_features = 0
        num_fields = 0

        def inc_fields(text):
            nonlocal num_fields
            num_fields += 1
            if num_fields > 25:
                embed_queue.append(EmbedWithAuthor(ctx))
                num_fields = 0
            if len(str(embed_queue[-1].to_dict())) + len(text) > 5800:
                embed_queue.append(EmbedWithAuthor(ctx))
                num_fields = 0

        for level in range(1, final_level + 1):
            level_features = _class['classFeatures'][level - 1]
            for f in level_features:
                if f.get('gainSubclassFeature'):
                    num_subclass_features += 1
                text = parse_data_entry(f['entries'])
                text = [text[i:i + 1024] for i in range(0, len(text), 1024)]
                inc_fields(text[0])
                embed_queue[-1].add_field(name=f['name'], value=text[0])
                for piece in text[1:]:
                    inc_fields(piece)
                    embed_queue[-1].add_field(name="\u200b", value=piece)
        for num in range(num_subclass_features):
            level_features = subclass['subclassFeatures'][num]
            for feature in level_features:
                for entry in feature.get('entries', []):
                    if not isinstance(entry, dict): continue
                    if not entry.get('type') == 'entries': continue
                    fe = {'name': entry['name'],
                          'text': parse_data_entry(entry['entries'])}
                    text = [fe['text'][i:i + 1024] for i in range(0, len(fe['text']), 1024)]
                    inc_fields(text[0])
                    embed_queue[-1].add_field(name=fe['name'], value=text[0])
                    for piece in text[1:]:
                        inc_fields(piece)
                        embed_queue[-1].add_field(name="\u200b", value=piece)

        for embed in embed_queue:
            embed.colour = color
            await self.bot.send_message(ctx.message.author, embed=embed)

        # Background Gen
        #    Inventory/Trait Gen
        background = background or random.choice(c.backgrounds)
        embed = EmbedWithAuthor(ctx)
        embed.title = background['name']
        embed.description = f"*Source: {background.get('source', 'Unknown')}*"

        ignored_fields = ['suggested characteristics', 'specialty',
                          'harrowing event']
        for trait in background['trait']:
            if trait['name'].lower() in ignored_fields: continue
            text = '\n'.join(t for t in trait['text'] if t)
            text = [text[i:i + 1024] for i in range(0, len(text), 1024)]
            embed.add_field(name=trait['name'], value=text[0])
            for piece in text[1:]:
                embed.add_field(name="\u200b", value=piece)
        embed.colour = color
        await self.bot.send_message(ctx.message.author, embed=embed)

        out = "{6}\n{0}, {1} {7} {2} {3}. {4} Background.\nStat Array: `{5}`\nI have PM'd you full character details.".format(
            name, race.name, _class['name'], final_level, background['name'], stats, ctx.message.author.mention,
            subclass['name'])

        await self.bot.edit_message(loadingMessage, out)
Ejemplo n.º 13
0
 def __init__(self):
     with open('./res/conditions.json', 'r') as f:
         self.conditions = json.load(f)
     with open('./res/rules.json', 'r') as f:
         self.rules = json.load(f)
     with open('./res/feats.json', 'r') as f:
         self.feats = json.load(f)
     with open('./res/races.json', 'r') as f:
         _raw = json.load(f)
         self.rfeats = []
         self.races = copy.deepcopy(_raw)
         self.fancyraces = [Race.from_data(r) for r in self.races]
         for race in _raw:
             for entry in race['entries']:
                 if isinstance(entry, dict) and 'name' in entry:
                     temp = {
                         'name': "{}: {}".format(race['name'],
                                                 entry['name']),
                         'text': parse_data_entry(entry['entries']),
                         'srd': race['srd']
                     }
                     self.rfeats.append(temp)
     with open('./res/classes.json', 'r', encoding='utf-8-sig') as f:
         _raw = json.load(f)
         self.cfeats = []
         self.classes = copy.deepcopy(_raw)
         for _class in _raw:
             for level in _class.get('classFeatures', []):
                 for feature in level:
                     fe = {
                         'name': f"{_class['name']}: {feature['name']}",
                         'text': parse_data_entry(feature['entries']),
                         'srd': _class['srd']
                     }
                     self.cfeats.append(fe)
                     options = [
                         e for e in feature['entries']
                         if isinstance(e, dict) and e['type'] == 'options'
                     ]
                     for option in options:
                         for opt_entry in option.get('entries', []):
                             fe = {
                                 'name':
                                 f"{_class['name']}: {feature['name']}: {_resolve_name(opt_entry)}",
                                 'text':
                                 f"{_parse_prereqs(opt_entry)}{parse_data_entry(opt_entry['entries'])}",
                                 'srd': _class['srd']
                             }
                             self.cfeats.append(fe)
             for subclass in _class.get('subclasses', []):
                 for level in subclass.get('subclassFeatures', []):
                     for feature in level:
                         options = [
                             f for f in feature.get('entries', []) if
                             isinstance(f, dict) and f['type'] == 'options'
                         ]  # battlemaster only
                         for option in options:
                             for opt_entry in option.get('entries', []):
                                 fe = {
                                     'name':
                                     f"{_class['name']}: {option['name']}: "
                                     f"{_resolve_name(opt_entry)}",
                                     'text':
                                     parse_data_entry(opt_entry['entries']),
                                     'srd':
                                     subclass.get('srd', False)
                                 }
                                 self.cfeats.append(fe)
                         for entry in feature.get('entries', []):
                             if not isinstance(entry, dict): continue
                             if not entry.get('type') == 'entries': continue
                             fe = {
                                 'name':
                                 f"{_class['name']}: {subclass['name']}: {entry['name']}",
                                 'text': parse_data_entry(entry['entries']),
                                 'srd': subclass.get('srd', False)
                             }
                             self.cfeats.append(fe)
                             options = [
                                 e for e in entry['entries']
                                 if isinstance(e, dict)
                                 and e['type'] == 'options'
                             ]
                             for option in options:
                                 for opt_entry in option.get('entries', []):
                                     fe = {
                                         'name':
                                         f"{_class['name']}: {subclass['name']}: {entry['name']}: "
                                         f"{_resolve_name(opt_entry)}",
                                         'text':
                                         parse_data_entry(
                                             opt_entry['entries']),
                                         'srd':
                                         subclass.get('srd', False)
                                     }
                                     self.cfeats.append(fe)
     with open('./res/bestiary.json', 'r') as f:
         self.monsters = json.load(f)
         self.monster_mash = [Monster.from_data(m) for m in self.monsters]
     with open('./res/spells.json', 'r') as f:
         self.spells = json.load(f)
     with open('./res/items.json', 'r') as f:
         _items = json.load(f)
         self.items = [i for i in _items if i.get('type') is not '$']
     with open('./res/auto_spells.json', 'r') as f:
         self.autospells = json.load(f)
     with open('./res/backgrounds.json', 'r') as f:
         self.backgrounds = json.load(f)
     self.subclasses = self.load_subclasses()
     with open('./res/itemprops.json', 'r') as f:
         self.itemprops = json.load(f)
Ejemplo n.º 14
0
    async def _class(self, ctx, name: str, level: int = None):
        """Looks up a class, or all features of a certain level."""
        if level is not None and not 0 < level < 21:
            return await ctx.send("Invalid level.")

        choices = compendium.classes + compendium.nclass_names
        result = await self._lookup_search(ctx, choices, name, lambda e: e['name'], search_type='class')
        if not result:
            return

        embed = EmbedWithAuthor(ctx)
        if level is None:
            embed.title = result['name']
            embed.add_field(name="Hit Die", value=f"1d{result['hd']['faces']}")
            embed.add_field(name="Saving Throws", value=', '.join(ABILITY_MAP.get(p) for p in result['proficiency']))

            levels = []
            starting_profs = f"You are proficient with the following items, " \
                             f"in addition to any proficiencies provided by your race or background.\n" \
                             f"Armor: {', '.join(result['startingProficiencies'].get('armor', ['None']))}\n" \
                             f"Weapons: {', '.join(result['startingProficiencies'].get('weapons', ['None']))}\n" \
                             f"Tools: {', '.join(result['startingProficiencies'].get('tools', ['None']))}\n" \
                             f"Skills: Choose {result['startingProficiencies']['skills']['choose']} from " \
                             f"{', '.join(result['startingProficiencies']['skills']['from'])}"

            equip_choices = '\n'.join(f"• {i}" for i in result['startingEquipment']['default'])
            gold_alt = f"Alternatively, you may start with {result['startingEquipment']['goldAlternative']} gp " \
                       f"to buy your own equipment." if 'goldAlternative' in result['startingEquipment'] else ''
            starting_items = f"You start with the following items, plus anything provided by your background.\n" \
                             f"{equip_choices}\n" \
                             f"{gold_alt}"
            for level in range(1, 21):
                level_str = []
                level_features = result['classFeatures'][level - 1]
                for feature in level_features:
                    level_str.append(feature.get('name'))
                levels.append(', '.join(level_str))

            embed.add_field(name="Starting Proficiencies", value=starting_profs, inline=False)
            embed.add_field(name="Starting Equipment", value=starting_items, inline=False)

            level_features_str = ""
            for i, l in enumerate(levels):
                level_features_str += f"`{i + 1}` {l}\n"
            embed.description = level_features_str

            embed.set_footer(text=f"Use {ctx.prefix}classfeat to look up a feature.")
        else:
            embed.title = f"{result['name']}, Level {level}"

            level_resources = {}
            level_features = result['classFeatures'][level - 1]

            for table in result.get('classTableGroups', []):
                relevant_row = table['rows'][level - 1]
                for i, col in enumerate(relevant_row):
                    level_resources[table['colLabels'][i]] = parse_data_entry([col])

            for res_name, res_value in level_resources.items():
                if res_value != '0':
                    embed.add_field(name=res_name, value=res_value)

            for f in level_features:
                text = parse_data_entry(f['entries'])
                embed.add_field(name=f['name'], value=(text[:1019] + "...") if len(text) > 1023 else text, inline=False)

            embed.set_footer(text=f"Use {ctx.prefix}classfeat to look up a feature if it is cut off.")

        await (await self._get_destination(ctx)).send(embed=embed)