Pyrel dev log

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Derakon
    Prophet
    • Dec 2009
    • 9022

    #16
    Originally posted by d_m
    As far as underlying implementation, I think a nice way to model this is to basically "give" the world to the effect instance every turn (or every place where the effect could end) and let it decide how it wants to do it. I think this is nice because it means the effect can do sanity checking (e.g. not just if the item is worn, but if the item still exists) rather than trying to do all that work in one gigantic "determine when to do callbacks for all effect types" mega-method.
    That does seem like a good idea from a bookkeeping perspective. My big concern would be handling message order, though. Presumably you'd do all effect messaging at a specific point during the world state update loop, and I suspect that would result in things like "You equip the Cloak of Acidproofing. The Kobold shaman is no longer slowed. Your items are shielded from acid!"

    Is targeting an intrinsic part of an effect? For instance, taking 3d6 fire damage might be a particular effect. It sounds like in your system the effect that targets one monster, versus a beam, versus a ball, versus "all evil creatures" would all be different effects.
    Targeting is an inherited trait that can be overridden. I tried to demonstrate that in my examples but I may have done a bad job. In short, an effect template can specify targeting, but so can an effect or even the object invoking the effect. That should reduce the degree of duplication.

    Actually what I should probably do is just make effects be templates. So you can write an "incomplete" effect that leaves key attributes out, and then inherit from it and fill in the missing bits. Or grab pieces of your favorite effects and combine them. Whatever.

    For the proof of concept I suggest you don't do any caching for now. In the long run you may want to but in the short term it will create lots of correctness bugs (IMO). And you may need less caching than you think.
    To state something more explicitly that I should have said earlier: each entity will have its "base stats", which are basically its permanent Mods; the "cached state" is the addition of all indefinite and temporary Mods to that permanent set. Not generating the cached set means that every time I want to determine if a fact is true of a given entity, I have to iterate over all of the Mods applied to that entity.

    I do agree that there's a lot of potential for bookkeeping errors here, but I think it's possible to set up a system where they just don't happen, and the benefit will be that working with that system will be far more pleasant.

    I guess I find this to be shoehorning a lot of different concepts (and game logic) into one system. Is permanent resistance unrelated to temporary resistance at the effects level? Do they get to share names? What would the temporary resist's name be?
    Okay, let's look at how resists work in specific. You can have:

    * Permanent resistance. You're a fire elemental or demon or something; it's just a part of who you are. You get a permanent effect registered in your entity record.
    * Indefinite resistance. You're wearing a Shield of Resist Fire. That shield has an onWield effect that applies resist fire to you.
    * Temporary resistance. You drank a potion of Resist Fire. Its onUse effect set a 30-turn timer that gives you resist fire for the duration.

    As far as defining things in the edit files, they'd all use the same basic effect ("apply resist fire"), but with different durations. Because of the hierarchical / inheritance-based approach to effect records, you can use as much or as little from an effect template as you like.

    Now, things get hairy here mostly because Angband has a special hack where permanent + temporary resistance is its own thing that gives added protection. I'll have to figure out how that works at some point. It may well be that temporary fire resistance will be its own thing that just happens to do the same thing that permanent/indefinite resistance does, only stacking.

    Sorry for being critical. Overall I am really excited about your work, I just feel like you're going down the same path that a lot of the Java/XML people did in the 90's and early 00's (trying to move program logic into data files). If the effects data files are going to be really heavyweight I'd want to be able to use more of a language to express them (sort of the way you could imagine a lisp programmer doing it) and otherwise I'd want to maybe build a bunch of simpler concepts that compose more naturally.
    I definitely get where you're coming from here. It's all too easy to go from data-driven programming to "data is the programming". My goal here is to make a system that cleanly covers everything we can currently do with items, both in Vanilla and in variants. However, a lot of that "everything" is going to be Python -- basically everything that either a) is referred to in a "proc" field, or b) cares about the values that Mods set on an entity, will be Python.

    In other words, the records in the edit files are just setting things up. They composite rules together, but those rules are all implemented only in the code. If you want new rules, you write new code.

    Originally posted by Nick
    Excerpt from my stream of consciousness:
    • This is the way Angband should be written
    • I have done no real object-oriented development
    • Angband is naturally OO
    • I really should learn some Python
    • I know C much better than any other language
    • I have 189100 lines of C code
    • plus edit files, pref files, help files...
    • and that's just in one variant
    • maybe I should have stayed in bed this morning
    Heh. Pyrel is currently at 1946 lines of Python, and if I do it properly it ought to be a reasonably pleasant introduction to the language. I've always wanted my projects to serve as good examples, as much as possible.

    Assuming that Pyrel does get finished, I'm assuming it will act as either Angband 4.0 or its own "variant" (depending on how much gameplay diverges; the hope is, not much). I very much doubt it'd be worthwhile to try porting over extant Angband variants to it, unless it enabled things that you really really wanted to do that current Angband simply isn't up to.

    Comment

    • Magnate
      Angband Devteam member
      • May 2007
      • 5110

      #17
      Originally posted by Derakon
      That does seem like a good idea from a bookkeeping perspective. My big concern would be handling message order, though. Presumably you'd do all effect messaging at a specific point during the world state update loop, and I suspect that would result in things like "You equip the Cloak of Acidproofing. The Kobold shaman is no longer slowed. Your items are shielded from acid!"
      I'm glad you're alive to this, because it's one of those potentially huge headaches, if you didn't think about it until later. Pyrel is already way beyond my understanding of code design so there is little I can offer, but making sure that the effects framework is aware of message ordering will be important. At some not too distant point it might be worth defining a hierarchy of message categories to help with this.
      Targeting is an inherited trait that can be overridden. I tried to demonstrate that in my examples but I may have done a bad job. In short, an effect template can specify targeting, but so can an effect or even the object invoking the effect. That should reduce the degree of duplication.
      In my thinking, all effects were going to have a 'target' field. Untargeted effects (like, say, Enlightenment) would have that field empty, and the code would just default to starting from the grid from which the effect originated (the 'who' in the current project() code). From what I understand of inheritance and so on, it looks like your framework would function in much the same way, dealing with a target if there is one while progressing seamlessly without.
      Actually what I should probably do is just make effects be templates. So you can write an "incomplete" effect that leaves key attributes out, and then inherit from it and fill in the missing bits. Or grab pieces of your favorite effects and combine them. Whatever.
      I got a bit confused about your exchange with d_m on data and code, because I missed the implications for how much would or wouldn't be in the edit files. But if it helps I could send you the edit file syntax I'd done for the effects rewrite I was planning for v4 (if I can find them - it was a few months ago now).
      To state something more explicitly that I should have said earlier: each entity will have its "base stats", which are basically its permanent Mods; the "cached state" is the addition of all indefinite and temporary Mods to that permanent set. Not generating the cached set means that every time I want to determine if a fact is true of a given entity, I have to iterate over all of the Mods applied to that entity.

      I do agree that there's a lot of potential for bookkeeping errors here, but I think it's possible to set up a system where they just don't happen, and the benefit will be that working with that system will be far more pleasant.
      I agree here - I don't share d_m's fear of correctness errors (because any that come up will be quite important bugs in the caching code, which will be well worth catching).
      * Permanent resistance. You're a fire elemental or demon or something; it's just a part of who you are. You get a permanent effect registered in your entity record.
      * Indefinite resistance. You're wearing a Shield of Resist Fire. That shield has an onWield effect that applies resist fire to you.
      * Temporary resistance. You drank a potion of Resist Fire. Its onUse effect set a 30-turn timer that gives you resist fire for the duration.
      I'd rename Permanent to Intrinsic, so that "Permanent" then refers to "Intrinsic or Indefinite", but otherwise I'm with you here. This is a good way to handle racial traits (both for monsters and player races).
      Now, things get hairy here mostly because Angband has a special hack where permanent + temporary resistance is its own thing that gives added protection. I'll have to figure out how that works at some point. It may well be that temporary fire resistance will be its own thing that just happens to do the same thing that permanent/indefinite resistance does, only stacking.
      This is an area where I don't think it's important to duplicate the current code exactly. I went to a lot of trouble to do so with the monster spells refactor, with some fairly horrible uses of random value structs, and I don't think it's worth it. Just make resists take a value instead of the current binary implementation, and make them stack sensibly (either additive or multiplicative, it doesn't matter because it's just a matter of balancing the item generation accordingly). So the entity's total resist is the sum/product of all its permanent and temporary sources (or of the highest of each source, if your rules engine does that instead).
      Assuming that Pyrel does get finished, I'm assuming it will act as either Angband 4.0 or its own "variant" (depending on how much gameplay diverges; the hope is, not much). I very much doubt it'd be worthwhile to try porting over extant Angband variants to it, unless it enabled things that you really really wanted to do that current Angband simply isn't up to.
      I very much hope that the single greatest feature of Pyrel, from a non-coder's point of view, will be that it can do all sorts of things that the current codebase can't! With that aim I'd encourage you to shoot for 99% replication of existing gameplay rather than 100%.

      Anyway, a few more scattered thoughts from further back:

      - I don't think it's wise to consider spells as OnUse effects. It would be helpful to allow them to be cast without any items if the designer so wanted it (psionics, for example). So they are sort-of "OnAction" effects instead.

      - one of the target types you didn't include is monster race - for banishment-type effects.

      - don't forget to think about saving throws. I really struggled with this - if you store the save rules in the effect, they have to be the same for all targets. Perhaps your inheritance stuff can help with this - allowing certain targets to override the default saving throw with an easier or harder one.

      - the "modifier amount" shouldn't be a simple signed integer - or at least, if it needs to be, there needs to be another field which holds a random value formula for the calculation of random amounts (e.g. the current variable high resists). There's also a whole can of worms about which factors get to influence the amount (caster skill, target level, etc. etc.) - as with saving throws, the inheritance system should make this easier.

      - I hope you're including detection/mapping among the things that this framework will deal with: each grid should know whether it's currently in LOS, previously been in LOS, fully detected or fuzzy-detected (as well as its light level and possibly other states) - then you can handle all of telepathy, mapping, enlightenment, lighting etc. etc. by creating effects which alter the states of grids. (The players *actual* LOS isn't an effect, but can be called after all effects have been processed for the turn, as a final update.)

      - please include an OnExpiry trigger, so that effects can be triggered by the expiry of other effects (see EF_SHROOM_SPRINTING, which currently has special-case code)

      - and of course OnHit for classic procs (and OnMiss for fumble effects).

      - don't forget to include a way of specifying the various PROJECT_ attributes, e.g. whether an effect terminates at a first or subsequent target, or jumps over them etc.

      Sorry this is a bit scatty - like d_m I am really excited by how far you've taken Pyrel in such a short time, and think it has huge potential to be the foundation for a new variant explosion of Python 'bands. Hope this is all constructive and helpful.
      "Been away so long I hardly knew the place, gee it's good to be back home" - The Beatles

      Comment

      • nppangband
        NPPAngband Maintainer
        • Dec 2008
        • 926

        #18
        Originally posted by Nick
        Excerpt from my stream of consciousness:
        • This is the way Angband should be written
        • I have done no real object-oriented development
        • Angband is naturally OO
        • I really should learn some Python
        • I know C much better than any other language
        • I have 189100 lines of C code
        • plus edit files, pref files, help files...
        • and that's just in one variant
        • maybe I should have stayed in bed this morning
        +1 on all that. But this might be time for us to "bite the bullet" and move in this direction. Of course, I am saying that without having the slightest idea how I could contribute, since the only two languages I knew are C and visual basic...and I guess there is that pascal class I took in high school.
        NPPAngband current home page: http://nppangband.bitshepherd.net/
        Source code repository:
        https://github.com/nppangband/NPPAngband_QT
        Downloads:
        https://app.box.com/s/1x7k65ghsmc31usmj329pb8415n1ux57

        Comment

        • Magnate
          Angband Devteam member
          • May 2007
          • 5110

          #19
          Originally posted by nppangband
          +1 on all that. But this might be time for us to "bite the bullet" and move in this direction. Of course, I am saying that without having the slightest idea how I could contribute, since the only two languages I knew are C and visual basic...and I guess there is that pascal class I took in high school.
          I am pretty sure you'd find python far easier to learn than C, and would be up and running with NPPPyrel in no time!
          "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

            #20
            Originally posted by Magnate
            I'm glad you're alive to this, because it's one of those potentially huge headaches, if you didn't think about it until later. Pyrel is already way beyond my understanding of code design so there is little I can offer, but making sure that the effects framework is aware of message ordering will be important. At some not too distant point it might be worth defining a hierarchy of message categories to help with this.
            By "hierarchy of message categories", are you saying something like "each message has a priority, and we display all enqueued messages in priority order"? That might work, but what we really want to ensure is that related messages show up together, and I'm having trouble seeing how we'd do that with a message prioritization scheme unless each message knows about the other messages that have already happened so it can position itself appropriately...and that sounds complicated.

            In my thinking, all effects were going to have a 'target' field. Untargeted effects (like, say, Enlightenment) would have that field empty, and the code would just default to starting from the grid from which the effect originated (the 'who' in the current project() code). From what I understand of inheritance and so on, it looks like your framework would function in much the same way, dealing with a target if there is one while progressing seamlessly without.
            That's the plan, yeah -- targets would be specified explicitly at some level, but most entities wouldn't need to specify the targets for their effects, because they'd be obtained through a template that they use instead. Just like how all body armor entries in object.txt don't need to specify that their equip slot is the "body" slot -- they get that from the "soft armor", "hard armor", and "dragon armor" templates.

            I got a bit confused about your exchange with d_m on data and code, because I missed the implications for how much would or wouldn't be in the edit files. But if it helps I could send you the edit file syntax I'd done for the effects rewrite I was planning for v4 (if I can find them - it was a few months ago now).
            We're sort of indirectly alluding to a semi-common problem in software development. You have a big system that the software developers maintain that encodes a bunch of rules, and then you have a separate part of the company that decides what the rules are. For example, the system might be calculating sales tax, and every time tax calculations are changed in some minor county somewhere in the country (sales tax in the States is hideously fine-grained that way), the legal team has to contact the developers and describe the change that needs to be made to the system. Sometimes they can be quite intricate, and communications is always a problem. The developers get sick of this, and/or they're too bored, so they decide "Hey, we'll make a system where the legal team can just make the changes to config files, and the engine will calculate the tax automatically!" Which sounds like a good idea, but they overdo it (and/or the initial problem they're trying to solve is too complex for this approach), and they end up basically writing their own programming language in the config files, which they then try to convince the legal team to try to use. But the legal team is a legal team, not a programming team, and besides the config "language" is a piece of crap.

            The short version of this is that you need to make your program exactly as abstracted as it needs to be, and no more. If it's insufficiently abstracted, then making minor changes becomes painful (imagine if every monster were defined in the code, with no monster.txt). However, if it's excessively abstracted, then making minor changes becomes painful again (because the abstraction introduces its own layers of complexity) and the engine becomes impossible to work with because it has to deal with all those abstractions.

            I'd rename Permanent to Intrinsic, so that "Permanent" then refers to "Intrinsic or Indefinite", but otherwise I'm with you here. This is a good way to handle racial traits (both for monsters and player races).
            Good idea.

            Anyway, a few more scattered thoughts from further back:

            - I don't think it's wise to consider spells as OnUse effects. It would be helpful to allow them to be cast without any items if the designer so wanted it (psionics, for example). So they are sort-of "OnAction" effects instead.
            My current plan for spells is that spellbooks will be containers that hold spell "items". So it's not that the spellbook will have an onUse effect, but that the spell in it will. In the sense that every spell is a conceptual item, then, spellcasting will work the same for psionics as for normal spellcasting. The big question with psionics is how to handle bookless casting. I admit to not having a good answer for that right now.

            - one of the target types you didn't include is monster race - for banishment-type effects.

            - please include an OnExpiry trigger, so that effects can be triggered by the expiry of other effects (see EF_SHROOM_SPRINTING, which currently has special-case code)

            - and of course OnHit for classic procs (and OnMiss for fumble effects).
            I'm not going to try to replicate every possible trigger right now -- that's detail-filling-in work. There will be room for those in the engine though. Adding a new trigger should just be a matter of adding new functions and then calling them at the appropriate times.

            - don't forget to think about saving throws. I really struggled with this - if you store the save rules in the effect, they have to be the same for all targets. Perhaps your inheritance stuff can help with this - allowing certain targets to override the default saving throw with an easier or harder one.

            - the "modifier amount" shouldn't be a simple signed integer - or at least, if it needs to be, there needs to be another field which holds a random value formula for the calculation of random amounts (e.g. the current variable high resists). There's also a whole can of worms about which factors get to influence the amount (caster skill, target level, etc. etc.) - as with saving throws, the inheritance system should make this easier.
            It's clear that various parameters for the effects will need to be able to take "arguments" of some kind so we can generate values the way we want to. For example, damage scaling with the user's device skill, level, hitpoints, etc. There's various more- or less-complicated methods that could be used here (anywhere from "adapt the "10+4d6M8" system" to "pass this string to eval()"); I'll need to spend some time evaluating my options.

            - I hope you're including detection/mapping among the things that this framework will deal with: each grid should know whether it's currently in LOS, previously been in LOS, fully detected or fuzzy-detected (as well as its light level and possibly other states) - then you can handle all of telepathy, mapping, enlightenment, lighting etc. etc. by creating effects which alter the states of grids. (The players *actual* LOS isn't an effect, but can be called after all effects have been processed for the turn, as a final update.)
            Some of this will be down to Terrain stuff, some of it to Item instances. Terrain instances (which include the basic empty floor) will include their light levels, and like other entities will need to track how much the player knows about them. Likewise, at some level we'll have to store "player knows what this thing is", "player knows where this thing is", and "player knows this thing exists" booleans. These will probably be properties that go all the way up to the basic Thing class, since I can see uses for "I know there's an X there, but not what it is precisely" for items, monsters, and terrain as well (c.f. traps).

            - don't forget to include a way of specifying the various PROJECT_ attributes, e.g. whether an effect terminates at a first or subsequent target, or jumps over them etc.
            My current assumption is that procs will invoke "bolt", "ball", "LOS", etc. handlers, which will then do the appropriate breakdown.

            Sorry this is a bit scatty - like d_m I am really excited by how far you've taken Pyrel in such a short time, and think it has huge potential to be the foundation for a new variant explosion of Python 'bands. Hope this is all constructive and helpful.
            I do want to emphasize that Pyrel is still more talk than action so far. There's only so much you can do in 2k lines of code! But I do appreciate all the feedback and support I've been getting from this forum. Hopefully Pyrel will soon be at the point where other people can start poking at it.

            Comment

            • Magnate
              Angband Devteam member
              • May 2007
              • 5110

              #21
              Originally posted by Derakon
              By "hierarchy of message categories", are you saying something like "each message has a priority, and we display all enqueued messages in priority order"? That might work, but what we really want to ensure is that related messages show up together, and I'm having trouble seeing how we'd do that with a message prioritization scheme unless each message knows about the other messages that have already happened so it can position itself appropriately...and that sounds complicated.
              Well, by 'hierarchy' I meant something like (off the top of my head):

              - initiation messages (entity X starts action Y - "it mumbles", "you activate it")
              - action messages (effect X reaches target Y - "something hits you", "the foo glows brightly")
              - reaction messages ("the critter shrieks in pain")
              - consequence messages (effect X affects target Y - "you feel yourself moving slower", "the foo is empty")
              - cleanup messages ("you combine some items in your pack", "the critter is no longer afraid")

              ... so I was envisaging a 'threading' mechanism where the same effect would have its messages threaded together and presented in that order, iterating over targets, unless it triggers another effect etc.
              The short version of this is that you need to make your program exactly as abstracted as it needs to be, and no more. If it's insufficiently abstracted, then making minor changes becomes painful (imagine if every monster were defined in the code, with no monster.txt). However, if it's excessively abstracted, then making minor changes becomes painful again (because the abstraction introduces its own layers of complexity) and the engine becomes impossible to work with because it has to deal with all those abstractions.
              Yes, I see that - thanks for the excellent illustration with the tax example.
              My current plan for spells is that spellbooks will be containers that hold spell "items". So it's not that the spellbook will have an onUse effect, but that the spell in it will. In the sense that every spell is a conceptual item, then, spellcasting will work the same for psionics as for normal spellcasting. The big question with psionics is how to handle bookless casting. I admit to not having a good answer for that right now.
              Your system should work providing one can have a non-object container for spells (@'s mind). As you say, the spell itself is the OnUse item, not the container.
              I'm not going to try to replicate every possible trigger right now -- that's detail-filling-in work. There will be room for those in the engine though. Adding a new trigger should just be a matter of adding new functions and then calling them at the appropriate times.
              That's great, but I hope this doesn't apply to effects themselves. My aim was to enable new effects to be created in edit files by defining their targets, range/duration and what they modify (be that knowledge, terrain, stats, etc.) Were you thinking along the same lines?
              It's clear that various parameters for the effects will need to be able to take "arguments" of some kind so we can generate values the way we want to. For example, damage scaling with the user's device skill, level, hitpoints, etc. There's various more- or less-complicated methods that could be used here (anywhere from "adapt the "10+4d6M8" system" to "pass this string to eval()"); I'll need to spend some time evaluating my options.
              Ok, that's fine - I wondered for a moment whether you were going to decide that effects shouldn't take variable arguments, that's all.
              Some of this will be down to Terrain stuff, some of it to Item instances. Terrain instances (which include the basic empty floor) will include their light levels, and like other entities will need to track how much the player knows about them. Likewise, at some level we'll have to store "player knows what this thing is", "player knows where this thing is", and "player knows this thing exists" booleans. These will probably be properties that go all the way up to the basic Thing class, since I can see uses for "I know there's an X there, but not what it is precisely" for items, monsters, and terrain as well (c.f. traps).
              That sounds fine, though I'm not sure I understand "other entities will need to track how much the player knows about them" - it reads as if "them" refers to "terrain instances" - if that's right, why wouldn't the terrain instances themselves track what the player knows about them?
              My current assumption is that procs will invoke "bolt", "ball", "LOS", etc. handlers, which will then do the appropriate breakdown.
              Right, this is exactly what I was trying to refactor out of the current codebase. The ball/bolt/los handlers are just wrappers for project(), so why not specify the details in the effect itself?
              I do want to emphasize that Pyrel is still more talk than action so far. There's only so much you can do in 2k lines of code! But I do appreciate all the feedback and support I've been getting from this forum. Hopefully Pyrel will soon be at the point where other people can start poking at it.
              Looking forward to that, but however long it takes you're to be congratulated on the clarity and thoroughness of your thinking - typing in the code is trivial by comparison.
              "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

                #22
                Originally posted by Magnate
                Well, by 'hierarchy' I meant something like (off the top of my head):

                - initiation messages (entity X starts action Y - "it mumbles", "you activate it")
                - action messages (effect X reaches target Y - "something hits you", "the foo glows brightly")
                - reaction messages ("the critter shrieks in pain")
                - consequence messages (effect X affects target Y - "you feel yourself moving slower", "the foo is empty")
                - cleanup messages ("you combine some items in your pack", "the critter is no longer afraid")

                ... so I was envisaging a 'threading' mechanism where the same effect would have its messages threaded together and presented in that order, iterating over targets, unless it triggers another effect etc.
                Hm, I'm still not quite understanding, but I think there'll be time enough to deal with this later.

                That's great, but I hope this doesn't apply to effects themselves. My aim was to enable new effects to be created in edit files by defining their targets, range/duration and what they modify (be that knowledge, terrain, stats, etc.) Were you thinking along the same lines?
                To the extent that a new effect doesn't require anything fundamentally different from an existing effect, they should be fully specifiable without modifying the Python code. So for example, if you have a "slow monster" effect, you can make new bolts/beams/balls/LOS/selected-target-species "slow monster" effects based on it. But you couldn't make an effect that slows only monsters that are at full health, because we don't have any existing effects that use that conditional.

                That sounds fine, though I'm not sure I understand "other entities will need to track how much the player knows about them" - it reads as if "them" refers to "terrain instances" - if that's right, why wouldn't the terrain instances themselves track what the player knows about them?
                What I meant is that every entity -- every Terrain, Item, and Creature instance -- needs to track what the player knows about it. Items need to know if the player knows their type, which runes have been ID'd, etc. Creatures need to know if the player knows their species and subspecies (assuming we do something like fuzzy telepathy). Terrain needs to know if the player has ever seen it or can see it right now.

                Right, this is exactly what I was trying to refactor out of the current codebase. The ball/bolt/los handlers are just wrappers for project(), so why not specify the details in the effect itself?
                I'll need to look at what project() does exactly -- it's not code I've ever really investigated -- but if I find myself writing lots of code that's basically "simple redirect" code (e.g. the "bolt" proc just calls project(BOLT) or whatever) then certainly that's the kind of thing that should be moved to the edit files.

                Comment

                • Magnate
                  Angband Devteam member
                  • May 2007
                  • 5110

                  #23
                  Originally posted by Derakon
                  To the extent that a new effect doesn't require anything fundamentally different from an existing effect, they should be fully specifiable without modifying the Python code. So for example, if you have a "slow monster" effect, you can make new bolts/beams/balls/LOS/selected-target-species "slow monster" effects based on it. But you couldn't make an effect that slows only monsters that are at full health, because we don't have any existing effects that use that conditional.
                  Yes, and doing that in edit files would get you to the making-your-own-language problem. But I was thinking of a slowing effect that only affected dragons, for example. We don't have exactly this effect at the moment, but if the code allows "all entities in LOS with RF_DRAGON" as a valid target, it should be possible to create it without any new code. That's the kind of thing I was getting at.
                  What I meant is that every entity -- every Terrain, Item, and Creature instance -- needs to track what the player knows about it. Items need to know if the player knows their type, which runes have been ID'd, etc. Creatures need to know if the player knows their species and subspecies (assuming we do something like fuzzy telepathy). Terrain needs to know if the player has ever seen it or can see it right now.
                  Yes, exactly. Great.
                  I'll need to look at what project() does exactly -- it's not code I've ever really investigated -- but if I find myself writing lots of code that's basically "simple redirect" code (e.g. the "bolt" proc just calls project(BOLT) or whatever) then certainly that's the kind of thing that should be moved to the edit files.
                  project() is the function that actually iterates over every grid in range and applies the effect to that grid - calling out to project_[m|p|o|f] if it needs to apply an effect to a monster, player, object or feature. The main reason I wanted to refactor effects in the first place was because of the limitations of this setup (e.g. the stuff I mentioned about variable amounts/durations/saving throws not being resolvable once you're inside project). I figured that moving the beam/bolt/LOS type determinants out to edit files would be better - but I'm happy to wait until you get there and see what you think.
                  "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

                    #24
                    Just a quick status update -- the first pass at a proper "equip item" command is done. Items go to their proper slots, and if another item's already there, it's unequipped. I haven't yet decided how to handle the prompting where there's ambiguities (e.g. trying to wield a ring when there's already two equipped rings); my current Prompt framework may need to be expanded. But I'm saving that for later -- let it percolate in my subconscious while I work on other things and maybe I'll have a good idea.

                    (Quick lesson on programming: if you've been working on the same problem for more than half an hour, or maybe even just fifteen minutes, and you're no closer to finding a solution, then stop working on it and do something else. You'll make no more progress by holding at it, and odds are good that other activities will jostle the appropriate neurons to guide you to a good solution)

                    Comment

                    • Gorbad
                      Apprentice
                      • Sep 2008
                      • 74

                      #25
                      Originally posted by Derakon
                      Hm, I'm still not quite understanding, but I think there'll be time enough to deal with this later.
                      Probably something like (forgive the not 100% correct Angband messages)

                      Code:
                      [
                          {"messages":{
                              "init":["it mumbles"],
                              "action":["something hits you"],
                              "result":["you feel your life draining away","you feel less powerful"],
                              "cleanup":["you die"],
                              },"actor":"monster_123"
                          },
                          {"messages":{
                              "init":"you activate the Bronze Wand",
                              "action":["it glows brightly"],
                              "reaction":["the snaga shrivels away in the light!","Ugluk the Uruk is unaffected!"],
                              "result":["the Bronze Wand of Light has no charges left","you no longer have the Bronze Wand of Light"],
                              "cleanup":["you combine some items in your pack"]
                              },"actor":"object7"
                          }
                      ]
                      Where you first buffer all the messages and can then iterate over the actors, and print their messages in order.

                      Comment

                      • Narvius
                        Knight
                        • Dec 2007
                        • 589

                        #26
                        The only sane way to handle multiple eligible equipment slots is likely to diplay a list of possible slots and let the user pick one. Especially once we consider monster races anything less specific starts to fall apart.
                        Even if we consider two ring slots, one occupied, one free, and the user equipping an additional one, automatically filling the free slot may not always be the desired behavior.
                        If you can convincingly pretend you're crazy, you probably are.

                        Comment

                        • Magnate
                          Angband Devteam member
                          • May 2007
                          • 5110

                          #27
                          Originally posted by Narvius
                          The only sane way to handle multiple eligible equipment slots is likely to diplay a list of possible slots and let the user pick one. Especially once we consider monster races anything less specific starts to fall apart.
                          Even if we consider two ring slots, one occupied, one free, and the user equipping an additional one, automatically filling the free slot may not always be the desired behavior.
                          I haven't seen the code yet but I'd hope it's flexible enough for a variant maintainer to define extra equipment slots in a single place (array, dictionary, whatever) ... and then define object types that can be wielded in those slots. Further down the road there will be display issues (I'm thinking less of the equipment screen than the character sheet), but I assume there won't be hard-coded references to slots anywhere in the game engine.
                          "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

                            #28
                            The plan, which isn't yet fully implemented, is that any time there's an ambiguity about where an item could be meaningfully equipped to, the user will be prompted for choice. That means that if an item can only go to one slot type, and the user only has one slot of that type (e.g. normal Angband and weapon slots), then the item is automatically equipped to that slot, displacing whatever may be there already. Likewise if there's only one eligible type and there's a free slot of that type, then the item automatically goes there (e.g. equipping your first ring). If all eligible slots are occupied, or there's multiple eligible slot types, then the user is prompted.

                            The only use cases I can think of for items that can go in multiple equipment slot types are bastard swords (one- or two-handed) and maybe shields (on the arm or strapped to your back), but that's enough that it seemed worth designing in from the start.

                            Originally posted by Magnate
                            I haven't seen the code yet but I'd hope it's flexible enough for a variant maintainer to define extra equipment slots in a single place (array, dictionary, whatever) ... and then define object types that can be wielded in those slots. Further down the road there will be display issues (I'm thinking less of the equipment screen than the character sheet), but I assume there won't be hard-coded references to slots anywhere in the game engine.
                            Equipment slots are defined entirely in the edit files -- you declare which slot types a creature has and how they're described, and items declare which slot types they can be equipped to. In the event that an item cares about which slot it is equipped to, that would probably be handled in code by a proc.

                            For example, the player's creature template:
                            Code:
                            {"name": "player",
                            "display": {"ascii": {"symbol": "@"}},
                            "category": "You",
                            "type": "player",
                            "equipDescToSlot": {
                                    "for melee": "weapon",
                                    "for ranged": "launcher",
                                    "on left finger": "finger",
                                    "on right finger": "finger",
                                    "around neck": "neck",
                                    "providing light": "light source",
                                    "on body": "body",
                                    "on back": "back",
                                    "providing cover": "shield",
                                    "on the noggin": "head",
                                    "on hands": "hands",
                                    "on feet": "feet",
                                    "holding ammo": "quiver"}
                            },
                            and the amulet template:
                            Code:
                            {"type": "amulet", "templateName": "amulet",
                            "display": {"ascii": {"color": "flavor", "symbol": "\""}},
                            "equipSlots": ["neck"],
                            "flags": ["FLAVORED"],
                            "weight": 0.3},
                            Status update: I'm still working out the best way to handle equipment that give bonuses to other equipment when wielded (and other similar effects). I need to refactor some code, too -- procs are going from being standalone functions to being class instances, so they can preprocess their arguments. I hope to get some of that done later today.

                            Comment

                            • Magnate
                              Angband Devteam member
                              • May 2007
                              • 5110

                              #29
                              Originally posted by Derakon
                              Equipment slots are defined entirely in the edit files -- you declare which slot types a creature has and how they're described, and items declare which slot types they can be equipped to. In the event that an item cares about which slot it is equipped to, that would probably be handled in code by a proc.
                              It's funny, I nearly said that I hoped equipment slots were entirely configurable in text files, then I remembered your previous post on the limits of that - but I'm really glad to see that they are.

                              Can you tell me what you mean by "proc"? Google's first result is the definition I use - http://www.wowwiki.com/proc. But I suspect you mean something more like the 5th result? A sort of multi-purpose function?
                              "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

                                #30
                                Originally posted by Magnate
                                Can you tell me what you mean by "proc"? Google's first result is the definition I use - http://www.wowwiki.com/proc. But I suspect you mean something more like the 5th result? A sort of multi-purpose function?
                                I'm using "proc" to refer to a set of functions that can be referred to (and parameterized) in the edit files, keyed to "go off" on various trigger conditions. So for example, if you had a D&D-style "flameburst" weapon (has a chance to deal 2d6 flame damage on striking), then the object entry would have an "onStrike" condition that invoked the "damageOpponent" proc with parameters "2d6" and "fire". "damageOpponent" would then be a function in the main code base that looks up the target of the attack and invokes some more generalized function to deal some amount of damage (in this case, 2d6) of some appropriate element (in this case, fire) to that target. That generalized function would take into account resistances and things of that nature.

                                The standard definition of "proc" that I'm aware of is like the flameburst enchant -- it's an effect that can occur when you hit something (in the case of weapons) or are hit by something (in the case of armor). I'm extending that to include more trigger conditions and to allow for non-instantaneous effects though.

                                The reason I need to refactor the procs to make them into class instances instead of straight-up functions is that some procs will have an effect that is variable on an item-to-item basis but constant for any given item. For example, Rings of Prowess have an onEquip proc that attaches a prowess bonus to a specific equipment slot; the bonus is constant for a specific ring but different rings give different bonuses. If I just left the proc as a straight function then each time you called it, it'd have to recalculate the bonus and would give a different result every time -- and of course, you wouldn't be able to tell what the bonus was without equipping it, since it wouldn't display properly in ID.

                                Comment

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