Beispiel #1
0
    def is_valid_classification(self, dbr, dbr_file, result):
        """
        Check if this item is of a valid classification for TQDB.

        """
        itemClass = dbr.get('Class')
        classification = dbr.get('itemClassification', None)

        if (itemClass not in self.ALLOWED and
                classification not in self.CLASSIFICATIONS.keys()):
            raise InvalidItemError(f"Item {dbr_file} is excluded due to Class "
                                   f"{itemClass} with itemClassification "
                                   f"{classification}.")
        elif (classification in self.CLASSIFICATIONS.keys() and
                'classification' not in result):
            # Only add the classification if it doesn't exist yet:
            result['classification'] = texts.get(
                self.CLASSIFICATIONS[classification]).strip()

            # For Monster Infrequents, make sure a drop difficulty exists:
            if classification == 'Rare':
                file_name = os.path.basename(dbr_file).split('_')
                if len(file_name) < 2 or file_name[1] not in DIFFICULTIES:
                    raise InvalidItemError(f"File name {file_name} does not "
                                           "specify difficulty, or difficulty "
                                           "not recognized.")

                # Set the difficulty for which this MI drops:
                result['dropsIn'] = texts.get(
                    DIFFICULTIES[file_name[1]]).strip()
Beispiel #2
0
    def parse(self, dbr, dbr_file, result):
        if 'tables' not in dbr:
            logging.debug(f'No table found in {dbr_file}')
            raise InvalidItemError(f"No table found in {dbr_file}")

        # Parse the references 'tables' file and set the result:
        loot = DBRParser.parse(
            dbr['tables'][0],
            # Always pass along any references that were set:
            result['references'],
        )
        result['loot_table'] = loot['loot_table']
Beispiel #3
0
    def parse(self, dbr, dbr_file, result):
        # If no tag exists, skip parsing:
        tag = dbr.get('itemNameTag', None)
        if not tag:
            raise InvalidItemError(f"Item {dbr_file} has no itemNameTag.")

        # Set the known item properties:
        result.update({
            'bitmap': dbr.get('bitmap', None),
            'itemLevel': dbr.get('itemLevel', None),
            'name': texts.get(tag),
            'tag': tag,
        })

        # Check if this item is part of a set:
        item_set_path = dbr.get('itemSetName', None)
        if item_set_path:
            # Read (don't parse to avoid recursion) the set to get the tag:
            item_set = DBRParser.read(item_set_path)

            # Only add the set if it has a tag:
            result['set'] = item_set.get(ItemSetParser.NAME, None)

        # Stop parsing here if requirement parsing isn't necessary
        if not self.should_parse_requirements(dbr, result):
            return

        # Cost prefix of this props is determined by its class
        cost_prefix = dbr['Class'].split('_')[1]
        cost_prefix = cost_prefix[:1].lower() + cost_prefix[1:]

        # Read cost file
        cost_properties = DBRParser.read(dbr.get(
            'itemCostName', self.REQUIREMENT_FALLBACK))

        # Grab the props level (it's a variable in the equations)
        for requirement in self.REQUIREMENTS:
            # Create the equation key
            equation_key = cost_prefix + requirement + 'Equation'
            req = requirement.lower() + 'Requirement'

            # Existing requirements shouldn't be overriden:
            if equation_key in cost_properties and req not in result:
                equation = cost_properties[equation_key]

                # camelCased variables are required for the equations:
                itemLevel = dbr['itemLevel']  # noqa
                totalAttCount = len(result['properties'])  # noqa

                # Eval the equation:
                result[req] = math.ceil(numexpr.evaluate(equation).item())
Beispiel #4
0
    def parse(self, dbr, dbr_file, result):
        # Skip formula without artifacts
        if self.ARTIFACT not in dbr:
            raise InvalidItemError(f"Artifact {dbr_file} has no {self.ARTIFACT}.")

        artifact = DBRParser.parse(dbr[self.ARTIFACT])

        # Update the result with the artifact:
        result['tag'] = artifact['tag']
        result['name'] = artifact['name']
        result['classification'] = artifact['classification']

        if self.BITMAP in dbr:
            result['bitmap'] = dbr[self.BITMAP]

        # Grab the reagents (ingredients):
        for reagent_key in ['reagent1', 'reagent2', 'reagent3']:
            # For some reason reagent DBRs are of type array, so grab [0]:
            reagent = DBRParser.parse(dbr[reagent_key + 'BaseName'][0])

            # Add the reagent (relic, scroll or artifact)
            result[reagent_key] = reagent['tag']

        # Add the potential completion bonuses
        bonuses = {}
        try:
            bonuses = DBRParser.parse(dbr['artifactBonusTableName'])
        except InvalidItemError as e:
            logging.debug("Could not parse artifact completion bonus "
                          f"information for {result['name']} in {dbr_file}. "
                          f"{e}")

        result['bonus'] = bonuses.get('table', [])

        # Last but not least, pop the 'properties' from this result, since
        # formula don't have the properties themselves, but their respective
        # artifacts do.
        result.pop('properties')
