예제 #1
0
    def parse_creature(self, dbr, dbr_file, result):
        """
        Parse the creature and its properties and skills.

        """
        # Grab the first set of properties:
        classification = dbr.get('monsterClassification', 'Common')
        tag = dbr.get('description', None)

        # Set the known properties for this creature
        if tag:
            race = dbr.get('characterRacialProfile', None)
            result.update({
                'classification':
                classification,
                'name':
                texts.get(tag),
                'race':
                race[0] if race else None,
                'level': [level for level in dbr.get('charLevel', [])],
                'tag':
                tag,
            })

        # Manually parse the defensive properties, since there's no template
        # tied for it for monsters:
        parsers.ParametersDefensiveParser().parse(dbr, dbr_file, result)

        # Iterate over the properties for each difficulty:
        properties = []
        for i in range(3):
            properties.append({})
            itr = TQDBParser.extract_values(dbr, '', i)

            # Set this creature's HP and MP as stats, not as bonuses:
            if self.HP in itr:
                hp = itr[self.HP]
                properties[i][self.HP] = texts.get('LifeText').format(hp)
            if self.MP in itr:
                mp = itr[self.MP]
                properties[i][self.MP] = texts.get('ManaText').format(mp)

            # Add non-character properties:
            for k, v in result['properties'].items():
                if k.startswith('character'):
                    continue

                # Add the property to the correct difficulty index:
                if isinstance(v, list):
                    # The property changes per difficulty:
                    properties[i][k] = v[i] if i < len(v) else v[-1]
                else:
                    # The property is constant:
                    properties[i][k] = v

        # Add the base damage, stats, regens, and resistances:
        result['properties'] = properties
예제 #2
0
    def parse(self, dbr, dbr_file, result):
        """
        Parse all the abilities a monster has.

        """
        # Initialize the abilities (to be indexed per level)
        abilities = []

        # Parse all the normal skills (17 max):
        for i in range(1, 18):
            nameTag = f'skillName{i}'
            levelTag = f'skillLevel{i}'

            # Skip unset skills or skills that are to be ignored:
            if (nameTag not in dbr or levelTag not in dbr
                    or str(dbr[nameTag]).lower() in self.IGNORE_SKILLS):
                continue

            try:
                skill = DBRParser.parse(dbr[nameTag])
            except InvalidItemError as e:
                logging.debug(
                    f"Skipping creature skill {nameTag} in {dbr_file} because it's invalid. {e}"
                )
                continue
            if not skill['properties']:
                continue

            # Store the skill, which will ensure a unique tag:
            skill_tag = storage.store_skill(skill)

            # Iterate over the difficulties:
            for difficulty in range(3):
                itr = TQDBParser.extract_values(dbr, 'skill', difficulty)

                # If a level is set to 0 for a difficulty, it won't be in the
                # extracted result, so use the KeyError safe 'get' method:
                level = itr.get(levelTag, 0)

                if not level:
                    continue

                if len(abilities) - 1 < difficulty:
                    # Create missing tiers:
                    abilities += ([{}] * (difficulty - len(abilities) + 1))

                abilities[difficulty][skill_tag] = level

        result['abilities'] = abilities
예제 #3
0
    def parse_field(self, field, dbr, result):
        """
        Parse all values for a given defensive field that is in the DBR.

        """
        for i in range(len(dbr[field])):
            iteration = TQDBParser.extract_values(dbr, field, i)

            # It's possible some values aren't set in this iteration:
            if field not in iteration:
                continue

            chance = iteration.get(f'{field}Chance', 0)
            value = iteration[field]

            # Format the value using the texts:
            formatted = texts.get(field).format(value)

            # Prepend a chance if it's set:
            if chance:
                formatted = texts.get(CHANCE).format(chance) + formatted

            TQDBParser.insert_value(field, formatted, result)
