v50 Steam/Premium information for editors
  • v50 information can now be added to pages in the main namespace. v0.47 information can still be found in the DF2014 namespace. See here for more details on the new versioning policy.
  • Use this page to report any issues related to the migration.
This notice may be cached—the current version can be found here.

Editing User:Button/BAMM

Jump to navigation Jump to search

Warning: You are not logged in.
Your IP address will be recorded in this page's edit history.


The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.

Latest revision Your text
Line 19: Line 19:
 
Sometimes I'm on a computer I can't put a git client on.
 
Sometimes I'm on a computer I can't put a git client on.
  
<pre># Modders' guide to BAMM! graphics references
+
# Modders' guide to BAMM! graphics references
 
# Place your conversions in a file named bamm_gfx_<identifier here>.txt . The file should be encoded in codepage 437, the same as the Dwarf Fortress raws.
 
# Place your conversions in a file named bamm_gfx_<identifier here>.txt . The file should be encoded in codepage 437, the same as the Dwarf Fortress raws.
  
Line 88: Line 88:
 
else:
 
else:
 
# Get a collection of all the targets that meet the reference
 
# Get a collection of all the targets that meet the reference
</pre>
 
 
<pre>class TemplateNode(TreeNode):
 
"""A TreeNode tailored for holding graphics templates.
 
 
TemplateNodes store the patterns which define what tags are graphics tags, and which of their tokens are immutable, identifying, graphical, or irrelevant. Each TemplateNode represents the template for a single type of graphics tag.
 
 
Members:
 
* _tag is the template string. It is a series of tokens separated by colons. Each token is one of the following:
 
    * LITERAL. This is a string, usually in all caps, which must be present in this exact form for a tag to match this template. The first token in a template is always a literal. Enums (e.g. the PLANT_GROWTH timing values of ALL and NONE) may simulated by splitting a template into multiple templates, one for each enum, with the enum in question present as a literal.
 
* VARIABLE. This is a single character, '?' '$' or '&' (quotes not present in _tag).
 
* '?' indicates that this is a graphics token. When matching a tag to a template, this can match any value. When merging occurs, this token's value will come from the graphics source.
 
* '&' indicates that this is an info token. When matching a tag to a template, this can match any value. When merging occurs, this token's value will come from the raw source.
 
* '$' indicates that this is an identifier token. When matching a tag to a template, this can match any value; but for two tags to match each other, they must share the same values for all identifier tokens in their template.
 
* VARIABLE RANGE. This is a variable character, followed by a range in parentheses, e.g. ?(0,3). This means that the tag can contain 0-3 (inclusive) tokens in this position, all of which hold graphical information. The second value is optional - if omitted, it means there is no upper bound on how many tokens can be in this series.
 
For examples, please see graphics_templates.config . The string between each pipe ( | ) is a valid TemplateNode._tag .
 
* _children is a dict containing the TemplateNodes representing the types of tags which are allowed to be children of the type of tag represented by this TemplateNode. The keys are the full _tags of the child TemplateNodes.
 
* _childref is the same as _children, but indexed by the first token (the "value") of the child's _tag, instead of the full _tag. For convenience/performance only.
 
* _parent is the TemplateNode representing the type of tag that the type of tag this TemplateNode represents can be a child of.
 
* _is_graphics_tag is a boolean that lets us know if this tag is a graphics tag, as opposed to a template included because it can have graphics children. This is necessary because some graphics tags are composed of only a single literal token.
 
"""
 
 
    # string does not contain the character '|'.
 
    def __init__(self, parent, string=""):
 
""" Initializes a TemplateNode
 
 
Parameters:
 
* parent is the parent TemplateNode of this node, or None. If it is None, this TemplateNode will replace the current global template_tree.
 
* string is the template-formatted string which will be this TemplateNode's _tag.
 
 
After creating itself, if the parent isn't None, it adds itself to its parent.
 
"""
 
        TreeNode.__init__(self, parent)
 
        self._is_graphics_tag = False
 
        self._childref = {}
 
        self._tag = None
 
        global template_tree
 
        if parent is None:
 
            self._parent = None
 
            template_tree = self
 
        else:
 
            if template_tree is None:
 
                self._parent = TemplateNode(None, "")
 
            else:
 
                self._parent = parent
 
 
            self._tag = string
 
 
            parent.add_child(self)
 
 
 
    def is_standalone_tag(self):
 