Beispiel #5
0
    def parse(self, dbr, dbr_file, result):
        file_name = os.path.basename(dbr_file).split('_')

        # Skip artifacts with unknown difficulties in which they drop:
        if file_name[0] not in DIFFICULTIES:
            raise InvalidItemError(f"File {file_name} has unknown difficulty.")

        # Artifact classification value (always Lesser, Greater or Divine)
        ac_value = dbr.get('artifactClassification', None)
        # Translation tag for this classification
        ac_tag = ARTIFACT_CLASSIFICATIONS[ac_value]

        result.update({
            # Bitmap has a different key name than items here.
            'bitmap': dbr.get('artifactBitmap', None),
            # Classification is either Lesser, Greater or Divine (translated)
            'classification': texts.get(ac_tag).strip(),
            # Difficulty it starts dropping is based on the file name
            'dropsIn': texts.get(DIFFICULTIES[file_name[0]]).strip(),
            # For artifacts the tag is in the Actor.tpl variable 'description'
            'name': texts.get(dbr['description']),
            'tag': dbr['description'],
        })
Beispiel #6
0
def parse(dbr_file, references=None):
    """
    Parse a DBR file according to its template.

    """
    if references is None:
        references = {}
    # Initialize the parsers map if necessary:
    global parsers
    if not parsers:
        parsers = load_parsers()

    # First check if the file has been parsed before:
    if dbr_file in storage.db:
        return storage.db[dbr_file]

    logging.debug(f'Parsing {dbr_file}')
    dbr = read(dbr_file)

    # Initialize an empty result, this variable will be updated by the parsers.
    result = {
        # Properties will be filled by all core attribute parsers, like
        # character, offensive, defensive, etc.
        'properties': {},
        # Any parser can pass references that another parser can then use:
        'references': references,
    }

    # There are still non-existent references, make sure the DBR isn't empty:
    if not dbr:
        return result

    # If a template exists for this type, parse it accordingly:
    template = get_template(dbr, dbr_file)

    # Construct a list of parsers to organize by priority:
    prioritized = []

    # Begin updating the result by the first template parser, if available:
    if template.key in parsers:
        prioritized.append(parsers[template.key])

    # Add any inherited template parsers:
    for t in template.templates:
        if t not in parsers:
            continue
        prioritized.append(parsers[t])

    # Prioritize the list and then run through the parsers:
    prioritized.sort(key=lambda p: p.get_priority(), reverse=True)
    for prioritized_parser in prioritized:
        try:
            prioritized_parser.parse(dbr, dbr_file, result)
        except InvalidItemError as e:
            # One of the parsers has determined this file shouldn't be parsed:
            raise InvalidItemError(f"Parser {prioritized_parser} for template "
                                   f"key {prioritized_parser.template.key} "
                                   "tells us this item is invalid and should "
                                   "be ignored.") from e

    # Pop the helper data references again:
    result.pop('references')

    # Retain the parsed result in memory, for reuse:
    storage.db[dbr_file] = result

    return result
Beispiel #7
0
    def parse(self, dbr, dbr_file, result):
        tag = dbr.get(self.NAME, None)

        if not tag or texts.get(tag) == tag:
            logging.warning(f'No tag or name for set found in {dbr_file}.')
            raise InvalidItemError(f'No tag or name for set found in {dbr_file}.')

        result.update({
            # Prepare the list of set items
            'items': [],
            'name': texts.get(tag),
            'tag': tag,
        })

        # Add the set members:
        for set_member_path in dbr['setMembers']:
            # Parse the set member:
            try:
                set_member = DBRParser.parse(set_member_path)
            except InvalidItemError as e:
                logging.debug(f"Could not parse set member {set_member_path} "
                              f"in {result['name']}. {e}")
                continue

            # Some sets are templates that don't have actual members
            # like (xpack3/items/set/set(00.dbr))
            if 'tag' not in set_member:
                continue

            # Add the tag to the items list:
            result['items'].append(set_member['tag'])

        # Skip any sets that have no members
        if len(result['items']) == 0:
            raise InvalidItemError(f"ItemSet {dbr_file} has no members.")

        # The number of set bonuses is equal to the number of set items minus 1
        bonus_number = len(result['items']) - 1

        # Because this parser has the lowest priority, all properties will
        # already have been parsed, so they can now be reconstructed to match
        # the set bonuses. Begin by initializing the properties for each set
        # bonus tier to an empty dict:
        properties = [{} for i in range(bonus_number)]

        # Insert the existing properties by adding them to the correct tier:
        for field, values in result['properties'].items():
            if not isinstance(values, list):
                properties[bonus_number - 1][field] = values

                # Don't parse any further
                continue

            # The starting tier is determined by the highest tier
            starting_index = bonus_number - len(values)

            # Now just iterate and add the properties to each tier:
            for index, value in enumerate(values):
                properties[starting_index + index][field] = value

        # Now set the tiered set bonuses:
        result['properties'] = properties

        # Pop off the first element of the properties, if it's empty:
        if len(result['properties']) > 1:
            if not result['properties'][0]:
                result['properties'].pop(0)