Artifact generation - what exactly do you care about?

Collapse
X
 
  • Time
  • Show
Clear All
new posts

  • hyperdex
    replied
    Thanks for the link, Derakon. It looks like I was going down the same path that you've already traversed. ;-) The key difference it that my example is more like a tree, where yours is more flat.

    <Edit: I removed a bug report that turned out to be wrong. I should have tried it out before complaining...>

    Leave a comment:


  • Derakon
    replied
    Have a link to the current ItemAllocator code. The lines from 35-54 generate the table; 56-65 select an item.

    Leave a comment:


  • hyperdex
    replied
    On a related note, how are the AllocationTables generated now? As an example, I am looking at 3.4.1's artifact.txt, and I can't really see how the numbers there translate into commonness values...

    My best guess is that the 'A:' line's first value is used, but the range of this is only 1-50, and that doesn't seem to provide enough of a range. As an example, is it really the case that on dungeon levels 20-100, the Phial is only 40 times more likely to be generated than Ringil? This seems low to me.

    By the way, RTFS (read the source) is a completely legitimate response to this, though if someone could point me to the right file(s) that would be appreciated. ;-)

    Leave a comment:


  • hyperdex
    replied
    I've been thinking about this a bit more, and though it's probably not directly useful I have some pseudocode that looks nice and might be useful...

    Code:
    import random
    
    class AllocationTable(object):
      def __init__(self, *pairs):
        self.pairs = pairs
        self.weight = sum([x[0] for x in pairs])
    
      def generate(self):
        x = random.randint(0, self.weight)
        for (weight, gen) in self.pairs:
          x = x - weight
          if x<0:
            return gen.generate()
    
      def __rmul__(self, c):
        return AllocationTable((c, self))
    
      def __add__(self, other):
        return AllocationTable(*(self.pairs + other.pairs))
    
    
    class Leaf(AllocationTable):
      def __init__(self, tval=0, sval=0, e_idx=None, a_idx=None):
        AllocationTable.__init__(self, (1, self))
        self.tval = tval
        self.sval = sval
        self.e_idx = e_idx
        self.a_idx = a_idx
    
      def generate(self):
        return (self.tval, self.sval, self.e_idx, self.a_idx)
    These objects allow us to easily manipulate allocation tables. An allocation table is internally represented as a lit of pairs. The first element of the pair is a weight (commonness) and the second is either a leaf (which generates a given object or artifact) or another allocation table. The __rmul__ and __add__ methods allow us to do things like the following...

    Code:
    objects = AllocationTable(*[(X.weight, Leaf(X.tval, X.sval)) for X in OBJECTS])
    artifacts = AllocationTable(*[(A.weight, Leaf(a_idx=A.idx)] for A in ARTIFACTS])
    
    normal = 399*objects + 1*artifacts
    and this implements Magnate's distribution using a 1/400 chance of generating an artifact. Note that to generate objects with this table, you call normal.generate().

    Suppose we wanted to implement a distribution where 1/5 of the time you get a book. You could do this:

    Code:
    books = AllocationTable(*[(X.weight, Leaf(X.tval, X.sval) for X in OBJECTS if X.tval==90])
    skewed = 1*books + 4*normal
    
    return skewed.generate()
    it is easy to see how this could be extended further. Note that we still have to account for dungeon level here.

    Dave

    Leave a comment:


  • Magnate
    replied
    Originally posted by Derakon
    Theoretically we could move those into procs as well, though it'd probably be more complicated.

    The main reason why the engine shouldn't have to know about artifacts is because it doesn't have to know about artifacts. In other words, as long as I can envision a reasonable way to do things such that the engine doesn't have to care about them, I would prefer they be done that way. Keep the engine simple and flexible; move the complexity into the procs (which perhaps ought to be renamed to "game scripts" at this point).
    Ok. I'm sorry to show such ignorance about this, it's not something I've ever come across in my limited coding experience. Once I get comfortable with procs, I'll happily use them wherever they make sense - including affixes and themes, if that's worth the trouble.
    Why must that be wrong? Just because a proc only triggers under specific circumstances doesn't mean that those circumstances can't come up all the time. And remember, the LootTemplate that the proc is attached to is already modifying that allocation table; just think of the proc as being able to do modifications that are more flexible than the LootTemplate was originally written to handle.
    Well yes, but this begs the question of why have loot templates at all, why not just have an 'on item generation' proc that deals with everything loot templates deal with, so the game just picks an item factory from the table, makes the item and thinks it's done. I'm assuming that at some point doing that becomes more trouble than it's worth, and it bothers me that I can't see a clear line between worthwhile and not. I don't see how looking in every possible container of items every time we want to put an artifact into an allocation table can be a sensible approach, but I guess we have to try it and see.
    I don't think that either of the proposed generation methods tied artifact generation to base item generation. We're well passed the days when the game would generate a base item, then try to turn that item into an artifact (so that e.g. to get Ringil you'd first have to get the game to generate a longsword). The only way in which artifact generation is "tied" to base item generation is in that they're in the same allocation table, so making one more likely makes the other less likely.
    I think fizzix and buzzkill meant something a little different by "tied". If you think of item generation as a decision tree, method #1 was a straight choice between an artifact and a non-artifact as the first decision. Whatever happened afterwards, there was no further connection (tie) between artifacts and non-artifacts.

    Method #2 puts them in the same allocation table and uses the same code to pick from it, so the tree paths don't diverge until all the allocation table creation decisions have been made, and the picking decisions. Only then are the two distinct.

    I think that's what they might have meant, anyway ;-)

    Leave a comment:


  • Derakon
    replied
    Originally posted by Magnate
    But in the meantime, why is it important that the engine should not know that artifacts exist? It knows about affixes and themes, so why not artifacts? Or would you ultimately like affix and theme application to be done by procs too?
    Theoretically we could move those into procs as well, though it'd probably be more complicated.

    The main reason why the engine shouldn't have to know about artifacts is because it doesn't have to know about artifacts. In other words, as long as I can envision a reasonable way to do things such that the engine doesn't have to care about them, I would prefer they be done that way. Keep the engine simple and flexible; move the complexity into the procs (which perhaps ought to be renamed to "game scripts" at this point).

    One particular reason for this question is about item naming. Artifact rings and amulets don't follow the same naming conventions as non-artifacts, and I'm struggling to see how to name things properly if the game must remain ignorant of artifact-ness. I can think of lots and lots of ways of sorting out item naming to be consistent, but every single one requires that the game have an "isArtifact" test. I'm guessing the solution is a proc ? :-)
    Yep! A proc that generates the name for the item is the solution here, with the fallback method being to use util.getGrammaticalName().

    Another thing that I think is causing me difficulties is how often a proc is used. Isn't the whole point of procs to trigger stuff under specific circumstances, like an item effect? So to use a proc to implement artifactChance, *every* time we create an allocation table, seems like it must be the wrong method.
    Why must that be wrong? Just because a proc only triggers under specific circumstances doesn't mean that those circumstances can't come up all the time. And remember, the LootTemplate that the proc is attached to is already modifying that allocation table; just think of the proc as being able to do modifications that are more flexible than the LootTemplate was originally written to handle.

    Originally posted by buzzkill
    I agree with this. I don't see any reason to tie artifact generation chance to base item generation.
    I don't think that either of the proposed generation methods tied artifact generation to base item generation. We're well passed the days when the game would generate a base item, then try to turn that item into an artifact (so that e.g. to get Ringil you'd first have to get the game to generate a longsword). The only way in which artifact generation is "tied" to base item generation is in that they're in the same allocation table, so making one more likely makes the other less likely.

    Leave a comment:


  • buzzkill
    replied
    Originally posted by fizzix
    ... an artifact is more than just a more powerful version of a weapon.
    I agree with this. I don't see any reason to tie artifact generation chance to base item generation.

    Leave a comment:


  • Magnate
    replied
    Originally posted by Derakon
    I'm fine with this but would prefer it be handled in a proc instead of in the game engine directly. I feel rather strongly that the engine should not need to know that artifacts exist (much as it has no clue about unique monsters).
    I'm sure that when I understand procs properly and can write and amend them with ease, I'll never look back.

    But in the meantime, why is it important that the engine should not know that artifacts exist? It knows about affixes and themes, so why not artifacts? Or would you ultimately like affix and theme application to be done by procs too?

    One particular reason for this question is about item naming. Artifact rings and amulets don't follow the same naming conventions as non-artifacts, and I'm struggling to see how to name things properly if the game must remain ignorant of artifact-ness. I can think of lots and lots of ways of sorting out item naming to be consistent, but every single one requires that the game have an "isArtifact" test. I'm guessing the solution is a proc ? :-)

    Another thing that I think is causing me difficulties is how often a proc is used. Isn't the whole point of procs to trigger stuff under specific circumstances, like an item effect? So to use a proc to implement artifactChance, *every* time we create an allocation table, seems like it must be the wrong method.
    Last edited by Magnate; December 21, 2012, 13:41.

    Leave a comment:


  • Magnate
    replied
    Originally posted by fizzix
    In general you might want it. For example, you can imagine someone wanting to drop a prayer book if the character is a priest, a magic book if it's a mage, and a weapon if it's a warrior. Angband, and most games, are ok with dropping items that certain classes can't use. But you can imagine that a game designer might want to eliminate all unusable items.
    Surely the easiest way of doing this is to have a loot template for each class, containing just the interesting stuff. You could even have a series of them "priest great", "priest good", "priest meh" type of thing.

    Leave a comment:


  • fizzix
    replied
    Originally posted by Derakon
    Presumably this would be for generating class-appropriate rewards for performing certain deeds?
    In general you might want it. For example, you can imagine someone wanting to drop a prayer book if the character is a priest, a magic book if it's a mage, and a weapon if it's a warrior. Angband, and most games, are ok with dropping items that certain classes can't use. But you can imagine that a game designer might want to eliminate all unusable items.

    Leave a comment:


  • Derakon
    replied
    Originally posted by fizzix
    Allowing the game to specify drops based on the player's class is something we should definitely support. How hard is that to do?
    First thought on how you'd do this: make a LootTemplate that accepts everything that any class cares about, with an "allocation table creation" proc that then filters things down based on the player's class. That proc would probably be pretty messy (though there's nothing preventing procs from having their own data files that they load when needed!), but it'd get the job done.

    Presumably this would be for generating class-appropriate rewards for performing certain deeds?

    Leave a comment:


  • fizzix
    replied
    Allowing the game to specify drops based on the player's class is something we should definitely support. How hard is that to do?

    Leave a comment:


  • Derakon
    replied
    Originally posted by Magnate
    I like the idea of using hyperdex's idea to get the best of both worlds. If I understand correctly it will be quite simple: if artifactChance is an absolute percentage, we multiply all non-artifact commonnesses by (100 / artifactChance - 1) * sum(artifact commonnesses) and multiply all artifact commonnesses by sum(nonartifact commonnesses). That should be doable. Derakon - would that be acceptable to you? Fizzix and I would like it!
    I'm fine with this but would prefer it be handled in a proc instead of in the game engine directly. I feel rather strongly that the engine should not need to know that artifacts exist (much as it has no clue about unique monsters). Proc-izing loot templates would also enable the "holy items" template that Estie suggested.

    To go into more detail, we'd add a mapping of proc triggers to Proc instances to the LootTemplate class. In the construction of the ItemAllocator's item table, we would have the following triggers:

    * allocation table creation: this is where we prevent already-generated artifacts from being regenerated.
    * allocation table selection: this is where we prevent an artifact from being selected twice (when re-using Allocators).
    * allocation table scaling: this proc would accept the entire table as an argument and would re-scale it based on the items in it (so that e.g. if you want a table that is merely biased towards, say, spellbooks while still allowing other items, you could just multiply their commonnesses; you could also implement artifactChance here).

    At appropriate points in the generation of the ItemAllocator, we would check the LootTemplate for these triggers and call them if they exist.

    "allocation table creation" is also where we would implement smart item filters that can't be handled by the normal LootTemplate. This is where you'd handle the "holy" template's more tricky bits.

    We might also want to have a proc trigger that fires immediately before the item would normally be generated and would have some final veto power (forcing the entire generation process to try again). I don't know if that's actually needed but I could believe it'd come in handy.

    Leave a comment:


  • Magnate
    replied
    Originally posted by Estie
    What happens if we introduce themed objects; for example, call all blessed weapons, holy avengers and apropriate artifacts "holy". Now if we, say, want to give priests/priest pits a higher chance to drop a "holy" item than normal, which of the 2 suggested methods would make that easier ?
    I've spent weeks creating the most flexible system I possibly could, and within half a day somebody comes up with something I hadn't thought of.

    At the moment you can specify a 'holy' loot template that includes blessed/pious items, HAs and the like. We can give priest-type monsters a chance of dropping an item from that template instead of (or as well as) a normal drop. That meets your suggestion except for one detail: there's currently no way of increasing the commonness of a specific artifact. So the answer to your question is that neither makes any odds.

    Ho hum. It seems a niche case but I'll think about it. I don't think, even if it's implementable, that it would make any difference which artifact generation method is used.

    Thanks for the debate folks - it's Derakon's baby so I went and re-did it Derakon's way. I like the idea of using hyperdex's idea to get the best of both worlds. If I understand correctly it will be quite simple: if artifactChance is an absolute percentage, we multiply all non-artifact commonnesses by (100 / artifactChance - 1) * sum(artifact commonnesses) and multiply all artifact commonnesses by sum(nonartifact commonnesses). That should be doable. Derakon - would that be acceptable to you? Fizzix and I would like it!

    Leave a comment:


  • Estie
    replied
    What happens if we introduce themed objects; for example, call all blessed weapons, holy avengers and apropriate artifacts "holy". Now if we, say, want to give priests/priest pits a higher chance to drop a "holy" item than normal, which of the 2 suggested methods would make that easier ?

    Leave a comment:

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