예제 #4
0
파일: base.py 프로젝트: fonsleenaars/tqdb
    def parse_field(self, field, dbr, result):
        """
        Parse all values for a given defensive field that is in the DBR.

        """
        for i in range(len(dbr[field])):
            iteration = TQDBParser.extract_values(dbr, field, i)

            # It's possible some values aren't set in this iteration:
            if field not in iteration:
                continue

            chance = iteration.get(f'{field}Chance', 0)
            value = iteration[field]

            # Format the value using the texts:
            formatted = texts.get(field).format(value)

            # Prepend a chance if it's set:
            if chance:
                formatted = texts.get(CHANCE).format(chance) + formatted

            TQDBParser.insert_value(field, formatted, result)
예제 #5
0
    def parse(self, dbr, dbr_file, result):
        """
        Parse an offensive/retaliation field.

        For each field, depending on the group it's in, this function will need
        to check both the absolute (flat increase) and modifier (% increase)
        versions as well as possible effects or damage durations.

        Args:
            field (str): Field name, as listed in the FIELDS list

        """
        # Set the result and prepare the global stores:
        self.result = result
        self.offensive = {}
        self.offensiveXOR = False
        self.retaliation = {}
        self.retaliationXOR = False

        for field, field_type in self.FIELDS.items():
            # Find whether the flat, modifier, or both fields are present:
            min = (
                f'{field}Min' if field != 'offensiveManaBurn'
                # Mana burn is the only field that differs from the rest
                else 'offensiveManaBurnDrainMin')
            mod = f'{field}Modifier'

            iterations = max(
                len(dbr[min]) if min in dbr else 0,
                len(dbr[mod]) if mod in dbr else 0, 0)

            # Now iterate as many times as is necessary for this field:
            for index in range(iterations):
                # Create a new copy of the DBR with the values for this index:
                iteration = TQDBParser.extract_values(dbr, field, index)

                if min in iteration:
                    # Parse the flat (+...) version:
                    self.parse_flat(field, field_type, iteration)
                if mod in iteration:
                    # Parse the modifier (+...%) version
                    self.parse_modifier(field, field_type, iteration)

        # Now add the global chance tags if they're set:
        offensive_key = 'offensiveGlobalChance'
        if offensive_key in dbr and self.offensive:
            # Skip 0 chance globals altogether
            chances = [chance for chance in dbr[offensive_key] if chance]

            for index, chance in enumerate(chances):
                self.parse_global(
                    offensive_key,
                    # The global chance for the offensive properties
                    chance,
                    # If any global offensive properties are XOR-ed:
                    self.offensiveXOR,
                    # The dictionary of global offensive properties
                    self.offensive,
                    # Index of this global chance
                    index)

        retaliation_key = 'retaliationGlobalChance'
        if retaliation_key in dbr and self.retaliation:
            # Skip 0 chance globals altogether
            chances = [chance for chance in dbr[retaliation_key] if chance]

            for index, chance in enumerate(chances):
                self.parse_global(
                    retaliation_key,
                    # The global chance for the offensive properties
                    chance,
                    # If any global offensive properties are XOR-ed:
                    self.retaliationXOR,
                    # The dictionary of global offensive properties
                    self.retaliation,
                    # Index of this global chance
                    index)
