Pyrel dev log, part 4

Collapse
X
 
  • Time
  • Show
Clear All
new posts

  • Kinematics
    replied
    Thinking about it some more, it could actually be possible to fully abstract away the adjective names, and just depend on slices of the AFFIX_TYPES. However it definitely requires an OrderedDict sequence, which I haven't gotten working yet. Aside from the problem I had with returning the value from the original read, if I define AFFIX_TYPES as an OrderedDict before using it, it still seems to generate things in a random order. Also, need to add a State type as the first entry.

    Leave a comment:


  • Magnate
    replied
    Originally posted by Kinematics
    Hmm. I believe a small metal shield had a different base defense rating than a small leather shield. Are you wanting to get rid of that distinction? Though even if you do, there's no reason a variant might not want it back, so I'd still be inclined to handle it. I could see it being a lower priority, though, or handled in a different manner.
    I already did get rid of both metal and leather. In Pyrel's object.txt, the base shields are, in ascending order of AC: Buckler, Small Shield, Large Shield, Kite Shield, Tower Shield, Bulwark. I am now thinking that we need to replace "small" and "large" because we should really avoid any adjectives in base item names.

    Leave a comment:


  • Kinematics
    replied
    Originally posted by Magnate
    You're looking at the original V base item names. In v4 I changed them so that no base item names contained words for materials (e.g. leather, metal), so that they didn't cause conflicts with affixes.
    Hmm. I believe a small metal shield had a different base defense rating than a small leather shield. Are you wanting to get rid of that distinction? Though even if you do, there's no reason a variant might not want it back, so I'd still be inclined to handle it. I could see it being a lower priority, though, or handled in a different manner.

    Originally posted by Magnate
    I think we just need more words for shields.
    Such as that.

    Originally posted by Magnate
    The number of any type of prefix is governed by AFFIX_LIMITS - so yes, make and material are currently set at one each, but we cannot assume that they'll always be limited to one.
    Right. I started off writing it with the assumption of one each, but decided to just refactor it so that any of them could have any number of instances. That way the getAdjective() function works regardless.

    Originally posted by Magnate
    I don't like hard-coding affix types into naming code. It has to work with any permutation of affix types - including multiple affixes for make/material/quality, or none.
    I'd prefer avoiding it, but at this point I think it's necessary. It's also in the proc code level now, not the core, and the procs are specifically designed to do all sorts of custom work, and must therefore be aware of exactly what they're working on.

    Also, simple order isn't sufficient. The first two adjectives describe the first noun, the second two adjectives describe the second noun. If you have two adjectives, even if they're in the correct order, it's not possible to say which noun they belong to without knowing which type of adjective is being referenced.

    Also also, the order of the affix types doesn't include 'state', which I figure encompasses several of the examples Derakon brought up, though that's a lesser item since it can simply be considered as always the first entry.

    Originally posted by Magnate
    Surely if we order affix types correctly in the data file and use an orderedDict, we can abstract this?
    If you can figure a way to abstract this in a sensible manner, please do. This code is what I came up with today. Maybe I'll come up with a better idea tomorrow.

    Leave a comment:


  • Magnate
    replied
    This looks fine, apart from the hard-coding of make/material/quality/state. Surely if we order affix types correctly in the data file and use an orderedDict, we can abstract this?

    Leave a comment:


  • Magnate
    replied
    I don't like hard-coding affix types into naming code. It has to work with any permutation of affix types - including multiple affixes for make/material/quality, or none.

    Leave a comment:


  • Magnate
    replied
    You're looking at the original V base item names. In v4 I changed them so that no base item names contained words for materials (e.g. leather, metal), so that they didn't cause conflicts with affixes. I missed the fact that "small" is both a weapon prefix and an intrinsic part of the name of a type of shield. I like your primaryPrefix idea, I think. I think we just need more words for shields.

    The number of any type of prefix is governed by AFFIX_LIMITS - so yes, make and material are currently set at one each, but we cannot assume that they'll always be limited to one.

    Leave a comment:


  • Kinematics
    replied
    Sample code:
    grammar pastebin
    nameProc pastebin

    grammar file is greatly simplified (and I remapped it to pretty much what I described above). It just puts together the bits provided, and pluralizes the possible nouns.

    nameProc ended up being fairly heavyweight to adapt to all the possible options, though simple cases (ie: no modifiers) should pass through with only a handful of checks. It also binds explicitly to the known types for the various prefixes, since I figure that information should be considered 'knowable' at the proc level. However that can be adjusted as long as the general concept still fits the grammar restrictions (two adjectives per noun, two nouns per phrase, and a couple postfix values).

    It has has several \todo's left in it (ie: problems that need fixed; can read to see what they are), and doesn't pull flavored items subtypes yet, since I wasn't sure where that info would end up. Also set a few values to dummy values since the nameInfo structure doesn't exist yet.

    Should probably be fairly easy to pass in a function call for setting state as part of the params initializer, for variants that want that. Then you can just set state with a call to that function and not have to duplicate the main functionality.

    It re-integrates Magnate's idea of using genus for situations with multiple affixes of a given type in order to reduce the potential name length. (aside: To do that it uses a bit of code I found for constructing a proper groupby, instead of the built-in version.)


    Also, for testing, I added this function to item.py:
    Code:
        def testDescription(self):
            import procs.nameProc
            test = procs.nameProc.DefaultItemNamer(None, None, None)
            testOutput = test.trigger(self)
            stopName = u""
    And this code to gentown.py:
    Code:
        r = things.items.itemLoader.makeItem(('ring', 'Slaying'), 80, curMap, (8, 5))
        affix = things.items.itemLoader.getAffix("Strength")
        affix.applyAffix(10, r)
        affix = things.items.itemLoader.getAffix("Resist Fire")
        affix.applyAffix(10, r)
        affix = things.items.itemLoader.getAffix("Resist Cold")
        affix.applyAffix(10, r)
        r.testDescription()
    Wasn't up for trying to make the proc actually provide the requested name to the UI. Seemed to keep causing an infinite loop while debugging since the debugger itself wanted to know the result of the function while it was running. Wasn't worth the hassle to fix at this point.
    Last edited by Kinematics; March 2, 2013, 09:45.

    Leave a comment:


  • Kinematics
    replied
    To clarify on the earlier point about the names vs positions, the signature of the function could be written as:

    Code:
    def getFulItemName(mainNoun, mainAdjective1 = None, mainAdjective2 = None, altNoun = None, altAdjective1 = None, altAdjective2 = None,
                       simplePostfix = None, fullPostfix = None, quantity = 1):
    where:
    altAdjective1 = state
    altAdjective2 = quality
    altNoun = primaryPrefix
    mainAdjective1 = material
    mainAdjective2 = make
    mainNoun = baseName
    simplePostfix = name
    fullPostfix = type

    To abstract away the meaning from the words used. Main and alt noun can be pluralized, and everything else is written as provided.

    The procs would still have references to the make/material/etc, since they are explicitly designed for the purpose of interfacing with the external data. That is, since procs are expected to be written for the needs of the variant, it's ok for them to 'know' about material vs quality vs whatever.

    Leave a comment:


  • Kinematics
    replied
    Another look at affixes:

    prefixes: quality, material, make. Logically can only have one of each, right? Unless you have some sort of Mithril and Adamantine Chain Mail, or some such, so material could conceivably have multiples. Quality and make are definitely singlular only, though.

    However, how would that get applied? Suppose we have a small shield, with material=mithril. A Mithril Small Shield? Or a Small Mithril Shield? The latter actually makes more sense.

    Of course we don't have just "a small shield", but rather "a small metal shield" (assuming mithril can only be applied to the metal shield, not to the leather one). In that case, "a mithril small metal shield" or "a small mithril shield"?

    Also, here, "small" seems to hold the same position as "set of" does for gloves. Technically, I suppose, there's no reason you couldn't have a single glove armor item (eg: Michael Jackson's "Rhinestone Glove", as opposed to "a Set of Rhinestone Gloves"). So I guess 'collectionType' isn't really the right name for it.

    We could also note that a flavor is really just a randomly generated (but persistent per game) material or color. You could also say that the colors for potions could be considered 'materials', in a sense, so 'flavor' is just 'randomly generated material' that has no effect on the object.

    Multihued Dragon Scale is a theme that has a prefix position. It can apply to hard boots. "A Multihued Dragon Scale Pair of Hard Leather Boots"? Or "A Pair of Multihued Dragon Scale Hard Leather Boots"? Or "A Pair of Multihued Dragon Scale Boots"?

    I'm thinking that the element: "a [pair of] hard leather boots", "a [small] metal shield" should be considered its own field -- primaryPrefix

    A theme's 'type' (for prefix themes) is defined by the majority type of its contained prefix-position affixes (eg: Multihued Dragon Scale contains 5 dragon scale affixes, which are prefix type 'material' (to fix: also has 4 affixes which are redundant with the dragon scale affixes), so it would be a 'material' type theme). 'Blassed' and 'Dwarven' would be type 'quality'.


    Prefix values:

    Material:
    Then: "a pair of [hard leather] boots", "a small [metal] shield" -- defaultMaterial
    defaultMaterial can then be replaced by a material affix or theme.
    Material goes after the primary prefix, even with no defaultMaterial.

    Quality:
    A [light] broadsword is simple enough.
    A [light] pair of hard leather boots, or a pair of light hard leather boots? A [light] small shield, or a small [light] shield?
    Quality is something that appears to best be placed before the primary prefix.

    Make:
    A [gnomish] shovel, a [dunadan] broadsword
    Make should go after material. EG: a mithril [dunadan] broadsword

    State:
    A [glowing] pair of hard leather boots (eg: boots are available for activation), a [singed] Walnut Staff of Teleport
    Something that can be inserted by proc.



    Initial proc called will collect information and pass it to the function that constructs the name. It then calls the function to put it all together. Custom procs can be written to handle determining object's state, individual values based on experience, etc., before passing the results to the final constructor function (or writing a custom constructor function, if needed).


    getNameProc(item)
    make decisions about what values to pass in -- flavor for material, theme for quality, etc

    The actual construction function is then extremely simple:
    Code:
    def getFulItemName(baseNoun, primaryPrefix = None, state = None, quality = None, material = None, make = None, name = None, type = None, quantity = 1):
        phrase = u""
    
        if state is not None:
            phrase += u"%s " % state
    
        if quality is not None:
            phrase += u"%s " % quality
    
        if primaryPrefix:
            pluralPrefix = makePlural(primaryPrefix, quantity)
        else:
            pluralPrefix = None
    
        if pluralPrefix is not None:
            phrase += u"%s " % pluralPrefix
    
        if material is not None:
            phrase += u"%s " % material
    
        if make is not None:
            phrase += u"%s " % make
    
        phrase += u"%s" % makePlural(baseNoun, quantity)
    
        if name is not None:
            if name[0].isalpha():
                phrase += u" of %s " %name
            else:
                phrase += u" %s " %name
        elif type is not None:
            phrase += u" %s" % type
    
        article = getArticle(phrase, quantity, name is not None)
    
        phrase = u"%s %s" %article %phrase
    
        return phrase
    'type' is the collection of suffixes, the building of which happens elsewhere.

    Basically, the top level proc makes all the decisions about what value to put in which parameters, and then calls this to put it all together.

    To an extent, this seems to hardcode certain value types in place, which may not be desirable. However that's really not the case. It's more that these are names that conveniently describe the -positions- where certain words can appear, and named to make that positioning understandable. You're also free to do things like use conjunctions in the values passed in (eg: "Mithril and Adaman") without affecting this part of the code.
    Last edited by Kinematics; March 2, 2013, 00:38.

    Leave a comment:


  • Kinematics
    replied
    Ok, definitely makes sense there.

    Leave a comment:


  • Derakon
    replied
    Some potential reasons to hand naming off to a Proc:

    * Items that can get partially damaged (e.g. a "singed Walnut Staff of Teleport" vs. a "blackened Scroll titled 'xyz' of Phase Door"). SAngband has a similar mechanic, I believe. The engine doesn't know what elements there are nor how to map various kinds of elemental damage to different item types.

    * Items that can only be used under certain conditions (e.g. when standing in water, or during the daytime, etc.) could have their names modified to reflect their availability.

    * Equipment that gains experience when it is used -- several variants (mostly derivations of ToME, I believe) have artifacts that can gain experience and learn new powers, and their experience and level is displayed in the item's name. Absolutely the engine should not know or care about this.

    Anything that the engine doesn't know about must perforce be handled by a proc.

    Leave a comment:


  • Kinematics
    replied
    The immunity issue may actually not be as complicated as I thought. Such factors are generally a product of affixes, which means we need to check stat procs. So process can be:

    Get all categories that contain the flag you're looking for (eg: harmed by cold, harmed by fire, etc).
    Get all items that have any of the selected categories.
    Process items.
    During process, have to check secondary aspects due to affixes.
    A jacket with resist fire would throw up an immunity flag during processing, preventing damage.
    An artifact with a unique category would throw up an immunity flag during processing, preventing damage.
    An item with special properties (wooden shoes that aren't harmed by electricity, or something) can include that flag at the top level, which would be found during processing, preventing damage.


    Essentially, immunities all get tossed over to the processing stage to be worked out. The initial item query only cares about collecting all items which -might- be subject to the damage being inflicted. A cold attack while you're holding 2 potions would only have to fully process those two, rather than all items in inventory.

    This also deals with having conflicting categories (eg: having Harmed by Fire in one category, and Resists Fire in another category). Processing doesn't care about "harmed by", it only cares about immunities, or resists, takes extra damage, or whatever.

    Leave a comment:


  • Kinematics
    replied
    itemName (alternate):

    baseNoun: as above; singular; lowercase
    amulet, boots, et

    formatString: fully decorated string for handling formatting
    %f - flavor
    %p - plural
    EG:
    %f %p[Amulet~] of Charisma

    Code:
    outString = item.formatString
    if item.flavor:
        outString.replace("%f", item.flavor)
    else:
        outstring.replace("%f ", "")
    and similar code for re-processing plural form

    This is more flexible with potential unusual naming forms, but a little more complicated in code.

    Leave a comment:


  • Magnate
    replied
    Originally posted by Derakon
    I was thinking the "unique" aspect would actually be a separate aspect of the nameInfo dict -- either a shouldUseDefiniteArticle boolean or simply invoking a different name Proc altogether.
    Yes that's ok - I guess the same applies to compoundNoun ("Set of", "Pair of" etc.) being a separate member of nameInfo.
    In short, I think there's definitely room for gameplay-meaningful categories, as opposed to ones that are just used for deciding what kinds of items are generated or how to describe them.
    I don't disagree with this - but in that case "categories" need to move up from the nameInfo dict to become a top-level member of the item class, since you're storing more than just naming info. Very happy for binary flags to be easier to operate on - leaving just pval stuff in the stats instance.\
    I don't object in principle to this, but I'm not certain what we'd use it for. Do you have any reasonably concrete examples?
    Wizard mode. We need to list all the subtypes of (e.g.) sword together. Either you have to rely on using orderedDict (to use the category that follows sword), or you use a basename. The latter feels better to me.

    Leave a comment:


  • Kinematics
    replied
    Originally posted by Derakon
    First off, given the myriad different ways we may want to name things, we should be handing naming off to a set of Procs. These functions would be handed an item and possibly a context, and be told to come up with a name for them. Basically they'd implement Item.getShortDescription().
    Definitely needs to be some functions that are responsible for all this, though I'm not sure yet whether procs are the best way to manage it.

    The most obvious reason to use Procs here is to handle the artifact/normal-item split
    Not sure on that. It's (potentially) useful (see categories, below) to mark artifacts as being unique. There is little distinction in the coding needed between regular items and artifacts, so duplicating an entire function solely for artifacts seems a little silly, particularly when the information needed to construct that name needs to be in the item record anyway.


    nameInfo:

    baseNoun (or baseName) - fundamental name of the object; can be used in isolation; the noun that is modified by flavor
    EG: amulet; potion; boots; shoes; greaves
    Use example: You activate a Silver Ring of Ice. The ring glows with a bright blue light!
    -- Mark for pluralisation (eg: "potion~")

    collectionType - collection descriptor used against the base name, if appropriate
    EG: "set of" gloves, "pair of" boots, "suit of" chain mail
    -- Mark for pluralisation (eg: "Set~ of"); capitalize appropriately (ie: not "set~ of")

    specialName - the 'name' of an artifact
    EG: Galadriel (the Phial of Galadriel), 'Elessar' (the Amulet 'Elessar')
    -- If present, can use 'the' as the article


    Extra:
    flavor - cosmetic descriptor of an object (optional)
    EG: silver amulet; red potion; mahogany staff; pair of white leather boots; pair of striated greaves
    -- Note: not included in nameInfo iteself, but referenced when constructing the name


    Procs:
    GetItemName() - properly cased full description (though limited in character length)
    GetItemNoun() - lowercase baseNoun, singular

    others...
    GetMonsterName()?
    ~~ really not convinced on setting the name call as a proc.. Can you clarify more on how you think it would be more useful than item.GetName()? The addition of the nameInfo and categories dictionaries to the item record seems to obsolete any uses.


    Categories:

    I really like the basic idea. Gets rid of the artificial restrictions of only being able to have a type and subtype to describe the item's characteristics.

    I agree that "harmed by xxx" probably doesn't belong there. Every single potion is going to be harmed by cold, so it would be more appropriate to flag the category "potion" as being harmed by cold. If an item is an exception to a category rule, they can include specific flags on the item to override (or if there's a significant subset of, say, "hardened potions", they can get their own category).

    From that, may be able to strip category-specific flags from most items, assuming that those flags are (almost) always present on items of a given category. EG: If all unique items can't be harmed by the elements, put those flags on the unique category, rather than on each individual item.


    The question is how to query it.

    A cold attack hits the player. I want all items that are harmed by cold. Options:

    Top-level flag: query each item for item.hasFlag(HARMED_BY_COLD)

    Direct category: query each item for HARMED_BY_COLD in item.categories

    Indirect category 1: get all categories that have the HARMED_BY_COLD flag; query each item for override flag, then each of those categories. Clumsy.

    Indirect category 2: query each item for item.hasFlag(HARMED_BY_COLD); item function queries its categories for that flag

    Indirect category 3: get all categories that have the HARMED_BY_COLD flag; get all items that are in those categories; query each such item for an override; process all resulting items.


    Main problem: overriding a category's general properties. EG: a leather jacket of resist fire. A leather jacket would be in the soft armor category, so would be harmed by fire, but the specific jacket would have immunity to fire.

    Need more thought. Just posting what I've got so far.

    Leave a comment:

Working...
😀
😂
🥰
😘
🤢
😎
😞
😡
👍
👎