Pyrel dev log, part 4

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • fizzix
    Prophet
    • Aug 2009
    • 3025

    #46
    Ah the great fuzzy space where simplicity runs up into possibility. I'm a fan of simplicity even if it presents access to some possibilities. Now, I think Derakon's opinion is that it's fine for the game designer to impose such restrictions, but the engine need not do that. And I guess that's ok, but you then do run into the problem of an engine being forced to support all sorts of crazy schemes, many of which, no sane designer should ever choose (i.e. circular dependencies.)

    I'm also a little behind on the discussion, so I can't remember whether these things have already been said. Anyway.

    All stats can support the following modifiers.

    Addition to base stat.
    Multiplication to base stat + additions to 1
    Addition to stat.

    Obviously adding to the base stat is the most important, and that's probably what you'd get with potions. Your final result would be,

    (STR + base stat mod) * multiplicative mod + stat addition.

    For derived stats it works the same except the initial value is derived from the top tier stats, and then all the base modifiers.

    The only difficulty is that the player needs to understand the difference between base modifiers and normal modifiers. If that seems like a subtlety you don't want to require, it's easy to change by forcing all modifiers to be either base modifiers or post-multiplication modifiers.

    Comment

    • Kinematics
      Apprentice
      • Feb 2013
      • 72

      #47
      Ah, ok. fizzix shows two additional use cases for me.


      Case 5) A temporary effect that increases a stat by 20%, but only base and gear totals; it doesn't modify or get modified by other temporary effects. This would be well-suited for a tiered system.

      Case 6) A temporary effect that increases a stat by a fixed value (eg: str +10), but that isn't affected by other multipliers (either in gear or other temporary effects). This would also be very well suited for a tiered system.

      Each of those could be done with a non-tiered queryable system, but it would be a bit of extra work (sort of like the extra work you described for handling my example). The value of the tiering would be in being able to prevent certain types of mods from affecting each other.

      The next implied question would be, is the default use case to want to prevent certain mods from affecting each other, or is the default use case that all mods should be treated equally? For either choice, doing the other will be a bit more work, so which approach will likely be less extra work for the majority of designs?


      So tiering would allow for

      (A + b) * c + d

      or even

      ((A + b) * c + d) * e + f

      and so forth.


      What I was suggesting would only (by default) allow for

      (A + b) * c



      So, for example, the Vampire bonus -- supposing its intended design is that the nighttime bonus a vampire gets is outside of any multipliers gear might provide. Having a ring of +20% str would not increase a nighttime bonus from +10 to +12. -That- makes sense for a tiering system.

      The primary sticking point still seems to be the multipliers, though. Should multipliers affect values on the same tier, or only tiers below? (Also: can a trigger call produce a multiplier instead of an add? Probably doesn't need to, though, since it's given curVal as a parameter, which -should- include the total from all lower tiers (aside from the possible bug), so any multiplication can be done internally.)

      As long as you can define a mod's tier per item/effect/etc, then the designer can enforce a particular type of ordering. Leaving it as is, with multiplies only affecting tiers below, simply means you need to segregate tiers in the same way. If you simply make all adds Type 1 by default, and all multiplies Type 2 by default, then most operations would proceed normally. You could also add a vampire bonus that's Type 2, and thus outside most normal multipliers.


      Ok, tiering as a means of restricting circular references didn't make sense, but tiering as a means of ordering arithmatic operations does.

      I think the ability to query for mod types (separate from tier name) would still be useful (eg: affect all temporary buffs on tier 1; double all racial bonuses; etc). And the handling of recursive blocking could still be done such that you don't need to enforce a dependency chain.

      Comment

      • fizzix
        Prophet
        • Aug 2009
        • 3025

        #48
        Originally posted by Kinematics
        Ok, tiering as a means of restricting circular references didn't make sense, but tiering as a means of ordering arithmatic operations does.
        These seem to be equivalent to me. Is that what you're trying to say?

        When I'm thinking of stat systems, the one thing I'm looking at is game balance. Game balance is much easier if you only have additive values. However, you can get much richer results with both additive and multiplicative bonuses. Unfortunately this makes calculations harder for the game-player as well.

        Of course pyrel should support both. But the worry is somewhat mitigated by the fact that these are so damn hard to get right from the design perspective that most designers are likely to only choose additive values.

        Comment

        • Derakon
          Prophet
          • Dec 2009
          • 9022

          #49
          Originally posted by Kinematics
          The primary sticking point still seems to be the multipliers, though. Should multipliers affect values on the same tier, or only tiers below?
          As written, a given tier can only consider tiers of strictly lower value when calculating itself, since otherwise circular dependencies can result (assuming you don't use some other method to avoid them). As you noted, you can always just stick multiplier mods on a higher tier than additive mods.

          For what it's worth, the only reason that additive and multiplier mods exist as separate things from proc-based mods is because I expect that they will be the profound majority of modifiers in the game, to the extent that it's worth having some "syntactic sugar" to support them. You could do everything through Procs, but it would make the data files needlessly wordy. My expectation is that each StatMod instance will only do one of add, multiply, or "trigger" (as you put it).

          (Also: can a trigger call produce a multiplier instead of an add? Probably doesn't need to, though, since it's given curVal as a parameter, which -should- include the total from all lower tiers (aside from the possible bug), so any multiplication can be done internally.)
          Exactly. If you want to produce a multiplier in a proc, then you just calculate what the additive effect of the multiplier would be, and return that instead. Ultimately all stat mods are additive; there's just different rules for how the addition is performed.

          I think the ability to query for mod types (separate from tier name) would still be useful (eg: affect all temporary buffs on tier 1; double all racial bonuses; etc).
          I could see that being potentially helpful, yes. It could be done by having the Stats instance maintain a mapping of categories to lists of stat modifiers. Of course now every stat must have a descriptive category attached to it in addition to a tier, or else the lookup table is useless. But if you're going to co-opt the tiering system to categorize modifiers then you might as well use strings anyway.

          I guess my bottom line on all of this is that I'm a bit tired of working on stats (I'm also a bit tired, period -- I didn't sleep well last night). If someone is willing to take on the work to implement a better system (and can convince me that it's truly better) then they're welcome to do so, but I'm not about to tackle it myself; I have bigger fish to fry.

          Comment

          • Kinematics
            Apprentice
            • Feb 2013
            • 72

            #50
            Originally posted by fizzix
            Originally posted by Kinematics
            Ok, tiering as a means of restricting circular references didn't make sense, but tiering as a means of ordering arithmatic operations does.
            These seem to be equivalent to me. Is that what you're trying to say?
            No, they're not equivalent. Ordering operations is whether 1 + 2 * 3 is equal to 7 or 9; essentially, the ability to add parentheses to the calculations. Circular references is an algorithmic issue separate of that. Ordering can influence circular references, but circular references have no bearing on arithmatic order.


            Originally posted by Derakon
            For what it's worth, the only reason that additive and multiplier mods exist as separate things from proc-based mods is because I expect that they will be the profound majority of modifiers in the game, to the extent that it's worth having some "syntactic sugar" to support them. You could do everything through Procs, but it would make the data files needlessly wordy. My expectation is that each StatMod instance will only do one of add, multiply, or "trigger" (as you put it).
            Yep, makes sense.

            I could see that being potentially helpful, yes. It could be done by having the Stats instance maintain a mapping of categories to lists of stat modifiers. Of course now every stat must have a descriptive category attached to it in addition to a tier**, or else the lookup table is useless. But if you're going to co-opt the tiering system to categorize modifiers then you might as well use strings anyway.
            ** Do you mean stat mod? (though yes, each one would need both a tier and a category)

            EG: A ring of +10 Str and a ring of +20% str can have different tiers, correct? They're not forced to the same tier simply because both work on Str, or both are permanent effects, right?

            Though going back to your earlier example, it seems like you are still binding tiers to stats, which is why you'd have to create an entire duplicate set of stats to handle multiplication.

            If tiers are bound to mods, the filtering is trivial; just perform a list comprehension/filter on a given stat's collection of mods. However if tiers are bound to stats, you'd have to get not only the stat you're working on, but all variants on that stat that were created to handle the artificial tiering.



            As an aside, I finally got the whole python environment installed and working, so I'll be able to play around with things a bit more directly, as I have time.

            Comment

            • Derakon
              Prophet
              • Dec 2009
              • 9022

              #51
              Tiers are bound to StatMods. An overall Stats instance is, broadly speaking, a mapping of strings to lists of StatMods, which are sorted by tier. When you go to the Stats and ask "What is the value of the STR stat?", it looks up "STR" in its mapping, finds a list of StatMods, iterates over them in order by tier, and generates a value.

              Stats themselves (as entries in that mapping) have no inherent tier. If a designer decides that they want to tier the stats, then that's up to them; just use one set of tiers for StatMods that apply to one set of stats, and another set of tiers for a different set of StatMods that apply to a different set of stats.

              I'm glad to have more eyes looking at the code. Let me know if you have any questions. You might consider joining the #angband-dev IRC channel on freenode.net, too.

              Comment

              • Kinematics
                Apprentice
                • Feb 2013
                • 72

                #52
                Got Python working in Visual Studio, so now can do actual debugging.

                Filed first bug in Bitbucket.

                Redid the stats.py code. Very little actual change (and two of those changes are just renaming functions so that they make sense). Now I'm just trying to figure out how to create something that will test the functionality properly.


                Question: Should a StatMod object contain the stat it affects? It seems strange to be creating a mod of "+3" that isn't tied to anything, and then adding that mod to, say, STR. Seems a disconnect compared to creating a mod of "+3", type "STR" (ie: a ring of str +3).

                No real technical issues with either choice, just feels more awkward for the mod to not know what it's modifying, particularly for more complicated mods (eg: the ring that increases dex based on magic device skill, the vampire bonus from time of day, etc).



                Also noticed that the thing I thought was a bug wasn't a bug in the way I thought it was, but was still a bug in how it got constructed.

                Seems you sort the mods by tier when they're added to a stats object (had missed that), so when you get stats they're already in proper order and shouldn't have the 1/3/2/3/1 problem ordering. However when you get all the mods to add up you also append all children stat objects - each of which will be sorted - but don't sort the combined list. That leaves the possibility of a list with tiers of [parent] 1/1/1/2/2/2 [child] 1/1/2/2. The tier 2s from the parent won't properly include the tier 1s from the child.

                Fixed this by sorting the combined list before returning it, though it means extra code being run on every query. Maybe want to create a combined list that gets populated and sorted when adding children, or something.

                Wouldn't be so concerned if the uncompiled debug version wasn't so slooooooow. It takes over 20 seconds to load the initial window on an i7 CPU. Compiled version only takes about 3 seconds though, and optimizations aren't something to worry about at this stage anyway. Still annoying.

                Comment

                • Derakon
                  Prophet
                  • Dec 2009
                  • 9022

                  #53
                  Originally posted by Kinematics
                  Question: Should a StatMod object contain the stat it affects? It seems strange to be creating a mod of "+3" that isn't tied to anything, and then adding that mod to, say, STR. Seems a disconnect compared to creating a mod of "+3", type "STR" (ie: a ring of str +3).

                  No real technical issues with either choice, just feels more awkward for the mod to not know what it's modifying, particularly for more complicated mods (eg: the ring that increases dex based on magic device skill, the vampire bonus from time of day, etc).
                  As a general rule, if an object doesn't need to know something, then it shouldn't know it. Otherwise you end up having values that you aren't really using, which means you aren't actively ensuring they remain accurate. If someone later comes to rely on them being accurate, they could accumulate bugs easily...

                  In any event, StatMods should only ever be accessed via Stats, and the Stats instance will know what statistic the StatMod is modifying. So the information is accessible, just not immediately at the point you're looking at.

                  Also noticed that the thing I thought was a bug wasn't a bug in the way I thought it was, but was still a bug in how it got constructed.

                  Seems you sort the mods by tier when they're added to a stats object (had missed that), so when you get stats they're already in proper order and shouldn't have the 1/3/2/3/1 problem ordering. However when you get all the mods to add up you also append all children stat objects - each of which will be sorted - but don't sort the combined list. That leaves the possibility of a list with tiers of [parent] 1/1/1/2/2/2 [child] 1/1/2/2. The tier 2s from the parent won't properly include the tier 1s from the child.

                  Fixed this by sorting the combined list before returning it, though it means extra code being run on every query. Maybe want to create a combined list that gets populated and sorted when adding children, or something.
                  Oop, good catch. Thanks! Functionally this means that getMod() will ignore child modifiers sometimes. Sorting the list in getAllStatModsFor() seems like a reasonable first pass; it'll fix the problem. If it turns out to be slow (as indicated by profiling) then we can optimize it later.

                  Wouldn't be so concerned if the uncompiled debug version wasn't so slooooooow. It takes over 20 seconds to load the initial window on an i7 CPU. Compiled version only takes about 3 seconds though, and optimizations aren't something to worry about at this stage anyway. Still annoying.
                  That's a bit painful. Eventual release versions of Pyrel can ship with precompiled code so the compiler doesn't have to do anything on first launch. Of course this doesn't have anything to do with deciding when to sort lists. It's just a matter of generating all those .pyc files.

                  Welcome to the Pyrel devteam.

                  Comment

                  • Magnate
                    Angband Devteam member
                    • May 2007
                    • 5110

                    #54
                    Originally posted by Derakon
                    Welcome to the Pyrel devteam.
                    Zippedidoodah!
                    "Been away so long I hardly knew the place, gee it's good to be back home" - The Beatles

                    Comment

                    • Derakon
                      Prophet
                      • Dec 2009
                      • 9022

                      #55
                      The devteam has been having some discussion about the proper way to name things. Kinematic has put a lot of thoughts up on his pull request. Given that I initially gave him some bad advice, I thought it would be best to try to hash out the proper way to handle things here on the forums, where we can have a discussion that's a) more permanent than IRC, and b) easier to track than pullreq comments.

                      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().

                      (Actually, we may want namer Procs for other purposes too, e.g. to figure out how to pluralize enemies when we write "14 Snagas scream in agony" or the like)

                      The most obvious reason to use Procs here is to handle the artifact/normal-item split, where artifacts get a definite article and a name, while normal items get an indefinite article, a quantity, and typically a prefix/suffix (Foo Bar of Baz). So we'd have an ArtifactName Proc, and a NormalItemName Proc (or whatever we want to call them). But as noted earlier in the thread, there are a few more splits: flavored items use a different naming scheme (Flavor Basetype of Subtype), some items are represented as compound nones (e.g. Pairs of Soft Leather Boots), and we rapidly get into the whole affix system as well. There's also the question of how we store naming information in the item records, and what exactly are type and subtype for?

                      I'm just going to lay down some thoughts here and let you guys loose on 'em.

                      1) We can store arbitrary information in the item record. Let's say we add a new element to the record: "nameInfo". This element is a dictionary that holds whatever we need it to hold. In particular, this dict should tell us which Proc to use to generate a name for the item. All of the other entries in the dict can be passed to the Proc as parameters. So for example:
                      Code:
                      "nameInfo": {"proc": "normal item name", "pluralization": "[Pair|Pairs] of Soft Leather Boots"}
                      
                      leads to...
                      
                      class NormalItemNameProc(NameProc):
                          def trigger(self, item, pluralization):
                              if item.quantity == 1:
                                  name = generate singular name
                              else:
                                  ...
                              ...
                              return name
                      ItemFactory becomes responsible for assembling the nameInfo dictionary from templates, just as it does many other records. But we avoid having a proliferation of naming-related fields in the Item class, which is helpful especially as we may have wildly-variant naming systems depending on item type.


                      2) What are type and subtype for? I see two major reasons now: one is that affixes want a way to refer to classes of items without having to enumerate every item in the class (e.g. the "Dwarven" theme can be applied to any hard body armor). The other is to help the user learn what different symbols are, via the '/' command. But that latter gets badly bogged down in display info -- the '[' symbol won't even be displayed if you're using tiles, for example, so how should you know to do /[ to learn what it is? So for now I'm going to ignore that use case; I think it can be worked around via sufficient UI effort.

                      Instead, I suggest that we let each item record define what categories the item falls into, and each item can be in as many or as few categories as it wants. Instead of having "type" and "subtype" we can just have "categories: ["general category", "specific category"]". For example:

                      Augmented Chain Mail: hard armor, body armor, augmented chain mail
                      Dagger: sword, sharp weapon, dagger
                      Halberd: polearm, sharp weapon, halberd
                      Potion of Cure Light Wounds: potion, cure light wounds, harmed by cold, harmed by shards
                      Staff of Cure Light Wounds: staff, cure light wounds, harmed by fire

                      Then the affix system can just say "I want to apply to all items that are the intersection of [body armor, hard armor] except for Brigandine", or whatever. I suspect it already does something fairly similar but operates on type and subtype instead.

                      And as you note, this kind of system is ideal for specifying what kinds of items can be damaged by elemental effects. Just query the player's inventory for items intersecting the category "harmed by X" and deal damage to them.

                      I think that about does it for now. Thoughts?

                      Comment

                      • Magnate
                        Angband Devteam member
                        • May 2007
                        • 5110

                        #56
                        I think your arbitrarily extensible categorisation idea is a stroke of genius. We bin type and subtype altogether (yay!) and operate on categories. Brilliant. Presumably one of the categories will be for use of the definite article:

                        Phial of Galadriel: light source, phial, unique

                        But we need to be careful that we don't confuse or conflate naming categories with object flags. Harmed by Foo is a flag, I think.

                        I do think we need the nameInfo field to have something called shortName or baseName or something - so we can easily and simply refer to the kind without any clutter. This field would contain "potion", "scroll", "dagger", "greaves", "soft boots", "phial" etc. No article, plural marker or Pair/Set/Suit - just the shortest unambiguous name of the kind. Yes this will in most cases duplicate one of the category entries, but I think it's important to know which.

                        I don't think this is incompatible with any of your suggestions. For example, Pair of Greaves would be:

                        { "categories": ["armour", "boots", "greaves"],
                        "pluralisation": "Pair~ of Greaves",
                        "baseName": "greaves" }
                        "Been away so long I hardly knew the place, gee it's good to be back home" - The Beatles

                        Comment

                        • Derakon
                          Prophet
                          • Dec 2009
                          • 9022

                          #57
                          Originally posted by Magnate
                          I think your arbitrarily extensible categorisation idea is a stroke of genius. We bin type and subtype altogether (yay!) and operate on categories. Brilliant. Presumably one of the categories will be for use of the definite article:

                          Phial of Galadriel: light source, phial, unique
                          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.

                          But we need to be careful that we don't confuse or conflate naming categories with object flags. Harmed by Foo is a flag, I think.
                          It's interesting that you bring up flags, since they're basically the same thing -- binary stats. In the case of categories, the binary nature is determined by presence/absence in a grouping; in the case of flags, it's a +1 or 0 in the Stats instance. The important thing, when deciding which to use, is in how we plan to make use of the information. My thinking is that the game will generate a set of Containers, one for each category, so that you can ask for all the items in a given intersection of categories (e.g. all items in the player's inventory that are potions). But flags are hidden in the Stats instance, so it's much harder to query them. If you wanted "harmed by foo" to be a flag, then to find all the items in the player's inventory that are harmed by whatever then you'd need to manually iterate over the contents of the inventory, asking each item for its HarmedByFoo stat.

                          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 do think we need the nameInfo field to have something called shortName or baseName or something - so we can easily and simply refer to the kind without any clutter. This field would contain "potion", "scroll", "dagger", "greaves", "soft boots", "phial" etc. No article, plural marker or Pair/Set/Suit - just the shortest unambiguous name of the kind. Yes this will in most cases duplicate one of the category entries, but I think it's important to know which.
                          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? Assuming we did do this then the cleanest way to implement it would probably be to have the baseName automatically be inserted into the categories list, so you don't have to have a duplicate string in every object entry.

                          Comment

                          • Kinematics
                            Apprentice
                            • Feb 2013
                            • 72

                            #58
                            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.

                            Comment

                            • Magnate
                              Angband Devteam member
                              • May 2007
                              • 5110

                              #59
                              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.
                              "Been away so long I hardly knew the place, gee it's good to be back home" - The Beatles

                              Comment

                              • Kinematics
                                Apprentice
                                • Feb 2013
                                • 72

                                #60
                                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.

                                Comment

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