예제 #6
0
    def parse(self, dbr, dbr_file, result):
        """
        Parse the base properties of a skill.

        These properties include the skillDisplayName, its friendly display
        name (the property returns a tag), and the maximum level of a skill.

        """
        # Store the path to this skill, it is used in tqdb.storage to ensure
        # all tags are unique.
        result['path'] = dbr_file

        if self.NAME in dbr:
            # The tag is the skillDisplayName property
            result['tag'] = self.FORMATTER.sub('', dbr[self.NAME])

            # Now try to find a friendly name for the tag:
            result['name'] = texts.get(result['tag'])

            if result['name'] == result['tag']:
                # If the tag wasn't returned, a friendly name weas found:
                logging.debug(f'No skill name found for {result["tag"]}')
        else:
            logging.debug(f'No skillDisplayName found in {dbr_file}')

        if self.DESC in dbr and texts.has(dbr[self.DESC]):
            # Also load the description, if it's known:
            result['description'] = texts.get(dbr[self.DESC])
        elif self.FILE in dbr:
            # Use the FileDescription instead:
            result['description'] = dbr['FileDescription']

        # Check the skill base fields:
        base_tiers = TQDBParser.highest_tier(dbr, self.FIELDS)

        for field in self.FIELDS:
            for index in range(base_tiers):
                itr_dbr = TQDBParser.extract_values(dbr, field, index)

                if field not in itr_dbr or itr_dbr[field] <= 0.01:
                    continue

                # Insert this skill property
                value = texts.get(field).format(itr_dbr[field])
                TQDBParser.insert_value(field, value, result)

        # Check the damage absorption skill properties:
        abs_tiers = TQDBParser.highest_tier(dbr, self.ABSORPTIONS)

        for field in self.ABSORPTIONS:
            for index in range(abs_tiers):
                itr_dbr = TQDBParser.extract_values(dbr, field, index)

                if field not in itr_dbr:
                    continue

                # Add 'skill' prefix and capitalize first letter:
                field_prefixed = 'skill' + field[:1].upper() + field[1:]
                value = itr_dbr[field]

                # Find qualifier damage type(s):
                damage_types = ', '.join([
                    texts.get(text_key)
                    for dmg_type, text_key in self.QUALIFIERS.items()
                    if dmg_type in dbr
                ])

                if damage_types:
                    TQDBParser.insert_value(
                        field_prefixed,
                        f'{texts.get(field_prefixed).format(value)} '
                        f'({damage_types})', result)
                else:
                    # If there is no qualifier, it's all damage:
                    TQDBParser.insert_value(
                        field_prefixed,
                        texts.get(field_prefixed).format(value), result)

        # Prepare two variables to determine the max number of tiers:
        skill_cap = dbr.get('skillUltimateLevel',
                            dbr.get('skillMaxLevel')) or 99
        props = result['properties']

        # The maximum number of properties is now the minimum between the skill
        # cap and the highest number of tiers available in the properties:
        max_tiers = min(TQDBParser.highest_tier(props, props.keys()),
                        skill_cap)

        # After all skill properties have been set, index them by level:
        properties = [{} for i in range(max_tiers)]

        # Insert the existing properties by adding them to the correct tier:
        for field, values in result['properties'].items():
            for index in range(max_tiers):
                # Each value is either a list or a flat value to repeat:
                if isinstance(values, list):
                    # Properties that are capped before this tier repeat their
                    # last value:
                    if index >= len(values):
                        properties[index][field] = values[len(values) - 1]
                    else:
                        properties[index][field] = values[index]
                else:
                    properties[index][field] = values

        # For summoned skills it's very likely a lot of extraneous empty
        # property tiers were added, filter those out:
        properties = [tier for tier in properties if tier]

        # Now set the reindexed properties:
        result['properties'] = properties