""" Returns True if this is a tag without any non-graphical information. """
 
        return ('$' not in self._tag
 
                ) and '&' not in self._tag and self._is_graphics_tag
 
 
    def add_child(self, node):
 
""" Adds TemplateNode node as a child of this TemplateNode.
 
 
If a child with node's ._tag already exists, node will NOT be added.
 
 
Parameters:
 
* node is an existing TemplateNode.
 
 
Returns:
 
* The child now living at this TemplateNode._children[node._tag]. This may be the parameter
 
node, or it may be a preexisting TemplateNode with the same ._tag as node._tag .
 
"""
 
        if node._tag in self._children.keys():
 
            return self._children[node._tag]
 
        else:
 
            self._children[node._tag] = node
 
            first_token = node._tag.split(':')[0]
 
            if first_token not in self._childref.keys():
 
                self._childref[first_token] = []
 
            self._childref[first_token].append(node)
 
            return node
 
 
    def get_child(self, tag):
 
""" Returns the child node matching tag.
 
 
"Matching" in this case can mean two different things:
 
* If there is a child of this node indexed as tag, that is a match and that node will be returned.
 
* If not, we compare tag to the child templates of this template, using template-to-tag comparison logic as defined in TemplateNode.get_template_match.
 
** If more than one child matches tag on this second criterion, the best match is chosen by TemplateNode._get_best_match.
 
 
Parameters:
 
* tag is a a tag or template tag, which we want to compare to the children of this TemplateNode.
 
 
Returns:
 
* The best-matching TemplateNode which is a child of self, or None if no matching children were found.
 
"""
 
        if tag in self._children.keys():
 
            return self._children[tag]
 
        else:
 
            return_possibilities = []
 
            first_token = tag.split(':')[0]
 
            if first_token in self._childref:
 
                for child in self._childref[first_token]:
 
                    return_node = child.get_template_match(tag)
 
                    if return_node is not None:
 
                        return_possibilities.append((child, return_node))
 
                if len(return_possibilities) == 1:
 
                    return return_possibilities[0][0]
 
                elif len(return_possibilities) == 0:
 
                    return None
 
                else:
 
                    # TODO error handling
 
                    userlog.debug("Found more than one matching child. \
 
                                  Matching children are:")
 
                    for poss in return_possibilities:
 
                        userlog.debug(poss[1])
 
                    possible_tags = [a[1] for a in return_possibilities]
 
                    winner = TemplateNode._get_best_match(possible_tags)
 
                    return return_possibilities[possible_tags.index(winner)][0]
 
            else:
 
                return None
 
 
    # This tells if a single tag matches a single tag; that is, it assumes
 
    # we've got one element of the |-separated list
 
    def get_template_match(self, tag_to_compare):
 
""" Returns an expanded configuration of this template that describes/matches the given tag.
 
 
Parameters:
 
* tag_to_compare can be a tag, a template, or a partially-templatized tag. If it is a template, it must be in expanded configuration.
 
 
Returns:
 
* The fully-expanded configuration of self._tag which matches tag_to_compare, as a list of strings, each a token. If there are no matches, returns None. If there are multiple possible configurations, the configuration to return is chosen by _get_best_match.
 
"""
 
        if self._tag is None:
 
            return None
 
        template_token_bag = []
 
        template_token_bag.append(self._tag.split(':'))
 
        candidate_tokens = tag_to_compare.split(':')
 
 
        ii = 0
 
        while (len(template_token_bag) > 0 and
 
              (ii < len(candidate_tokens) or
 
                ii < len(template_token_bag[0]))):
 
            good_to_go = False
 
            for var in template_token_bag:
 
                if ii < len(candidate_tokens) and ii >= len(var):
 
                    template_token_bag.remove(var)
 
                elif ii >= len(var) and len(var) == len(candidate_tokens):
 
                    good_to_go = True
 
                elif (ii >= len(candidate_tokens) and
 
                      (ii >= len(var) or
 
                      (ii < len(var) and '(0,' not in var[ii]))):
 
                    template_token_bag.remove(var)
 
                elif ('&' == var[ii] or '?' == var[ii] or '$' == var[ii] or
 
                      (ii < len(candidate_tokens) and
 
                      var[ii] == candidate_tokens[ii])):
 
                    # This case is an auto-pass
 
                    good_to_go = True
 
                else:
 
                    if '&' in var[ii] or '?' in var[ii] or '$' in var[ii]:
 
                        varii_type = var[ii][0]
 
                        varii_range = var[ii][2:var[ii].index(')')].split(',')
 
                        # If len(varii_range) == 1 then we have a range of
 
                        # format (x,), indicating any number of :'s
 
                        if len(varii_range[1]) == 0:
 
                            varii_range[1] = len(candidate_tokens)-len(var) + 1
 
                        # For every possible length (the +1 is because range is
 
                        # exclusive-end and my notation is inclusive-end)
 
                        for jj in range(int(varii_range[0]),
 
                                        int(varii_range[1])+1):
 
                            # Make a copy of var
 
                            new_var = var[:]
 
                            # Remove the range item
 
                            del new_var[ii]
 
                            # Replace it with (one of the possible lengths)
 
                            # times the multiplied symbol
 
                            # If jj is 0 the range item is just removed
 
                            for kk in range(0, jj):
 
                                new_var.insert(ii, varii_type)
 
                            # Place the new variant in the token bag for
 
                            # evaluation
 
                            template_token_bag.append(new_var)
 
                    # No counting, there is a new template_token_bag[ii]
 
                    template_token_bag.remove(var)
 
            if good_to_go:
 
                ii += 1
 
        for tag in template_token_bag:
 
            if len(tag) != len(candidate_tokens):
 
                template_token_bag.remove(tag)  # This isn't working properly?
 
 
        if len(template_token_bag) == 0:
 
            return None
 
        elif len(template_token_bag) == 1:
 
            return template_token_bag[0]
 
        else:
 
            highest_priority = TemplateNode._get_best_match(template_token_bag)
 
            userlog.debug("More than one template matched.\nTag: %s Matches:",
 
                          tag_to_compare)
 
            for template in template_token_bag:
 
                userlog.debug("\t%s", template)
 
            userlog.debug("The highest-priority match is %s", highest_priority)
 
            # Technically this does in fact have a matching template
 
            return highest_priority
 
 
    @staticmethod
 
    def _get_best_match(template_tokens_bag):
 
""" Chooses between different templates which both have been found to match a tag.
 
 
It chooses based on the number of tokens which are NOT wildcards. The principle is that a template which matches a tag with fewer wildcards is probably a better match, because matches on wildcards are cheap.
 
 
Parameters:
 
* template_tokens_bag is a list of fully-expanded templates, with each template configured as a list of tokens.
 
 
Returns:
 
* the list in template_tokens_bag with the least wildcards. If there is a tie on the number of wildcards, it returns the earliest one in the list.
 
"""
 
        if not template_tokens_bag:        # empty bag returns false
 
            return None
 
        elif len(template_tokens_bag) == 1:
 
            return template_tokens_bag[0]
 
        else:
 
            best_currently = template_tokens_bag[0]
 
            best_tokens = 0
 
            for tag in template_tokens_bag:
 
                challenger_tokens = 0
 
                for token in tag:
 
                    if token != '?' and token != '&' and token != '$':
 
                        challenger_tokens = challenger_tokens + 1
 
                if challenger_tokens > best_tokens:
 
                    best_currently = tag
 
                    best_tokens = challenger_tokens
 
            return best_currently
 
 
    def how_many_generations(self):
 
""" Returns how deep into the template tree this node lives."""
 
# TODO consider moving this into TreeNode
 
        temp_node = self
 
        count = -1
 
        global template_tree
 
        while temp_node != template_tree:
 
            temp_node = temp_node._parent
 
            count = count + 1
 
        return count</pre>
 

Please note that all contributions to Dwarf Fortress Wiki are considered to be released under the GFDL & MIT (see Dwarf Fortress Wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To protect the wiki against automated edit spam, we kindly ask you to solve the following CAPTCHA:

Cancel Editing help (opens in new window)