예제 #7
0
파일: creatures.py 프로젝트: rehevkor5/tqdb
    def parse(self, dbr, dbr_file, result):
        """
        Parse the monster.

        """
        self.parse_creature(dbr, dbr_file, result)

        # Don't parse any further for tagless or level-less creatures:
        if 'tag' not in result or not result['level']:
            return

        # Iterate over normal, epic & legendary version of the boss:
        loot = []
        for index in range(3):
            # Initialize an empty result:
            loot.append({})

            # Create a DBR that only has the equipment for this difficulty:
            difficulty_dbr = TQDBParser.extract_values(dbr, '', index)

            # Parse all the equipment in this difficulty
            difficulty_dbr = self.parse_difficulty(difficulty_dbr, dbr_file)

            # Only store the equipment if there was any:
            if difficulty_dbr:
                loot[index] = difficulty_dbr

        # If there is any tiered data to store, store it:
        if any(tier for tier in loot if tier):
            result['loot'] = loot

        chests = []
        tag = result['tag']

        # Find the chest for each difficulty:
        for index in range(3):
            # Initialize an empty result:
            chests.append({})

            # Grab the chest to parse:
            if tag in CHESTS and CHESTS[tag][index]:
                # Grab the level for this index, or the last one:
                level = (
                    result['level'][index]
                    if len(result['level']) > index
                    else result['level'][-1])

                # Parse the chest and pass the monsters level as a reference:
                loot = DBRParser.parse(
                    DB / CHESTS[tag][index],
                    {'level': level},
                )

                # Convert all item chances to 4 point precision:
                chests[index] = dict(
                    (k, float('{0:.4f}'.format(v))) for k, v
                    in loot['loot_table'].items())

        # If there is any tiered data to store, store it:
        if any(tier for tier in chests if tier):
            result['chest'] = chests

        # Check if this monster is limited to a difficulty:
        if len(result['level']) != len(set(result['level'])):
            # If a level is repeated, it means a creature doesn't spawn in
            # some difficulties. The 'normal' difficulty level is either
            # repeated in Epic and Legendary, so find the index and subtract
            # 1 from that to get all difficulties that should be removed:
            for i in range(result['level'].count(result['level'][0]) - 1):
                result['properties'][i] = {}
                result['abilities'][i] = {}
                result['level'][i] = None
예제 #8
0
파일: skills.py 프로젝트: fonsleenaars/tqdb
    def parse(self, dbr, dbr_file, result):
        """
        Parse the base properties of a skill.

        These properties include the skillDisplayName, its friendly display
        name (the property returns a tag), and the maximum level of a skill.

        """
        # Store the path to this skill, it is used in tqdb.storage to ensure
        # all tags are unique.
        result['path'] = dbr_file

        if self.NAME in dbr:
            # The tag is the skillDisplayName property
            result['tag'] = self.FORMATTER.sub('', dbr[self.NAME])

            # Now try to find a friendly name for the tag:
            result['name'] = texts.get(result['tag'])

            if result['name'] == result['tag']:
                # If the tag wasn't returned, a friendly name weas found:
                logging.debug(f'No skill name found for {result["tag"]}')
        else:
            logging.debug(f'No skillDisplayName found in {dbr_file}')

        if self.DESC in dbr and texts.has(dbr[self.DESC]):
            # Also load the description, if it's known:
            result['description'] = texts.get(dbr[self.DESC])
        elif self.FILE in dbr:
            # Use the FileDescription instead:
            result['description'] = dbr['FileDescription']

        # Check the skill base fields:
        base_tiers = TQDBParser.highest_tier(dbr, self.FIELDS)

        for field in self.FIELDS:
            for index in range(base_tiers):
                itr_dbr = TQDBParser.extract_values(dbr, field, index)

                if field not in itr_dbr or itr_dbr[field] <= 0.01:
                    continue

                # Insert this skill property
                value = texts.get(field).format(itr_dbr[field])
                TQDBParser.insert_value(field, value, result)

        # Check the damage absorption skill properties:
        abs_tiers = TQDBParser.highest_tier(dbr, self.ABSORPTIONS)

        for field in self.ABSORPTIONS:
            for index in range(abs_tiers):
                itr_dbr = TQDBParser.extract_values(dbr, field, index)

                if field not in itr_dbr:
                    continue

                # Add 'skill' prefix and capitalize first letter:
                field_prefixed = 'skill' + field[:1].upper() + field[1:]
                value = itr_dbr[field]

                # Find qualifier damage type(s):
                damage_types = ', '.join([
                    texts.get(text_key)
                    for dmg_type, text_key in self.QUALIFIERS.items()
                    if dmg_type in dbr])

                if damage_types:
                    TQDBParser.insert_value(
                        field_prefixed,
                        f'{texts.get(field_prefixed).format(value)} '
                        f'({damage_types})',
                        result)
                else:
                    # If there is no qualifier, it's all damage:
                    TQDBParser.insert_value(
                        field_prefixed,
                        texts.get(field_prefixed).format(value),
                        result)

        # Prepare two variables to determine the max number of tiers:
        skill_cap = dbr.get('skillUltimateLevel', dbr.get('skillMaxLevel'))
        props = result['properties']

        # The maximum number of properties is now the minimum between the skill
        # cap and the highest number of tiers available in the properties:
        max_tiers = min(
            TQDBParser.highest_tier(props, props.keys()),
            skill_cap)

        # After all skill properties have been set, index them by level:
        properties = [{} for i in range(max_tiers)]

        # Insert the existing properties by adding them to the correct tier:
        for field, values in result['properties'].items():
            for index in range(max_tiers):
                # Each value is either a list or a flat value to repeat:
                if isinstance(values, list):
                    # Properties that are capped before this tier repeat their
                    # last value:
                    if index >= len(values):
                        properties[index][field] = values[len(values) - 1]
                    else:
                        properties[index][field] = values[index]
                else:
                    properties[index][field] = values

        # For summoned skills it's very likely a lot of extraneous empty
        # property tiers were added, filter those out:
        properties = [tier for tier in properties if tier]

        # Now set the reindexed properties:
        result['properties'] = properties
예제 #9
0
파일: base.py 프로젝트: fonsleenaars/tqdb
    def parse(self, dbr, dbr_file, result):
        """
        Parse an offensive/retaliation field.

        For each field, depending on the group it's in, this function will need
        to check both the absolute (flat increase) and modifier (% increase)
        versions as well as possible effects or damage durations.

        Args:
            field (str): Field name, as listed in the FIELDS list

        """
        # Set the result and prepare the global stores:
        self.result = result
        self.offensive = {}
        self.offensiveXOR = False
        self.retaliation = {}
        self.retaliationXOR = False

        for field, field_type in self.FIELDS.items():
            # Find whether the flat, modifier, or both fields are present:
            min = (f'{field}Min'
                   if field != 'offensiveManaBurn'
                   # Mana burn is the only field that differs from the rest
                   else 'offensiveManaBurnDrainMin')
            mod = f'{field}Modifier'

            iterations = max(
                len(dbr[min]) if min in dbr else 0,
                len(dbr[mod]) if mod in dbr else 0,
                0)

            # Now iterate as many times as is necessary for this field:
            for index in range(iterations):
                # Create a new copy of the DBR with the values for this index:
                iteration = TQDBParser.extract_values(dbr, field, index)

                if min in iteration:
                    # Parse the flat (+...) version:
                    self.parse_flat(field, field_type, iteration)
                if mod in iteration:
                    # Parse the modifier (+...%) version
                    self.parse_modifier(field, field_type, iteration)

        # Now add the global chance tags if they're set:
        offensive_key = 'offensiveGlobalChance'
        if offensive_key in dbr and self.offensive:
            # Skip 0 chance globals altogether
            chances = [chance for chance in dbr[offensive_key] if chance]

            for index, chance in enumerate(chances):
                self.parse_global(
                    offensive_key,
                    # The global chance for the offensive properties
                    chance,
                    # If any global offensive properties are XOR-ed:
                    self.offensiveXOR,
                    # The dictionary of global offensive properties
                    self.offensive,
                    # Index of this global chance
                    index)

        retaliation_key = 'retaliationGlobalChance'
        if retaliation_key in dbr and self.retaliation:
            # Skip 0 chance globals altogether
            chances = [chance for chance in dbr[retaliation_key] if chance]

            for index, chance in enumerate(chances):
                self.parse_global(
                    retaliation_key,
                    # The global chance for the offensive properties
                    chance,
                    # If any global offensive properties are XOR-ed:
                    self.retaliationXOR,
                    # The dictionary of global offensive properties
                    self.retaliation,
                    # Index of this global chance
                    index)